From 925a0617d27e7b042fba66e80edb0cedc0fa9fb8 Mon Sep 17 00:00:00 2001 From: Chad Froebel Date: Tue, 12 Feb 2013 22:04:41 -0500 Subject: [PATCH 001/111] block: Adding ROW scheduling algorithm This patch adds the implementation of a new scheduling algorithm - ROW. The policy of this algorithm is to prioritize READ requests over WRITE as much as possible without starving the WRITE requests. Change-Id: I4ed52ea21d43b0e7c0769b2599779a3d3869c519 Signed-off-by: Tatyana Brokhman block: ROW: Correct minimum values of ROW tunable parameters The ROW scheduling algorithm exposes several tunable parameters. This patch updates the minimum allowed values for those parameters. Change-Id: I5ec19d54b694e2e83ad5376bd99cc91f084967f5 Signed-off-by: Tatyana Brokhman block: ROW: Fix forced dispatch This patch fixes forced dispatch in the ROW scheduling algorithm. When the dispatch function is called with the forced flag on, we can't delay the dispatch of the requests that are in scheduler queues. Thus, when dispatch is called with forced turned on, we need to cancel idling, or not to idle at all. Change-Id: I3aa0da33ad7b59c0731c696f1392b48525b52ddc Signed-off-by: Tatyana Brokhman block: Add support for reinsert a dispatched req Add support for reinserting a dispatched request back to the scheduler's internal data structures. This capability is used by the device driver when it chooses to interrupt the current request transmission and execute another (more urgent) pending request. For example: interrupting long write in order to handle pending read. The device driver re-inserts the remaining write request back to the scheduler, to be rescheduled for transmission later on. Add API for verifying whether the current scheduler supports reinserting requests mechanism. If reinsert mechanism isn't supported by the scheduler, this code path will never be activated. Change-Id: I5c982a66b651ebf544aae60063ac8a340d79e67f Signed-off-by: Tatyana Brokhman block: Add API for urgent request handling This patch add support in block & elevator layers for handling urgent requests. The decision if a request is urgent or not is taken by the scheduler. Urgent request notification is passed to the underlying block device driver (eMMC for example). Block device driver may decide to interrupt the currently running low priority request to serve the new urgent request. By doing so READ latency is greatly reduced in read&write collision scenarios. Note that if the current scheduler doesn't implement the urgent request mechanism, this code path is never activated. Change-Id: I8aa74b9b45c0d3a2221bd4e82ea76eb4103e7cfa Signed-off-by: Tatyana Brokhman row: Adding support for reinsert already dispatched req Add support for reinserting already dispatched request back to the schedulers internal data structures. The request will be reinserted back to the queue (head) it was dispatched from as if it was never dispatched. Change-Id: I70954df300774409c25b5821465fb3aa33d8feb5 Signed-off-by: Tatyana Brokhman block:row: fix idling mechanism in ROW This patch addresses the following issues found in the ROW idling mechanism: 1. Fix the delay passed to queue_delayed_work (pass actual delay and not the time when to start the work) 2. Change the idle time and the idling-trigger frequency to be HZ dependent (instead of using msec_to_jiffies()) 3. Destroy idle_workqueue() in queue_exit Change-Id: If86513ad6b4be44fb7a860f29bd2127197d8d5bf Signed-off-by: Tatyana Brokhman row: Add support for urgent request handling This patch adds support for handling urgent requests. ROW queue can be marked as "urgent" so if it was un-served in last dispatch cycle and a request was added to it - it will trigger issuing an urgent-request-notification to the block device driver. The block device driver may choose at stop the transmission of current ongoing request to handle the urgent one. Foe example: long WRITE may be stopped to handle an urgent READ. This decreases READ latency. Change-Id: I84954c13f5e3b1b5caeadc9fe1f9aa21208cb35e Signed-off-by: Tatyana Brokhman block: row: Add some debug information on ROW queues 1. Add a counter for number of requests on queue. 2. Add function to print queues status (number requests currently on queue and number of already dispatched requests in current dispatch cycle). Change-Id: I1e98b9ca33853e6e6a8ddc53240f6cd6981e6024 Signed-off-by: Tatyana Brokhman block: row: Insert dispatch_quantum into struct row_queue There is really no point in keeping the dispatch quantum of a queue outside of it. By inserting it to the row_queue structure we spare extra level in accessing it. Change-Id: Ic77571818b643e71f9aafbb2ca93d0a92158b199 Signed-off-by: Tatyana Brokhman block: row: fix sysfs functions - idle_time conversion idle_time was updated to be stored in msec instead of jiffies. So there is no need to convert the value when reading from user or displaying the value to him. Change-Id: I58e074b204e90a90536d32199ac668112966e9cf Signed-off-by: Tatyana Brokhman block: row: Aggregate row_queue parameters to one structure Each ROW queues has several parameters which default values are defined in separate arrays. This patch aggregates all default values into one array. The values in question are: - is idling enabled for the queue - queue quantum - can the queue notify on urgent request Change-Id: I3821b0a042542295069b340406a16b1000873ec6 Signed-off-by: Tatyana Brokhman block: fixes required to make the kernel compile with ROW. --- Documentation/block/row-iosched.txt | 117 ++++ block/Kconfig.iosched | 22 + block/Makefile | 1 + block/blk-core.c | 70 ++- block/blk-settings.c | 12 + block/blk.h | 11 + block/elevator.c | 40 ++ block/row-iosched.c | 790 ++++++++++++++++++++++++++++ include/linux/blkdev.h | 6 + include/linux/elevator.h | 7 + 10 files changed, 1074 insertions(+), 2 deletions(-) create mode 100644 Documentation/block/row-iosched.txt create mode 100644 block/row-iosched.c diff --git a/Documentation/block/row-iosched.txt b/Documentation/block/row-iosched.txt new file mode 100644 index 00000000000..987bd883444 --- /dev/null +++ b/Documentation/block/row-iosched.txt @@ -0,0 +1,117 @@ +Introduction +============ + +The ROW scheduling algorithm will be used in mobile devices as default +block layer IO scheduling algorithm. ROW stands for "READ Over WRITE" +which is the main requests dispatch policy of this algorithm. + +The ROW IO scheduler was developed with the mobile devices needs in +mind. In mobile devices we favor user experience upon everything else, +thus we want to give READ IO requests as much priority as possible. +The main idea of the ROW scheduling policy is: +If there are READ requests in pipe - dispatch them but don't starve +the WRITE requests too much. + +Software description +==================== +The requests are kept in queues according to their priority. The +dispatching of requests is done in a Round Robin manner with a +different slice for each queue. The dispatch quantum for a specific +queue is defined according to the queues priority. READ queues are +given bigger dispatch quantum than the WRITE queues, within a dispatch +cycle. + +At the moment there are 6 types of queues the requests are +distributed to: +- High priority READ queue +- High priority Synchronous WRITE queue +- Regular priority READ queue +- Regular priority Synchronous WRITE queue +- Regular priority WRITE queue +- Low priority READ queue + +If in a certain dispatch cycle one of the queues was empty and didn't +use its quantum that queue will be marked as "un-served". If we're in a +middle of a dispatch cycle dispatching from queue Y and a request +arrives for queue X that was un-served in the previous cycle, if X's +priority is higher than Y's, queue X will be preempted in the favor of +queue Y. This won't mean that cycle is restarted. The "dispatched" +counter of queue X will remain unchanged. Once queue Y uses up it's quantum +(or there will be no more requests left on it) we'll switch back to queue X +and allow it to finish it's quantum. + +For READ requests queues we allow idling in within a dispatch quantum in +order to give the application a chance to insert more requests. Idling +means adding some extra time for serving a certain queue even if the +queue is empty. The idling is enabled if we identify the application is +inserting requests in a high frequency. + +For idling on READ queues we use timer mechanism. When the timer expires, +if there are requests in the scheduler we will signal the underlying driver +(for example the MMC driver) to fetch another request for dispatch. + +The ROW algorithm takes the scheduling policy one step further, making +it a bit more "user-needs oriented", by allowing the application to +hint on the urgency of its requests. For example: even among the READ +requests several requests may be more urgent for completion then others. +The former will go to the High priority READ queue, that is given the +bigger dispatch quantum than any other queue. + +ROW scheduler will support special services for block devices that +supports High Priority Requests. That is, the scheduler may inform the +device upon urgent requests using new callback make_urgent_request. +In addition it will support rescheduling of requests that were +interrupted. For example, if the device issues a long write request and +a sudden high priority read interrupt pops in, the scheduler will +inform the device about the urgent request, so the device can stop the +current write request and serve the high priority read request. In such +a case the device may also send back to the scheduler the reminder of +the interrupted write request, such that the scheduler may continue +sending high priority requests without the need to interrupt the +ongoing write again and again. The write remainder will be sent later on +according to the scheduler policy. + +Design +====== +Existing algorithms (cfq, deadline) sort the io requests according LBA. +When deciding on the next request to dispatch they choose the closest +request to the current disk head position (from handling last +dispatched request). This is done in order to reduce the disk head +movement to a minimum. +We feel that this functionality isn't really needed in mobile devices. +Usually applications that write/read large chunks of data insert the +requests in already sorted LBA order. Thus dealing with sort trees adds +unnecessary complexity. + +We're planing to try this enhancement in the future to check if the +performance is influenced by it. + +SMP/multi-core +============== +At the moment the code is acceded from 2 contexts: +- Application context (from block/elevator layer): adding the requests. +- Underlying driver context (for example the mmc driver thread): dispatching + the requests and notifying on completion. + +One lock is used to synchronize between the two. This lock is provided +by the underlying driver along with the dispatch queue. + +Config options +============== +1. hp_read_quantum: dispatch quantum for the high priority READ queue +2. rp_read_quantum: dispatch quantum for the regular priority READ queue +3. hp_swrite_quantum: dispatch quantum for the high priority Synchronous + WRITE queue +4. rp_swrite_quantum: dispatch quantum for the regular priority + Synchronous WRITE queue +5. rp_write_quantum: dispatch quantum for the regular priority WRITE + queue +6. lp_read_quantum: dispatch quantum for the low priority READ queue +7. lp_swrite_quantum: dispatch quantum for the low priority Synchronous + WRITE queue +8. read_idle: how long to idle on read queue in Msec (in case idling + is enabled on that queue). +9. read_idle_freq: frequency of inserting READ requests that will + trigger idling. This is the time in Msec between inserting two READ + requests + diff --git a/block/Kconfig.iosched b/block/Kconfig.iosched index 3199b76f795..3f6e6cc7520 100644 --- a/block/Kconfig.iosched +++ b/block/Kconfig.iosched @@ -21,6 +21,17 @@ config IOSCHED_DEADLINE a new point in the service tree and doing a batch of IO from there in case of expiry. +config IOSCHED_ROW + tristate "ROW I/O scheduler" + default y + ---help--- + The ROW I/O scheduler gives priority to READ requests over the + WRITE requests when dispatching, without starving WRITE requests. + Requests are kept in priority queues. Dispatching is done in a RR + manner when the dispatch quantum for each queue is calculated + according to queue priority. + Most suitable for mobile devices. + config IOSCHED_CFQ tristate "CFQ I/O scheduler" # If BLK_CGROUP is a module, CFQ has to be built as module. @@ -53,6 +64,16 @@ choice config DEFAULT_DEADLINE bool "Deadline" if IOSCHED_DEADLINE=y + config DEFAULT_ROW + bool "ROW" if IOSCHED_ROW=y + help + The ROW I/O scheduler gives priority to READ requests + over the WRITE requests when dispatching, without starving + WRITE requests. Requests are kept in priority queues. + Dispatching is done in a RR manner when the dispatch quantum + for each queue is defined according to queue priority. + Most suitable for mobile devices. + config DEFAULT_CFQ bool "CFQ" if IOSCHED_CFQ=y @@ -64,6 +85,7 @@ endchoice config DEFAULT_IOSCHED string default "deadline" if DEFAULT_DEADLINE + default "row" if DEFAULT_ROW default "cfq" if DEFAULT_CFQ default "noop" if DEFAULT_NOOP diff --git a/block/Makefile b/block/Makefile index 0fec4b3fab5..824ae82cd20 100644 --- a/block/Makefile +++ b/block/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_BLK_CGROUP) += blk-cgroup.o 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_ROW) += row-iosched.o obj-$(CONFIG_IOSCHED_CFQ) += cfq-iosched.o obj-$(CONFIG_BLOCK_COMPAT) += compat_ioctl.o diff --git a/block/blk-core.c b/block/blk-core.c index 64cc22daac7..0b62cf8f2a0 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -296,13 +296,26 @@ EXPORT_SYMBOL(blk_sync_queue); * Description: * See @blk_run_queue. This variant must be called with the queue lock * held and interrupts disabled. + * Device driver will be notified of an urgent request + * pending under the following conditions: + * 1. The driver and the current scheduler support urgent reques handling + * 2. There is an urgent request pending in the scheduler + * 3. There isn't already an urgent request in flight, meaning previously + * notified urgent request completed (!q->notified_urgent) */ void __blk_run_queue(struct request_queue *q) { if (unlikely(blk_queue_stopped(q))) return; - q->request_fn(q); + if (!q->notified_urgent && + q->elevator->elevator_type->ops.elevator_is_urgent_fn && + q->urgent_request_fn && + q->elevator->elevator_type->ops.elevator_is_urgent_fn(q)) { + q->notified_urgent = true; + q->urgent_request_fn(q); + } else + q->request_fn(q); } EXPORT_SYMBOL(__blk_run_queue); @@ -929,6 +942,50 @@ void blk_requeue_request(struct request_queue *q, struct request *rq) } EXPORT_SYMBOL(blk_requeue_request); +/** + * blk_reinsert_request() - Insert a request back to the scheduler + * @q: request queue + * @rq: request to be inserted + * + * This function inserts the request back to the scheduler as if + * it was never dispatched. + * + * Return: 0 on success, error code on fail + */ +int blk_reinsert_request(struct request_queue *q, struct request *rq) +{ + if (unlikely(!rq) || unlikely(!q)) + return -EIO; + + blk_delete_timer(rq); + blk_clear_rq_complete(rq); + trace_block_rq_requeue(q, rq); + + if (blk_rq_tagged(rq)) + blk_queue_end_tag(q, rq); + + BUG_ON(blk_queued_rq(rq)); + + return elv_reinsert_request(q, rq); +} +EXPORT_SYMBOL(blk_reinsert_request); + +/** + * blk_reinsert_req_sup() - check whether the scheduler supports + * reinsertion of requests + * @q: request queue + * + * Returns true if the current scheduler supports reinserting + * request. False otherwise + */ +bool blk_reinsert_req_sup(struct request_queue *q) +{ + if (unlikely(!q)) + return false; + return q->elevator->elevator_type->ops.elevator_reinsert_req_fn ? true : false; +} +EXPORT_SYMBOL(blk_reinsert_req_sup); + static void add_acct_request(struct request_queue *q, struct request *rq, int where) { @@ -1972,8 +2029,17 @@ struct request *blk_fetch_request(struct request_queue *q) struct request *rq; rq = blk_peek_request(q); - if (rq) + if (rq) { + /* + * Assumption: the next request fetched from scheduler after we + * notified "urgent request pending" - will be the urgent one + */ + if (q->notified_urgent && !q->dispatched_urgent) { + q->dispatched_urgent = true; + (void)blk_mark_rq_urgent(rq); + } blk_start_request(rq); + } return rq; } EXPORT_SYMBOL(blk_fetch_request); diff --git a/block/blk-settings.c b/block/blk-settings.c index fa1eb0449a0..7d3ee7fa50d 100644 --- a/block/blk-settings.c +++ b/block/blk-settings.c @@ -99,6 +99,18 @@ void blk_queue_lld_busy(struct request_queue *q, lld_busy_fn *fn) } EXPORT_SYMBOL_GPL(blk_queue_lld_busy); +/** + * blk_urgent_request() - Set an urgent_request handler function for queue + * @q: queue + * @fn: handler for urgent requests + * + */ +void blk_urgent_request(struct request_queue *q, request_fn_proc *fn) +{ + q->urgent_request_fn = fn; +} +EXPORT_SYMBOL(blk_urgent_request); + /** * blk_set_default_limits - reset limits to default values * @lim: the queue_limits structure to reset diff --git a/block/blk.h b/block/blk.h index d6586287adc..2285f51c9ab 100644 --- a/block/blk.h +++ b/block/blk.h @@ -28,6 +28,7 @@ void __generic_unplug_device(struct request_queue *); */ enum rq_atomic_flags { REQ_ATOM_COMPLETE = 0, + REQ_ATOM_URGENT = 1, }; /* @@ -44,6 +45,16 @@ static inline void blk_clear_rq_complete(struct request *rq) clear_bit(REQ_ATOM_COMPLETE, &rq->atomic_flags); } +static inline int blk_mark_rq_urgent(struct request *rq) +{ + return test_and_set_bit(REQ_ATOM_URGENT, &rq->atomic_flags); +} + +static inline void blk_clear_rq_urgent(struct request *rq) +{ + clear_bit(REQ_ATOM_URGENT, &rq->atomic_flags); +} + /* * Internal elevator interface */ diff --git a/block/elevator.c b/block/elevator.c index b0b38ce0dcb..b328d8ab5ea 100644 --- a/block/elevator.c +++ b/block/elevator.c @@ -606,6 +606,41 @@ void elv_requeue_request(struct request_queue *q, struct request *rq) __elv_add_request(q, rq, ELEVATOR_INSERT_REQUEUE); } +/** + * elv_reinsert_request() - Insert a request back to the scheduler + * @q: request queue where request should be inserted + * @rq: request to be inserted + * + * This function returns the request back to the scheduler to be + * inserted as if it was never dispatched + * + * Return: 0 on success, error code on failure + */ +int elv_reinsert_request(struct request_queue *q, struct request *rq) +{ + int res; + + if (!q->elevator->elevator_type->ops.elevator_reinsert_req_fn) + return -EPERM; + + res = q->elevator->elevator_type->ops.elevator_reinsert_req_fn(q, rq); + if (!res) { + /* + * it already went through dequeue, we need to decrement the + * in_flight count again + */ + if (blk_account_rq(rq)) { + q->in_flight[rq_is_sync(rq)]--; + if (rq->cmd_flags & REQ_SORTED) + elv_deactivate_rq(q, rq); + } + rq->cmd_flags &= ~REQ_STARTED; + q->nr_sorted++; + } + + return res; +} + void elv_drain_elevator(struct request_queue *q) { static int printed; @@ -810,6 +845,11 @@ void elv_completed_request(struct request_queue *q, struct request *rq) { struct elevator_queue *e = q->elevator; + if (test_bit(REQ_ATOM_URGENT, &rq->atomic_flags)) { + q->notified_urgent = false; + q->dispatched_urgent = false; + blk_clear_rq_urgent(rq); + } /* * request is released from the driver, io must be done */ diff --git a/block/row-iosched.c b/block/row-iosched.c new file mode 100644 index 00000000000..a67ec325af0 --- /dev/null +++ b/block/row-iosched.c @@ -0,0 +1,790 @@ +/* + * ROW (Read Over Write) I/O scheduler. + * + * Copyright (c) 2012-2013, 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. + */ + +/* See Documentation/block/row-iosched.txt */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * enum row_queue_prio - Priorities of the ROW queues + * + * This enum defines the priorities (and the number of queues) + * the requests will be disptributed to. The higher priority - + * the bigger is the dispatch quantum given to that queue. + * ROWQ_PRIO_HIGH_READ - is the higher priority queue. + * + */ +enum row_queue_prio { + ROWQ_PRIO_HIGH_READ = 0, + ROWQ_PRIO_REG_READ, + ROWQ_PRIO_HIGH_SWRITE, + ROWQ_PRIO_REG_SWRITE, + ROWQ_PRIO_REG_WRITE, + ROWQ_PRIO_LOW_READ, + ROWQ_PRIO_LOW_SWRITE, + ROWQ_MAX_PRIO, +}; + +/** + * struct row_queue_params - ROW queue parameters + * @idling_enabled: Flag indicating whether idling is enable on + * the queue + * @quantum: Number of requests to be dispatched from this queue + * in a dispatch cycle + * @is_urgent: Flags indicating whether the queue can notify on + * urgent requests + * + */ +struct row_queue_params { + bool idling_enabled; + int quantum; + bool is_urgent; +}; + +/* + * This array holds the default values of the different configurables + * for each ROW queue. Each row of the array holds the following values: + * {idling_enabled, quantum, is_urgent} + * Each row corresponds to a queue with the same index (according to + * enum row_queue_prio) + */ +static const struct row_queue_params row_queues_def[] = { +/* idling_enabled, quantum, is_urgent */ + {true, 100, true}, /* ROWQ_PRIO_HIGH_READ */ + {true, 100, true}, /* ROWQ_PRIO_REG_READ */ + {false, 2, false}, /* ROWQ_PRIO_HIGH_SWRITE */ + {false, 1, false}, /* ROWQ_PRIO_REG_SWRITE */ + {false, 1, false}, /* ROWQ_PRIO_REG_WRITE */ + {false, 1, false}, /* ROWQ_PRIO_LOW_READ */ + {false, 1, false} /* ROWQ_PRIO_LOW_SWRITE */ +}; + +/* Default values for idling on read queues (in msec) */ +#define ROW_IDLE_TIME_MSEC 5 +#define ROW_READ_FREQ_MSEC 20 + +/** + * struct rowq_idling_data - parameters for idling on the queue + * @last_insert_time: time the last request was inserted + * to the queue + * @begin_idling: flag indicating wether we should idle + * + */ +struct rowq_idling_data { + ktime_t last_insert_time; + bool begin_idling; +}; + +/** + * struct row_queue - requests grouping structure + * @rdata: parent row_data structure + * @fifo: fifo of requests + * @prio: queue priority (enum row_queue_prio) + * @nr_dispatched: number of requests already dispatched in + * the current dispatch cycle + * @slice: number of requests to dispatch in a cycle + * @nr_req: number of requests in queue + * @dispatch quantum: number of requests this queue may + * dispatch in a dispatch cycle + * @idle_data: data for idling on queues + * + */ +struct row_queue { + struct row_data *rdata; + struct list_head fifo; + enum row_queue_prio prio; + + unsigned int nr_dispatched; + unsigned int slice; + + unsigned int nr_req; + int disp_quantum; + + /* used only for READ queues */ + struct rowq_idling_data idle_data; +}; + +/** + * struct idling_data - data for idling on empty rqueue + * @idle_time: idling duration (jiffies) + * @freq: min time between two requests that + * triger idling (msec) + * @idle_work: pointer to struct delayed_work + * + */ +struct idling_data { + unsigned long idle_time; + u32 freq; + + struct workqueue_struct *idle_workqueue; + struct delayed_work idle_work; +}; + +/** + * struct row_queue - Per block device rqueue structure + * @dispatch_queue: dispatch rqueue + * @row_queues: array of priority request queues + * @curr_queue: index in the row_queues array of the + * currently serviced rqueue + * @read_idle: data for idling after READ request + * @nr_reqs: nr_reqs[0] holds the number of all READ requests in + * scheduler, nr_reqs[1] holds the number of all WRITE + * requests in scheduler + * @cycle_flags: used for marking unserved queueus + * + */ +struct row_data { + struct request_queue *dispatch_queue; + + struct row_queue row_queues[ROWQ_MAX_PRIO]; + + enum row_queue_prio curr_queue; + + struct idling_data read_idle; + unsigned int nr_reqs[2]; + + unsigned int cycle_flags; +}; + +#define RQ_ROWQ(rq) ((struct row_queue *) ((rq)->elevator_private[0])) + +#define row_log(q, fmt, args...) \ + blk_add_trace_msg(q, "%s():" fmt , __func__, ##args) +#define row_log_rowq(rdata, rowq_id, fmt, args...) \ + blk_add_trace_msg(rdata->dispatch_queue, "rowq%d " fmt, \ + rowq_id, ##args) + +static inline void row_mark_rowq_unserved(struct row_data *rd, + enum row_queue_prio qnum) +{ + rd->cycle_flags |= (1 << qnum); +} + +static inline void row_clear_rowq_unserved(struct row_data *rd, + enum row_queue_prio qnum) +{ + rd->cycle_flags &= ~(1 << qnum); +} + +static inline int row_rowq_unserved(struct row_data *rd, + enum row_queue_prio qnum) +{ + return rd->cycle_flags & (1 << qnum); +} + +static inline void __maybe_unused row_dump_queues_stat(struct row_data *rd) +{ + int i; + + row_log(rd->dispatch_queue, " Queues status:"); + for (i = 0; i < ROWQ_MAX_PRIO; i++) + row_log(rd->dispatch_queue, + "queue%d: dispatched= %d, nr_req=%d", i, + rd->row_queues[i].nr_dispatched, + rd->row_queues[i].nr_req); +} + +/******************** Static helper functions ***********************/ +/* + * kick_queue() - Wake up device driver queue thread + * @work: pointer to struct work_struct + * + * This is a idling delayed work function. It's purpose is to wake up the + * device driver in order for it to start fetching requests. + * + */ +static void kick_queue(struct work_struct *work) +{ + struct delayed_work *idle_work = to_delayed_work(work); + struct idling_data *read_data = + container_of(idle_work, struct idling_data, idle_work); + struct row_data *rd = + container_of(read_data, struct row_data, read_idle); + + row_log_rowq(rd, rd->curr_queue, "Performing delayed work"); + /* Mark idling process as done */ + rd->row_queues[rd->curr_queue].idle_data.begin_idling = false; + + if (!(rd->nr_reqs[0] + rd->nr_reqs[1])) + row_log(rd->dispatch_queue, "No requests in scheduler"); + else { + spin_lock_irq(rd->dispatch_queue->queue_lock); + __blk_run_queue(rd->dispatch_queue); + spin_unlock_irq(rd->dispatch_queue->queue_lock); + } +} + +/* + * row_restart_disp_cycle() - Restart the dispatch cycle + * @rd: pointer to struct row_data + * + * This function restarts the dispatch cycle by: + * - Setting current queue to ROWQ_PRIO_HIGH_READ + * - For each queue: reset the number of requests dispatched in + * the cycle + */ +static inline void row_restart_disp_cycle(struct row_data *rd) +{ + int i; + + for (i = 0; i < ROWQ_MAX_PRIO; i++) + rd->row_queues[i].nr_dispatched = 0; + + rd->curr_queue = ROWQ_PRIO_HIGH_READ; + row_log(rd->dispatch_queue, "Restarting cycle"); +} + +static inline void row_get_next_queue(struct row_data *rd) +{ + rd->curr_queue++; + if (rd->curr_queue == ROWQ_MAX_PRIO) + row_restart_disp_cycle(rd); +} + +/******************* Elevator callback functions *********************/ + +/* + * row_add_request() - Add request to the scheduler + * @q: requests queue + * @rq: request to add + * + */ +static void row_add_request(struct request_queue *q, + struct request *rq) +{ + struct row_data *rd = (struct row_data *)q->elevator->elevator_data; + struct row_queue *rqueue = RQ_ROWQ(rq); + + list_add_tail(&rq->queuelist, &rqueue->fifo); + rd->nr_reqs[rq_data_dir(rq)]++; + rqueue->nr_req++; + rq_set_fifo_time(rq, jiffies); /* for statistics*/ + + if (row_queues_def[rqueue->prio].idling_enabled) { + if (delayed_work_pending(&rd->read_idle.idle_work)) + (void)cancel_delayed_work( + &rd->read_idle.idle_work); + if (ktime_to_ms(ktime_sub(ktime_get(), + rqueue->idle_data.last_insert_time)) < + rd->read_idle.freq) { + rqueue->idle_data.begin_idling = true; + row_log_rowq(rd, rqueue->prio, "Enable idling"); + } else { + rqueue->idle_data.begin_idling = false; + row_log_rowq(rd, rqueue->prio, "Disable idling"); + } + + rqueue->idle_data.last_insert_time = ktime_get(); + } + if (row_queues_def[rqueue->prio].is_urgent && + row_rowq_unserved(rd, rqueue->prio)) { + row_log_rowq(rd, rqueue->prio, + "added urgent request (total on queue=%d)", + rqueue->nr_req); + } else + row_log_rowq(rd, rqueue->prio, + "added request (total on queue=%d)", rqueue->nr_req); +} + +/** + * row_reinsert_req() - Reinsert request back to the scheduler + * @q: requests queue + * @rq: request to add + * + * Reinsert the given request back to the queue it was + * dispatched from as if it was never dispatched. + * + * Returns 0 on success, error code otherwise + */ +static int row_reinsert_req(struct request_queue *q, + struct request *rq) +{ + struct row_data *rd = q->elevator->elevator_data; + struct row_queue *rqueue = RQ_ROWQ(rq); + + /* Verify rqueue is legitimate */ + if (rqueue->prio >= ROWQ_MAX_PRIO) { + pr_err("\n\nROW BUG: row_reinsert_req() rqueue->prio = %d\n", + rqueue->prio); + blk_dump_rq_flags(rq, ""); + return -EIO; + } + + list_add(&rq->queuelist, &rqueue->fifo); + rd->nr_reqs[rq_data_dir(rq)]++; + rqueue->nr_req++; + + row_log_rowq(rd, rqueue->prio, + "request reinserted (total on queue=%d)", rqueue->nr_req); + + return 0; +} + +/** + * row_urgent_pending() - Return TRUE if there is an urgent + * request on scheduler + * @q: requests queue + */ +static bool row_urgent_pending(struct request_queue *q) +{ + struct row_data *rd = q->elevator->elevator_data; + int i; + + for (i = 0; i < ROWQ_MAX_PRIO; i++) + if (row_queues_def[i].is_urgent && row_rowq_unserved(rd, i) && + !list_empty(&rd->row_queues[i].fifo)) { + row_log_rowq(rd, i, + "Urgent request pending (curr=%i)", + rd->curr_queue); + return true; + } + + return false; +} + +/** + * row_remove_request() - Remove given request from scheduler + * @q: requests queue + * @rq: request to remove + * + */ +static void row_remove_request(struct request_queue *q, + struct request *rq) +{ + struct row_data *rd = (struct row_data *)q->elevator->elevator_data; + struct row_queue *rqueue = RQ_ROWQ(rq); + + rq_fifo_clear(rq); + rqueue->nr_req--; + rd->nr_reqs[rq_data_dir(rq)]--; +} + +/* + * row_dispatch_insert() - move request to dispatch queue + * @rd: pointer to struct row_data + * + * This function moves the next request to dispatch from + * rd->curr_queue to the dispatch queue + * + */ +static void row_dispatch_insert(struct row_data *rd) +{ + struct request *rq; + + rq = rq_entry_fifo(rd->row_queues[rd->curr_queue].fifo.next); + row_remove_request(rd->dispatch_queue, rq); + elv_dispatch_add_tail(rd->dispatch_queue, rq); + rd->row_queues[rd->curr_queue].nr_dispatched++; + row_clear_rowq_unserved(rd, rd->curr_queue); + row_log_rowq(rd, rd->curr_queue, " Dispatched request nr_disp = %d", + rd->row_queues[rd->curr_queue].nr_dispatched); +} + +/* + * row_choose_queue() - choose the next queue to dispatch from + * @rd: pointer to struct row_data + * + * Updates rd->curr_queue. Returns 1 if there are requests to + * dispatch, 0 if there are no requests in scheduler + * + */ +static int row_choose_queue(struct row_data *rd) +{ + int prev_curr_queue = rd->curr_queue; + + if (!(rd->nr_reqs[0] + rd->nr_reqs[1])) { + row_log(rd->dispatch_queue, "No more requests in scheduler"); + return 0; + } + + row_get_next_queue(rd); + + /* + * Loop over all queues to find the next queue that is not empty. + * Stop when you get back to curr_queue + */ + while (list_empty(&rd->row_queues[rd->curr_queue].fifo) + && rd->curr_queue != prev_curr_queue) { + /* Mark rqueue as unserved */ + row_mark_rowq_unserved(rd, rd->curr_queue); + row_get_next_queue(rd); + } + + return 1; +} + +/* + * row_dispatch_requests() - selects the next request to dispatch + * @q: requests queue + * @force: ignored + * + * Return 0 if no requests were moved to the dispatch queue. + * 1 otherwise + * + */ +static int row_dispatch_requests(struct request_queue *q, int force) +{ + struct row_data *rd = (struct row_data *)q->elevator->elevator_data; + int ret = 0, currq, i; + + currq = rd->curr_queue; + + /* + * Find the first unserved queue (with higher priority then currq) + * that is not empty + */ + for (i = 0; i < currq; i++) { + if (row_rowq_unserved(rd, i) && + !list_empty(&rd->row_queues[i].fifo)) { + row_log_rowq(rd, currq, + " Preemting for unserved rowq%d. (nr_req=%u)", + i, rd->row_queues[currq].nr_req); + rd->curr_queue = i; + row_dispatch_insert(rd); + ret = 1; + goto done; + } + } + + if (rd->row_queues[currq].nr_dispatched >= + rd->row_queues[currq].disp_quantum) { + rd->row_queues[currq].nr_dispatched = 0; + row_log_rowq(rd, currq, "Expiring rqueue"); + ret = row_choose_queue(rd); + if (ret) + row_dispatch_insert(rd); + goto done; + } + + /* Dispatch from curr_queue */ + if (list_empty(&rd->row_queues[currq].fifo)) { + /* check idling */ + if (delayed_work_pending(&rd->read_idle.idle_work)) { + if (force) { + (void)cancel_delayed_work( + &rd->read_idle.idle_work); + row_log_rowq(rd, currq, + "Canceled delayed work - forced dispatch"); + } else { + row_log_rowq(rd, currq, + "Delayed work pending. Exiting"); + goto done; + } + } + + if (!force && row_queues_def[currq].idling_enabled && + rd->row_queues[currq].idle_data.begin_idling) { + if (!queue_delayed_work(rd->read_idle.idle_workqueue, + &rd->read_idle.idle_work, + rd->read_idle.idle_time)) { + row_log_rowq(rd, currq, + "Work already on queue!"); + pr_err("ROW_BUG: Work already on queue!"); + } else + row_log_rowq(rd, currq, + "Scheduled delayed work. exiting"); + goto done; + } else { + row_log_rowq(rd, currq, + "Currq empty. Choose next queue"); + ret = row_choose_queue(rd); + if (!ret) + goto done; + } + } + + ret = 1; + row_dispatch_insert(rd); + +done: + return ret; +} + +/* + * row_init_queue() - Init scheduler data structures + * @q: requests queue + * + * Return pointer to struct row_data to be saved in elevator for + * this dispatch queue + * + */ +static void *row_init_queue(struct request_queue *q) +{ + + struct row_data *rdata; + int i; + + rdata = kmalloc_node(sizeof(*rdata), + GFP_KERNEL | __GFP_ZERO, q->node); + if (!rdata) + return NULL; + + for (i = 0; i < ROWQ_MAX_PRIO; i++) { + INIT_LIST_HEAD(&rdata->row_queues[i].fifo); + rdata->row_queues[i].disp_quantum = row_queues_def[i].quantum; + rdata->row_queues[i].rdata = rdata; + rdata->row_queues[i].prio = i; + rdata->row_queues[i].idle_data.begin_idling = false; + rdata->row_queues[i].idle_data.last_insert_time = + ktime_set(0, 0); + } + + /* + * Currently idling is enabled only for READ queues. If we want to + * enable it for write queues also, note that idling frequency will + * be the same in both cases + */ + rdata->read_idle.idle_time = msecs_to_jiffies(ROW_IDLE_TIME_MSEC); + /* Maybe 0 on some platforms */ + if (!rdata->read_idle.idle_time) + rdata->read_idle.idle_time = 1; + rdata->read_idle.freq = ROW_READ_FREQ_MSEC; + rdata->read_idle.idle_workqueue = alloc_workqueue("row_idle_work", + WQ_MEM_RECLAIM | WQ_HIGHPRI, 0); + if (!rdata->read_idle.idle_workqueue) + panic("Failed to create idle workqueue\n"); + INIT_DELAYED_WORK(&rdata->read_idle.idle_work, kick_queue); + + rdata->curr_queue = ROWQ_PRIO_HIGH_READ; + rdata->dispatch_queue = q; + + rdata->nr_reqs[READ] = rdata->nr_reqs[WRITE] = 0; + + return rdata; +} + +/* + * row_exit_queue() - called on unloading the RAW scheduler + * @e: poiner to struct elevator_queue + * + */ +static void row_exit_queue(struct elevator_queue *e) +{ + struct row_data *rd = (struct row_data *)e->elevator_data; + int i; + + for (i = 0; i < ROWQ_MAX_PRIO; i++) + BUG_ON(!list_empty(&rd->row_queues[i].fifo)); + (void)cancel_delayed_work_sync(&rd->read_idle.idle_work); + BUG_ON(delayed_work_pending(&rd->read_idle.idle_work)); + destroy_workqueue(rd->read_idle.idle_workqueue); + kfree(rd); +} + +/* + * row_merged_requests() - Called when 2 requests are merged + * @q: requests queue + * @rq: request the two requests were merged into + * @next: request that was merged + */ +static void row_merged_requests(struct request_queue *q, struct request *rq, + struct request *next) +{ + struct row_queue *rqueue = RQ_ROWQ(next); + + list_del_init(&next->queuelist); + rqueue->nr_req--; + + rqueue->rdata->nr_reqs[rq_data_dir(rq)]--; +} + +/* + * get_queue_type() - Get queue type for a given request + * + * This is a helping function which purpose is to determine what + * ROW queue the given request should be added to (and + * dispatched from leter on) + * + * TODO: Right now only 3 queues are used REG_READ, REG_WRITE + * and REG_SWRITE + */ +static enum row_queue_prio get_queue_type(struct request *rq) +{ + const int data_dir = rq_data_dir(rq); + const bool is_sync = rq_is_sync(rq); + + if (data_dir == READ) + return ROWQ_PRIO_REG_READ; + else if (is_sync) + return ROWQ_PRIO_REG_SWRITE; + else + return ROWQ_PRIO_REG_WRITE; +} + +/* + * row_set_request() - Set ROW data structures associated with this request. + * @q: requests queue + * @rq: pointer to the request + * @gfp_mask: ignored + * + */ +static int +row_set_request(struct request_queue *q, struct request *rq, gfp_t gfp_mask) +{ + struct row_data *rd = (struct row_data *)q->elevator->elevator_data; + unsigned long flags; + + spin_lock_irqsave(q->queue_lock, flags); + rq->elevator_private[0] = + (void *)(&rd->row_queues[get_queue_type(rq)]); + spin_unlock_irqrestore(q->queue_lock, flags); + + return 0; +} + +/********** Helping sysfs functions/defenitions for ROW attributes ******/ +static ssize_t row_var_show(int var, char *page) +{ + return snprintf(page, 100, "%d\n", var); +} + +static ssize_t row_var_store(int *var, const char *page, size_t count) +{ + int err; + err = kstrtoul(page, 10, (unsigned long *)var); + + return count; +} + +#define SHOW_FUNCTION(__FUNC, __VAR, __CONV) \ +static ssize_t __FUNC(struct elevator_queue *e, char *page) \ +{ \ + struct row_data *rowd = e->elevator_data; \ + int __data = __VAR; \ + if (__CONV) \ + __data = jiffies_to_msecs(__data); \ + return row_var_show(__data, (page)); \ +} +SHOW_FUNCTION(row_hp_read_quantum_show, + rowd->row_queues[ROWQ_PRIO_HIGH_READ].disp_quantum, 0); +SHOW_FUNCTION(row_rp_read_quantum_show, + rowd->row_queues[ROWQ_PRIO_REG_READ].disp_quantum, 0); +SHOW_FUNCTION(row_hp_swrite_quantum_show, + rowd->row_queues[ROWQ_PRIO_HIGH_SWRITE].disp_quantum, 0); +SHOW_FUNCTION(row_rp_swrite_quantum_show, + rowd->row_queues[ROWQ_PRIO_REG_SWRITE].disp_quantum, 0); +SHOW_FUNCTION(row_rp_write_quantum_show, + rowd->row_queues[ROWQ_PRIO_REG_WRITE].disp_quantum, 0); +SHOW_FUNCTION(row_lp_read_quantum_show, + rowd->row_queues[ROWQ_PRIO_LOW_READ].disp_quantum, 0); +SHOW_FUNCTION(row_lp_swrite_quantum_show, + rowd->row_queues[ROWQ_PRIO_LOW_SWRITE].disp_quantum, 0); +SHOW_FUNCTION(row_read_idle_show, rowd->read_idle.idle_time, 0); +SHOW_FUNCTION(row_read_idle_freq_show, rowd->read_idle.freq, 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 row_data *rowd = e->elevator_data; \ + int __data; \ + int ret = row_var_store(&__data, (page), count); \ + if (__CONV) \ + __data = (int)msecs_to_jiffies(__data); \ + if (__data < (MIN)) \ + __data = (MIN); \ + else if (__data > (MAX)) \ + __data = (MAX); \ + *(__PTR) = __data; \ + return ret; \ +} +STORE_FUNCTION(row_hp_read_quantum_store, +&rowd->row_queues[ROWQ_PRIO_HIGH_READ].disp_quantum, 1, INT_MAX, 0); +STORE_FUNCTION(row_rp_read_quantum_store, + &rowd->row_queues[ROWQ_PRIO_REG_READ].disp_quantum, + 1, INT_MAX, 0); +STORE_FUNCTION(row_hp_swrite_quantum_store, + &rowd->row_queues[ROWQ_PRIO_HIGH_SWRITE].disp_quantum, + 1, INT_MAX, 0); +STORE_FUNCTION(row_rp_swrite_quantum_store, + &rowd->row_queues[ROWQ_PRIO_REG_SWRITE].disp_quantum, + 1, INT_MAX, 0); +STORE_FUNCTION(row_rp_write_quantum_store, + &rowd->row_queues[ROWQ_PRIO_REG_WRITE].disp_quantum, + 1, INT_MAX, 0); +STORE_FUNCTION(row_lp_read_quantum_store, + &rowd->row_queues[ROWQ_PRIO_LOW_READ].disp_quantum, + 1, INT_MAX, 0); +STORE_FUNCTION(row_lp_swrite_quantum_store, + &rowd->row_queues[ROWQ_PRIO_LOW_SWRITE].disp_quantum, + 1, INT_MAX, 1); +STORE_FUNCTION(row_read_idle_store, &rowd->read_idle.idle_time, 1, INT_MAX, 0); +STORE_FUNCTION(row_read_idle_freq_store, &rowd->read_idle.freq, 1, INT_MAX, 0); + +#undef STORE_FUNCTION + +#define ROW_ATTR(name) \ + __ATTR(name, S_IRUGO|S_IWUSR, row_##name##_show, \ + row_##name##_store) + +static struct elv_fs_entry row_attrs[] = { + ROW_ATTR(hp_read_quantum), + ROW_ATTR(rp_read_quantum), + ROW_ATTR(hp_swrite_quantum), + ROW_ATTR(rp_swrite_quantum), + ROW_ATTR(rp_write_quantum), + ROW_ATTR(lp_read_quantum), + ROW_ATTR(lp_swrite_quantum), + ROW_ATTR(read_idle), + ROW_ATTR(read_idle_freq), + __ATTR_NULL +}; + +static struct elevator_type iosched_row = { + .ops = { + .elevator_merge_req_fn = row_merged_requests, + .elevator_dispatch_fn = row_dispatch_requests, + .elevator_add_req_fn = row_add_request, + .elevator_reinsert_req_fn = row_reinsert_req, + .elevator_is_urgent_fn = row_urgent_pending, + .elevator_former_req_fn = elv_rb_former_request, + .elevator_latter_req_fn = elv_rb_latter_request, + .elevator_set_req_fn = row_set_request, + .elevator_init_fn = row_init_queue, + .elevator_exit_fn = row_exit_queue, + }, + + .elevator_attrs = row_attrs, + .elevator_name = "row", + .elevator_owner = THIS_MODULE, +}; + +static int __init row_init(void) +{ + elv_register(&iosched_row); + return 0; +} + +static void __exit row_exit(void) +{ + elv_unregister(&iosched_row); +} + +module_init(row_init); +module_exit(row_exit); + +MODULE_LICENSE("GPLv2"); +MODULE_DESCRIPTION("Read Over Write IO scheduler"); diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index cd93f9994ad..5edc4108c9a 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -275,6 +275,7 @@ struct request_queue struct request_list rq; request_fn_proc *request_fn; + request_fn_proc *urgent_request_fn; make_request_fn *make_request_fn; prep_rq_fn *prep_rq_fn; unprep_rq_fn *unprep_rq_fn; @@ -350,6 +351,8 @@ struct request_queue struct list_head timeout_list; struct queue_limits limits; + bool notified_urgent; + bool dispatched_urgent; /* * sg stuff @@ -657,6 +660,8 @@ extern struct request *blk_make_request(struct request_queue *, struct bio *, gfp_t); extern void blk_insert_request(struct request_queue *, struct request *, int, void *); extern void blk_requeue_request(struct request_queue *, struct request *); +extern int blk_reinsert_request(struct request_queue *q, struct request *rq); +extern bool blk_reinsert_req_sup(struct request_queue *q); extern void blk_add_request_payload(struct request *rq, struct page *page, unsigned int len); extern int blk_rq_check_limits(struct request_queue *q, struct request *rq); @@ -801,6 +806,7 @@ extern struct request_queue *blk_init_queue_node(request_fn_proc *rfn, extern struct request_queue *blk_init_queue(request_fn_proc *, spinlock_t *); extern struct request_queue *blk_init_allocated_queue(struct request_queue *, request_fn_proc *, spinlock_t *); +extern void blk_urgent_request(struct request_queue *q, request_fn_proc *fn); extern void blk_cleanup_queue(struct request_queue *); extern void blk_queue_make_request(struct request_queue *, make_request_fn *); extern void blk_queue_bounce_limit(struct request_queue *, u64); diff --git a/include/linux/elevator.h b/include/linux/elevator.h index 21a8ebf2dc3..e5b50bac8a6 100644 --- a/include/linux/elevator.h +++ b/include/linux/elevator.h @@ -20,6 +20,9 @@ typedef void (elevator_bio_merged_fn) (struct request_queue *, typedef int (elevator_dispatch_fn) (struct request_queue *, int); typedef void (elevator_add_req_fn) (struct request_queue *, struct request *); +typedef int (elevator_reinsert_req_fn) (struct request_queue *, + struct request *); +typedef bool (elevator_is_urgent_fn) (struct request_queue *); typedef struct request *(elevator_request_list_fn) (struct request_queue *, struct request *); typedef void (elevator_completed_req_fn) (struct request_queue *, struct request *); typedef int (elevator_may_queue_fn) (struct request_queue *, int); @@ -42,6 +45,9 @@ struct elevator_ops elevator_dispatch_fn *elevator_dispatch_fn; elevator_add_req_fn *elevator_add_req_fn; + elevator_reinsert_req_fn *elevator_reinsert_req_fn; + elevator_is_urgent_fn *elevator_is_urgent_fn; + elevator_activate_req_fn *elevator_activate_req_fn; elevator_deactivate_req_fn *elevator_deactivate_req_fn; @@ -109,6 +115,7 @@ extern void elv_merged_request(struct request_queue *, struct request *, int); extern void elv_bio_merged(struct request_queue *q, struct request *, struct bio *); extern void elv_requeue_request(struct request_queue *, struct request *); +extern int elv_reinsert_request(struct request_queue *, struct request *); extern struct request *elv_former_request(struct request_queue *, struct request *); extern struct request *elv_latter_request(struct request_queue *, struct request *); extern int elv_register_queue(struct request_queue *q); From 70fa2b2cd15e1e33a7ce2589db8e337c9864e5d1 Mon Sep 17 00:00:00 2001 From: Chad Froebel Date: Tue, 12 Feb 2013 22:08:45 -0500 Subject: [PATCH 002/111] vigor: block: set ROW as default iosched Change-Id: I2af87c132f95cdae97b381e96cf9a9cc4253c1c5 --- arch/arm/configs/vigor_aosp_defconfig | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/arch/arm/configs/vigor_aosp_defconfig b/arch/arm/configs/vigor_aosp_defconfig index 524aaa3e9ba..410a6ad202d 100644 --- a/arch/arm/configs/vigor_aosp_defconfig +++ b/arch/arm/configs/vigor_aosp_defconfig @@ -166,11 +166,13 @@ CONFIG_LBDAF=y # CONFIG_IOSCHED_NOOP=y CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_ROW=y CONFIG_IOSCHED_CFQ=y -CONFIG_DEFAULT_DEADLINE=y +# CONFIG_DEFAULT_DEADLINE is not set +CONFIG_DEFAULT_ROW=y # CONFIG_DEFAULT_CFQ is not set # CONFIG_DEFAULT_NOOP is not set -CONFIG_DEFAULT_IOSCHED="deadline" +CONFIG_DEFAULT_IOSCHED="row" # CONFIG_INLINE_SPIN_TRYLOCK is not set # CONFIG_INLINE_SPIN_TRYLOCK_BH is not set # CONFIG_INLINE_SPIN_LOCK is not set From 22954df6a848f3edad3128ac59dfc862e9d6763b Mon Sep 17 00:00:00 2001 From: helicopter88 Date: Wed, 6 Mar 2013 11:11:01 +0000 Subject: [PATCH 003/111] gpu-msm: Update adreno drivers Change-Id: If4e5e42ca585824ed2ea636895d9645917cd2a1f Signed-off-by: helicopter88 --- drivers/gpu/msm/Makefile | 1 + drivers/gpu/msm/a3xx_reg.h | 523 +++++ drivers/gpu/msm/adreno.c | 48 + drivers/gpu/msm/adreno_a3xx.c | 2774 ++++++++++++++++++++++++ drivers/gpu/msm/adreno_a3xx_snapshot.c | 332 +++ drivers/gpu/msm/adreno_a3xx_trace.c | 20 + drivers/gpu/msm/adreno_a3xx_trace.h | 89 + drivers/gpu/msm/adreno_drawctxt.c | 3 +- drivers/gpu/msm/adreno_ringbuffer.c | 2 +- drivers/gpu/msm/kgsl.c | 61 +- drivers/gpu/msm/kgsl.h | 6 + drivers/gpu/msm/kgsl_device.h | 12 +- drivers/gpu/msm/kgsl_iommu.h | 121 ++ drivers/gpu/msm/kgsl_pwrctrl.c | 3 +- drivers/gpu/msm/kgsl_pwrscale_msm.c | 200 ++ drivers/gpu/msm/kgsl_sync.c | 211 ++ drivers/gpu/msm/kgsl_sync.h | 75 + include/linux/fmem.h | 62 + include/linux/ion.h | 7 + include/linux/msm_adsp.h | 78 + include/linux/msm_kgsl.h | 16 +- include/linux/pm_qos.h | 153 ++ include/linux/sync.h | 314 +++ 23 files changed, 5093 insertions(+), 18 deletions(-) create mode 100644 drivers/gpu/msm/a3xx_reg.h create mode 100644 drivers/gpu/msm/adreno_a3xx.c create mode 100644 drivers/gpu/msm/adreno_a3xx_snapshot.c create mode 100644 drivers/gpu/msm/adreno_a3xx_trace.c create mode 100644 drivers/gpu/msm/adreno_a3xx_trace.h create mode 100644 drivers/gpu/msm/kgsl_iommu.h create mode 100644 drivers/gpu/msm/kgsl_pwrscale_msm.c create mode 100644 drivers/gpu/msm/kgsl_sync.c create mode 100644 drivers/gpu/msm/kgsl_sync.h create mode 100644 include/linux/fmem.h create mode 100644 include/linux/msm_adsp.h create mode 100644 include/linux/pm_qos.h create mode 100644 include/linux/sync.h diff --git a/drivers/gpu/msm/Makefile b/drivers/gpu/msm/Makefile index 7b8f3e633d1..65774c34ae1 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/a3xx_reg.h b/drivers/gpu/msm/a3xx_reg.h new file mode 100644 index 00000000000..8ec94318067 --- /dev/null +++ b/drivers/gpu/msm/a3xx_reg.h @@ -0,0 +1,523 @@ +/* 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 _A300_REG_H +#define _A300_REG_H + +/* Interrupt bit positions within RBBM_INT_0 */ + +#define A3XX_INT_RBBM_GPU_IDLE 0 +#define A3XX_INT_RBBM_AHB_ERROR 1 +#define A3XX_INT_RBBM_REG_TIMEOUT 2 +#define A3XX_INT_RBBM_ME_MS_TIMEOUT 3 +#define A3XX_INT_RBBM_PFP_MS_TIMEOUT 4 +#define A3XX_INT_RBBM_ATB_BUS_OVERFLOW 5 +#define A3XX_INT_VFD_ERROR 6 +#define A3XX_INT_CP_SW_INT 7 +#define A3XX_INT_CP_T0_PACKET_IN_IB 8 +#define A3XX_INT_CP_OPCODE_ERROR 9 +#define A3XX_INT_CP_RESERVED_BIT_ERROR 10 +#define A3XX_INT_CP_HW_FAULT 11 +#define A3XX_INT_CP_DMA 12 +#define A3XX_INT_CP_IB2_INT 13 +#define A3XX_INT_CP_IB1_INT 14 +#define A3XX_INT_CP_RB_INT 15 +#define A3XX_INT_CP_REG_PROTECT_FAULT 16 +#define A3XX_INT_CP_RB_DONE_TS 17 +#define A3XX_INT_CP_VS_DONE_TS 18 +#define A3XX_INT_CP_PS_DONE_TS 19 +#define A3XX_INT_CACHE_FLUSH_TS 20 +#define A3XX_INT_CP_AHB_ERROR_HALT 21 +#define A3XX_INT_MISC_HANG_DETECT 24 +#define A3XX_INT_UCHE_OOB_ACCESS 25 + +/* Register definitions */ + +#define A3XX_RBBM_HW_VERSION 0x000 +#define A3XX_RBBM_HW_RELEASE 0x001 +#define A3XX_RBBM_HW_CONFIGURATION 0x002 +#define A3XX_RBBM_CLOCK_CTL 0x010 +#define A3XX_RBBM_SP_HYST_CNT 0x012 +#define A3XX_RBBM_SW_RESET_CMD 0x018 +#define A3XX_RBBM_AHB_CTL0 0x020 +#define A3XX_RBBM_AHB_CTL1 0x021 +#define A3XX_RBBM_AHB_CMD 0x022 +#define A3XX_RBBM_AHB_ERROR_STATUS 0x027 +#define A3XX_RBBM_GPR0_CTL 0x02E +/* This the same register as on A2XX, just in a different place */ +#define A3XX_RBBM_STATUS 0x030 +#define A3XX_RBBM_WAIT_IDLE_CLOCKS_CTL 0x33 +#define A3XX_RBBM_INTERFACE_HANG_INT_CTL 0x50 +#define A3XX_RBBM_INTERFACE_HANG_MASK_CTL0 0x51 +#define A3XX_RBBM_INTERFACE_HANG_MASK_CTL1 0x54 +#define A3XX_RBBM_INTERFACE_HANG_MASK_CTL2 0x57 +#define A3XX_RBBM_INTERFACE_HANG_MASK_CTL3 0x5A +#define A3XX_RBBM_INT_CLEAR_CMD 0x061 +#define A3XX_RBBM_INT_0_MASK 0x063 +#define A3XX_RBBM_INT_0_STATUS 0x064 +#define A3XX_RBBM_GPU_BUSY_MASKED 0x88 +#define A3XX_RBBM_RBBM_CTL 0x100 +#define A3XX_RBBM_RBBM_CTL 0x100 +#define A3XX_RBBM_PERFCTR_PWR_1_LO 0x0EC +#define A3XX_RBBM_PERFCTR_PWR_1_HI 0x0ED +#define A3XX_RBBM_DEBUG_BUS_CTL 0x111 +#define A3XX_RBBM_DEBUG_BUS_DATA_STATUS 0x112 +/* Following two are same as on A2XX, just in a different place */ +#define A3XX_CP_PFP_UCODE_ADDR 0x1C9 +#define A3XX_CP_PFP_UCODE_DATA 0x1CA +#define A3XX_CP_ROQ_ADDR 0x1CC +#define A3XX_CP_ROQ_DATA 0x1CD +#define A3XX_CP_MEQ_ADDR 0x1DA +#define A3XX_CP_MEQ_DATA 0x1DB +#define A3XX_CP_HW_FAULT 0x45C +#define A3XX_CP_AHB_FAULT 0x54D +#define A3XX_CP_PROTECT_CTRL 0x45E +#define A3XX_CP_PROTECT_STATUS 0x45F +#define A3XX_CP_PROTECT_REG_0 0x460 +#define A3XX_CP_PROTECT_REG_1 0x461 +#define A3XX_CP_PROTECT_REG_2 0x462 +#define A3XX_CP_PROTECT_REG_3 0x463 +#define A3XX_CP_PROTECT_REG_4 0x464 +#define A3XX_CP_PROTECT_REG_5 0x465 +#define A3XX_CP_PROTECT_REG_6 0x466 +#define A3XX_CP_PROTECT_REG_7 0x467 +#define A3XX_CP_PROTECT_REG_8 0x468 +#define A3XX_CP_PROTECT_REG_9 0x469 +#define A3XX_CP_PROTECT_REG_A 0x46A +#define A3XX_CP_PROTECT_REG_B 0x46B +#define A3XX_CP_PROTECT_REG_C 0x46C +#define A3XX_CP_PROTECT_REG_D 0x46D +#define A3XX_CP_PROTECT_REG_E 0x46E +#define A3XX_CP_PROTECT_REG_F 0x46F +#define A3XX_CP_SCRATCH_REG2 0x57A +#define A3XX_CP_SCRATCH_REG3 0x57B +#define A3XX_VSC_BIN_SIZE 0xC01 +#define A3XX_VSC_SIZE_ADDRESS 0xC02 +#define A3XX_VSC_PIPE_CONFIG_0 0xC06 +#define A3XX_VSC_PIPE_DATA_ADDRESS_0 0xC07 +#define A3XX_VSC_PIPE_DATA_LENGTH_0 0xC08 +#define A3XX_VSC_PIPE_CONFIG_1 0xC09 +#define A3XX_VSC_PIPE_DATA_ADDRESS_1 0xC0A +#define A3XX_VSC_PIPE_DATA_LENGTH_1 0xC0B +#define A3XX_VSC_PIPE_CONFIG_2 0xC0C +#define A3XX_VSC_PIPE_DATA_ADDRESS_2 0xC0D +#define A3XX_VSC_PIPE_DATA_LENGTH_2 0xC0E +#define A3XX_VSC_PIPE_CONFIG_3 0xC0F +#define A3XX_VSC_PIPE_DATA_ADDRESS_3 0xC10 +#define A3XX_VSC_PIPE_DATA_LENGTH_3 0xC11 +#define A3XX_VSC_PIPE_CONFIG_4 0xC12 +#define A3XX_VSC_PIPE_DATA_ADDRESS_4 0xC13 +#define A3XX_VSC_PIPE_DATA_LENGTH_4 0xC14 +#define A3XX_VSC_PIPE_CONFIG_5 0xC15 +#define A3XX_VSC_PIPE_DATA_ADDRESS_5 0xC16 +#define A3XX_VSC_PIPE_DATA_LENGTH_5 0xC17 +#define A3XX_VSC_PIPE_CONFIG_6 0xC18 +#define A3XX_VSC_PIPE_DATA_ADDRESS_6 0xC19 +#define A3XX_VSC_PIPE_DATA_LENGTH_6 0xC1A +#define A3XX_VSC_PIPE_CONFIG_7 0xC1B +#define A3XX_VSC_PIPE_DATA_ADDRESS_7 0xC1C +#define A3XX_VSC_PIPE_DATA_LENGTH_7 0xC1D +#define A3XX_GRAS_CL_USER_PLANE_X0 0xCA0 +#define A3XX_GRAS_CL_USER_PLANE_Y0 0xCA1 +#define A3XX_GRAS_CL_USER_PLANE_Z0 0xCA2 +#define A3XX_GRAS_CL_USER_PLANE_W0 0xCA3 +#define A3XX_GRAS_CL_USER_PLANE_X1 0xCA4 +#define A3XX_GRAS_CL_USER_PLANE_Y1 0xCA5 +#define A3XX_GRAS_CL_USER_PLANE_Z1 0xCA6 +#define A3XX_GRAS_CL_USER_PLANE_W1 0xCA7 +#define A3XX_GRAS_CL_USER_PLANE_X2 0xCA8 +#define A3XX_GRAS_CL_USER_PLANE_Y2 0xCA9 +#define A3XX_GRAS_CL_USER_PLANE_Z2 0xCAA +#define A3XX_GRAS_CL_USER_PLANE_W2 0xCAB +#define A3XX_GRAS_CL_USER_PLANE_X3 0xCAC +#define A3XX_GRAS_CL_USER_PLANE_Y3 0xCAD +#define A3XX_GRAS_CL_USER_PLANE_Z3 0xCAE +#define A3XX_GRAS_CL_USER_PLANE_W3 0xCAF +#define A3XX_GRAS_CL_USER_PLANE_X4 0xCB0 +#define A3XX_GRAS_CL_USER_PLANE_Y4 0xCB1 +#define A3XX_GRAS_CL_USER_PLANE_Z4 0xCB2 +#define A3XX_GRAS_CL_USER_PLANE_W4 0xCB3 +#define A3XX_GRAS_CL_USER_PLANE_X5 0xCB4 +#define A3XX_GRAS_CL_USER_PLANE_Y5 0xCB5 +#define A3XX_GRAS_CL_USER_PLANE_Z5 0xCB6 +#define A3XX_GRAS_CL_USER_PLANE_W5 0xCB7 +#define A3XX_VFD_PERFCOUNTER0_SELECT 0xE44 +#define A3XX_VPC_VPC_DEBUG_RAM_SEL 0xE61 +#define A3XX_VPC_VPC_DEBUG_RAM_READ 0xE62 +#define A3XX_UCHE_CACHE_INVALIDATE0_REG 0xEA0 +#define A3XX_GRAS_CL_CLIP_CNTL 0x2040 +#define A3XX_GRAS_CL_GB_CLIP_ADJ 0x2044 +#define A3XX_GRAS_CL_VPORT_XOFFSET 0x2048 +#define A3XX_GRAS_CL_VPORT_ZOFFSET 0x204C +#define A3XX_GRAS_CL_VPORT_ZSCALE 0x204D +#define A3XX_GRAS_SU_POINT_MINMAX 0x2068 +#define A3XX_GRAS_SU_POINT_SIZE 0x2069 +#define A3XX_GRAS_SU_POLY_OFFSET_SCALE 0x206C +#define A3XX_GRAS_SU_POLY_OFFSET_OFFSET 0x206D +#define A3XX_GRAS_SU_MODE_CONTROL 0x2070 +#define A3XX_GRAS_SC_CONTROL 0x2072 +#define A3XX_GRAS_SC_SCREEN_SCISSOR_TL 0x2074 +#define A3XX_GRAS_SC_SCREEN_SCISSOR_BR 0x2075 +#define A3XX_GRAS_SC_WINDOW_SCISSOR_TL 0x2079 +#define A3XX_GRAS_SC_WINDOW_SCISSOR_BR 0x207A +#define A3XX_RB_MODE_CONTROL 0x20C0 +#define A3XX_RB_RENDER_CONTROL 0x20C1 +#define A3XX_RB_MSAA_CONTROL 0x20C2 +#define A3XX_RB_MRT_CONTROL0 0x20C4 +#define A3XX_RB_MRT_BUF_INFO0 0x20C5 +#define A3XX_RB_MRT_BLEND_CONTROL0 0x20C7 +#define A3XX_RB_MRT_BLEND_CONTROL1 0x20CB +#define A3XX_RB_MRT_BLEND_CONTROL2 0x20CF +#define A3XX_RB_MRT_BLEND_CONTROL3 0x20D3 +#define A3XX_RB_BLEND_RED 0x20E4 +#define A3XX_RB_COPY_CONTROL 0x20EC +#define A3XX_RB_COPY_DEST_INFO 0x20EF +#define A3XX_RB_DEPTH_CONTROL 0x2100 +#define A3XX_RB_STENCIL_CONTROL 0x2104 +#define A3XX_PC_VSTREAM_CONTROL 0x21E4 +#define A3XX_PC_VERTEX_REUSE_BLOCK_CNTL 0x21EA +#define A3XX_PC_PRIM_VTX_CNTL 0x21EC +#define A3XX_PC_RESTART_INDEX 0x21ED +#define A3XX_HLSQ_CONTROL_0_REG 0x2200 +#define A3XX_HLSQ_VS_CONTROL_REG 0x2204 +#define A3XX_HLSQ_CONST_FSPRESV_RANGE_REG 0x2207 +#define A3XX_HLSQ_CL_NDRANGE_0_REG 0x220A +#define A3XX_HLSQ_CL_NDRANGE_2_REG 0x220C +#define A3XX_HLSQ_CL_CONTROL_0_REG 0x2211 +#define A3XX_HLSQ_CL_CONTROL_1_REG 0x2212 +#define A3XX_HLSQ_CL_KERNEL_CONST_REG 0x2214 +#define A3XX_HLSQ_CL_KERNEL_GROUP_X_REG 0x2215 +#define A3XX_HLSQ_CL_KERNEL_GROUP_Z_REG 0x2217 +#define A3XX_HLSQ_CL_WG_OFFSET_REG 0x221A +#define A3XX_VFD_CONTROL_0 0x2240 +#define A3XX_VFD_INDEX_MIN 0x2242 +#define A3XX_VFD_INDEX_MAX 0x2243 +#define A3XX_VFD_FETCH_INSTR_0_0 0x2246 +#define A3XX_VFD_FETCH_INSTR_0_4 0x224E +#define A3XX_VFD_FETCH_INSTR_1_F 0x2265 +#define A3XX_VFD_DECODE_INSTR_0 0x2266 +#define A3XX_VFD_VS_THREADING_THRESHOLD 0x227E +#define A3XX_VPC_ATTR 0x2280 +#define A3XX_VPC_VARY_CYLWRAP_ENABLE_1 0x228B +#define A3XX_SP_SP_CTRL_REG 0x22C0 +#define A3XX_SP_VS_CTRL_REG0 0x22C4 +#define A3XX_SP_VS_CTRL_REG1 0x22C5 +#define A3XX_SP_VS_PARAM_REG 0x22C6 +#define A3XX_SP_VS_OUT_REG_7 0x22CE +#define A3XX_SP_VS_VPC_DST_REG_0 0x22D0 +#define A3XX_SP_VS_OBJ_OFFSET_REG 0x22D4 +#define A3XX_SP_VS_PVT_MEM_ADDR_REG 0x22D7 +#define A3XX_SP_VS_PVT_MEM_SIZE_REG 0x22D8 +#define A3XX_SP_VS_LENGTH_REG 0x22DF +#define A3XX_SP_FS_CTRL_REG0 0x22E0 +#define A3XX_SP_FS_CTRL_REG1 0x22E1 +#define A3XX_SP_FS_OBJ_OFFSET_REG 0x22E2 +#define A3XX_SP_FS_PVT_MEM_ADDR_REG 0x22E5 +#define A3XX_SP_FS_PVT_MEM_SIZE_REG 0x22E6 +#define A3XX_SP_FS_FLAT_SHAD_MODE_REG_0 0x22E8 +#define A3XX_SP_FS_FLAT_SHAD_MODE_REG_1 0x22E9 +#define A3XX_SP_FS_OUTPUT_REG 0x22EC +#define A3XX_SP_FS_MRT_REG_0 0x22F0 +#define A3XX_SP_FS_IMAGE_OUTPUT_REG_0 0x22F4 +#define A3XX_SP_FS_IMAGE_OUTPUT_REG_3 0x22F7 +#define A3XX_SP_FS_LENGTH_REG 0x22FF +#define A3XX_TPL1_TP_VS_TEX_OFFSET 0x2340 +#define A3XX_TPL1_TP_FS_TEX_OFFSET 0x2342 +#define A3XX_TPL1_TP_FS_BORDER_COLOR_BASE_ADDR 0x2343 +#define A3XX_VBIF_FIXED_SORT_EN 0x300C +#define A3XX_VBIF_FIXED_SORT_SEL0 0x300D +#define A3XX_VBIF_FIXED_SORT_SEL1 0x300E +#define A3XX_VBIF_ABIT_SORT 0x301C +#define A3XX_VBIF_ABIT_SORT_CONF 0x301D +#define A3XX_VBIF_GATE_OFF_WRREQ_EN 0x302A +#define A3XX_VBIF_IN_RD_LIM_CONF0 0x302C +#define A3XX_VBIF_IN_RD_LIM_CONF1 0x302D +#define A3XX_VBIF_IN_WR_LIM_CONF0 0x3030 +#define A3XX_VBIF_IN_WR_LIM_CONF1 0x3031 +#define A3XX_VBIF_OUT_RD_LIM_CONF0 0x3034 +#define A3XX_VBIF_OUT_WR_LIM_CONF0 0x3035 +#define A3XX_VBIF_DDR_OUT_MAX_BURST 0x3036 +#define A3XX_VBIF_ARB_CTL 0x303C +#define A3XX_VBIF_OUT_AXI_AOOO_EN 0x305E +#define A3XX_VBIF_OUT_AXI_AOOO 0x305F + +/* Bit flags for RBBM_CTL */ +#define RBBM_RBBM_CTL_RESET_PWR_CTR1 (1 << 1) +#define RBBM_RBBM_CTL_ENABLE_PWR_CTR1 (1 << 17) + +/* Various flags used by the context switch code */ + +#define SP_MULTI 0 +#define SP_BUFFER_MODE 1 +#define SP_TWO_VTX_QUADS 0 +#define SP_PIXEL_BASED 0 +#define SP_R8G8B8A8_UNORM 8 +#define SP_FOUR_PIX_QUADS 1 + +#define HLSQ_DIRECT 0 +#define HLSQ_BLOCK_ID_SP_VS 4 +#define HLSQ_SP_VS_INSTR 0 +#define HLSQ_SP_FS_INSTR 0 +#define HLSQ_BLOCK_ID_SP_FS 6 +#define HLSQ_TWO_PIX_QUADS 0 +#define HLSQ_TWO_VTX_QUADS 0 +#define HLSQ_BLOCK_ID_TP_TEX 2 +#define HLSQ_TP_TEX_SAMPLERS 0 +#define HLSQ_TP_TEX_MEMOBJ 1 +#define HLSQ_BLOCK_ID_TP_MIPMAP 3 +#define HLSQ_TP_MIPMAP_BASE 1 +#define HLSQ_FOUR_PIX_QUADS 1 + +#define RB_FACTOR_ONE 1 +#define RB_BLEND_OP_ADD 0 +#define RB_FACTOR_ZERO 0 +#define RB_DITHER_DISABLE 0 +#define RB_DITHER_ALWAYS 1 +#define RB_FRAG_NEVER 0 +#define RB_ENDIAN_NONE 0 +#define RB_R8G8B8A8_UNORM 8 +#define RB_RESOLVE_PASS 2 +#define RB_CLEAR_MODE_RESOLVE 1 +#define RB_TILINGMODE_LINEAR 0 +#define RB_REF_NEVER 0 +#define RB_FRAG_LESS 1 +#define RB_REF_ALWAYS 7 +#define RB_STENCIL_KEEP 0 +#define RB_RENDERING_PASS 0 +#define RB_TILINGMODE_32X32 2 + +#define PC_DRAW_TRIANGLES 2 +#define PC_DI_PT_RECTLIST 8 +#define PC_DI_SRC_SEL_AUTO_INDEX 2 +#define PC_DI_INDEX_SIZE_16_BIT 0 +#define PC_DI_IGNORE_VISIBILITY 0 +#define PC_DI_PT_TRILIST 4 +#define PC_DI_SRC_SEL_IMMEDIATE 1 +#define PC_DI_INDEX_SIZE_32_BIT 1 + +#define UCHE_ENTIRE_CACHE 1 +#define UCHE_OP_INVALIDATE 1 + +/* + * The following are bit field shifts within some of the registers defined + * above. These are used in the context switch code in conjunction with the + * _SET macro + */ + +#define GRAS_CL_CLIP_CNTL_CLIP_DISABLE 16 +#define GRAS_CL_CLIP_CNTL_IJ_PERSP_CENTER 12 +#define GRAS_CL_CLIP_CNTL_PERSP_DIVISION_DISABLE 21 +#define GRAS_CL_CLIP_CNTL_VP_CLIP_CODE_IGNORE 19 +#define GRAS_CL_CLIP_CNTL_VP_XFORM_DISABLE 20 +#define GRAS_CL_CLIP_CNTL_ZFAR_CLIP_DISABLE 17 +#define GRAS_CL_VPORT_XSCALE_VPORT_XSCALE 0 +#define GRAS_CL_VPORT_YSCALE_VPORT_YSCALE 0 +#define GRAS_CL_VPORT_ZSCALE_VPORT_ZSCALE 0 +#define GRAS_SC_CONTROL_RASTER_MODE 12 +#define GRAS_SC_CONTROL_RENDER_MODE 4 +#define GRAS_SC_SCREEN_SCISSOR_BR_BR_X 0 +#define GRAS_SC_SCREEN_SCISSOR_BR_BR_Y 16 +#define GRAS_SC_WINDOW_SCISSOR_BR_BR_X 0 +#define GRAS_SC_WINDOW_SCISSOR_BR_BR_Y 16 +#define GRAS_SU_CTRLMODE_LINEHALFWIDTH 03 +#define HLSQ_CONSTFSPRESERVEDRANGEREG_ENDENTRY 16 +#define HLSQ_CONSTFSPRESERVEDRANGEREG_STARTENTRY 0 +#define HLSQ_CTRL0REG_CHUNKDISABLE 26 +#define HLSQ_CTRL0REG_CONSTSWITCHMODE 27 +#define HLSQ_CTRL0REG_FSSUPERTHREADENABLE 6 +#define HLSQ_CTRL0REG_FSTHREADSIZE 4 +#define HLSQ_CTRL0REG_LAZYUPDATEDISABLE 28 +#define HLSQ_CTRL0REG_RESERVED2 10 +#define HLSQ_CTRL0REG_SPCONSTFULLUPDATE 29 +#define HLSQ_CTRL0REG_SPSHADERRESTART 9 +#define HLSQ_CTRL0REG_TPFULLUPDATE 30 +#define HLSQ_CTRL1REG_RESERVED1 9 +#define HLSQ_CTRL1REG_VSSUPERTHREADENABLE 8 +#define HLSQ_CTRL1REG_VSTHREADSIZE 6 +#define HLSQ_CTRL2REG_PRIMALLOCTHRESHOLD 26 +#define HLSQ_FSCTRLREG_FSCONSTLENGTH 0 +#define HLSQ_FSCTRLREG_FSCONSTSTARTOFFSET 12 +#define HLSQ_FSCTRLREG_FSINSTRLENGTH 24 +#define HLSQ_VSCTRLREG_VSINSTRLENGTH 24 +#define PC_PRIM_VTX_CONTROL_POLYMODE_BACK_PTYPE 8 +#define PC_PRIM_VTX_CONTROL_POLYMODE_FRONT_PTYPE 5 +#define PC_PRIM_VTX_CONTROL_PROVOKING_VTX_LAST 25 +#define PC_PRIM_VTX_CONTROL_STRIDE_IN_VPC 0 +#define PC_DRAW_INITIATOR_PRIM_TYPE 0 +#define PC_DRAW_INITIATOR_SOURCE_SELECT 6 +#define PC_DRAW_INITIATOR_VISIBILITY_CULLING_MODE 9 +#define PC_DRAW_INITIATOR_INDEX_SIZE 0x0B +#define PC_DRAW_INITIATOR_SMALL_INDEX 0x0D +#define PC_DRAW_INITIATOR_PRE_DRAW_INITIATOR_ENABLE 0x0E +#define RB_COPYCONTROL_COPY_GMEM_BASE 14 +#define RB_COPYCONTROL_RESOLVE_CLEAR_MODE 4 +#define RB_COPYDESTBASE_COPY_DEST_BASE 4 +#define RB_COPYDESTINFO_COPY_COMPONENT_ENABLE 14 +#define RB_COPYDESTINFO_COPY_DEST_ENDIAN 18 +#define RB_COPYDESTINFO_COPY_DEST_FORMAT 2 +#define RB_COPYDESTINFO_COPY_DEST_TILE 0 +#define RB_COPYDESTPITCH_COPY_DEST_PITCH 0 +#define RB_DEPTHCONTROL_Z_TEST_FUNC 4 +#define RB_MODECONTROL_RENDER_MODE 8 +#define RB_MODECONTROL_MARB_CACHE_SPLIT_MODE 15 +#define RB_MODECONTROL_PACKER_TIMER_ENABLE 16 +#define RB_MRTBLENDCONTROL_ALPHA_BLEND_OPCODE 21 +#define RB_MRTBLENDCONTROL_ALPHA_DEST_FACTOR 24 +#define RB_MRTBLENDCONTROL_ALPHA_SRC_FACTOR 16 +#define RB_MRTBLENDCONTROL_CLAMP_ENABLE 29 +#define RB_MRTBLENDCONTROL_RGB_BLEND_OPCODE 5 +#define RB_MRTBLENDCONTROL_RGB_DEST_FACTOR 8 +#define RB_MRTBLENDCONTROL_RGB_SRC_FACTOR 0 +#define RB_MRTBUFBASE_COLOR_BUF_BASE 4 +#define RB_MRTBUFINFO_COLOR_BUF_PITCH 17 +#define RB_MRTBUFINFO_COLOR_FORMAT 0 +#define RB_MRTBUFINFO_COLOR_TILE_MODE 6 +#define RB_MRTCONTROL_COMPONENT_ENABLE 24 +#define RB_MRTCONTROL_DITHER_MODE 12 +#define RB_MRTCONTROL_READ_DEST_ENABLE 3 +#define RB_MRTCONTROL_ROP_CODE 8 +#define RB_MSAACONTROL_MSAA_DISABLE 10 +#define RB_MSAACONTROL_SAMPLE_MASK 16 +#define RB_RENDERCONTROL_ALPHA_TEST_FUNC 24 +#define RB_RENDERCONTROL_BIN_WIDTH 4 +#define RB_RENDERCONTROL_DISABLE_COLOR_PIPE 12 +#define RB_STENCILCONTROL_STENCIL_FAIL 11 +#define RB_STENCILCONTROL_STENCIL_FAIL_BF 23 +#define RB_STENCILCONTROL_STENCIL_FUNC 8 +#define RB_STENCILCONTROL_STENCIL_FUNC_BF 20 +#define RB_STENCILCONTROL_STENCIL_ZFAIL 17 +#define RB_STENCILCONTROL_STENCIL_ZFAIL_BF 29 +#define RB_STENCILCONTROL_STENCIL_ZPASS 14 +#define RB_STENCILCONTROL_STENCIL_ZPASS_BF 26 +#define SP_FSCTRLREG0_FSFULLREGFOOTPRINT 10 +#define SP_FSCTRLREG0_FSHALFREGFOOTPRINT 4 +#define SP_FSCTRLREG0_FSICACHEINVALID 2 +#define SP_FSCTRLREG0_FSINOUTREGOVERLAP 18 +#define SP_FSCTRLREG0_FSINSTRBUFFERMODE 1 +#define SP_FSCTRLREG0_FSLENGTH 24 +#define SP_FSCTRLREG0_FSSUPERTHREADMODE 21 +#define SP_FSCTRLREG0_FSTHREADMODE 0 +#define SP_FSCTRLREG0_FSTHREADSIZE 20 +#define SP_FSCTRLREG0_PIXLODENABLE 22 +#define SP_FSCTRLREG1_FSCONSTLENGTH 0 +#define SP_FSCTRLREG1_FSINITIALOUTSTANDING 20 +#define SP_FSCTRLREG1_HALFPRECVAROFFSET 24 +#define SP_FSMRTREG_REGID 0 +#define SP_FSMRTREG_PRECISION 8 +#define SP_FSOUTREG_PAD0 2 +#define SP_IMAGEOUTPUTREG_MRTFORMAT 0 +#define SP_IMAGEOUTPUTREG_DEPTHOUTMODE 3 +#define SP_IMAGEOUTPUTREG_PAD0 6 +#define SP_OBJOFFSETREG_CONSTOBJECTSTARTOFFSET 16 +#define SP_OBJOFFSETREG_SHADEROBJOFFSETINIC 25 +#define SP_SHADERLENGTH_LEN 0 +#define SP_SPCTRLREG_CONSTMODE 18 +#define SP_SPCTRLREG_LOMODE 22 +#define SP_SPCTRLREG_SLEEPMODE 20 +#define SP_VSCTRLREG0_VSFULLREGFOOTPRINT 10 +#define SP_VSCTRLREG0_VSICACHEINVALID 2 +#define SP_VSCTRLREG0_VSINSTRBUFFERMODE 1 +#define SP_VSCTRLREG0_VSLENGTH 24 +#define SP_VSCTRLREG0_VSSUPERTHREADMODE 21 +#define SP_VSCTRLREG0_VSTHREADMODE 0 +#define SP_VSCTRLREG0_VSTHREADSIZE 20 +#define SP_VSCTRLREG1_VSINITIALOUTSTANDING 24 +#define SP_VSOUTREG_COMPMASK0 9 +#define SP_VSPARAMREG_POSREGID 0 +#define SP_VSPARAMREG_PSIZEREGID 8 +#define SP_VSPARAMREG_TOTALVSOUTVAR 20 +#define SP_VSVPCDSTREG_OUTLOC0 0 +#define TPL1_TPTEXOFFSETREG_BASETABLEPTR 16 +#define TPL1_TPTEXOFFSETREG_MEMOBJOFFSET 8 +#define TPL1_TPTEXOFFSETREG_SAMPLEROFFSET 0 +#define UCHE_INVALIDATE1REG_OPCODE 0x1C +#define UCHE_INVALIDATE1REG_ALLORPORTION 0x1F +#define VFD_BASEADDR_BASEADDR 0 +#define VFD_CTRLREG0_PACKETSIZE 18 +#define VFD_CTRLREG0_STRMDECINSTRCNT 22 +#define VFD_CTRLREG0_STRMFETCHINSTRCNT 27 +#define VFD_CTRLREG0_TOTALATTRTOVS 0 +#define VFD_CTRLREG1_MAXSTORAGE 0 +#define VFD_CTRLREG1_REGID4INST 24 +#define VFD_CTRLREG1_REGID4VTX 16 +#define VFD_DECODEINSTRUCTIONS_CONSTFILL 4 +#define VFD_DECODEINSTRUCTIONS_FORMAT 6 +#define VFD_DECODEINSTRUCTIONS_LASTCOMPVALID 29 +#define VFD_DECODEINSTRUCTIONS_REGID 12 +#define VFD_DECODEINSTRUCTIONS_SHIFTCNT 24 +#define VFD_DECODEINSTRUCTIONS_SWITCHNEXT 30 +#define VFD_DECODEINSTRUCTIONS_WRITEMASK 0 +#define VFD_FETCHINSTRUCTIONS_BUFSTRIDE 7 +#define VFD_FETCHINSTRUCTIONS_FETCHSIZE 0 +#define VFD_FETCHINSTRUCTIONS_INDEXDECODE 18 +#define VFD_FETCHINSTRUCTIONS_STEPRATE 24 +#define VFD_FETCHINSTRUCTIONS_SWITCHNEXT 17 +#define VFD_THREADINGTHRESHOLD_REGID_VTXCNT 8 +#define VFD_THREADINGTHRESHOLD_REGID_THRESHOLD 0 +#define VFD_THREADINGTHRESHOLD_RESERVED6 4 +#define VPC_VPCATTR_LMSIZE 28 +#define VPC_VPCATTR_THRHDASSIGN 12 +#define VPC_VPCATTR_TOTALATTR 0 +#define VPC_VPCPACK_NUMFPNONPOSVAR 8 +#define VPC_VPCPACK_NUMNONPOSVSVAR 16 +#define VPC_VPCVARPSREPLMODE_COMPONENT08 0 +#define VPC_VPCVARPSREPLMODE_COMPONENT09 2 +#define VPC_VPCVARPSREPLMODE_COMPONENT0A 4 +#define VPC_VPCVARPSREPLMODE_COMPONENT0B 6 +#define VPC_VPCVARPSREPLMODE_COMPONENT0C 8 +#define VPC_VPCVARPSREPLMODE_COMPONENT0D 10 +#define VPC_VPCVARPSREPLMODE_COMPONENT0E 12 +#define VPC_VPCVARPSREPLMODE_COMPONENT0F 14 +#define VPC_VPCVARPSREPLMODE_COMPONENT10 16 +#define VPC_VPCVARPSREPLMODE_COMPONENT11 18 +#define VPC_VPCVARPSREPLMODE_COMPONENT12 20 +#define VPC_VPCVARPSREPLMODE_COMPONENT13 22 +#define VPC_VPCVARPSREPLMODE_COMPONENT14 24 +#define VPC_VPCVARPSREPLMODE_COMPONENT15 26 +#define VPC_VPCVARPSREPLMODE_COMPONENT16 28 +#define VPC_VPCVARPSREPLMODE_COMPONENT17 30 + +/* RBBM Debug bus block IDs */ +#define RBBM_BLOCK_ID_NONE 0x0 +#define RBBM_BLOCK_ID_CP 0x1 +#define RBBM_BLOCK_ID_RBBM 0x2 +#define RBBM_BLOCK_ID_VBIF 0x3 +#define RBBM_BLOCK_ID_HLSQ 0x4 +#define RBBM_BLOCK_ID_UCHE 0x5 +#define RBBM_BLOCK_ID_PC 0x8 +#define RBBM_BLOCK_ID_VFD 0x9 +#define RBBM_BLOCK_ID_VPC 0xa +#define RBBM_BLOCK_ID_TSE 0xb +#define RBBM_BLOCK_ID_RAS 0xc +#define RBBM_BLOCK_ID_VSC 0xd +#define RBBM_BLOCK_ID_SP_0 0x10 +#define RBBM_BLOCK_ID_SP_1 0x11 +#define RBBM_BLOCK_ID_SP_2 0x12 +#define RBBM_BLOCK_ID_SP_3 0x13 +#define RBBM_BLOCK_ID_TPL1_0 0x18 +#define RBBM_BLOCK_ID_TPL1_1 0x19 +#define RBBM_BLOCK_ID_TPL1_2 0x1a +#define RBBM_BLOCK_ID_TPL1_3 0x1b +#define RBBM_BLOCK_ID_RB_0 0x20 +#define RBBM_BLOCK_ID_RB_1 0x21 +#define RBBM_BLOCK_ID_RB_2 0x22 +#define RBBM_BLOCK_ID_RB_3 0x23 +#define RBBM_BLOCK_ID_MARB_0 0x28 +#define RBBM_BLOCK_ID_MARB_1 0x29 +#define RBBM_BLOCK_ID_MARB_2 0x2a +#define RBBM_BLOCK_ID_MARB_3 0x2b + +/* RBBM_CLOCK_CTL default value */ +#define A3XX_RBBM_CLOCK_CTL_DEFAULT 0xBFFFFFFF + +#endif diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c index d38528a391e..da19b65b4a1 100644 --- a/drivers/gpu/msm/adreno.c +++ b/drivers/gpu/msm/adreno.c @@ -1145,6 +1145,53 @@ void adreno_regwrite(struct kgsl_device *device, unsigned int offsetwords, __raw_writel(value, reg); } +static void 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); + } + } +} + static int kgsl_check_interrupt_timestamp(struct kgsl_device *device, unsigned int timestamp) { @@ -1451,6 +1498,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_a3xx.c b/drivers/gpu/msm/adreno_a3xx.c new file mode 100644 index 00000000000..27146927095 --- /dev/null +++ b/drivers/gpu/msm/adreno_a3xx.c @@ -0,0 +1,2774 @@ +/* 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 "kgsl.h" +#include "adreno.h" +#include "kgsl_sharedmem.h" +#include "kgsl_cffdump.h" +#include "a3xx_reg.h" +#include "adreno_a3xx_trace.h" + +/* + * Set of registers to dump for A3XX on postmortem and snapshot. + * Registers in pairs - first value is the start offset, second + * is the stop offset (inclusive) + */ + +const unsigned int a3xx_registers[] = { + 0x0000, 0x0002, 0x0010, 0x0012, 0x0018, 0x0018, 0x0020, 0x0027, + 0x0029, 0x002b, 0x002e, 0x0033, 0x0040, 0x0042, 0x0050, 0x005c, + 0x0060, 0x006c, 0x0080, 0x0082, 0x0084, 0x0088, 0x0090, 0x00e5, + 0x00ea, 0x00ed, 0x0100, 0x0100, 0x0110, 0x0123, 0x01c0, 0x01c1, + 0x01c3, 0x01c5, 0x01c7, 0x01c7, 0x01d5, 0x01d9, 0x01dc, 0x01dd, + 0x01ea, 0x01ea, 0x01ee, 0x01f1, 0x01f5, 0x01f5, 0x01fc, 0x01ff, + 0x0440, 0x0440, 0x0443, 0x0443, 0x0445, 0x0445, 0x044d, 0x044f, + 0x0452, 0x0452, 0x0454, 0x046f, 0x047c, 0x047c, 0x047f, 0x047f, + 0x0578, 0x057f, 0x0600, 0x0602, 0x0605, 0x0607, 0x060a, 0x060e, + 0x0612, 0x0614, 0x0c01, 0x0c02, 0x0c06, 0x0c1d, 0x0c3d, 0x0c3f, + 0x0c48, 0x0c4b, 0x0c80, 0x0c80, 0x0c88, 0x0c8b, 0x0ca0, 0x0cb7, + 0x0cc0, 0x0cc1, 0x0cc6, 0x0cc7, 0x0ce4, 0x0ce5, 0x0e00, 0x0e05, + 0x0e0c, 0x0e0c, 0x0e22, 0x0e23, 0x0e41, 0x0e45, 0x0e64, 0x0e65, + 0x0e80, 0x0e82, 0x0e84, 0x0e89, 0x0ea0, 0x0ea1, 0x0ea4, 0x0ea7, + 0x0ec4, 0x0ecb, 0x0ee0, 0x0ee0, 0x0f00, 0x0f01, 0x0f03, 0x0f09, + 0x2040, 0x2040, 0x2044, 0x2044, 0x2048, 0x204d, 0x2068, 0x2069, + 0x206c, 0x206d, 0x2070, 0x2070, 0x2072, 0x2072, 0x2074, 0x2075, + 0x2079, 0x207a, 0x20c0, 0x20d3, 0x20e4, 0x20ef, 0x2100, 0x2109, + 0x210c, 0x210c, 0x210e, 0x210e, 0x2110, 0x2111, 0x2114, 0x2115, + 0x21e4, 0x21e4, 0x21ea, 0x21ea, 0x21ec, 0x21ed, 0x21f0, 0x21f0, + 0x2200, 0x2212, 0x2214, 0x2217, 0x221a, 0x221a, 0x2240, 0x227e, + 0x2280, 0x228b, 0x22c0, 0x22c0, 0x22c4, 0x22ce, 0x22d0, 0x22d8, + 0x22df, 0x22e6, 0x22e8, 0x22e9, 0x22ec, 0x22ec, 0x22f0, 0x22f7, + 0x22ff, 0x22ff, 0x2340, 0x2343, 0x2348, 0x2349, 0x2350, 0x2356, + 0x2360, 0x2360, 0x2440, 0x2440, 0x2444, 0x2444, 0x2448, 0x244d, + 0x2468, 0x2469, 0x246c, 0x246d, 0x2470, 0x2470, 0x2472, 0x2472, + 0x2474, 0x2475, 0x2479, 0x247a, 0x24c0, 0x24d3, 0x24e4, 0x24ef, + 0x2500, 0x2509, 0x250c, 0x250c, 0x250e, 0x250e, 0x2510, 0x2511, + 0x2514, 0x2515, 0x25e4, 0x25e4, 0x25ea, 0x25ea, 0x25ec, 0x25ed, + 0x25f0, 0x25f0, 0x2600, 0x2612, 0x2614, 0x2617, 0x261a, 0x261a, + 0x2640, 0x267e, 0x2680, 0x268b, 0x26c0, 0x26c0, 0x26c4, 0x26ce, + 0x26d0, 0x26d8, 0x26df, 0x26e6, 0x26e8, 0x26e9, 0x26ec, 0x26ec, + 0x26f0, 0x26f7, 0x26ff, 0x26ff, 0x2740, 0x2743, 0x2748, 0x2749, + 0x2750, 0x2756, 0x2760, 0x2760, 0x300C, 0x300E, 0x301C, 0x301D, + 0x302A, 0x302A, 0x302C, 0x302D, 0x3030, 0x3031, 0x3034, 0x3036, + 0x303C, 0x303C, 0x305E, 0x305F, +}; + +const unsigned int a3xx_registers_count = ARRAY_SIZE(a3xx_registers) / 2; + +/* Simple macro to facilitate bit setting in the gmem2sys and sys2gmem + * functions. + */ + +#define _SET(_shift, _val) ((_val) << (_shift)) + +/* + **************************************************************************** + * + * Context state shadow structure: + * + * +---------------------+------------+-------------+---------------------+---+ + * | ALU Constant Shadow | Reg Shadow | C&V Buffers | Shader Instr Shadow |Tex| + * +---------------------+------------+-------------+---------------------+---+ + * + * 8K - ALU Constant Shadow (8K aligned) + * 4K - H/W Register Shadow (8K aligned) + * 5K - Command and Vertex Buffers + * 8K - Shader Instruction Shadow + * ~6K - Texture Constant Shadow + * + * + *************************************************************************** + */ + +/* Sizes of all sections in state shadow memory */ +#define ALU_SHADOW_SIZE (8*1024) /* 8KB */ +#define REG_SHADOW_SIZE (4*1024) /* 4KB */ +#define CMD_BUFFER_SIZE (5*1024) /* 5KB */ +#define TEX_SIZE_MEM_OBJECTS 896 /* bytes */ +#define TEX_SIZE_MIPMAP 1936 /* bytes */ +#define TEX_SIZE_SAMPLER_OBJ 256 /* bytes */ +#define TEX_SHADOW_SIZE \ + ((TEX_SIZE_MEM_OBJECTS + TEX_SIZE_MIPMAP + \ + TEX_SIZE_SAMPLER_OBJ)*2) /* ~6KB */ +#define SHADER_SHADOW_SIZE (8*1024) /* 8KB */ + +/* Total context size, excluding GMEM shadow */ +#define CONTEXT_SIZE \ + (ALU_SHADOW_SIZE+REG_SHADOW_SIZE + \ + CMD_BUFFER_SIZE+SHADER_SHADOW_SIZE + \ + TEX_SHADOW_SIZE) + +/* Offsets to different sections in context shadow memory */ +#define REG_OFFSET ALU_SHADOW_SIZE +#define CMD_OFFSET (REG_OFFSET+REG_SHADOW_SIZE) +#define SHADER_OFFSET (CMD_OFFSET+CMD_BUFFER_SIZE) +#define TEX_OFFSET (SHADER_OFFSET+SHADER_SHADOW_SIZE) +#define VS_TEX_OFFSET_MEM_OBJECTS TEX_OFFSET +#define VS_TEX_OFFSET_MIPMAP (VS_TEX_OFFSET_MEM_OBJECTS+TEX_SIZE_MEM_OBJECTS) +#define VS_TEX_OFFSET_SAMPLER_OBJ (VS_TEX_OFFSET_MIPMAP+TEX_SIZE_MIPMAP) +#define FS_TEX_OFFSET_MEM_OBJECTS \ + (VS_TEX_OFFSET_SAMPLER_OBJ+TEX_SIZE_SAMPLER_OBJ) +#define FS_TEX_OFFSET_MIPMAP (FS_TEX_OFFSET_MEM_OBJECTS+TEX_SIZE_MEM_OBJECTS) +#define FS_TEX_OFFSET_SAMPLER_OBJ (FS_TEX_OFFSET_MIPMAP+TEX_SIZE_MIPMAP) + +/* The offset for fragment shader data in HLSQ context */ +#define SSIZE (16*1024) + +#define HLSQ_SAMPLER_OFFSET 0x000 +#define HLSQ_MEMOBJ_OFFSET 0x400 +#define HLSQ_MIPMAP_OFFSET 0x800 + +/* Use shadow RAM */ +#define HLSQ_SHADOW_BASE (0x10000+SSIZE*2) + +#define REG_TO_MEM_LOOP_COUNT_SHIFT 18 + +#define BUILD_PC_DRAW_INITIATOR(prim_type, source_select, index_size, \ + vis_cull_mode) \ + (((prim_type) << PC_DRAW_INITIATOR_PRIM_TYPE) | \ + ((source_select) << PC_DRAW_INITIATOR_SOURCE_SELECT) | \ + ((index_size & 1) << PC_DRAW_INITIATOR_INDEX_SIZE) | \ + ((index_size >> 1) << PC_DRAW_INITIATOR_SMALL_INDEX) | \ + ((vis_cull_mode) << PC_DRAW_INITIATOR_VISIBILITY_CULLING_MODE) | \ + (1 << PC_DRAW_INITIATOR_PRE_DRAW_INITIATOR_ENABLE)) + +/* + * List of context registers (starting from dword offset 0x2000). + * Each line contains start and end of a range of registers. + */ +static const unsigned int context_register_ranges[] = { + A3XX_GRAS_CL_CLIP_CNTL, A3XX_GRAS_CL_CLIP_CNTL, + A3XX_GRAS_CL_GB_CLIP_ADJ, A3XX_GRAS_CL_GB_CLIP_ADJ, + A3XX_GRAS_CL_VPORT_XOFFSET, A3XX_GRAS_CL_VPORT_ZSCALE, + A3XX_GRAS_SU_POINT_MINMAX, A3XX_GRAS_SU_POINT_SIZE, + A3XX_GRAS_SU_POLY_OFFSET_SCALE, A3XX_GRAS_SU_POLY_OFFSET_OFFSET, + A3XX_GRAS_SU_MODE_CONTROL, A3XX_GRAS_SU_MODE_CONTROL, + A3XX_GRAS_SC_CONTROL, A3XX_GRAS_SC_CONTROL, + A3XX_GRAS_SC_SCREEN_SCISSOR_TL, A3XX_GRAS_SC_SCREEN_SCISSOR_BR, + A3XX_GRAS_SC_WINDOW_SCISSOR_TL, A3XX_GRAS_SC_WINDOW_SCISSOR_BR, + A3XX_RB_MODE_CONTROL, A3XX_RB_MRT_BLEND_CONTROL3, + A3XX_RB_BLEND_RED, A3XX_RB_COPY_DEST_INFO, + A3XX_RB_DEPTH_CONTROL, A3XX_RB_DEPTH_CONTROL, + A3XX_PC_VSTREAM_CONTROL, A3XX_PC_VSTREAM_CONTROL, + A3XX_PC_VERTEX_REUSE_BLOCK_CNTL, A3XX_PC_VERTEX_REUSE_BLOCK_CNTL, + A3XX_PC_PRIM_VTX_CNTL, A3XX_PC_RESTART_INDEX, + A3XX_HLSQ_CONTROL_0_REG, A3XX_HLSQ_CONST_FSPRESV_RANGE_REG, + A3XX_HLSQ_CL_NDRANGE_0_REG, A3XX_HLSQ_CL_NDRANGE_0_REG, + A3XX_HLSQ_CL_NDRANGE_2_REG, A3XX_HLSQ_CL_CONTROL_1_REG, + A3XX_HLSQ_CL_KERNEL_CONST_REG, A3XX_HLSQ_CL_KERNEL_GROUP_Z_REG, + A3XX_HLSQ_CL_WG_OFFSET_REG, A3XX_HLSQ_CL_WG_OFFSET_REG, + A3XX_VFD_CONTROL_0, A3XX_VFD_VS_THREADING_THRESHOLD, + A3XX_SP_SP_CTRL_REG, A3XX_SP_SP_CTRL_REG, + A3XX_SP_VS_CTRL_REG0, A3XX_SP_VS_OUT_REG_7, + A3XX_SP_VS_VPC_DST_REG_0, A3XX_SP_VS_PVT_MEM_SIZE_REG, + A3XX_SP_VS_LENGTH_REG, A3XX_SP_FS_PVT_MEM_SIZE_REG, + A3XX_SP_FS_FLAT_SHAD_MODE_REG_0, A3XX_SP_FS_FLAT_SHAD_MODE_REG_1, + A3XX_SP_FS_OUTPUT_REG, A3XX_SP_FS_OUTPUT_REG, + A3XX_SP_FS_MRT_REG_0, A3XX_SP_FS_IMAGE_OUTPUT_REG_3, + A3XX_SP_FS_LENGTH_REG, A3XX_SP_FS_LENGTH_REG, + A3XX_TPL1_TP_VS_TEX_OFFSET, A3XX_TPL1_TP_FS_BORDER_COLOR_BASE_ADDR, + A3XX_VPC_ATTR, A3XX_VPC_VARY_CYLWRAP_ENABLE_1, +}; + +/* Global registers that need to be saved separately */ +static const unsigned int global_registers[] = { + A3XX_GRAS_CL_USER_PLANE_X0, A3XX_GRAS_CL_USER_PLANE_Y0, + A3XX_GRAS_CL_USER_PLANE_Z0, A3XX_GRAS_CL_USER_PLANE_W0, + A3XX_GRAS_CL_USER_PLANE_X1, A3XX_GRAS_CL_USER_PLANE_Y1, + A3XX_GRAS_CL_USER_PLANE_Z1, A3XX_GRAS_CL_USER_PLANE_W1, + A3XX_GRAS_CL_USER_PLANE_X2, A3XX_GRAS_CL_USER_PLANE_Y2, + A3XX_GRAS_CL_USER_PLANE_Z2, A3XX_GRAS_CL_USER_PLANE_W2, + A3XX_GRAS_CL_USER_PLANE_X3, A3XX_GRAS_CL_USER_PLANE_Y3, + A3XX_GRAS_CL_USER_PLANE_Z3, A3XX_GRAS_CL_USER_PLANE_W3, + A3XX_GRAS_CL_USER_PLANE_X4, A3XX_GRAS_CL_USER_PLANE_Y4, + A3XX_GRAS_CL_USER_PLANE_Z4, A3XX_GRAS_CL_USER_PLANE_W4, + A3XX_GRAS_CL_USER_PLANE_X5, A3XX_GRAS_CL_USER_PLANE_Y5, + A3XX_GRAS_CL_USER_PLANE_Z5, A3XX_GRAS_CL_USER_PLANE_W5, + A3XX_VSC_BIN_SIZE, + A3XX_VSC_PIPE_CONFIG_0, A3XX_VSC_PIPE_CONFIG_1, + A3XX_VSC_PIPE_CONFIG_2, A3XX_VSC_PIPE_CONFIG_3, + A3XX_VSC_PIPE_CONFIG_4, A3XX_VSC_PIPE_CONFIG_5, + A3XX_VSC_PIPE_CONFIG_6, A3XX_VSC_PIPE_CONFIG_7, + A3XX_VSC_PIPE_DATA_ADDRESS_0, A3XX_VSC_PIPE_DATA_ADDRESS_1, + A3XX_VSC_PIPE_DATA_ADDRESS_2, A3XX_VSC_PIPE_DATA_ADDRESS_3, + A3XX_VSC_PIPE_DATA_ADDRESS_4, A3XX_VSC_PIPE_DATA_ADDRESS_5, + A3XX_VSC_PIPE_DATA_ADDRESS_6, A3XX_VSC_PIPE_DATA_ADDRESS_7, + A3XX_VSC_PIPE_DATA_LENGTH_0, A3XX_VSC_PIPE_DATA_LENGTH_1, + A3XX_VSC_PIPE_DATA_LENGTH_2, A3XX_VSC_PIPE_DATA_LENGTH_3, + A3XX_VSC_PIPE_DATA_LENGTH_4, A3XX_VSC_PIPE_DATA_LENGTH_5, + A3XX_VSC_PIPE_DATA_LENGTH_6, A3XX_VSC_PIPE_DATA_LENGTH_7, + A3XX_VSC_SIZE_ADDRESS +}; + +#define GLOBAL_REGISTER_COUNT ARRAY_SIZE(global_registers) + +/* A scratchpad used to build commands during context create */ +static struct tmp_ctx { + unsigned int *cmd; /* Next available dword in C&V buffer */ + + /* Addresses in comamnd buffer where registers are saved */ + uint32_t reg_values[GLOBAL_REGISTER_COUNT]; + uint32_t gmem_base; /* Base GPU address of GMEM */ +} tmp_ctx; + +#ifndef GSL_CONTEXT_SWITCH_CPU_SYNC +/* + * Function for executing dest = ( (reg & and) ROL rol ) | or + */ +static unsigned int *rmw_regtomem(unsigned int *cmd, + unsigned int reg, unsigned int and, + unsigned int rol, unsigned int or, + unsigned int dest) +{ + /* CP_SCRATCH_REG2 = (CP_SCRATCH_REG2 & 0x00000000) | reg */ + *cmd++ = cp_type3_packet(CP_REG_RMW, 3); + *cmd++ = (1 << 30) | A3XX_CP_SCRATCH_REG2; + *cmd++ = 0x00000000; /* AND value */ + *cmd++ = reg; /* OR address */ + + /* CP_SCRATCH_REG2 = ( (CP_SCRATCH_REG2 & and) ROL rol ) | or */ + *cmd++ = cp_type3_packet(CP_REG_RMW, 3); + *cmd++ = (rol << 24) | A3XX_CP_SCRATCH_REG2; + *cmd++ = and; /* AND value */ + *cmd++ = or; /* OR value */ + + *cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2); + *cmd++ = A3XX_CP_SCRATCH_REG2; + *cmd++ = dest; + + return cmd; +} +#endif + +static void build_regconstantsave_cmds(struct adreno_device *adreno_dev, + struct adreno_context *drawctxt) +{ + unsigned int *cmd = tmp_ctx.cmd; + unsigned int *start; + unsigned int i; + + drawctxt->constant_save_commands[0].hostptr = cmd; + drawctxt->constant_save_commands[0].gpuaddr = + virt2gpu(cmd, &drawctxt->gpustate); + cmd++; + + start = cmd; + + *cmd++ = cp_type3_packet(CP_WAIT_FOR_IDLE, 1); + *cmd++ = 0; + +#ifndef CONFIG_MSM_KGSL_DISABLE_SHADOW_WRITES + /* + * Context 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 */ + +#else + /* + * Make sure the HW context has the correct register values before + * reading them. + */ + + /* Write context registers into shadow */ + for (i = 0; i < ARRAY_SIZE(context_register_ranges) / 2; i++) { + unsigned int start = context_register_ranges[i * 2]; + unsigned int end = context_register_ranges[i * 2 + 1]; + *cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2); + *cmd++ = ((end - start + 1) << REG_TO_MEM_LOOP_COUNT_SHIFT) | + start; + *cmd++ = ((drawctxt->gpustate.gpuaddr + REG_OFFSET) + & 0xFFFFE000) + (start - 0x2000) * 4; + } +#endif + + /* Need to handle some of the global registers separately */ + for (i = 0; i < ARRAY_SIZE(global_registers); i++) { + *cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2); + *cmd++ = global_registers[i]; + *cmd++ = tmp_ctx.reg_values[i]; + } + + /* Save vertex shader constants */ + *cmd++ = cp_type3_packet(CP_COND_EXEC, 4); + *cmd++ = drawctxt->cond_execs[2].gpuaddr >> 2; + *cmd++ = drawctxt->cond_execs[2].gpuaddr >> 2; + *cmd++ = 0x0000FFFF; + *cmd++ = 3; /* EXEC_COUNT */ + *cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2); + drawctxt->constant_save_commands[1].hostptr = cmd; + drawctxt->constant_save_commands[1].gpuaddr = + virt2gpu(cmd, &drawctxt->gpustate); + /* + From fixup: + + dwords = SP_VS_CTRL_REG1.VSCONSTLENGTH / 4 + src = (HLSQ_SHADOW_BASE + 0x2000) / 4 + + From register spec: + SP_VS_CTRL_REG1.VSCONSTLENGTH [09:00]: 0-512, unit = 128bits. + */ + *cmd++ = 0; /* (dwords << REG_TO_MEM_LOOP_COUNT_SHIFT) | src */ + /* ALU constant shadow base */ + *cmd++ = drawctxt->gpustate.gpuaddr & 0xfffffffc; + + /* Save fragment shader constants */ + *cmd++ = cp_type3_packet(CP_COND_EXEC, 4); + *cmd++ = drawctxt->cond_execs[3].gpuaddr >> 2; + *cmd++ = drawctxt->cond_execs[3].gpuaddr >> 2; + *cmd++ = 0x0000FFFF; + *cmd++ = 3; /* EXEC_COUNT */ + *cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2); + drawctxt->constant_save_commands[2].hostptr = cmd; + drawctxt->constant_save_commands[2].gpuaddr = + virt2gpu(cmd, &drawctxt->gpustate); + /* + From fixup: + + dwords = SP_FS_CTRL_REG1.FSCONSTLENGTH / 4 + src = (HLSQ_SHADOW_BASE + 0x2000 + SSIZE) / 4 + + From register spec: + SP_FS_CTRL_REG1.FSCONSTLENGTH [09:00]: 0-512, unit = 128bits. + */ + *cmd++ = 0; /* (dwords << REG_TO_MEM_LOOP_COUNT_SHIFT) | src */ + + /* + From fixup: + + base = drawctxt->gpustate.gpuaddr (ALU constant shadow base) + offset = SP_FS_OBJ_OFFSET_REG.CONSTOBJECTSTARTOFFSET + + From register spec: + SP_FS_OBJ_OFFSET_REG.CONSTOBJECTSTARTOFFSET [16:24]: Constant object + start offset in on chip RAM, + 128bit aligned + + dst = base + offset + Because of the base alignment we can use + dst = base | offset + */ + *cmd++ = 0; /* dst */ + + /* Save VS texture memory objects */ + *cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2); + *cmd++ = + ((TEX_SIZE_MEM_OBJECTS / 4) << REG_TO_MEM_LOOP_COUNT_SHIFT) | + ((HLSQ_SHADOW_BASE + HLSQ_MEMOBJ_OFFSET) / 4); + *cmd++ = + (drawctxt->gpustate.gpuaddr + + VS_TEX_OFFSET_MEM_OBJECTS) & 0xfffffffc; + + /* Save VS texture mipmap pointers */ + *cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2); + *cmd++ = + ((TEX_SIZE_MIPMAP / 4) << REG_TO_MEM_LOOP_COUNT_SHIFT) | + ((HLSQ_SHADOW_BASE + HLSQ_MIPMAP_OFFSET) / 4); + *cmd++ = + (drawctxt->gpustate.gpuaddr + VS_TEX_OFFSET_MIPMAP) & 0xfffffffc; + + /* Save VS texture sampler objects */ + *cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2); + *cmd++ = ((TEX_SIZE_SAMPLER_OBJ / 4) << REG_TO_MEM_LOOP_COUNT_SHIFT) | + ((HLSQ_SHADOW_BASE + HLSQ_SAMPLER_OFFSET) / 4); + *cmd++ = + (drawctxt->gpustate.gpuaddr + + VS_TEX_OFFSET_SAMPLER_OBJ) & 0xfffffffc; + + /* Save FS texture memory objects */ + *cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2); + *cmd++ = + ((TEX_SIZE_MEM_OBJECTS / 4) << REG_TO_MEM_LOOP_COUNT_SHIFT) | + ((HLSQ_SHADOW_BASE + HLSQ_MEMOBJ_OFFSET + SSIZE) / 4); + *cmd++ = + (drawctxt->gpustate.gpuaddr + + FS_TEX_OFFSET_MEM_OBJECTS) & 0xfffffffc; + + /* Save FS texture mipmap pointers */ + *cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2); + *cmd++ = + ((TEX_SIZE_MIPMAP / 4) << REG_TO_MEM_LOOP_COUNT_SHIFT) | + ((HLSQ_SHADOW_BASE + HLSQ_MIPMAP_OFFSET + SSIZE) / 4); + *cmd++ = + (drawctxt->gpustate.gpuaddr + FS_TEX_OFFSET_MIPMAP) & 0xfffffffc; + + /* Save FS texture sampler objects */ + *cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2); + *cmd++ = + ((TEX_SIZE_SAMPLER_OBJ / 4) << REG_TO_MEM_LOOP_COUNT_SHIFT) | + ((HLSQ_SHADOW_BASE + HLSQ_SAMPLER_OFFSET + SSIZE) / 4); + *cmd++ = + (drawctxt->gpustate.gpuaddr + + FS_TEX_OFFSET_SAMPLER_OBJ) & 0xfffffffc; + + /* Create indirect buffer command for above command sequence */ + create_ib1(drawctxt, drawctxt->regconstant_save, start, cmd); + + tmp_ctx.cmd = cmd; +} + +/* Copy GMEM contents to system memory shadow. */ +static unsigned int *build_gmem2sys_cmds(struct adreno_device *adreno_dev, + struct adreno_context *drawctxt, + struct gmem_shadow_t *shadow) +{ + unsigned int *cmds = tmp_ctx.cmd; + unsigned int *start = cmds; + + *cmds++ = cp_type0_packet(A3XX_RBBM_CLOCK_CTL, 1); + *cmds++ = A3XX_RBBM_CLOCK_CTL_DEFAULT; + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3); + *cmds++ = CP_REG(A3XX_RB_MODE_CONTROL); + + /* RB_MODE_CONTROL */ + *cmds++ = _SET(RB_MODECONTROL_RENDER_MODE, RB_RESOLVE_PASS) | + _SET(RB_MODECONTROL_MARB_CACHE_SPLIT_MODE, 1) | + _SET(RB_MODECONTROL_PACKER_TIMER_ENABLE, 1); + /* RB_RENDER_CONTROL */ + *cmds++ = _SET(RB_RENDERCONTROL_BIN_WIDTH, shadow->width >> 5) | + _SET(RB_RENDERCONTROL_DISABLE_COLOR_PIPE, 1); + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 5); + *cmds++ = CP_REG(A3XX_RB_COPY_CONTROL); + /* RB_COPY_CONTROL */ + *cmds++ = _SET(RB_COPYCONTROL_RESOLVE_CLEAR_MODE, + RB_CLEAR_MODE_RESOLVE) | + _SET(RB_COPYCONTROL_COPY_GMEM_BASE, + tmp_ctx.gmem_base >> 14); + /* RB_COPY_DEST_BASE */ + *cmds++ = _SET(RB_COPYDESTBASE_COPY_DEST_BASE, + shadow->gmemshadow.gpuaddr >> 5); + /* RB_COPY_DEST_PITCH */ + *cmds++ = _SET(RB_COPYDESTPITCH_COPY_DEST_PITCH, + (shadow->pitch * 4) / 32); + /* RB_COPY_DEST_INFO */ + *cmds++ = _SET(RB_COPYDESTINFO_COPY_DEST_TILE, + RB_TILINGMODE_LINEAR) | + _SET(RB_COPYDESTINFO_COPY_DEST_FORMAT, RB_R8G8B8A8_UNORM) | + _SET(RB_COPYDESTINFO_COPY_COMPONENT_ENABLE, 0X0F) | + _SET(RB_COPYDESTINFO_COPY_DEST_ENDIAN, RB_ENDIAN_NONE); + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); + *cmds++ = CP_REG(A3XX_GRAS_SC_CONTROL); + /* GRAS_SC_CONTROL */ + *cmds++ = _SET(GRAS_SC_CONTROL_RENDER_MODE, 2); + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3); + *cmds++ = CP_REG(A3XX_VFD_CONTROL_0); + /* VFD_CONTROL_0 */ + *cmds++ = _SET(VFD_CTRLREG0_TOTALATTRTOVS, 4) | + _SET(VFD_CTRLREG0_PACKETSIZE, 2) | + _SET(VFD_CTRLREG0_STRMDECINSTRCNT, 1) | + _SET(VFD_CTRLREG0_STRMFETCHINSTRCNT, 1); + /* VFD_CONTROL_1 */ + *cmds++ = _SET(VFD_CTRLREG1_MAXSTORAGE, 1) | + _SET(VFD_CTRLREG1_REGID4VTX, 252) | + _SET(VFD_CTRLREG1_REGID4INST, 252); + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3); + *cmds++ = CP_REG(A3XX_VFD_FETCH_INSTR_0_0); + /* VFD_FETCH_INSTR_0_0 */ + *cmds++ = _SET(VFD_FETCHINSTRUCTIONS_FETCHSIZE, 11) | + _SET(VFD_FETCHINSTRUCTIONS_BUFSTRIDE, 12) | + _SET(VFD_FETCHINSTRUCTIONS_STEPRATE, 1); + /* VFD_FETCH_INSTR_1_0 */ + *cmds++ = _SET(VFD_BASEADDR_BASEADDR, + shadow->quad_vertices.gpuaddr); + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); + *cmds++ = CP_REG(A3XX_VFD_DECODE_INSTR_0); + /* VFD_DECODE_INSTR_0 */ + *cmds++ = _SET(VFD_DECODEINSTRUCTIONS_WRITEMASK, 0x0F) | + _SET(VFD_DECODEINSTRUCTIONS_CONSTFILL, 1) | + _SET(VFD_DECODEINSTRUCTIONS_FORMAT, 2) | + _SET(VFD_DECODEINSTRUCTIONS_SHIFTCNT, 12) | + _SET(VFD_DECODEINSTRUCTIONS_LASTCOMPVALID, 1); + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 5); + *cmds++ = CP_REG(A3XX_HLSQ_CONTROL_0_REG); + /* HLSQ_CONTROL_0_REG */ + *cmds++ = _SET(HLSQ_CTRL0REG_FSTHREADSIZE, HLSQ_FOUR_PIX_QUADS) | + _SET(HLSQ_CTRL0REG_FSSUPERTHREADENABLE, 1) | + _SET(HLSQ_CTRL0REG_RESERVED2, 1) | + _SET(HLSQ_CTRL0REG_SPCONSTFULLUPDATE, 1); + /* HLSQ_CONTROL_1_REG */ + *cmds++ = _SET(HLSQ_CTRL1REG_VSTHREADSIZE, HLSQ_TWO_VTX_QUADS) | + _SET(HLSQ_CTRL1REG_VSSUPERTHREADENABLE, 1); + /* HLSQ_CONTROL_2_REG */ + *cmds++ = _SET(HLSQ_CTRL2REG_PRIMALLOCTHRESHOLD, 31); + /* HLSQ_CONTROL_3_REG */ + *cmds++ = 0x00000000; + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 5); + *cmds++ = CP_REG(A3XX_HLSQ_VS_CONTROL_REG); + /* HLSQ_VS_CONTROL_REG */ + *cmds++ = _SET(HLSQ_VSCTRLREG_VSINSTRLENGTH, 1); + /* HLSQ_FS_CONTROL_REG */ + *cmds++ = _SET(HLSQ_FSCTRLREG_FSCONSTLENGTH, 1) | + _SET(HLSQ_FSCTRLREG_FSCONSTSTARTOFFSET, 128) | + _SET(HLSQ_FSCTRLREG_FSINSTRLENGTH, 1); + /* HLSQ_CONST_VSPRESV_RANGE_REG */ + *cmds++ = 0x00000000; + /* HLSQ_CONST_FSPRESV_RANGE_REQ */ + *cmds++ = _SET(HLSQ_CONSTFSPRESERVEDRANGEREG_STARTENTRY, 32) | + _SET(HLSQ_CONSTFSPRESERVEDRANGEREG_ENDENTRY, 32); + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); + *cmds++ = CP_REG(A3XX_SP_FS_LENGTH_REG); + /* SP_FS_LENGTH_REG */ + *cmds++ = _SET(SP_SHADERLENGTH_LEN, 1); + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); + *cmds++ = CP_REG(A3XX_SP_SP_CTRL_REG); + /* SP_SP_CTRL_REG */ + *cmds++ = _SET(SP_SPCTRLREG_SLEEPMODE, 1) | + _SET(SP_SPCTRLREG_LOMODE, 1); + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 12); + *cmds++ = CP_REG(A3XX_SP_VS_CTRL_REG0); + /* SP_VS_CTRL_REG0 */ + *cmds++ = _SET(SP_VSCTRLREG0_VSTHREADMODE, SP_MULTI) | + _SET(SP_VSCTRLREG0_VSINSTRBUFFERMODE, SP_BUFFER_MODE) | + _SET(SP_VSCTRLREG0_VSICACHEINVALID, 1) | + _SET(SP_VSCTRLREG0_VSFULLREGFOOTPRINT, 1) | + _SET(SP_VSCTRLREG0_VSTHREADSIZE, SP_TWO_VTX_QUADS) | + _SET(SP_VSCTRLREG0_VSSUPERTHREADMODE, 1) | + _SET(SP_VSCTRLREG0_VSLENGTH, 1); + /* SP_VS_CTRL_REG1 */ + *cmds++ = _SET(SP_VSCTRLREG1_VSINITIALOUTSTANDING, 4); + /* SP_VS_PARAM_REG */ + *cmds++ = _SET(SP_VSPARAMREG_PSIZEREGID, 252); + /* SP_VS_OUT_REG_0 */ + *cmds++ = 0x00000000; + /* SP_VS_OUT_REG_1 */ + *cmds++ = 0x00000000; + /* SP_VS_OUT_REG_2 */ + *cmds++ = 0x00000000; + /* SP_VS_OUT_REG_3 */ + *cmds++ = 0x00000000; + /* SP_VS_OUT_REG_4 */ + *cmds++ = 0x00000000; + /* SP_VS_OUT_REG_5 */ + *cmds++ = 0x00000000; + /* SP_VS_OUT_REG_6 */ + *cmds++ = 0x00000000; + /* SP_VS_OUT_REG_7 */ + *cmds++ = 0x00000000; + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 7); + *cmds++ = CP_REG(A3XX_SP_VS_VPC_DST_REG_0); + /* SP_VS_VPC_DST_REG_0 */ + *cmds++ = 0x00000000; + /* SP_VS_VPC_DST_REG_1 */ + *cmds++ = 0x00000000; + /* SP_VS_VPC_DST_REG_2 */ + *cmds++ = 0x00000000; + /* SP_VS_VPC_DST_REG_3 */ + *cmds++ = 0x00000000; + /* SP_VS_OBJ_OFFSET_REG */ + *cmds++ = 0x00000000; + /* SP_VS_OBJ_START_REG */ + *cmds++ = 0x00000000; + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 6); + *cmds++ = CP_REG(A3XX_SP_VS_LENGTH_REG); + /* SP_VS_LENGTH_REG */ + *cmds++ = _SET(SP_SHADERLENGTH_LEN, 1); + /* SP_FS_CTRL_REG0 */ + *cmds++ = _SET(SP_FSCTRLREG0_FSTHREADMODE, SP_MULTI) | + _SET(SP_FSCTRLREG0_FSINSTRBUFFERMODE, SP_BUFFER_MODE) | + _SET(SP_FSCTRLREG0_FSICACHEINVALID, 1) | + _SET(SP_FSCTRLREG0_FSHALFREGFOOTPRINT, 1) | + _SET(SP_FSCTRLREG0_FSINOUTREGOVERLAP, 1) | + _SET(SP_FSCTRLREG0_FSTHREADSIZE, SP_FOUR_PIX_QUADS) | + _SET(SP_FSCTRLREG0_FSSUPERTHREADMODE, 1) | + _SET(SP_FSCTRLREG0_FSLENGTH, 1); + /* SP_FS_CTRL_REG1 */ + *cmds++ = _SET(SP_FSCTRLREG1_FSCONSTLENGTH, 1) | + _SET(SP_FSCTRLREG1_HALFPRECVAROFFSET, 63); + /* SP_FS_OBJ_OFFSET_REG */ + *cmds++ = _SET(SP_OBJOFFSETREG_CONSTOBJECTSTARTOFFSET, 128) | + _SET(SP_OBJOFFSETREG_SHADEROBJOFFSETINIC, 127); + /* SP_FS_OBJ_START_REG */ + *cmds++ = 0x00000000; + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3); + *cmds++ = CP_REG(A3XX_SP_FS_FLAT_SHAD_MODE_REG_0); + /* SP_FS_FLAT_SHAD_MODE_REG_0 */ + *cmds++ = 0x00000000; + /* SP_FS_FLAT_SHAD_MODE_REG_1 */ + *cmds++ = 0x00000000; + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); + *cmds++ = CP_REG(A3XX_SP_FS_OUTPUT_REG); + /* SP_FS_OUTPUT_REG */ + *cmds++ = _SET(SP_IMAGEOUTPUTREG_DEPTHOUTMODE, SP_PIXEL_BASED); + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 5); + *cmds++ = CP_REG(A3XX_SP_FS_MRT_REG_0); + /* SP_FS_MRT_REG_0 */ + *cmds++ = _SET(SP_FSMRTREG_PRECISION, 1); + + /* SP_FS_MRT_REG_1 */ + *cmds++ = 0x00000000; + /* SP_FS_MRT_REG_2 */ + *cmds++ = 0x00000000; + /* SP_FS_MRT_REG_3 */ + *cmds++ = 0x00000000; + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 11); + *cmds++ = CP_REG(A3XX_VPC_ATTR); + /* VPC_ATTR */ + *cmds++ = _SET(VPC_VPCATTR_THRHDASSIGN, 1) | + _SET(VPC_VPCATTR_LMSIZE, 1); + /* VPC_PACK */ + *cmds++ = 0x00000000; + /* VPC_VARRYING_INTERUPT_MODE_0 */ + *cmds++ = 0x00000000; + /* VPC_VARRYING_INTERUPT_MODE_1 */ + *cmds++ = 0x00000000; + /* VPC_VARRYING_INTERUPT_MODE_2 */ + *cmds++ = 0x00000000; + /* VPC_VARRYING_INTERUPT_MODE_3 */ + *cmds++ = 0x00000000; + /* VPC_VARYING_PS_REPL_MODE_0 */ + *cmds++ = 0x00000000; + /* VPC_VARYING_PS_REPL_MODE_1 */ + *cmds++ = 0x00000000; + /* VPC_VARYING_PS_REPL_MODE_2 */ + *cmds++ = 0x00000000; + /* VPC_VARYING_PS_REPL_MODE_3 */ + *cmds++ = 0x00000000; + + *cmds++ = cp_type3_packet(CP_LOAD_STATE, 10); + *cmds++ = (0 << CP_LOADSTATE_DSTOFFSET_SHIFT) + | (HLSQ_DIRECT << CP_LOADSTATE_STATESRC_SHIFT) + | (HLSQ_BLOCK_ID_SP_VS << CP_LOADSTATE_STATEBLOCKID_SHIFT) + | (1 << CP_LOADSTATE_NUMOFUNITS_SHIFT); + *cmds++ = (HLSQ_SP_VS_INSTR << CP_LOADSTATE_STATETYPE_SHIFT) + | (0 << CP_LOADSTATE_EXTSRCADDR_SHIFT); + + /* (sy)(rpt3)mov.f32f32 r0.y, (r)r1.y; */ + *cmds++ = 0x00000000; *cmds++ = 0x13001000; + /* end; */ + *cmds++ = 0x00000000; *cmds++ = 0x00000000; + /* nop; */ + *cmds++ = 0x00000000; *cmds++ = 0x00000000; + /* nop; */ + *cmds++ = 0x00000000; *cmds++ = 0x00000000; + + + *cmds++ = cp_type0_packet(A3XX_VFD_PERFCOUNTER0_SELECT, 1); + *cmds++ = 0x00000000; + + *cmds++ = cp_type3_packet(CP_WAIT_FOR_IDLE, 1); + *cmds++ = 0x00000000; + + *cmds++ = cp_type3_packet(CP_LOAD_STATE, 10); + *cmds++ = (0 << CP_LOADSTATE_DSTOFFSET_SHIFT) + | (HLSQ_DIRECT << CP_LOADSTATE_STATESRC_SHIFT) + | (HLSQ_BLOCK_ID_SP_FS << CP_LOADSTATE_STATEBLOCKID_SHIFT) + | (1 << CP_LOADSTATE_NUMOFUNITS_SHIFT); + *cmds++ = (HLSQ_SP_FS_INSTR << CP_LOADSTATE_STATETYPE_SHIFT) + | (0 << CP_LOADSTATE_EXTSRCADDR_SHIFT); + + /* (sy)(rpt3)mov.f32f32 r0.y, (r)c0.x; */ + *cmds++ = 0x00000000; *cmds++ = 0x30201b00; + /* end; */ + *cmds++ = 0x00000000; *cmds++ = 0x03000000; + /* nop; */ + *cmds++ = 0x00000000; *cmds++ = 0x00000000; + /* nop; */ + *cmds++ = 0x00000000; *cmds++ = 0x00000000; + + + + *cmds++ = cp_type0_packet(A3XX_VFD_PERFCOUNTER0_SELECT, 1); + *cmds++ = 0x00000000; + + *cmds++ = cp_type3_packet(CP_WAIT_FOR_IDLE, 1); + *cmds++ = 0x00000000; + + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); + *cmds++ = CP_REG(A3XX_RB_MSAA_CONTROL); + /* RB_MSAA_CONTROL */ + *cmds++ = _SET(RB_MSAACONTROL_MSAA_DISABLE, 1) | + _SET(RB_MSAACONTROL_SAMPLE_MASK, 0xFFFF); + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); + *cmds++ = CP_REG(A3XX_RB_DEPTH_CONTROL); + /* RB_DEPTH_CONTROL */ + *cmds++ = _SET(RB_DEPTHCONTROL_Z_TEST_FUNC, RB_FRAG_NEVER); + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); + *cmds++ = CP_REG(A3XX_RB_STENCIL_CONTROL); + /* RB_STENCIL_CONTROL */ + *cmds++ = _SET(RB_STENCILCONTROL_STENCIL_FUNC, RB_REF_NEVER) | + _SET(RB_STENCILCONTROL_STENCIL_FAIL, RB_STENCIL_KEEP) | + _SET(RB_STENCILCONTROL_STENCIL_ZPASS, RB_STENCIL_KEEP) | + _SET(RB_STENCILCONTROL_STENCIL_ZFAIL, RB_STENCIL_KEEP) | + _SET(RB_STENCILCONTROL_STENCIL_FUNC_BF, RB_REF_NEVER) | + _SET(RB_STENCILCONTROL_STENCIL_FAIL_BF, RB_STENCIL_KEEP) | + _SET(RB_STENCILCONTROL_STENCIL_ZPASS_BF, RB_STENCIL_KEEP) | + _SET(RB_STENCILCONTROL_STENCIL_ZFAIL_BF, RB_STENCIL_KEEP); + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); + *cmds++ = CP_REG(A3XX_GRAS_SU_MODE_CONTROL); + /* GRAS_SU_MODE_CONTROL */ + *cmds++ = 0x00000000; + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); + *cmds++ = CP_REG(A3XX_RB_MRT_CONTROL0); + /* RB_MRT_CONTROL0 */ + *cmds++ = _SET(RB_MRTCONTROL_READ_DEST_ENABLE, 1) | + _SET(RB_MRTCONTROL_ROP_CODE, 12) | + _SET(RB_MRTCONTROL_DITHER_MODE, RB_DITHER_ALWAYS) | + _SET(RB_MRTCONTROL_COMPONENT_ENABLE, 0xF); + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3); + *cmds++ = CP_REG(A3XX_RB_MRT_BLEND_CONTROL0); + /* RB_MRT_BLEND_CONTROL0 */ + *cmds++ = _SET(RB_MRTBLENDCONTROL_RGB_SRC_FACTOR, RB_FACTOR_ONE) | + _SET(RB_MRTBLENDCONTROL_RGB_BLEND_OPCODE, RB_BLEND_OP_ADD) | + _SET(RB_MRTBLENDCONTROL_RGB_DEST_FACTOR, RB_FACTOR_ZERO) | + _SET(RB_MRTBLENDCONTROL_ALPHA_SRC_FACTOR, RB_FACTOR_ONE) | + _SET(RB_MRTBLENDCONTROL_ALPHA_DEST_FACTOR, RB_FACTOR_ZERO) | + _SET(RB_MRTBLENDCONTROL_CLAMP_ENABLE, 1); + /* RB_MRT_CONTROL1 */ + *cmds++ = _SET(RB_MRTCONTROL_READ_DEST_ENABLE, 1) | + _SET(RB_MRTCONTROL_DITHER_MODE, RB_DITHER_DISABLE) | + _SET(RB_MRTCONTROL_COMPONENT_ENABLE, 0xF); + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3); + *cmds++ = CP_REG(A3XX_RB_MRT_BLEND_CONTROL1); + /* RB_MRT_BLEND_CONTROL1 */ + *cmds++ = _SET(RB_MRTBLENDCONTROL_RGB_SRC_FACTOR, RB_FACTOR_ONE) | + _SET(RB_MRTBLENDCONTROL_RGB_BLEND_OPCODE, RB_BLEND_OP_ADD) | + _SET(RB_MRTBLENDCONTROL_RGB_DEST_FACTOR, RB_FACTOR_ZERO) | + _SET(RB_MRTBLENDCONTROL_ALPHA_SRC_FACTOR, RB_FACTOR_ONE) | + _SET(RB_MRTBLENDCONTROL_ALPHA_DEST_FACTOR, RB_FACTOR_ZERO) | + _SET(RB_MRTBLENDCONTROL_CLAMP_ENABLE, 1); + /* RB_MRT_CONTROL2 */ + *cmds++ = _SET(RB_MRTCONTROL_READ_DEST_ENABLE, 1) | + _SET(RB_MRTCONTROL_DITHER_MODE, RB_DITHER_DISABLE) | + _SET(RB_MRTCONTROL_COMPONENT_ENABLE, 0xF); + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3); + *cmds++ = CP_REG(A3XX_RB_MRT_BLEND_CONTROL2); + /* RB_MRT_BLEND_CONTROL2 */ + *cmds++ = _SET(RB_MRTBLENDCONTROL_RGB_SRC_FACTOR, RB_FACTOR_ONE) | + _SET(RB_MRTBLENDCONTROL_RGB_BLEND_OPCODE, RB_BLEND_OP_ADD) | + _SET(RB_MRTBLENDCONTROL_RGB_DEST_FACTOR, RB_FACTOR_ZERO) | + _SET(RB_MRTBLENDCONTROL_ALPHA_SRC_FACTOR, RB_FACTOR_ONE) | + _SET(RB_MRTBLENDCONTROL_ALPHA_DEST_FACTOR, RB_FACTOR_ZERO) | + _SET(RB_MRTBLENDCONTROL_CLAMP_ENABLE, 1); + /* RB_MRT_CONTROL3 */ + *cmds++ = _SET(RB_MRTCONTROL_READ_DEST_ENABLE, 1) | + _SET(RB_MRTCONTROL_DITHER_MODE, RB_DITHER_DISABLE) | + _SET(RB_MRTCONTROL_COMPONENT_ENABLE, 0xF); + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); + *cmds++ = CP_REG(A3XX_RB_MRT_BLEND_CONTROL3); + /* RB_MRT_BLEND_CONTROL3 */ + *cmds++ = _SET(RB_MRTBLENDCONTROL_RGB_SRC_FACTOR, RB_FACTOR_ONE) | + _SET(RB_MRTBLENDCONTROL_RGB_BLEND_OPCODE, RB_BLEND_OP_ADD) | + _SET(RB_MRTBLENDCONTROL_RGB_DEST_FACTOR, RB_FACTOR_ZERO) | + _SET(RB_MRTBLENDCONTROL_ALPHA_SRC_FACTOR, RB_FACTOR_ONE) | + _SET(RB_MRTBLENDCONTROL_ALPHA_DEST_FACTOR, RB_FACTOR_ZERO) | + _SET(RB_MRTBLENDCONTROL_CLAMP_ENABLE, 1); + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 5); + *cmds++ = CP_REG(A3XX_VFD_INDEX_MIN); + /* VFD_INDEX_MIN */ + *cmds++ = 0x00000000; + /* VFD_INDEX_MAX */ + *cmds++ = 0x155; + /* VFD_INSTANCEID_OFFSET */ + *cmds++ = 0x00000000; + /* VFD_INDEX_OFFSET */ + *cmds++ = 0x00000000; + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); + *cmds++ = CP_REG(A3XX_VFD_VS_THREADING_THRESHOLD); + /* VFD_VS_THREADING_THRESHOLD */ + *cmds++ = _SET(VFD_THREADINGTHRESHOLD_REGID_THRESHOLD, 15) | + _SET(VFD_THREADINGTHRESHOLD_REGID_VTXCNT, 252); + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); + *cmds++ = CP_REG(A3XX_TPL1_TP_VS_TEX_OFFSET); + /* TPL1_TP_VS_TEX_OFFSET */ + *cmds++ = 0; + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); + *cmds++ = CP_REG(A3XX_TPL1_TP_FS_TEX_OFFSET); + /* TPL1_TP_FS_TEX_OFFSET */ + *cmds++ = _SET(TPL1_TPTEXOFFSETREG_SAMPLEROFFSET, 16) | + _SET(TPL1_TPTEXOFFSETREG_MEMOBJOFFSET, 16) | + _SET(TPL1_TPTEXOFFSETREG_BASETABLEPTR, 224); + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); + *cmds++ = CP_REG(A3XX_PC_PRIM_VTX_CNTL); + /* PC_PRIM_VTX_CNTL */ + *cmds++ = _SET(PC_PRIM_VTX_CONTROL_POLYMODE_FRONT_PTYPE, + PC_DRAW_TRIANGLES) | + _SET(PC_PRIM_VTX_CONTROL_POLYMODE_BACK_PTYPE, + PC_DRAW_TRIANGLES) | + _SET(PC_PRIM_VTX_CONTROL_PROVOKING_VTX_LAST, 1); + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3); + *cmds++ = CP_REG(A3XX_GRAS_SC_WINDOW_SCISSOR_TL); + /* GRAS_SC_WINDOW_SCISSOR_TL */ + *cmds++ = 0x00000000; + /* GRAS_SC_WINDOW_SCISSOR_BR */ + *cmds++ = _SET(GRAS_SC_WINDOW_SCISSOR_BR_BR_X, shadow->width - 1) | + _SET(GRAS_SC_WINDOW_SCISSOR_BR_BR_Y, shadow->height - 1); + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3); + *cmds++ = CP_REG(A3XX_GRAS_SC_SCREEN_SCISSOR_TL); + /* GRAS_SC_SCREEN_SCISSOR_TL */ + *cmds++ = 0x00000000; + /* GRAS_SC_SCREEN_SCISSOR_BR */ + *cmds++ = _SET(GRAS_SC_SCREEN_SCISSOR_BR_BR_X, shadow->width - 1) | + _SET(GRAS_SC_SCREEN_SCISSOR_BR_BR_Y, shadow->height - 1); + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 5); + *cmds++ = CP_REG(A3XX_GRAS_CL_VPORT_XOFFSET); + /* GRAS_CL_VPORT_XOFFSET */ + *cmds++ = 0x00000000; + /* GRAS_CL_VPORT_XSCALE */ + *cmds++ = _SET(GRAS_CL_VPORT_XSCALE_VPORT_XSCALE, 0x3f800000); + /* GRAS_CL_VPORT_YOFFSET */ + *cmds++ = 0x00000000; + /* GRAS_CL_VPORT_YSCALE */ + *cmds++ = _SET(GRAS_CL_VPORT_YSCALE_VPORT_YSCALE, 0x3f800000); + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3); + *cmds++ = CP_REG(A3XX_GRAS_CL_VPORT_ZOFFSET); + /* GRAS_CL_VPORT_ZOFFSET */ + *cmds++ = 0x00000000; + /* GRAS_CL_VPORT_ZSCALE */ + *cmds++ = _SET(GRAS_CL_VPORT_ZSCALE_VPORT_ZSCALE, 0x3f800000); + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); + *cmds++ = CP_REG(A3XX_GRAS_CL_CLIP_CNTL); + /* GRAS_CL_CLIP_CNTL */ + *cmds++ = _SET(GRAS_CL_CLIP_CNTL_CLIP_DISABLE, 1) | + _SET(GRAS_CL_CLIP_CNTL_ZFAR_CLIP_DISABLE, 1) | + _SET(GRAS_CL_CLIP_CNTL_VP_CLIP_CODE_IGNORE, 1) | + _SET(GRAS_CL_CLIP_CNTL_VP_XFORM_DISABLE, 1) | + _SET(GRAS_CL_CLIP_CNTL_PERSP_DIVISION_DISABLE, 1); + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); + *cmds++ = CP_REG(A3XX_GRAS_CL_GB_CLIP_ADJ); + /* GRAS_CL_GB_CLIP_ADJ */ + *cmds++ = 0x00000000; + + *cmds++ = cp_type3_packet(CP_WAIT_FOR_IDLE, 1); + *cmds++ = 0x00000000; + + + /* oxili_generate_context_roll_packets */ + *cmds++ = cp_type0_packet(A3XX_SP_VS_CTRL_REG0, 1); + *cmds++ = 0x00000400; + + *cmds++ = cp_type0_packet(A3XX_SP_FS_CTRL_REG0, 1); + *cmds++ = 0x00000400; + + *cmds++ = cp_type0_packet(A3XX_SP_VS_PVT_MEM_SIZE_REG, 1); + *cmds++ = 0x00008000; /* SP_VS_MEM_SIZE_REG */ + + *cmds++ = cp_type0_packet(A3XX_SP_FS_PVT_MEM_SIZE_REG, 1); + *cmds++ = 0x00008000; /* SP_FS_MEM_SIZE_REG */ + + /* Clear cache invalidate bit when re-loading the shader control regs */ + *cmds++ = cp_type0_packet(A3XX_SP_VS_CTRL_REG0, 1); + *cmds++ = _SET(SP_VSCTRLREG0_VSTHREADMODE, SP_MULTI) | + _SET(SP_VSCTRLREG0_VSINSTRBUFFERMODE, SP_BUFFER_MODE) | + _SET(SP_VSCTRLREG0_VSFULLREGFOOTPRINT, 1) | + _SET(SP_VSCTRLREG0_VSTHREADSIZE, SP_TWO_VTX_QUADS) | + _SET(SP_VSCTRLREG0_VSSUPERTHREADMODE, 1) | + _SET(SP_VSCTRLREG0_VSLENGTH, 1); + + *cmds++ = cp_type0_packet(A3XX_SP_FS_CTRL_REG0, 1); + *cmds++ = _SET(SP_FSCTRLREG0_FSTHREADMODE, SP_MULTI) | + _SET(SP_FSCTRLREG0_FSINSTRBUFFERMODE, SP_BUFFER_MODE) | + _SET(SP_FSCTRLREG0_FSHALFREGFOOTPRINT, 1) | + _SET(SP_FSCTRLREG0_FSINOUTREGOVERLAP, 1) | + _SET(SP_FSCTRLREG0_FSTHREADSIZE, SP_FOUR_PIX_QUADS) | + _SET(SP_FSCTRLREG0_FSSUPERTHREADMODE, 1) | + _SET(SP_FSCTRLREG0_FSLENGTH, 1); + + *cmds++ = cp_type0_packet(A3XX_SP_VS_PVT_MEM_SIZE_REG, 1); + *cmds++ = 0x00000000; /* SP_VS_MEM_SIZE_REG */ + + *cmds++ = cp_type0_packet(A3XX_SP_FS_PVT_MEM_SIZE_REG, 1); + *cmds++ = 0x00000000; /* SP_FS_MEM_SIZE_REG */ + + /* end oxili_generate_context_roll_packets */ + + /* + * Resolve using two draw calls with a dummy register + * write in between. This is a HLM workaround + * that should be removed later. + */ + *cmds++ = cp_type3_packet(CP_DRAW_INDX_2, 6); + *cmds++ = 0x00000000; /* Viz query info */ + *cmds++ = BUILD_PC_DRAW_INITIATOR(PC_DI_PT_TRILIST, + PC_DI_SRC_SEL_IMMEDIATE, + PC_DI_INDEX_SIZE_32_BIT, + PC_DI_IGNORE_VISIBILITY); + *cmds++ = 0x00000003; /* Num indices */ + *cmds++ = 0x00000000; /* Index 0 */ + *cmds++ = 0x00000001; /* Index 1 */ + *cmds++ = 0x00000002; /* Index 2 */ + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); + *cmds++ = CP_REG(A3XX_HLSQ_CL_CONTROL_0_REG); + *cmds++ = 0x00000000; + + *cmds++ = cp_type3_packet(CP_DRAW_INDX_2, 6); + *cmds++ = 0x00000000; /* Viz query info */ + *cmds++ = BUILD_PC_DRAW_INITIATOR(PC_DI_PT_TRILIST, + PC_DI_SRC_SEL_IMMEDIATE, + PC_DI_INDEX_SIZE_32_BIT, + PC_DI_IGNORE_VISIBILITY); + *cmds++ = 0x00000003; /* Num indices */ + *cmds++ = 0x00000002; /* Index 0 */ + *cmds++ = 0x00000001; /* Index 1 */ + *cmds++ = 0x00000003; /* Index 2 */ + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); + *cmds++ = CP_REG(A3XX_HLSQ_CL_CONTROL_0_REG); + *cmds++ = 0x00000000; + + *cmds++ = cp_type3_packet(CP_WAIT_FOR_IDLE, 1); + *cmds++ = 0x00000000; + + /* Create indirect buffer command for above command sequence */ + create_ib1(drawctxt, shadow->gmem_save, start, cmds); + + return cmds; +} +static void build_shader_save_cmds(struct adreno_device *adreno_dev, + struct adreno_context *drawctxt) +{ + unsigned int *cmd = tmp_ctx.cmd; + unsigned int *start; + + /* Reserve space for boolean values used for COND_EXEC packet */ + drawctxt->cond_execs[0].hostptr = cmd; + drawctxt->cond_execs[0].gpuaddr = virt2gpu(cmd, &drawctxt->gpustate); + *cmd++ = 0; + drawctxt->cond_execs[1].hostptr = cmd; + drawctxt->cond_execs[1].gpuaddr = virt2gpu(cmd, &drawctxt->gpustate); + *cmd++ = 0; + + drawctxt->shader_save_commands[0].hostptr = cmd; + drawctxt->shader_save_commands[0].gpuaddr = + virt2gpu(cmd, &drawctxt->gpustate); + *cmd++ = 0; + drawctxt->shader_save_commands[1].hostptr = cmd; + drawctxt->shader_save_commands[1].gpuaddr = + virt2gpu(cmd, &drawctxt->gpustate); + *cmd++ = 0; + + start = cmd; + + /* Save vertex shader */ + + *cmd++ = cp_type3_packet(CP_COND_EXEC, 4); + *cmd++ = drawctxt->cond_execs[0].gpuaddr >> 2; + *cmd++ = drawctxt->cond_execs[0].gpuaddr >> 2; + *cmd++ = 0x0000FFFF; + *cmd++ = 3; /* EXEC_COUNT */ + + *cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2); + drawctxt->shader_save_commands[2].hostptr = cmd; + drawctxt->shader_save_commands[2].gpuaddr = + virt2gpu(cmd, &drawctxt->gpustate); + /* + From fixup: + + dwords = SP_VS_CTRL_REG0.VS_LENGTH * 8 + + From regspec: + SP_VS_CTRL_REG0.VS_LENGTH [31:24]: VS length, unit = 256bits. + If bit31 is 1, it means overflow + or any long shader. + + src = (HLSQ_SHADOW_BASE + 0x1000)/4 + */ + *cmd++ = 0; /*(dwords << REG_TO_MEM_LOOP_COUNT_SHIFT) | src */ + *cmd++ = (drawctxt->gpustate.gpuaddr + SHADER_OFFSET) & 0xfffffffc; + + /* Save fragment shader */ + *cmd++ = cp_type3_packet(CP_COND_EXEC, 4); + *cmd++ = drawctxt->cond_execs[1].gpuaddr >> 2; + *cmd++ = drawctxt->cond_execs[1].gpuaddr >> 2; + *cmd++ = 0x0000FFFF; + *cmd++ = 3; /* EXEC_COUNT */ + + *cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2); + drawctxt->shader_save_commands[3].hostptr = cmd; + drawctxt->shader_save_commands[3].gpuaddr = + virt2gpu(cmd, &drawctxt->gpustate); + /* + From fixup: + + dwords = SP_FS_CTRL_REG0.FS_LENGTH * 8 + + From regspec: + SP_FS_CTRL_REG0.FS_LENGTH [31:24]: FS length, unit = 256bits. + If bit31 is 1, it means overflow + or any long shader. + + fs_offset = SP_FS_OBJ_OFFSET_REG.SHADEROBJOFFSETINIC * 32 + From regspec: + + SP_FS_OBJ_OFFSET_REG.SHADEROBJOFFSETINIC [31:25]: + First instruction of the whole shader will be stored from + the offset in instruction cache, unit = 256bits, a cache line. + It can start from 0 if no VS available. + + src = (HLSQ_SHADOW_BASE + 0x1000 + SSIZE + fs_offset)/4 + */ + *cmd++ = 0; /*(dwords << REG_TO_MEM_LOOP_COUNT_SHIFT) | src */ + *cmd++ = (drawctxt->gpustate.gpuaddr + SHADER_OFFSET + + (SHADER_SHADOW_SIZE / 2)) & 0xfffffffc; + + /* Create indirect buffer command for above command sequence */ + create_ib1(drawctxt, drawctxt->shader_save, start, cmd); + + tmp_ctx.cmd = cmd; +} + +/* + * Make an IB to modify context save IBs with the correct shader instruction + * and constant sizes and offsets. + */ + +static void build_save_fixup_cmds(struct adreno_device *adreno_dev, + struct adreno_context *drawctxt) +{ + unsigned int *cmd = tmp_ctx.cmd; + unsigned int *start = cmd; + + /* Flush HLSQ lazy updates */ + *cmd++ = cp_type3_packet(CP_EVENT_WRITE, 1); + *cmd++ = 0x7; /* HLSQ_FLUSH */ + *cmd++ = cp_type3_packet(CP_WAIT_FOR_IDLE, 1); + *cmd++ = 0; + + *cmd++ = cp_type0_packet(A3XX_UCHE_CACHE_INVALIDATE0_REG, 2); + *cmd++ = 0x00000000; /* No start addr for full invalidate */ + *cmd++ = (unsigned int) + UCHE_ENTIRE_CACHE << UCHE_INVALIDATE1REG_ALLORPORTION | + UCHE_OP_INVALIDATE << UCHE_INVALIDATE1REG_OPCODE | + 0; /* No end addr for full invalidate */ + + /* Make sure registers are flushed */ + *cmd++ = cp_type3_packet(CP_CONTEXT_UPDATE, 1); + *cmd++ = 0; + +#ifdef GSL_CONTEXT_SWITCH_CPU_SYNC + + /* Save shader sizes */ + *cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2); + *cmd++ = A3XX_SP_VS_CTRL_REG0; + *cmd++ = drawctxt->shader_save_commands[2].gpuaddr; + + *cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2); + *cmd++ = A3XX_SP_FS_CTRL_REG0; + *cmd++ = drawctxt->shader_save_commands[3].gpuaddr; + + /* Save shader offsets */ + *cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2); + *cmd++ = A3XX_SP_FS_OBJ_OFFSET_REG; + *cmd++ = drawctxt->shader_save_commands[1].gpuaddr; + + /* Save constant sizes */ + *cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2); + *cmd++ = A3XX_SP_VS_CTRL_REG1; + *cmd++ = drawctxt->constant_save_commands[1].gpuaddr; + *cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2); + *cmd++ = A3XX_SP_FS_CTRL_REG1; + *cmd++ = drawctxt->constant_save_commands[2].gpuaddr; + + /* Save FS constant offset */ + *cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2); + *cmd++ = A3XX_SP_FS_OBJ_OFFSET_REG; + *cmd++ = drawctxt->constant_save_commands[0].gpuaddr; + + + /* Save VS instruction store mode */ + *cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2); + *cmd++ = A3XX_SP_VS_CTRL_REG0; + *cmd++ = drawctxt->cond_execs[0].gpuaddr; + + /* Save FS instruction store mode */ + *cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2); + *cmd++ = A3XX_SP_FS_CTRL_REG0; + *cmd++ = drawctxt->cond_execs[1].gpuaddr; +#else + + /* Shader save */ + cmd = rmw_regtomem(cmd, A3XX_SP_VS_CTRL_REG0, 0x7f000000, + 11+REG_TO_MEM_LOOP_COUNT_SHIFT, + (HLSQ_SHADOW_BASE + 0x1000) / 4, + drawctxt->shader_save_commands[2].gpuaddr); + + /* CP_SCRATCH_REG2 = (CP_SCRATCH_REG2 & 0x00000000) | SP_FS_CTRL_REG0 */ + *cmd++ = cp_type3_packet(CP_REG_RMW, 3); + *cmd++ = (1 << 30) | A3XX_CP_SCRATCH_REG2; + *cmd++ = 0x00000000; /* AND value */ + *cmd++ = A3XX_SP_FS_CTRL_REG0; /* OR address */ + /* CP_SCRATCH_REG2 = ( (CP_SCRATCH_REG2 & 0x7f000000) >> 21 ) + | ((HLSQ_SHADOW_BASE+0x1000+SSIZE)/4) */ + *cmd++ = cp_type3_packet(CP_REG_RMW, 3); + *cmd++ = ((11 + REG_TO_MEM_LOOP_COUNT_SHIFT) << 24) | + A3XX_CP_SCRATCH_REG2; + *cmd++ = 0x7f000000; /* AND value */ + *cmd++ = (HLSQ_SHADOW_BASE + 0x1000 + SSIZE) / 4; /* OR value */ + + /* + * CP_SCRATCH_REG3 = (CP_SCRATCH_REG3 & 0x00000000) | + * SP_FS_OBJ_OFFSET_REG + */ + + *cmd++ = cp_type3_packet(CP_REG_RMW, 3); + *cmd++ = (1 << 30) | A3XX_CP_SCRATCH_REG3; + *cmd++ = 0x00000000; /* AND value */ + *cmd++ = A3XX_SP_FS_OBJ_OFFSET_REG; /* OR address */ + /* + * CP_SCRATCH_REG3 = ( (CP_SCRATCH_REG3 & 0xfe000000) >> 25 ) | + * 0x00000000 + */ + *cmd++ = cp_type3_packet(CP_REG_RMW, 3); + *cmd++ = A3XX_CP_SCRATCH_REG3; + *cmd++ = 0xfe000000; /* AND value */ + *cmd++ = 0x00000000; /* OR value */ + /* + * CP_SCRATCH_REG2 = (CP_SCRATCH_REG2 & 0xffffffff) | CP_SCRATCH_REG3 + */ + *cmd++ = cp_type3_packet(CP_REG_RMW, 3); + *cmd++ = (1 << 30) | A3XX_CP_SCRATCH_REG2; + *cmd++ = 0xffffffff; /* AND value */ + *cmd++ = A3XX_CP_SCRATCH_REG3; /* OR address */ + + *cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2); + *cmd++ = A3XX_CP_SCRATCH_REG2; + *cmd++ = drawctxt->shader_save_commands[3].gpuaddr; + + /* Constant save */ + cmd = rmw_regtomem(cmd, A3XX_SP_VS_CTRL_REG1, 0x000003ff, + 2 + REG_TO_MEM_LOOP_COUNT_SHIFT, + (HLSQ_SHADOW_BASE + 0x2000) / 4, + drawctxt->constant_save_commands[1].gpuaddr); + + cmd = rmw_regtomem(cmd, A3XX_SP_FS_CTRL_REG1, 0x000003ff, + 2 + REG_TO_MEM_LOOP_COUNT_SHIFT, + (HLSQ_SHADOW_BASE + 0x2000 + SSIZE) / 4, + drawctxt->constant_save_commands[2].gpuaddr); + + cmd = rmw_regtomem(cmd, A3XX_SP_FS_OBJ_OFFSET_REG, 0x00ff0000, + 18, drawctxt->gpustate.gpuaddr & 0xfffffe00, + drawctxt->constant_save_commands[2].gpuaddr + + sizeof(unsigned int)); + + /* Modify constant save conditionals */ + cmd = rmw_regtomem(cmd, A3XX_SP_VS_CTRL_REG1, 0x000003ff, + 0, 0, drawctxt->cond_execs[2].gpuaddr); + + cmd = rmw_regtomem(cmd, A3XX_SP_FS_CTRL_REG1, 0x000003ff, + 0, 0, drawctxt->cond_execs[3].gpuaddr); + + /* Save VS instruction store mode */ + + cmd = rmw_regtomem(cmd, A3XX_SP_VS_CTRL_REG0, 0x00000002, + 31, 0, drawctxt->cond_execs[0].gpuaddr); + + /* Save FS instruction store mode */ + cmd = rmw_regtomem(cmd, A3XX_SP_FS_CTRL_REG0, 0x00000002, + 31, 0, drawctxt->cond_execs[1].gpuaddr); + +#endif + + create_ib1(drawctxt, drawctxt->save_fixup, start, cmd); + + tmp_ctx.cmd = cmd; +} + +/****************************************************************************/ +/* Functions to build context restore IBs */ +/****************************************************************************/ + +static unsigned int *build_sys2gmem_cmds(struct adreno_device *adreno_dev, + struct adreno_context *drawctxt, + struct gmem_shadow_t *shadow) +{ + unsigned int *cmds = tmp_ctx.cmd; + unsigned int *start = cmds; + + *cmds++ = cp_type0_packet(A3XX_RBBM_CLOCK_CTL, 1); + *cmds++ = A3XX_RBBM_CLOCK_CTL_DEFAULT; + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 5); + *cmds++ = CP_REG(A3XX_HLSQ_CONTROL_0_REG); + /* HLSQ_CONTROL_0_REG */ + *cmds++ = _SET(HLSQ_CTRL0REG_FSTHREADSIZE, HLSQ_FOUR_PIX_QUADS) | + _SET(HLSQ_CTRL0REG_FSSUPERTHREADENABLE, 1) | + _SET(HLSQ_CTRL0REG_SPSHADERRESTART, 1) | + _SET(HLSQ_CTRL0REG_CHUNKDISABLE, 1) | + _SET(HLSQ_CTRL0REG_SPCONSTFULLUPDATE, 1); + /* HLSQ_CONTROL_1_REG */ + *cmds++ = _SET(HLSQ_CTRL1REG_VSTHREADSIZE, HLSQ_TWO_VTX_QUADS) | + _SET(HLSQ_CTRL1REG_VSSUPERTHREADENABLE, 1); + /* HLSQ_CONTROL_2_REG */ + *cmds++ = _SET(HLSQ_CTRL2REG_PRIMALLOCTHRESHOLD, 31); + /* HLSQ_CONTROL3_REG */ + *cmds++ = 0x00000000; + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3); + *cmds++ = CP_REG(A3XX_RB_MRT_BUF_INFO0); + /* RB_MRT_BUF_INFO0 */ + *cmds++ = _SET(RB_MRTBUFINFO_COLOR_FORMAT, RB_R8G8B8A8_UNORM) | + _SET(RB_MRTBUFINFO_COLOR_TILE_MODE, RB_TILINGMODE_32X32) | + _SET(RB_MRTBUFINFO_COLOR_BUF_PITCH, + (shadow->gmem_pitch * 4 * 8) / 256); + /* RB_MRT_BUF_BASE0 */ + *cmds++ = _SET(RB_MRTBUFBASE_COLOR_BUF_BASE, tmp_ctx.gmem_base >> 5); + + /* Texture samplers */ + *cmds++ = cp_type3_packet(CP_LOAD_STATE, 4); + *cmds++ = (16 << CP_LOADSTATE_DSTOFFSET_SHIFT) + | (HLSQ_DIRECT << CP_LOADSTATE_STATESRC_SHIFT) + | (HLSQ_BLOCK_ID_TP_TEX << CP_LOADSTATE_STATEBLOCKID_SHIFT) + | (1 << CP_LOADSTATE_NUMOFUNITS_SHIFT); + *cmds++ = (HLSQ_TP_TEX_SAMPLERS << CP_LOADSTATE_STATETYPE_SHIFT) + | (0 << CP_LOADSTATE_EXTSRCADDR_SHIFT); + *cmds++ = 0x00000240; + *cmds++ = 0x00000000; + + *cmds++ = cp_type0_packet(A3XX_VFD_PERFCOUNTER0_SELECT, 1); + *cmds++ = 0x00000000; + + /* Texture memobjs */ + *cmds++ = cp_type3_packet(CP_LOAD_STATE, 6); + *cmds++ = (16 << CP_LOADSTATE_DSTOFFSET_SHIFT) + | (HLSQ_DIRECT << CP_LOADSTATE_STATESRC_SHIFT) + | (HLSQ_BLOCK_ID_TP_TEX << CP_LOADSTATE_STATEBLOCKID_SHIFT) + | (1 << CP_LOADSTATE_NUMOFUNITS_SHIFT); + *cmds++ = (HLSQ_TP_TEX_MEMOBJ << CP_LOADSTATE_STATETYPE_SHIFT) + | (0 << CP_LOADSTATE_EXTSRCADDR_SHIFT); + *cmds++ = 0x4cc06880; + *cmds++ = shadow->height | (shadow->width << 14); + *cmds++ = (shadow->pitch*4*8) << 9; + *cmds++ = 0x00000000; + + *cmds++ = cp_type0_packet(A3XX_VFD_PERFCOUNTER0_SELECT, 1); + *cmds++ = 0x00000000; + + /* Mipmap bases */ + *cmds++ = cp_type3_packet(CP_LOAD_STATE, 16); + *cmds++ = (224 << CP_LOADSTATE_DSTOFFSET_SHIFT) + | (HLSQ_DIRECT << CP_LOADSTATE_STATESRC_SHIFT) + | (HLSQ_BLOCK_ID_TP_MIPMAP << CP_LOADSTATE_STATEBLOCKID_SHIFT) + | (14 << CP_LOADSTATE_NUMOFUNITS_SHIFT); + *cmds++ = (HLSQ_TP_MIPMAP_BASE << CP_LOADSTATE_STATETYPE_SHIFT) + | (0 << CP_LOADSTATE_EXTSRCADDR_SHIFT); + *cmds++ = shadow->gmemshadow.gpuaddr; + *cmds++ = 0x00000000; + *cmds++ = 0x00000000; + *cmds++ = 0x00000000; + *cmds++ = 0x00000000; + *cmds++ = 0x00000000; + *cmds++ = 0x00000000; + *cmds++ = 0x00000000; + *cmds++ = 0x00000000; + *cmds++ = 0x00000000; + *cmds++ = 0x00000000; + *cmds++ = 0x00000000; + *cmds++ = 0x00000000; + *cmds++ = 0x00000000; + + *cmds++ = cp_type0_packet(A3XX_VFD_PERFCOUNTER0_SELECT, 1); + *cmds++ = 0x00000000; + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 5); + *cmds++ = CP_REG(A3XX_HLSQ_VS_CONTROL_REG); + /* HLSQ_VS_CONTROL_REG */ + *cmds++ = _SET(HLSQ_VSCTRLREG_VSINSTRLENGTH, 1); + /* HLSQ_FS_CONTROL_REG */ + *cmds++ = _SET(HLSQ_FSCTRLREG_FSCONSTLENGTH, 1) | + _SET(HLSQ_FSCTRLREG_FSCONSTSTARTOFFSET, 128) | + _SET(HLSQ_FSCTRLREG_FSINSTRLENGTH, 2); + /* HLSQ_CONST_VSPRESV_RANGE_REG */ + *cmds++ = 0x00000000; + /* HLSQ_CONST_FSPRESV_RANGE_REG */ + *cmds++ = 0x00000000; + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); + *cmds++ = CP_REG(A3XX_SP_FS_LENGTH_REG); + /* SP_FS_LENGTH_REG */ + *cmds++ = _SET(SP_SHADERLENGTH_LEN, 2); + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 12); + *cmds++ = CP_REG(A3XX_SP_VS_CTRL_REG0); + /* SP_VS_CTRL_REG0 */ + *cmds++ = _SET(SP_VSCTRLREG0_VSTHREADMODE, SP_MULTI) | + _SET(SP_VSCTRLREG0_VSINSTRBUFFERMODE, SP_BUFFER_MODE) | + _SET(SP_VSCTRLREG0_VSICACHEINVALID, 1) | + _SET(SP_VSCTRLREG0_VSFULLREGFOOTPRINT, 2) | + _SET(SP_VSCTRLREG0_VSTHREADSIZE, SP_TWO_VTX_QUADS) | + _SET(SP_VSCTRLREG0_VSLENGTH, 1); + /* SP_VS_CTRL_REG1 */ + *cmds++ = _SET(SP_VSCTRLREG1_VSINITIALOUTSTANDING, 8); + /* SP_VS_PARAM_REG */ + *cmds++ = _SET(SP_VSPARAMREG_POSREGID, 4) | + _SET(SP_VSPARAMREG_PSIZEREGID, 252) | + _SET(SP_VSPARAMREG_TOTALVSOUTVAR, 1); + /* SP_VS_OUT_REG0 */ + *cmds++ = _SET(SP_VSOUTREG_COMPMASK0, 3); + /* SP_VS_OUT_REG1 */ + *cmds++ = 0x00000000; + /* SP_VS_OUT_REG2 */ + *cmds++ = 0x00000000; + /* SP_VS_OUT_REG3 */ + *cmds++ = 0x00000000; + /* SP_VS_OUT_REG4 */ + *cmds++ = 0x00000000; + /* SP_VS_OUT_REG5 */ + *cmds++ = 0x00000000; + /* SP_VS_OUT_REG6 */ + *cmds++ = 0x00000000; + /* SP_VS_OUT_REG7 */ + *cmds++ = 0x00000000; + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 7); + *cmds++ = CP_REG(A3XX_SP_VS_VPC_DST_REG_0); + /* SP_VS_VPC_DST_REG0 */ + *cmds++ = _SET(SP_VSVPCDSTREG_OUTLOC0, 8); + /* SP_VS_VPC_DST_REG1 */ + *cmds++ = 0x00000000; + /* SP_VS_VPC_DST_REG2 */ + *cmds++ = 0x00000000; + /* SP_VS_VPC_DST_REG3 */ + *cmds++ = 0x00000000; + /* SP_VS_OBJ_OFFSET_REG */ + *cmds++ = 0x00000000; + /* SP_VS_OBJ_START_REG */ + *cmds++ = 0x00000000; + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 6); + *cmds++ = CP_REG(A3XX_SP_VS_LENGTH_REG); + /* SP_VS_LENGTH_REG */ + *cmds++ = _SET(SP_SHADERLENGTH_LEN, 1); + /* SP_FS_CTRL_REG0 */ + *cmds++ = _SET(SP_FSCTRLREG0_FSTHREADMODE, SP_MULTI) | + _SET(SP_FSCTRLREG0_FSINSTRBUFFERMODE, SP_BUFFER_MODE) | + _SET(SP_FSCTRLREG0_FSICACHEINVALID, 1) | + _SET(SP_FSCTRLREG0_FSHALFREGFOOTPRINT, 1) | + _SET(SP_FSCTRLREG0_FSFULLREGFOOTPRINT, 1) | + _SET(SP_FSCTRLREG0_FSINOUTREGOVERLAP, 1) | + _SET(SP_FSCTRLREG0_FSTHREADSIZE, SP_FOUR_PIX_QUADS) | + _SET(SP_FSCTRLREG0_FSSUPERTHREADMODE, 1) | + _SET(SP_FSCTRLREG0_PIXLODENABLE, 1) | + _SET(SP_FSCTRLREG0_FSLENGTH, 2); + /* SP_FS_CTRL_REG1 */ + *cmds++ = _SET(SP_FSCTRLREG1_FSCONSTLENGTH, 1) | + _SET(SP_FSCTRLREG1_FSINITIALOUTSTANDING, 2) | + _SET(SP_FSCTRLREG1_HALFPRECVAROFFSET, 63); + /* SP_FS_OBJ_OFFSET_REG */ + *cmds++ = _SET(SP_OBJOFFSETREG_CONSTOBJECTSTARTOFFSET, 128) | + _SET(SP_OBJOFFSETREG_SHADEROBJOFFSETINIC, 126); + /* SP_FS_OBJ_START_REG */ + *cmds++ = 0x00000000; + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3); + *cmds++ = CP_REG(A3XX_SP_FS_FLAT_SHAD_MODE_REG_0); + /* SP_FS_FLAT_SHAD_MODE_REG0 */ + *cmds++ = 0x00000000; + /* SP_FS_FLAT_SHAD_MODE_REG1 */ + *cmds++ = 0x00000000; + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); + *cmds++ = CP_REG(A3XX_SP_FS_OUTPUT_REG); + /* SP_FS_OUT_REG */ + *cmds++ = _SET(SP_FSOUTREG_PAD0, SP_PIXEL_BASED); + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 5); + *cmds++ = CP_REG(A3XX_SP_FS_MRT_REG_0); + /* SP_FS_MRT_REG0 */ + *cmds++ = _SET(SP_FSMRTREG_PRECISION, 1); + /* SP_FS_MRT_REG1 */ + *cmds++ = 0; + /* SP_FS_MRT_REG2 */ + *cmds++ = 0; + /* SP_FS_MRT_REG3 */ + *cmds++ = 0; + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 11); + *cmds++ = CP_REG(A3XX_VPC_ATTR); + /* VPC_ATTR */ + *cmds++ = _SET(VPC_VPCATTR_TOTALATTR, 2) | + _SET(VPC_VPCATTR_THRHDASSIGN, 1) | + _SET(VPC_VPCATTR_LMSIZE, 1); + /* VPC_PACK */ + *cmds++ = _SET(VPC_VPCPACK_NUMFPNONPOSVAR, 2) | + _SET(VPC_VPCPACK_NUMNONPOSVSVAR, 2); + /* VPC_VARYING_INTERP_MODE_0 */ + *cmds++ = 0x00000000; + /* VPC_VARYING_INTERP_MODE1 */ + *cmds++ = 0x00000000; + /* VPC_VARYING_INTERP_MODE2 */ + *cmds++ = 0x00000000; + /* VPC_VARYING_IINTERP_MODE3 */ + *cmds++ = 0x00000000; + /* VPC_VARRYING_PS_REPL_MODE_0 */ + *cmds++ = _SET(VPC_VPCVARPSREPLMODE_COMPONENT08, 1) | + _SET(VPC_VPCVARPSREPLMODE_COMPONENT09, 2) | + _SET(VPC_VPCVARPSREPLMODE_COMPONENT0A, 1) | + _SET(VPC_VPCVARPSREPLMODE_COMPONENT0B, 2) | + _SET(VPC_VPCVARPSREPLMODE_COMPONENT0C, 1) | + _SET(VPC_VPCVARPSREPLMODE_COMPONENT0D, 2) | + _SET(VPC_VPCVARPSREPLMODE_COMPONENT0E, 1) | + _SET(VPC_VPCVARPSREPLMODE_COMPONENT0F, 2) | + _SET(VPC_VPCVARPSREPLMODE_COMPONENT10, 1) | + _SET(VPC_VPCVARPSREPLMODE_COMPONENT11, 2) | + _SET(VPC_VPCVARPSREPLMODE_COMPONENT12, 1) | + _SET(VPC_VPCVARPSREPLMODE_COMPONENT13, 2) | + _SET(VPC_VPCVARPSREPLMODE_COMPONENT14, 1) | + _SET(VPC_VPCVARPSREPLMODE_COMPONENT15, 2) | + _SET(VPC_VPCVARPSREPLMODE_COMPONENT16, 1) | + _SET(VPC_VPCVARPSREPLMODE_COMPONENT17, 2); + /* VPC_VARRYING_PS_REPL_MODE_1 */ + *cmds++ = _SET(VPC_VPCVARPSREPLMODE_COMPONENT08, 1) | + _SET(VPC_VPCVARPSREPLMODE_COMPONENT09, 2) | + _SET(VPC_VPCVARPSREPLMODE_COMPONENT0A, 1) | + _SET(VPC_VPCVARPSREPLMODE_COMPONENT0B, 2) | + _SET(VPC_VPCVARPSREPLMODE_COMPONENT0C, 1) | + _SET(VPC_VPCVARPSREPLMODE_COMPONENT0D, 2) | + _SET(VPC_VPCVARPSREPLMODE_COMPONENT0E, 1) | + _SET(VPC_VPCVARPSREPLMODE_COMPONENT0F, 2) | + _SET(VPC_VPCVARPSREPLMODE_COMPONENT10, 1) | + _SET(VPC_VPCVARPSREPLMODE_COMPONENT11, 2) | + _SET(VPC_VPCVARPSREPLMODE_COMPONENT12, 1) | + _SET(VPC_VPCVARPSREPLMODE_COMPONENT13, 2) | + _SET(VPC_VPCVARPSREPLMODE_COMPONENT14, 1) | + _SET(VPC_VPCVARPSREPLMODE_COMPONENT15, 2) | + _SET(VPC_VPCVARPSREPLMODE_COMPONENT16, 1) | + _SET(VPC_VPCVARPSREPLMODE_COMPONENT17, 2); + /* VPC_VARRYING_PS_REPL_MODE_2 */ + *cmds++ = _SET(VPC_VPCVARPSREPLMODE_COMPONENT08, 1) | + _SET(VPC_VPCVARPSREPLMODE_COMPONENT09, 2) | + _SET(VPC_VPCVARPSREPLMODE_COMPONENT0A, 1) | + _SET(VPC_VPCVARPSREPLMODE_COMPONENT0B, 2) | + _SET(VPC_VPCVARPSREPLMODE_COMPONENT0C, 1) | + _SET(VPC_VPCVARPSREPLMODE_COMPONENT0D, 2) | + _SET(VPC_VPCVARPSREPLMODE_COMPONENT0E, 1) | + _SET(VPC_VPCVARPSREPLMODE_COMPONENT0F, 2) | + _SET(VPC_VPCVARPSREPLMODE_COMPONENT10, 1) | + _SET(VPC_VPCVARPSREPLMODE_COMPONENT11, 2) | + _SET(VPC_VPCVARPSREPLMODE_COMPONENT12, 1) | + _SET(VPC_VPCVARPSREPLMODE_COMPONENT13, 2) | + _SET(VPC_VPCVARPSREPLMODE_COMPONENT14, 1) | + _SET(VPC_VPCVARPSREPLMODE_COMPONENT15, 2) | + _SET(VPC_VPCVARPSREPLMODE_COMPONENT16, 1) | + _SET(VPC_VPCVARPSREPLMODE_COMPONENT17, 2); + /* VPC_VARRYING_PS_REPL_MODE_3 */ + *cmds++ = _SET(VPC_VPCVARPSREPLMODE_COMPONENT08, 1) | + _SET(VPC_VPCVARPSREPLMODE_COMPONENT09, 2) | + _SET(VPC_VPCVARPSREPLMODE_COMPONENT0A, 1) | + _SET(VPC_VPCVARPSREPLMODE_COMPONENT0B, 2) | + _SET(VPC_VPCVARPSREPLMODE_COMPONENT0C, 1) | + _SET(VPC_VPCVARPSREPLMODE_COMPONENT0D, 2) | + _SET(VPC_VPCVARPSREPLMODE_COMPONENT0E, 1) | + _SET(VPC_VPCVARPSREPLMODE_COMPONENT0F, 2) | + _SET(VPC_VPCVARPSREPLMODE_COMPONENT10, 1) | + _SET(VPC_VPCVARPSREPLMODE_COMPONENT11, 2) | + _SET(VPC_VPCVARPSREPLMODE_COMPONENT12, 1) | + _SET(VPC_VPCVARPSREPLMODE_COMPONENT13, 2) | + _SET(VPC_VPCVARPSREPLMODE_COMPONENT14, 1) | + _SET(VPC_VPCVARPSREPLMODE_COMPONENT15, 2) | + _SET(VPC_VPCVARPSREPLMODE_COMPONENT16, 1) | + _SET(VPC_VPCVARPSREPLMODE_COMPONENT17, 2); + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); + *cmds++ = CP_REG(A3XX_SP_SP_CTRL_REG); + /* SP_SP_CTRL_REG */ + *cmds++ = _SET(SP_SPCTRLREG_SLEEPMODE, 1) | + _SET(SP_SPCTRLREG_LOMODE, 1); + + /* Load vertex shader */ + *cmds++ = cp_type3_packet(CP_LOAD_STATE, 10); + *cmds++ = (0 << CP_LOADSTATE_DSTOFFSET_SHIFT) + | (HLSQ_DIRECT << CP_LOADSTATE_STATESRC_SHIFT) + | (HLSQ_BLOCK_ID_SP_VS << CP_LOADSTATE_STATEBLOCKID_SHIFT) + | (1 << CP_LOADSTATE_NUMOFUNITS_SHIFT); + *cmds++ = (HLSQ_SP_VS_INSTR << CP_LOADSTATE_STATETYPE_SHIFT) + | (0 << CP_LOADSTATE_EXTSRCADDR_SHIFT); + /* (sy)end; */ + *cmds++ = 0x00000000; *cmds++ = 0x13001000; + /* nop; */ + *cmds++ = 0x00000000; *cmds++ = 0x00000000; + /* nop; */ + *cmds++ = 0x00000000; *cmds++ = 0x00000000; + /* nop; */ + *cmds++ = 0x00000000; *cmds++ = 0x00000000; + + *cmds++ = cp_type0_packet(A3XX_VFD_PERFCOUNTER0_SELECT, 1); + *cmds++ = 0x00000000; + + *cmds++ = cp_type3_packet(CP_WAIT_FOR_IDLE, 1); + *cmds++ = 0x00000000; + + + /* Load fragment shader */ + *cmds++ = cp_type3_packet(CP_LOAD_STATE, 18); + *cmds++ = (0 << CP_LOADSTATE_DSTOFFSET_SHIFT) + | (HLSQ_DIRECT << CP_LOADSTATE_STATESRC_SHIFT) + | (HLSQ_BLOCK_ID_SP_FS << CP_LOADSTATE_STATEBLOCKID_SHIFT) + | (2 << CP_LOADSTATE_NUMOFUNITS_SHIFT); + *cmds++ = (HLSQ_SP_FS_INSTR << CP_LOADSTATE_STATETYPE_SHIFT) + | (0 << CP_LOADSTATE_EXTSRCADDR_SHIFT); + /* (sy)(rpt1)bary.f (ei)r0.z, (r)0, r0.x; */ + *cmds++ = 0x00002000; *cmds++ = 0x57309902; + /* (rpt5)nop; */ + *cmds++ = 0x00000000; *cmds++ = 0x00000500; + /* sam (f32)r0.xyzw, r0.z, s#0, t#0; */ + *cmds++ = 0x00000005; *cmds++ = 0xa0c01f00; + /* (sy)mov.f32f32 r1.x, r0.x; */ + *cmds++ = 0x00000000; *cmds++ = 0x30040b00; + /* mov.f32f32 r1.y, r0.y; */ + *cmds++ = 0x00000000; *cmds++ = 0x03000000; + /* mov.f32f32 r1.z, r0.z; */ + *cmds++ = 0x00000000; *cmds++ = 0x00000000; + /* mov.f32f32 r1.w, r0.w; */ + *cmds++ = 0x00000000; *cmds++ = 0x00000000; + /* end; */ + *cmds++ = 0x00000000; *cmds++ = 0x00000000; + + *cmds++ = cp_type0_packet(A3XX_VFD_PERFCOUNTER0_SELECT, 1); + *cmds++ = 0x00000000; + + *cmds++ = cp_type3_packet(CP_WAIT_FOR_IDLE, 1); + *cmds++ = 0x00000000; + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3); + *cmds++ = CP_REG(A3XX_VFD_CONTROL_0); + /* VFD_CONTROL_0 */ + *cmds++ = _SET(VFD_CTRLREG0_TOTALATTRTOVS, 8) | + _SET(VFD_CTRLREG0_PACKETSIZE, 2) | + _SET(VFD_CTRLREG0_STRMDECINSTRCNT, 2) | + _SET(VFD_CTRLREG0_STRMFETCHINSTRCNT, 2); + /* VFD_CONTROL_1 */ + *cmds++ = _SET(VFD_CTRLREG1_MAXSTORAGE, 2) | + _SET(VFD_CTRLREG1_REGID4VTX, 252) | + _SET(VFD_CTRLREG1_REGID4INST, 252); + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 5); + *cmds++ = CP_REG(A3XX_VFD_FETCH_INSTR_0_0); + /* VFD_FETCH_INSTR_0_0 */ + *cmds++ = _SET(VFD_FETCHINSTRUCTIONS_FETCHSIZE, 7) | + _SET(VFD_FETCHINSTRUCTIONS_BUFSTRIDE, 8) | + _SET(VFD_FETCHINSTRUCTIONS_SWITCHNEXT, 1) | + _SET(VFD_FETCHINSTRUCTIONS_STEPRATE, 1); + /* VFD_FETCH_INSTR_1_0 */ + *cmds++ = _SET(VFD_BASEADDR_BASEADDR, + shadow->quad_vertices_restore.gpuaddr); + /* VFD_FETCH_INSTR_0_1 */ + *cmds++ = _SET(VFD_FETCHINSTRUCTIONS_FETCHSIZE, 11) | + _SET(VFD_FETCHINSTRUCTIONS_BUFSTRIDE, 12) | + _SET(VFD_FETCHINSTRUCTIONS_INDEXDECODE, 1) | + _SET(VFD_FETCHINSTRUCTIONS_STEPRATE, 1); + /* VFD_FETCH_INSTR_1_1 */ + *cmds++ = _SET(VFD_BASEADDR_BASEADDR, + shadow->quad_vertices_restore.gpuaddr + 16); + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3); + *cmds++ = CP_REG(A3XX_VFD_DECODE_INSTR_0); + /* VFD_DECODE_INSTR_0 */ + *cmds++ = _SET(VFD_DECODEINSTRUCTIONS_WRITEMASK, 0x0F) | + _SET(VFD_DECODEINSTRUCTIONS_CONSTFILL, 1) | + _SET(VFD_DECODEINSTRUCTIONS_FORMAT, 1) | + _SET(VFD_DECODEINSTRUCTIONS_SHIFTCNT, 8) | + _SET(VFD_DECODEINSTRUCTIONS_LASTCOMPVALID, 1) | + _SET(VFD_DECODEINSTRUCTIONS_SWITCHNEXT, 1); + /* VFD_DECODE_INSTR_1 */ + *cmds++ = _SET(VFD_DECODEINSTRUCTIONS_WRITEMASK, 0x0F) | + _SET(VFD_DECODEINSTRUCTIONS_CONSTFILL, 1) | + _SET(VFD_DECODEINSTRUCTIONS_FORMAT, 2) | + _SET(VFD_DECODEINSTRUCTIONS_REGID, 4) | + _SET(VFD_DECODEINSTRUCTIONS_SHIFTCNT, 12) | + _SET(VFD_DECODEINSTRUCTIONS_LASTCOMPVALID, 1); + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); + *cmds++ = CP_REG(A3XX_RB_DEPTH_CONTROL); + /* RB_DEPTH_CONTROL */ + *cmds++ = _SET(RB_DEPTHCONTROL_Z_TEST_FUNC, RB_FRAG_LESS); + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); + *cmds++ = CP_REG(A3XX_RB_STENCIL_CONTROL); + /* RB_STENCIL_CONTROL */ + *cmds++ = _SET(RB_STENCILCONTROL_STENCIL_FUNC, RB_REF_ALWAYS) | + _SET(RB_STENCILCONTROL_STENCIL_FAIL, RB_STENCIL_KEEP) | + _SET(RB_STENCILCONTROL_STENCIL_ZPASS, RB_STENCIL_KEEP) | + _SET(RB_STENCILCONTROL_STENCIL_ZFAIL, RB_STENCIL_KEEP) | + _SET(RB_STENCILCONTROL_STENCIL_FUNC_BF, RB_REF_ALWAYS) | + _SET(RB_STENCILCONTROL_STENCIL_FAIL_BF, RB_STENCIL_KEEP) | + _SET(RB_STENCILCONTROL_STENCIL_ZPASS_BF, RB_STENCIL_KEEP) | + _SET(RB_STENCILCONTROL_STENCIL_ZFAIL_BF, RB_STENCIL_KEEP); + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); + *cmds++ = CP_REG(A3XX_RB_MODE_CONTROL); + /* RB_MODE_CONTROL */ + *cmds++ = _SET(RB_MODECONTROL_RENDER_MODE, RB_RENDERING_PASS) | + _SET(RB_MODECONTROL_MARB_CACHE_SPLIT_MODE, 1); + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); + *cmds++ = CP_REG(A3XX_RB_RENDER_CONTROL); + /* RB_RENDER_CONTROL */ + *cmds++ = _SET(RB_RENDERCONTROL_BIN_WIDTH, shadow->width >> 5) | + _SET(RB_RENDERCONTROL_ALPHA_TEST_FUNC, 7); + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); + *cmds++ = CP_REG(A3XX_RB_MSAA_CONTROL); + /* RB_MSAA_CONTROL */ + *cmds++ = _SET(RB_MSAACONTROL_MSAA_DISABLE, 1) | + _SET(RB_MSAACONTROL_SAMPLE_MASK, 0xFFFF); + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); + *cmds++ = CP_REG(A3XX_RB_MRT_CONTROL0); + /* RB_MRT_CONTROL0 */ + *cmds++ = _SET(RB_MRTCONTROL_ROP_CODE, 12) | + _SET(RB_MRTCONTROL_DITHER_MODE, RB_DITHER_DISABLE) | + _SET(RB_MRTCONTROL_COMPONENT_ENABLE, 0xF); + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3); + *cmds++ = CP_REG(A3XX_RB_MRT_BLEND_CONTROL0); + /* RB_MRT_BLENDCONTROL0 */ + *cmds++ = _SET(RB_MRTBLENDCONTROL_RGB_SRC_FACTOR, RB_FACTOR_ONE) | + _SET(RB_MRTBLENDCONTROL_RGB_BLEND_OPCODE, RB_BLEND_OP_ADD) | + _SET(RB_MRTBLENDCONTROL_RGB_DEST_FACTOR, RB_FACTOR_ZERO) | + _SET(RB_MRTBLENDCONTROL_ALPHA_SRC_FACTOR, RB_FACTOR_ONE) | + _SET(RB_MRTBLENDCONTROL_ALPHA_BLEND_OPCODE, RB_BLEND_OP_ADD) | + _SET(RB_MRTBLENDCONTROL_ALPHA_DEST_FACTOR, RB_FACTOR_ZERO) | + _SET(RB_MRTBLENDCONTROL_CLAMP_ENABLE, 1); + /* RB_MRT_CONTROL1 */ + *cmds++ = _SET(RB_MRTCONTROL_READ_DEST_ENABLE, 1) | + _SET(RB_MRTCONTROL_ROP_CODE, 12) | + _SET(RB_MRTCONTROL_DITHER_MODE, RB_DITHER_ALWAYS) | + _SET(RB_MRTCONTROL_COMPONENT_ENABLE, 0xF); + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3); + *cmds++ = CP_REG(A3XX_RB_MRT_BLEND_CONTROL1); + /* RB_MRT_BLENDCONTROL1 */ + *cmds++ = _SET(RB_MRTBLENDCONTROL_RGB_SRC_FACTOR, RB_FACTOR_ONE) | + _SET(RB_MRTBLENDCONTROL_RGB_BLEND_OPCODE, RB_BLEND_OP_ADD) | + _SET(RB_MRTBLENDCONTROL_RGB_DEST_FACTOR, RB_FACTOR_ZERO) | + _SET(RB_MRTBLENDCONTROL_ALPHA_SRC_FACTOR, RB_FACTOR_ONE) | + _SET(RB_MRTBLENDCONTROL_ALPHA_BLEND_OPCODE, RB_BLEND_OP_ADD) | + _SET(RB_MRTBLENDCONTROL_ALPHA_DEST_FACTOR, RB_FACTOR_ZERO) | + _SET(RB_MRTBLENDCONTROL_CLAMP_ENABLE, 1); + /* RB_MRT_CONTROL2 */ + *cmds++ = _SET(RB_MRTCONTROL_READ_DEST_ENABLE, 1) | + _SET(RB_MRTCONTROL_ROP_CODE, 12) | + _SET(RB_MRTCONTROL_DITHER_MODE, RB_DITHER_ALWAYS) | + _SET(RB_MRTCONTROL_COMPONENT_ENABLE, 0xF); + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3); + *cmds++ = CP_REG(A3XX_RB_MRT_BLEND_CONTROL2); + /* RB_MRT_BLENDCONTROL2 */ + *cmds++ = _SET(RB_MRTBLENDCONTROL_RGB_SRC_FACTOR, RB_FACTOR_ONE) | + _SET(RB_MRTBLENDCONTROL_RGB_BLEND_OPCODE, RB_BLEND_OP_ADD) | + _SET(RB_MRTBLENDCONTROL_RGB_DEST_FACTOR, RB_FACTOR_ZERO) | + _SET(RB_MRTBLENDCONTROL_ALPHA_SRC_FACTOR, RB_FACTOR_ONE) | + _SET(RB_MRTBLENDCONTROL_ALPHA_BLEND_OPCODE, RB_BLEND_OP_ADD) | + _SET(RB_MRTBLENDCONTROL_ALPHA_DEST_FACTOR, RB_FACTOR_ZERO) | + _SET(RB_MRTBLENDCONTROL_CLAMP_ENABLE, 1); + /* RB_MRT_CONTROL3 */ + *cmds++ = _SET(RB_MRTCONTROL_READ_DEST_ENABLE, 1) | + _SET(RB_MRTCONTROL_ROP_CODE, 12) | + _SET(RB_MRTCONTROL_DITHER_MODE, RB_DITHER_ALWAYS) | + _SET(RB_MRTCONTROL_COMPONENT_ENABLE, 0xF); + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); + *cmds++ = CP_REG(A3XX_RB_MRT_BLEND_CONTROL3); + /* RB_MRT_BLENDCONTROL3 */ + *cmds++ = _SET(RB_MRTBLENDCONTROL_RGB_SRC_FACTOR, RB_FACTOR_ONE) | + _SET(RB_MRTBLENDCONTROL_RGB_BLEND_OPCODE, RB_BLEND_OP_ADD) | + _SET(RB_MRTBLENDCONTROL_RGB_DEST_FACTOR, RB_FACTOR_ZERO) | + _SET(RB_MRTBLENDCONTROL_ALPHA_SRC_FACTOR, RB_FACTOR_ONE) | + _SET(RB_MRTBLENDCONTROL_ALPHA_BLEND_OPCODE, RB_BLEND_OP_ADD) | + _SET(RB_MRTBLENDCONTROL_ALPHA_DEST_FACTOR, RB_FACTOR_ZERO) | + _SET(RB_MRTBLENDCONTROL_CLAMP_ENABLE, 1); + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 5); + *cmds++ = CP_REG(A3XX_VFD_INDEX_MIN); + /* VFD_INDEX_MIN */ + *cmds++ = 0x00000000; + /* VFD_INDEX_MAX */ + *cmds++ = 340; + /* VFD_INDEX_OFFSET */ + *cmds++ = 0x00000000; + /* TPL1_TP_VS_TEX_OFFSET */ + *cmds++ = 0x00000000; + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); + *cmds++ = CP_REG(A3XX_VFD_VS_THREADING_THRESHOLD); + /* VFD_VS_THREADING_THRESHOLD */ + *cmds++ = _SET(VFD_THREADINGTHRESHOLD_REGID_THRESHOLD, 15) | + _SET(VFD_THREADINGTHRESHOLD_REGID_VTXCNT, 252); + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); + *cmds++ = CP_REG(A3XX_TPL1_TP_VS_TEX_OFFSET); + /* TPL1_TP_VS_TEX_OFFSET */ + *cmds++ = 0x00000000; + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); + *cmds++ = CP_REG(A3XX_TPL1_TP_FS_TEX_OFFSET); + /* TPL1_TP_FS_TEX_OFFSET */ + *cmds++ = _SET(TPL1_TPTEXOFFSETREG_SAMPLEROFFSET, 16) | + _SET(TPL1_TPTEXOFFSETREG_MEMOBJOFFSET, 16) | + _SET(TPL1_TPTEXOFFSETREG_BASETABLEPTR, 224); + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); + *cmds++ = CP_REG(A3XX_GRAS_SC_CONTROL); + /* GRAS_SC_CONTROL */ + /*cmds++ = _SET(GRAS_SC_CONTROL_RASTER_MODE, 1); + *cmds++ = _SET(GRAS_SC_CONTROL_RASTER_MODE, 1) |*/ + *cmds++ = 0x04001000; + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); + *cmds++ = CP_REG(A3XX_GRAS_SU_MODE_CONTROL); + /* GRAS_SU_MODE_CONTROL */ + *cmds++ = _SET(GRAS_SU_CTRLMODE_LINEHALFWIDTH, 2); + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3); + *cmds++ = CP_REG(A3XX_GRAS_SC_WINDOW_SCISSOR_TL); + /* GRAS_SC_WINDOW_SCISSOR_TL */ + *cmds++ = 0x00000000; + /* GRAS_SC_WINDOW_SCISSOR_BR */ + *cmds++ = _SET(GRAS_SC_WINDOW_SCISSOR_BR_BR_X, shadow->width - 1) | + _SET(GRAS_SC_WINDOW_SCISSOR_BR_BR_Y, shadow->height - 1); + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3); + *cmds++ = CP_REG(A3XX_GRAS_SC_SCREEN_SCISSOR_TL); + /* GRAS_SC_SCREEN_SCISSOR_TL */ + *cmds++ = 0x00000000; + /* GRAS_SC_SCREEN_SCISSOR_BR */ + *cmds++ = _SET(GRAS_SC_SCREEN_SCISSOR_BR_BR_X, shadow->width - 1) | + _SET(GRAS_SC_SCREEN_SCISSOR_BR_BR_Y, shadow->height - 1); + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 5); + *cmds++ = CP_REG(A3XX_GRAS_CL_VPORT_XOFFSET); + /* GRAS_CL_VPORT_XOFFSET */ + *cmds++ = 0x00000000; + /* GRAS_CL_VPORT_XSCALE */ + *cmds++ = _SET(GRAS_CL_VPORT_XSCALE_VPORT_XSCALE, 0x3F800000); + /* GRAS_CL_VPORT_YOFFSET */ + *cmds++ = 0x00000000; + /* GRAS_CL_VPORT_YSCALE */ + *cmds++ = _SET(GRAS_CL_VPORT_YSCALE_VPORT_YSCALE, 0x3F800000); + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3); + *cmds++ = CP_REG(A3XX_GRAS_CL_VPORT_ZOFFSET); + /* GRAS_CL_VPORT_ZOFFSET */ + *cmds++ = 0x00000000; + /* GRAS_CL_VPORT_ZSCALE */ + *cmds++ = _SET(GRAS_CL_VPORT_ZSCALE_VPORT_ZSCALE, 0x3F800000); + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); + *cmds++ = CP_REG(A3XX_GRAS_CL_CLIP_CNTL); + /* GRAS_CL_CLIP_CNTL */ + *cmds++ = _SET(GRAS_CL_CLIP_CNTL_IJ_PERSP_CENTER, 1); + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); + *cmds++ = CP_REG(A3XX_SP_FS_IMAGE_OUTPUT_REG_0); + /* SP_FS_IMAGE_OUTPUT_REG_0 */ + *cmds++ = _SET(SP_IMAGEOUTPUTREG_MRTFORMAT, SP_R8G8B8A8_UNORM); + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); + *cmds++ = CP_REG(A3XX_PC_PRIM_VTX_CNTL); + /* PC_PRIM_VTX_CONTROL */ + *cmds++ = _SET(PC_PRIM_VTX_CONTROL_STRIDE_IN_VPC, 2) | + _SET(PC_PRIM_VTX_CONTROL_POLYMODE_FRONT_PTYPE, + PC_DRAW_TRIANGLES) | + _SET(PC_PRIM_VTX_CONTROL_POLYMODE_BACK_PTYPE, + PC_DRAW_TRIANGLES) | + _SET(PC_PRIM_VTX_CONTROL_PROVOKING_VTX_LAST, 1); + + + /* oxili_generate_context_roll_packets */ + *cmds++ = cp_type0_packet(A3XX_SP_VS_CTRL_REG0, 1); + *cmds++ = 0x00000400; + + *cmds++ = cp_type0_packet(A3XX_SP_FS_CTRL_REG0, 1); + *cmds++ = 0x00000400; + + *cmds++ = cp_type0_packet(A3XX_SP_VS_PVT_MEM_SIZE_REG, 1); + *cmds++ = 0x00008000; /* SP_VS_MEM_SIZE_REG */ + + *cmds++ = cp_type0_packet(A3XX_SP_FS_PVT_MEM_SIZE_REG, 1); + *cmds++ = 0x00008000; /* SP_FS_MEM_SIZE_REG */ + + /* Clear cache invalidate bit when re-loading the shader control regs */ + *cmds++ = cp_type0_packet(A3XX_SP_VS_CTRL_REG0, 1); + *cmds++ = _SET(SP_VSCTRLREG0_VSTHREADMODE, SP_MULTI) | + _SET(SP_VSCTRLREG0_VSINSTRBUFFERMODE, SP_BUFFER_MODE) | + _SET(SP_VSCTRLREG0_VSFULLREGFOOTPRINT, 2) | + _SET(SP_VSCTRLREG0_VSTHREADSIZE, SP_TWO_VTX_QUADS) | + _SET(SP_VSCTRLREG0_VSLENGTH, 1); + + *cmds++ = cp_type0_packet(A3XX_SP_FS_CTRL_REG0, 1); + *cmds++ = _SET(SP_FSCTRLREG0_FSTHREADMODE, SP_MULTI) | + _SET(SP_FSCTRLREG0_FSINSTRBUFFERMODE, SP_BUFFER_MODE) | + _SET(SP_FSCTRLREG0_FSHALFREGFOOTPRINT, 1) | + _SET(SP_FSCTRLREG0_FSFULLREGFOOTPRINT, 1) | + _SET(SP_FSCTRLREG0_FSINOUTREGOVERLAP, 1) | + _SET(SP_FSCTRLREG0_FSTHREADSIZE, SP_FOUR_PIX_QUADS) | + _SET(SP_FSCTRLREG0_FSSUPERTHREADMODE, 1) | + _SET(SP_FSCTRLREG0_FSLENGTH, 2); + + *cmds++ = cp_type0_packet(A3XX_SP_VS_PVT_MEM_SIZE_REG, 1); + *cmds++ = 0x00000000; /* SP_VS_MEM_SIZE_REG */ + + *cmds++ = cp_type0_packet(A3XX_SP_FS_PVT_MEM_SIZE_REG, 1); + *cmds++ = 0x00000000; /* SP_FS_MEM_SIZE_REG */ + + /* end oxili_generate_context_roll_packets */ + + *cmds++ = cp_type3_packet(CP_DRAW_INDX, 3); + *cmds++ = 0x00000000; /* Viz query info */ + *cmds++ = BUILD_PC_DRAW_INITIATOR(PC_DI_PT_RECTLIST, + PC_DI_SRC_SEL_AUTO_INDEX, + PC_DI_INDEX_SIZE_16_BIT, + PC_DI_IGNORE_VISIBILITY); + *cmds++ = 0x00000002; /* Num indices */ + + /* 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 *lcc_start; + + int i; + + /* Flush HLSQ lazy updates */ + *cmd++ = cp_type3_packet(CP_EVENT_WRITE, 1); + *cmd++ = 0x7; /* HLSQ_FLUSH */ + *cmd++ = cp_type3_packet(CP_WAIT_FOR_IDLE, 1); + *cmd++ = 0; + + *cmd++ = cp_type0_packet(A3XX_UCHE_CACHE_INVALIDATE0_REG, 2); + *cmd++ = 0x00000000; /* No start addr for full invalidate */ + *cmd++ = (unsigned int) + UCHE_ENTIRE_CACHE << UCHE_INVALIDATE1REG_ALLORPORTION | + UCHE_OP_INVALIDATE << UCHE_INVALIDATE1REG_OPCODE | + 0; /* No end addr for full invalidate */ + + lcc_start = cmd; + + /* 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 + + for (i = 0; i < ARRAY_SIZE(context_register_ranges) / 2; i++) { + cmd = reg_range(cmd, context_register_ranges[i * 2], + context_register_ranges[i * 2 + 1]); + } + + lcc_start[0] = cp_type3_packet(CP_LOAD_CONSTANT_CONTEXT, + (cmd - lcc_start) - 1); + +#ifdef CONFIG_MSM_KGSL_DISABLE_SHADOW_WRITES + lcc_start[2] |= (0 << 24) | (4 << 16); /* Disable shadowing. */ +#else + lcc_start[2] |= (1 << 24) | (4 << 16); +#endif + + for (i = 0; i < ARRAY_SIZE(global_registers); i++) { + *cmd++ = cp_type0_packet(global_registers[i], 1); + tmp_ctx.reg_values[i] = virt2gpu(cmd, &drawctxt->gpustate); + *cmd++ = 0x00000000; + } + + create_ib1(drawctxt, drawctxt->reg_restore, start, cmd); + tmp_ctx.cmd = cmd; +} + +static void build_constantrestore_cmds(struct adreno_device *adreno_dev, + struct adreno_context *drawctxt) +{ + unsigned int *cmd = tmp_ctx.cmd; + unsigned int *start = cmd; + unsigned int mode = 4; /* Indirect mode */ + unsigned int stateblock; + unsigned int numunits; + unsigned int statetype; + + drawctxt->cond_execs[2].hostptr = cmd; + drawctxt->cond_execs[2].gpuaddr = virt2gpu(cmd, &drawctxt->gpustate); + *cmd++ = 0; + drawctxt->cond_execs[3].hostptr = cmd; + drawctxt->cond_execs[3].gpuaddr = virt2gpu(cmd, &drawctxt->gpustate); + *cmd++ = 0; + +#ifndef CONFIG_MSM_KGSL_DISABLE_SHADOW_WRITES + *cmd++ = cp_type3_packet(CP_LOAD_CONSTANT_CONTEXT, 3); + *cmd++ = (drawctxt->gpustate.gpuaddr + REG_OFFSET) & 0xFFFFE000; + *cmd++ = 4 << 16; + *cmd++ = 0x0; +#endif + /* HLSQ full update */ + *cmd++ = cp_type3_packet(CP_SET_CONSTANT, 2); + *cmd++ = CP_REG(A3XX_HLSQ_CONTROL_0_REG); + *cmd++ = 0x68000240; /* A3XX_HLSQ_CONTROL_0_REG */ + +#ifndef CONFIG_MSM_KGSL_DISABLE_SHADOW_WRITES + /* Re-enable shadowing */ + *cmd++ = cp_type3_packet(CP_LOAD_CONSTANT_CONTEXT, 3); + *cmd++ = (drawctxt->gpustate.gpuaddr + REG_OFFSET) & 0xFFFFE000; + *cmd++ = (4 << 16) | (1 << 24); + *cmd++ = 0x0; +#endif + + /* Load vertex shader constants */ + *cmd++ = cp_type3_packet(CP_COND_EXEC, 4); + *cmd++ = drawctxt->cond_execs[2].gpuaddr >> 2; + *cmd++ = drawctxt->cond_execs[2].gpuaddr >> 2; + *cmd++ = 0x0000ffff; + *cmd++ = 3; /* EXEC_COUNT */ + *cmd++ = cp_type3_packet(CP_LOAD_STATE, 2); + drawctxt->constant_load_commands[0].hostptr = cmd; + drawctxt->constant_load_commands[0].gpuaddr = virt2gpu(cmd, + &drawctxt->gpustate); + + /* + From fixup: + + mode = 4 (indirect) + stateblock = 4 (Vertex constants) + numunits = SP_VS_CTRL_REG1.VSCONSTLENGTH * 2; (256bit units) + + From register spec: + SP_VS_CTRL_REG1.VSCONSTLENGTH [09:00]: 0-512, unit = 128bits. + + ord1 = (numunits<<22) | (stateblock<<19) | (mode<<16); + */ + + *cmd++ = 0; /* ord1 */ + *cmd++ = ((drawctxt->gpustate.gpuaddr) & 0xfffffffc) | 1; + + /* Load fragment shader constants */ + *cmd++ = cp_type3_packet(CP_COND_EXEC, 4); + *cmd++ = drawctxt->cond_execs[3].gpuaddr >> 2; + *cmd++ = drawctxt->cond_execs[3].gpuaddr >> 2; + *cmd++ = 0x0000ffff; + *cmd++ = 3; /* EXEC_COUNT */ + *cmd++ = cp_type3_packet(CP_LOAD_STATE, 2); + drawctxt->constant_load_commands[1].hostptr = cmd; + drawctxt->constant_load_commands[1].gpuaddr = + virt2gpu(cmd, &drawctxt->gpustate); + /* + From fixup: + + mode = 4 (indirect) + stateblock = 6 (Fragment constants) + numunits = SP_FS_CTRL_REG1.FSCONSTLENGTH * 2; (256bit units) + + From register spec: + SP_FS_CTRL_REG1.FSCONSTLENGTH [09:00]: 0-512, unit = 128bits. + + ord1 = (numunits<<22) | (stateblock<<19) | (mode<<16); + */ + + *cmd++ = 0; /* ord1 */ + drawctxt->constant_load_commands[2].hostptr = cmd; + drawctxt->constant_load_commands[2].gpuaddr = + virt2gpu(cmd, &drawctxt->gpustate); + /* + From fixup: + base = drawctxt->gpustate.gpuaddr (ALU constant shadow base) + offset = SP_FS_OBJ_OFFSET_REG.CONSTOBJECTSTARTOFFSET + + From register spec: + SP_FS_OBJ_OFFSET_REG.CONSTOBJECTSTARTOFFSET [16:24]: Constant object + start offset in on chip RAM, + 128bit aligned + + ord2 = base + offset | 1 + Because of the base alignment we can use + ord2 = base | offset | 1 + */ + *cmd++ = 0; /* ord2 */ + + /* Restore VS texture memory objects */ + stateblock = 0; + statetype = 1; + numunits = (TEX_SIZE_MEM_OBJECTS / 7) / 4; + + *cmd++ = cp_type3_packet(CP_LOAD_STATE, 2); + *cmd++ = (numunits << 22) | (stateblock << 19) | (mode << 16); + *cmd++ = ((drawctxt->gpustate.gpuaddr + VS_TEX_OFFSET_MEM_OBJECTS) + & 0xfffffffc) | statetype; + + /* Restore VS texture mipmap addresses */ + stateblock = 1; + statetype = 1; + numunits = TEX_SIZE_MIPMAP / 4; + *cmd++ = cp_type3_packet(CP_LOAD_STATE, 2); + *cmd++ = (numunits << 22) | (stateblock << 19) | (mode << 16); + *cmd++ = ((drawctxt->gpustate.gpuaddr + VS_TEX_OFFSET_MIPMAP) + & 0xfffffffc) | statetype; + + /* Restore VS texture sampler objects */ + stateblock = 0; + statetype = 0; + numunits = (TEX_SIZE_SAMPLER_OBJ / 2) / 4; + *cmd++ = cp_type3_packet(CP_LOAD_STATE, 2); + *cmd++ = (numunits << 22) | (stateblock << 19) | (mode << 16); + *cmd++ = ((drawctxt->gpustate.gpuaddr + VS_TEX_OFFSET_SAMPLER_OBJ) + & 0xfffffffc) | statetype; + + /* Restore FS texture memory objects */ + stateblock = 2; + statetype = 1; + numunits = (TEX_SIZE_MEM_OBJECTS / 7) / 4; + *cmd++ = cp_type3_packet(CP_LOAD_STATE, 2); + *cmd++ = (numunits << 22) | (stateblock << 19) | (mode << 16); + *cmd++ = ((drawctxt->gpustate.gpuaddr + FS_TEX_OFFSET_MEM_OBJECTS) + & 0xfffffffc) | statetype; + + /* Restore FS texture mipmap addresses */ + stateblock = 3; + statetype = 1; + numunits = TEX_SIZE_MIPMAP / 4; + *cmd++ = cp_type3_packet(CP_LOAD_STATE, 2); + *cmd++ = (numunits << 22) | (stateblock << 19) | (mode << 16); + *cmd++ = ((drawctxt->gpustate.gpuaddr + FS_TEX_OFFSET_MIPMAP) + & 0xfffffffc) | statetype; + + /* Restore FS texture sampler objects */ + stateblock = 2; + statetype = 0; + numunits = (TEX_SIZE_SAMPLER_OBJ / 2) / 4; + *cmd++ = cp_type3_packet(CP_LOAD_STATE, 2); + *cmd++ = (numunits << 22) | (stateblock << 19) | (mode << 16); + *cmd++ = ((drawctxt->gpustate.gpuaddr + FS_TEX_OFFSET_SAMPLER_OBJ) + & 0xfffffffc) | statetype; + + create_ib1(drawctxt, drawctxt->constant_restore, start, cmd); + tmp_ctx.cmd = cmd; +} + +static void build_shader_restore_cmds(struct adreno_device *adreno_dev, + struct adreno_context *drawctxt) +{ + unsigned int *cmd = tmp_ctx.cmd; + unsigned int *start = cmd; + + /* Vertex shader */ + *cmd++ = cp_type3_packet(CP_COND_EXEC, 4); + *cmd++ = drawctxt->cond_execs[0].gpuaddr >> 2; + *cmd++ = drawctxt->cond_execs[0].gpuaddr >> 2; + *cmd++ = 1; + *cmd++ = 3; /* EXEC_COUNT */ + + *cmd++ = cp_type3_packet(CP_LOAD_STATE, 2); + drawctxt->shader_load_commands[0].hostptr = cmd; + drawctxt->shader_load_commands[0].gpuaddr = + virt2gpu(cmd, &drawctxt->gpustate); + /* + From fixup: + + mode = 4 (indirect) + stateblock = 4 (Vertex shader) + numunits = SP_VS_CTRL_REG0.VS_LENGTH + + From regspec: + SP_VS_CTRL_REG0.VS_LENGTH [31:24]: VS length, unit = 256bits. + If bit31 is 1, it means overflow + or any long shader. + + ord1 = (numunits<<22) | (stateblock<<19) | (mode<<11) + */ + *cmd++ = 0; /*ord1 */ + *cmd++ = (drawctxt->gpustate.gpuaddr + SHADER_OFFSET) & 0xfffffffc; + + /* Fragment shader */ + *cmd++ = cp_type3_packet(CP_COND_EXEC, 4); + *cmd++ = drawctxt->cond_execs[1].gpuaddr >> 2; + *cmd++ = drawctxt->cond_execs[1].gpuaddr >> 2; + *cmd++ = 1; + *cmd++ = 3; /* EXEC_COUNT */ + + *cmd++ = cp_type3_packet(CP_LOAD_STATE, 2); + drawctxt->shader_load_commands[1].hostptr = cmd; + drawctxt->shader_load_commands[1].gpuaddr = + virt2gpu(cmd, &drawctxt->gpustate); + /* + From fixup: + + mode = 4 (indirect) + stateblock = 6 (Fragment shader) + numunits = SP_FS_CTRL_REG0.FS_LENGTH + + From regspec: + SP_FS_CTRL_REG0.FS_LENGTH [31:24]: FS length, unit = 256bits. + If bit31 is 1, it means overflow + or any long shader. + + ord1 = (numunits<<22) | (stateblock<<19) | (mode<<11) + */ + *cmd++ = 0; /*ord1 */ + *cmd++ = (drawctxt->gpustate.gpuaddr + SHADER_OFFSET + + (SHADER_SHADOW_SIZE / 2)) & 0xfffffffc; + + create_ib1(drawctxt, drawctxt->shader_restore, start, cmd); + tmp_ctx.cmd = cmd; +} + +static void build_hlsqcontrol_restore_cmds(struct adreno_device *adreno_dev, + struct adreno_context *drawctxt) +{ + unsigned int *cmd = tmp_ctx.cmd; + unsigned int *start = cmd; + + *cmd++ = cp_type3_packet(CP_SET_CONSTANT, 2); + *cmd++ = CP_REG(A3XX_HLSQ_CONTROL_0_REG); + drawctxt->hlsqcontrol_restore_commands[0].hostptr = cmd; + drawctxt->hlsqcontrol_restore_commands[0].gpuaddr + = virt2gpu(cmd, &drawctxt->gpustate); + *cmd++ = 0; + + /* Create indirect buffer command for above command sequence */ + create_ib1(drawctxt, drawctxt->hlsqcontrol_restore, start, cmd); + + tmp_ctx.cmd = cmd; +} + +/* IB that modifies the shader and constant sizes and offsets in restore IBs. */ +static void build_restore_fixup_cmds(struct adreno_device *adreno_dev, + struct adreno_context *drawctxt) +{ + unsigned int *cmd = tmp_ctx.cmd; + unsigned int *start = cmd; + +#ifdef GSL_CONTEXT_SWITCH_CPU_SYNC + /* Save shader sizes */ + *cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2); + *cmd++ = A3XX_SP_VS_CTRL_REG0; + *cmd++ = drawctxt->shader_load_commands[0].gpuaddr; + + *cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2); + *cmd++ = A3XX_SP_FS_CTRL_REG0; + *cmd++ = drawctxt->shader_load_commands[1].gpuaddr; + + /* Save constant sizes */ + *cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2); + *cmd++ = A3XX_SP_VS_CTRL_REG1; + *cmd++ = drawctxt->constant_load_commands[0].gpuaddr; + + *cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2); + *cmd++ = A3XX_SP_FS_CTRL_REG1; + *cmd++ = drawctxt->constant_load_commands[1].gpuaddr; + + /* Save constant offsets */ + *cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2); + *cmd++ = A3XX_SP_FS_OBJ_OFFSET_REG; + *cmd++ = drawctxt->constant_load_commands[2].gpuaddr; +#else + /* Save shader sizes */ + cmd = rmw_regtomem(cmd, A3XX_SP_VS_CTRL_REG0, 0x7f000000, + 30, (4 << 19) | (4 << 16), + drawctxt->shader_load_commands[0].gpuaddr); + + cmd = rmw_regtomem(cmd, A3XX_SP_FS_CTRL_REG0, 0x7f000000, + 30, (6 << 19) | (4 << 16), + drawctxt->shader_load_commands[1].gpuaddr); + + /* Save constant sizes */ + cmd = rmw_regtomem(cmd, A3XX_SP_VS_CTRL_REG1, 0x000003ff, + 23, (4 << 19) | (4 << 16), + drawctxt->constant_load_commands[0].gpuaddr); + + cmd = rmw_regtomem(cmd, A3XX_SP_FS_CTRL_REG1, 0x000003ff, + 23, (6 << 19) | (4 << 16), + drawctxt->constant_load_commands[1].gpuaddr); + + /* Modify constant restore conditionals */ + cmd = rmw_regtomem(cmd, A3XX_SP_VS_CTRL_REG1, 0x000003ff, + 0, 0, drawctxt->cond_execs[2].gpuaddr); + + cmd = rmw_regtomem(cmd, A3XX_SP_FS_CTRL_REG1, 0x000003ff, + 0, 0, drawctxt->cond_execs[3].gpuaddr); + + /* Save fragment constant shadow offset */ + cmd = rmw_regtomem(cmd, A3XX_SP_FS_OBJ_OFFSET_REG, 0x00ff0000, + 18, (drawctxt->gpustate.gpuaddr & 0xfffffe00) | 1, + drawctxt->constant_load_commands[2].gpuaddr); +#endif + + /* Use mask value to avoid flushing HLSQ which would cause the HW to + discard all the shader data */ + + cmd = rmw_regtomem(cmd, A3XX_HLSQ_CONTROL_0_REG, 0x9ffffdff, + 0, 0, drawctxt->hlsqcontrol_restore_commands[0].gpuaddr); + + create_ib1(drawctxt, drawctxt->restore_fixup, start, cmd); + + tmp_ctx.cmd = cmd; +} + +static int a3xx_create_gpustate_shadow(struct adreno_device *adreno_dev, + struct adreno_context *drawctxt) +{ + drawctxt->flags |= CTXT_FLAGS_STATE_SHADOW; + + build_regrestore_cmds(adreno_dev, drawctxt); + build_constantrestore_cmds(adreno_dev, drawctxt); + build_hlsqcontrol_restore_cmds(adreno_dev, drawctxt); + build_regconstantsave_cmds(adreno_dev, drawctxt); + build_shader_save_cmds(adreno_dev, drawctxt); + build_shader_restore_cmds(adreno_dev, drawctxt); + build_restore_fixup_cmds(adreno_dev, drawctxt); + build_save_fixup_cmds(adreno_dev, drawctxt); + + return 0; +} + +/* create buffers for saving/restoring registers, constants, & GMEM */ +static int a3xx_create_gmem_shadow(struct adreno_device *adreno_dev, + struct adreno_context *drawctxt) +{ + int result; + + calc_gmemsize(&drawctxt->context_gmem_shadow, adreno_dev->gmem_size); + tmp_ctx.gmem_base = adreno_dev->gmem_base; + + result = kgsl_allocate(&drawctxt->context_gmem_shadow.gmemshadow, + drawctxt->pagetable, drawctxt->context_gmem_shadow.size); + + if (result) + return result; + + build_quad_vtxbuff(drawctxt, &drawctxt->context_gmem_shadow, + &tmp_ctx.cmd); + + tmp_ctx.cmd = build_gmem2sys_cmds(adreno_dev, drawctxt, + &drawctxt->context_gmem_shadow); + 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); + + drawctxt->flags |= CTXT_FLAGS_GMEM_SHADOW; + + return 0; +} + +static int a3xx_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); + + if (ret) + return ret; + + kgsl_sharedmem_set(&drawctxt->gpustate, 0, 0, CONTEXT_SIZE); + tmp_ctx.cmd = drawctxt->gpustate.hostptr + CMD_OFFSET; + + if (!(drawctxt->flags & CTXT_FLAGS_PREAMBLE)) { + ret = a3xx_create_gpustate_shadow(adreno_dev, drawctxt); + if (ret) + goto done; + + drawctxt->flags |= CTXT_FLAGS_SHADER_SAVE; + } + + if (!(drawctxt->flags & CTXT_FLAGS_NOGMEMALLOC)) + ret = a3xx_create_gmem_shadow(adreno_dev, drawctxt); + +done: + if (ret) + kgsl_sharedmem_free(&drawctxt->gpustate); + + return ret; +} + +static void a3xx_drawctxt_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"); + + if (!(context->flags & CTXT_FLAGS_PREAMBLE)) { + /* Fixup self modifying IBs for save operations */ + adreno_ringbuffer_issuecmds(device, context, + KGSL_CMD_FLAGS_NONE, context->save_fixup, 3); + + /* save registers and constants. */ + adreno_ringbuffer_issuecmds(device, context, + KGSL_CMD_FLAGS_NONE, + context->regconstant_save, 3); + + if (context->flags & CTXT_FLAGS_SHADER_SAVE) { + /* Save shader instructions */ + adreno_ringbuffer_issuecmds(device, context, + KGSL_CMD_FLAGS_PMODE, context->shader_save, 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, context, + KGSL_CMD_FLAGS_PMODE, + context->context_gmem_shadow. + gmem_save, 3); + context->flags |= CTXT_FLAGS_GMEM_RESTORE; + } +} + +static void a3xx_drawctxt_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 pagetable and thats it */ + kgsl_mmu_setstate(&device->mmu, device->mmu.defaultpagetable, + adreno_dev->drawctxt_active->id); + 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_MEMSTORE_OFFSET(KGSL_MEMSTORE_GLOBAL, current_context); + cmds[4] = context->id; + adreno_ringbuffer_issuecmds(device, context, KGSL_CMD_FLAGS_NONE, + cmds, 5); + kgsl_mmu_setstate(&device->mmu, context->pagetable, context->id); + + /* + * Restore GMEM. (note: changes shader. + * Shader must not already be restored.) + */ + + if (context->flags & CTXT_FLAGS_GMEM_RESTORE) { + adreno_ringbuffer_issuecmds(device, context, + KGSL_CMD_FLAGS_PMODE, + context->context_gmem_shadow. + gmem_restore, 3); + context->flags &= ~CTXT_FLAGS_GMEM_RESTORE; + } + + if (!(context->flags & CTXT_FLAGS_PREAMBLE)) { + adreno_ringbuffer_issuecmds(device, context, + KGSL_CMD_FLAGS_NONE, context->reg_restore, 3); + + /* Fixup self modifying IBs for restore operations */ + adreno_ringbuffer_issuecmds(device, context, + KGSL_CMD_FLAGS_NONE, + context->restore_fixup, 3); + + adreno_ringbuffer_issuecmds(device, context, + KGSL_CMD_FLAGS_NONE, + context->constant_restore, 3); + + if (context->flags & CTXT_FLAGS_SHADER_RESTORE) + adreno_ringbuffer_issuecmds(device, context, + KGSL_CMD_FLAGS_NONE, + context->shader_restore, 3); + + /* Restore HLSQ_CONTROL_0 register */ + adreno_ringbuffer_issuecmds(device, context, + KGSL_CMD_FLAGS_NONE, + context->hlsqcontrol_restore, 3); + } +} + +static void a3xx_rb_init(struct adreno_device *adreno_dev, + struct adreno_ringbuffer *rb) +{ + unsigned int *cmds, cmds_gpu; + cmds = adreno_ringbuffer_allocspace(rb, 18); + cmds_gpu = rb->buffer_desc.gpuaddr + sizeof(uint) * (rb->wptr - 18); + + GSL_RB_WRITE(cmds, cmds_gpu, cp_type3_packet(CP_ME_INIT, 17)); + GSL_RB_WRITE(cmds, cmds_gpu, 0x000003f7); + GSL_RB_WRITE(cmds, cmds_gpu, 0x00000000); + GSL_RB_WRITE(cmds, cmds_gpu, 0x00000000); + GSL_RB_WRITE(cmds, cmds_gpu, 0x00000000); + GSL_RB_WRITE(cmds, cmds_gpu, 0x00000080); + GSL_RB_WRITE(cmds, cmds_gpu, 0x00000100); + GSL_RB_WRITE(cmds, cmds_gpu, 0x00000180); + GSL_RB_WRITE(cmds, cmds_gpu, 0x00006600); + GSL_RB_WRITE(cmds, cmds_gpu, 0x00000150); + GSL_RB_WRITE(cmds, cmds_gpu, 0x0000014e); + GSL_RB_WRITE(cmds, cmds_gpu, 0x00000154); + GSL_RB_WRITE(cmds, cmds_gpu, 0x00000001); + GSL_RB_WRITE(cmds, cmds_gpu, 0x00000000); + GSL_RB_WRITE(cmds, cmds_gpu, 0x00000000); + /* Protected mode control - turned off for A3XX */ + GSL_RB_WRITE(cmds, cmds_gpu, 0x00000000); + GSL_RB_WRITE(cmds, cmds_gpu, 0x00000000); + GSL_RB_WRITE(cmds, cmds_gpu, 0x00000000); + + adreno_ringbuffer_submit(rb); +} + +static void a3xx_err_callback(struct adreno_device *adreno_dev, int bit) +{ + struct kgsl_device *device = &adreno_dev->dev; + const char *err = ""; + + switch (bit) { + case A3XX_INT_RBBM_AHB_ERROR: { + unsigned int reg; + + adreno_regread(device, A3XX_RBBM_AHB_ERROR_STATUS, ®); + + /* + * Return the word address of the erroring register so that it + * matches the register specification + */ + + KGSL_DRV_CRIT(device, + "RBBM | AHB bus error | %s | addr=%x | ports=%x:%x\n", + reg & (1 << 28) ? "WRITE" : "READ", + (reg & 0xFFFFF) >> 2, (reg >> 20) & 0x3, + (reg >> 24) & 0x3); + + /* Clear the error */ + adreno_regwrite(device, A3XX_RBBM_AHB_CMD, (1 << 3)); + return; + } + case A3XX_INT_RBBM_REG_TIMEOUT: + err = "RBBM: AHB register timeout"; + break; + case A3XX_INT_RBBM_ME_MS_TIMEOUT: + err = "RBBM: ME master split timeout"; + break; + case A3XX_INT_RBBM_PFP_MS_TIMEOUT: + err = "RBBM: PFP master split timeout"; + break; + case A3XX_INT_RBBM_ATB_BUS_OVERFLOW: + err = "RBBM: ATB bus oveflow"; + break; + case A3XX_INT_VFD_ERROR: + err = "VFD: Out of bounds access"; + break; + case A3XX_INT_CP_T0_PACKET_IN_IB: + err = "ringbuffer TO packet in IB interrupt"; + break; + case A3XX_INT_CP_OPCODE_ERROR: + err = "ringbuffer opcode error interrupt"; + break; + case A3XX_INT_CP_RESERVED_BIT_ERROR: + err = "ringbuffer reserved bit error interrupt"; + break; + case A3XX_INT_CP_HW_FAULT: + err = "ringbuffer hardware fault"; + break; + case A3XX_INT_CP_REG_PROTECT_FAULT: + err = "ringbuffer protected mode error interrupt"; + break; + case A3XX_INT_CP_AHB_ERROR_HALT: + err = "ringbuffer AHB error interrupt"; + break; + case A3XX_INT_MISC_HANG_DETECT: + err = "MISC: GPU hang detected"; + break; + case A3XX_INT_UCHE_OOB_ACCESS: + err = "UCHE: Out of bounds access"; + break; + } + + KGSL_DRV_CRIT(device, "%s\n", err); + kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_OFF); +} + +static void a3xx_cp_callback(struct adreno_device *adreno_dev, int irq) +{ + struct kgsl_device *device = &adreno_dev->dev; + + if (irq == A3XX_INT_CP_RB_INT) { + unsigned int context_id; + kgsl_sharedmem_readl(&device->memstore, &context_id, + KGSL_MEMSTORE_OFFSET(KGSL_MEMSTORE_GLOBAL, + current_context)); + if (context_id < KGSL_MEMSTORE_MAX) { + kgsl_sharedmem_writel(&device->memstore, + KGSL_MEMSTORE_OFFSET(context_id, + ts_cmp_enable), 0); + wmb(); + } + KGSL_CMD_WARN(device, "ringbuffer rb interrupt\n"); + } + + wake_up_interruptible_all(&device->wait_queue); + + /* Schedule work to free mem and issue ibs */ + queue_work(device->work_queue, &device->ts_expired_ws); + + atomic_notifier_call_chain(&device->ts_notifier_list, + device->id, NULL); +} + +#define A3XX_IRQ_CALLBACK(_c) { .func = _c } + +#define A3XX_INT_MASK \ + ((1 << A3XX_INT_RBBM_AHB_ERROR) | \ + (1 << A3XX_INT_RBBM_ATB_BUS_OVERFLOW) | \ + (1 << A3XX_INT_CP_T0_PACKET_IN_IB) | \ + (1 << A3XX_INT_CP_OPCODE_ERROR) | \ + (1 << A3XX_INT_CP_RESERVED_BIT_ERROR) | \ + (1 << A3XX_INT_CP_HW_FAULT) | \ + (1 << A3XX_INT_CP_IB1_INT) | \ + (1 << A3XX_INT_CP_IB2_INT) | \ + (1 << A3XX_INT_CP_RB_INT) | \ + (1 << A3XX_INT_CP_REG_PROTECT_FAULT) | \ + (1 << A3XX_INT_CP_AHB_ERROR_HALT) | \ + (1 << A3XX_INT_UCHE_OOB_ACCESS)) + +static struct { + void (*func)(struct adreno_device *, int); +} a3xx_irq_funcs[] = { + A3XX_IRQ_CALLBACK(NULL), /* 0 - RBBM_GPU_IDLE */ + A3XX_IRQ_CALLBACK(a3xx_err_callback), /* 1 - RBBM_AHB_ERROR */ + A3XX_IRQ_CALLBACK(a3xx_err_callback), /* 2 - RBBM_REG_TIMEOUT */ + A3XX_IRQ_CALLBACK(a3xx_err_callback), /* 3 - RBBM_ME_MS_TIMEOUT */ + A3XX_IRQ_CALLBACK(a3xx_err_callback), /* 4 - RBBM_PFP_MS_TIMEOUT */ + A3XX_IRQ_CALLBACK(a3xx_err_callback), /* 5 - RBBM_ATB_BUS_OVERFLOW */ + A3XX_IRQ_CALLBACK(a3xx_err_callback), /* 6 - RBBM_VFD_ERROR */ + A3XX_IRQ_CALLBACK(NULL), /* 7 - CP_SW */ + A3XX_IRQ_CALLBACK(a3xx_err_callback), /* 8 - CP_T0_PACKET_IN_IB */ + A3XX_IRQ_CALLBACK(a3xx_err_callback), /* 9 - CP_OPCODE_ERROR */ + A3XX_IRQ_CALLBACK(a3xx_err_callback), /* 10 - CP_RESERVED_BIT_ERROR */ + A3XX_IRQ_CALLBACK(a3xx_err_callback), /* 11 - CP_HW_FAULT */ + A3XX_IRQ_CALLBACK(NULL), /* 12 - CP_DMA */ + A3XX_IRQ_CALLBACK(a3xx_cp_callback), /* 13 - CP_IB2_INT */ + A3XX_IRQ_CALLBACK(a3xx_cp_callback), /* 14 - CP_IB1_INT */ + A3XX_IRQ_CALLBACK(a3xx_cp_callback), /* 15 - CP_RB_INT */ + A3XX_IRQ_CALLBACK(a3xx_err_callback), /* 16 - CP_REG_PROTECT_FAULT */ + A3XX_IRQ_CALLBACK(NULL), /* 17 - CP_RB_DONE_TS */ + A3XX_IRQ_CALLBACK(NULL), /* 18 - CP_VS_DONE_TS */ + A3XX_IRQ_CALLBACK(NULL), /* 19 - CP_PS_DONE_TS */ + A3XX_IRQ_CALLBACK(NULL), /* 20 - CP_CACHE_FLUSH_TS */ + A3XX_IRQ_CALLBACK(a3xx_err_callback), /* 21 - CP_AHB_ERROR_FAULT */ + A3XX_IRQ_CALLBACK(NULL), /* 22 - Unused */ + A3XX_IRQ_CALLBACK(NULL), /* 23 - Unused */ + A3XX_IRQ_CALLBACK(NULL), /* 24 - MISC_HANG_DETECT */ + A3XX_IRQ_CALLBACK(a3xx_err_callback), /* 25 - UCHE_OOB_ACCESS */ + /* 26 to 31 - Unused */ +}; + +static irqreturn_t a3xx_irq_handler(struct adreno_device *adreno_dev) +{ + struct kgsl_device *device = &adreno_dev->dev; + irqreturn_t ret = IRQ_NONE; + unsigned int status, tmp; + int i; + + adreno_regread(&adreno_dev->dev, A3XX_RBBM_INT_0_STATUS, &status); + + for (tmp = status, i = 0; tmp && i < ARRAY_SIZE(a3xx_irq_funcs); i++) { + if (tmp & 1) { + if (a3xx_irq_funcs[i].func != NULL) { + a3xx_irq_funcs[i].func(adreno_dev, i); + ret = IRQ_HANDLED; + } else { + KGSL_DRV_CRIT(device, + "Unhandled interrupt bit %x\n", i); + } + } + + tmp >>= 1; + } + + trace_kgsl_a3xx_irq_status(device, status); + + if (status) + adreno_regwrite(&adreno_dev->dev, A3XX_RBBM_INT_CLEAR_CMD, + status); + return ret; +} + +static void a3xx_irq_control(struct adreno_device *adreno_dev, int state) +{ + struct kgsl_device *device = &adreno_dev->dev; + + if (state) + adreno_regwrite(device, A3XX_RBBM_INT_0_MASK, A3XX_INT_MASK); + else + adreno_regwrite(device, A3XX_RBBM_INT_0_MASK, 0); +} + +static unsigned int a3xx_busy_cycles(struct adreno_device *adreno_dev) +{ + struct kgsl_device *device = &adreno_dev->dev; + unsigned int reg, val; + + /* Freeze the counter */ + adreno_regread(device, A3XX_RBBM_RBBM_CTL, ®); + reg &= ~RBBM_RBBM_CTL_ENABLE_PWR_CTR1; + adreno_regwrite(device, A3XX_RBBM_RBBM_CTL, reg); + + /* Read the value */ + adreno_regread(device, A3XX_RBBM_PERFCTR_PWR_1_LO, &val); + + /* Reset the counter */ + reg |= RBBM_RBBM_CTL_RESET_PWR_CTR1; + adreno_regwrite(device, A3XX_RBBM_RBBM_CTL, reg); + + /* Re-enable the counter */ + reg &= ~RBBM_RBBM_CTL_RESET_PWR_CTR1; + reg |= RBBM_RBBM_CTL_ENABLE_PWR_CTR1; + adreno_regwrite(device, A3XX_RBBM_RBBM_CTL, reg); + + return val; +} + +static void a3xx_start(struct adreno_device *adreno_dev) +{ + struct kgsl_device *device = &adreno_dev->dev; + + /* Set up 16 deep read/write request queues */ + + adreno_regwrite(device, A3XX_VBIF_IN_RD_LIM_CONF0, 0x10101010); + adreno_regwrite(device, A3XX_VBIF_IN_RD_LIM_CONF1, 0x10101010); + adreno_regwrite(device, A3XX_VBIF_OUT_RD_LIM_CONF0, 0x10101010); + adreno_regwrite(device, A3XX_VBIF_OUT_WR_LIM_CONF0, 0x10101010); + adreno_regwrite(device, A3XX_VBIF_DDR_OUT_MAX_BURST, 0x00000303); + adreno_regwrite(device, A3XX_VBIF_IN_WR_LIM_CONF0, 0x10101010); + adreno_regwrite(device, A3XX_VBIF_IN_WR_LIM_CONF1, 0x10101010); + + /* Enable WR-REQ */ + adreno_regwrite(device, A3XX_VBIF_GATE_OFF_WRREQ_EN, 0x000000FF); + + /* Set up round robin arbitration between both AXI ports */ + adreno_regwrite(device, A3XX_VBIF_ARB_CTL, 0x00000030); + + /* Set up AOOO */ + adreno_regwrite(device, A3XX_VBIF_OUT_AXI_AOOO_EN, 0x0000003C); + adreno_regwrite(device, A3XX_VBIF_OUT_AXI_AOOO, 0x003C003C); + + if (cpu_is_apq8064()) { + /* Enable 1K sort */ + adreno_regwrite(device, A3XX_VBIF_ABIT_SORT, 0x000000FF); + adreno_regwrite(device, A3XX_VBIF_ABIT_SORT_CONF, 0x000000A4); + } + /* Make all blocks contribute to the GPU BUSY perf counter */ + adreno_regwrite(device, A3XX_RBBM_GPU_BUSY_MASKED, 0xFFFFFFFF); + + /* Tune the hystersis counters for SP and CP idle detection */ + adreno_regwrite(device, A3XX_RBBM_SP_HYST_CNT, 0x10); + adreno_regwrite(device, A3XX_RBBM_WAIT_IDLE_CLOCKS_CTL, 0x10); + + /* Enable the RBBM error reporting bits. This lets us get + useful information on failure */ + + adreno_regwrite(device, A3XX_RBBM_AHB_CTL0, 0x00000001); + + /* Enable AHB error reporting */ + adreno_regwrite(device, A3XX_RBBM_AHB_CTL1, 0xA6FFFFFF); + + /* Turn on the power counters */ + adreno_regwrite(device, A3XX_RBBM_RBBM_CTL, 0x00030000); + + /* Turn on hang detection - this spews a lot of useful information + * into the RBBM registers on a hang */ + + adreno_regwrite(device, A3XX_RBBM_INTERFACE_HANG_INT_CTL, + (1 << 16) | 0xFFF); + + /* Enable Clock gating */ + adreno_regwrite(device, A3XX_RBBM_CLOCK_CTL, + A3XX_RBBM_CLOCK_CTL_DEFAULT); + +} + +/* Defined in adreno_a3xx_snapshot.c */ +void *a3xx_snapshot(struct adreno_device *adreno_dev, void *snapshot, + int *remain, int hang); + +struct adreno_gpudev adreno_a3xx_gpudev = { + .reg_rbbm_status = A3XX_RBBM_STATUS, + .reg_cp_pfp_ucode_addr = A3XX_CP_PFP_UCODE_ADDR, + .reg_cp_pfp_ucode_data = A3XX_CP_PFP_UCODE_DATA, + + .ctxt_create = a3xx_drawctxt_create, + .ctxt_save = a3xx_drawctxt_save, + .ctxt_restore = a3xx_drawctxt_restore, + .ctxt_draw_workaround = NULL, + .rb_init = a3xx_rb_init, + .irq_control = a3xx_irq_control, + .irq_handler = a3xx_irq_handler, + .busy_cycles = a3xx_busy_cycles, + .start = a3xx_start, + .snapshot = a3xx_snapshot, +}; diff --git a/drivers/gpu/msm/adreno_a3xx_snapshot.c b/drivers/gpu/msm/adreno_a3xx_snapshot.c new file mode 100644 index 00000000000..a3bee4dc5c1 --- /dev/null +++ b/drivers/gpu/msm/adreno_a3xx_snapshot.c @@ -0,0 +1,332 @@ +/* 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" +#include "a3xx_reg.h" + +#define DEBUG_SECTION_SZ(_dwords) (((_dwords) * sizeof(unsigned int)) \ + + sizeof(struct kgsl_snapshot_debug)) + +#define SHADER_MEMORY_SIZE 0x4000 + +static int a3xx_snapshot_shader_memory(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(SHADER_MEMORY_SIZE)) { + SNAPSHOT_ERR_NOMEM(device, "SHADER MEMORY"); + return 0; + } + + header->type = SNAPSHOT_DEBUG_SHADER_MEMORY; + header->size = SHADER_MEMORY_SIZE; + + for (i = 0; i < SHADER_MEMORY_SIZE; i++) + adreno_regread(device, 0x4000 + i, &data[i]); + + return DEBUG_SECTION_SZ(SHADER_MEMORY_SIZE); +} + +#define VPC_MEMORY_BANKS 4 +#define VPC_MEMORY_SIZE 512 + +static int a3xx_snapshot_vpc_memory(struct kgsl_device *device, void *snapshot, + int remain, void *priv) +{ + struct kgsl_snapshot_debug *header = snapshot; + unsigned int *data = snapshot + sizeof(*header); + int size = VPC_MEMORY_BANKS * VPC_MEMORY_SIZE; + int bank, addr, i = 0; + + if (remain < DEBUG_SECTION_SZ(size)) { + SNAPSHOT_ERR_NOMEM(device, "VPC MEMORY"); + return 0; + } + + header->type = SNAPSHOT_DEBUG_VPC_MEMORY; + header->size = size; + + for (bank = 0; bank < VPC_MEMORY_BANKS; bank++) { + for (addr = 0; addr < VPC_MEMORY_SIZE; addr++) { + unsigned int val = bank | (addr << 4); + adreno_regwrite(device, + A3XX_VPC_VPC_DEBUG_RAM_SEL, val); + adreno_regread(device, + A3XX_VPC_VPC_DEBUG_RAM_READ, &data[i++]); + } + } + + return DEBUG_SECTION_SZ(size); +} + +#define CP_MEQ_SIZE 16 +static int a3xx_snapshot_cp_meq(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(CP_MEQ_SIZE)) { + SNAPSHOT_ERR_NOMEM(device, "CP MEQ DEBUG"); + return 0; + } + + header->type = SNAPSHOT_DEBUG_CP_MEQ; + header->size = CP_MEQ_SIZE; + + adreno_regwrite(device, A3XX_CP_MEQ_ADDR, 0x0); + for (i = 0; i < CP_MEQ_SIZE; i++) + adreno_regread(device, A3XX_CP_MEQ_DATA, &data[i]); + + return DEBUG_SECTION_SZ(CP_MEQ_SIZE); +} + +static int a3xx_snapshot_cp_pm4_ram(struct kgsl_device *device, void *snapshot, + int remain, void *priv) +{ + struct adreno_device *adreno_dev = ADRENO_DEVICE(device); + struct kgsl_snapshot_debug *header = snapshot; + unsigned int *data = snapshot + sizeof(*header); + int i, size = adreno_dev->pm4_fw_size - 1; + + if (remain < DEBUG_SECTION_SZ(size)) { + SNAPSHOT_ERR_NOMEM(device, "CP PM4 RAM DEBUG"); + return 0; + } + + header->type = SNAPSHOT_DEBUG_CP_PM4_RAM; + header->size = size; + + /* + * Read the firmware from the GPU rather than use our cache in order to + * try to catch mis-programming or corruption in the hardware. We do + * use the cached version of the size, however, instead of trying to + * maintain always changing hardcoded constants + */ + + adreno_regwrite(device, REG_CP_ME_RAM_RADDR, 0x0); + for (i = 0; i < size; i++) + adreno_regread(device, REG_CP_ME_RAM_DATA, &data[i]); + + return DEBUG_SECTION_SZ(size); +} + +static int a3xx_snapshot_cp_pfp_ram(struct kgsl_device *device, void *snapshot, + int remain, void *priv) +{ + struct adreno_device *adreno_dev = ADRENO_DEVICE(device); + struct kgsl_snapshot_debug *header = snapshot; + unsigned int *data = snapshot + sizeof(*header); + int i, size = adreno_dev->pfp_fw_size - 1; + + if (remain < DEBUG_SECTION_SZ(size)) { + SNAPSHOT_ERR_NOMEM(device, "CP PFP RAM DEBUG"); + return 0; + } + + header->type = SNAPSHOT_DEBUG_CP_PFP_RAM; + header->size = size; + + /* + * Read the firmware from the GPU rather than use our cache in order to + * try to catch mis-programming or corruption in the hardware. We do + * use the cached version of the size, however, instead of trying to + * maintain always changing hardcoded constants + */ + kgsl_regwrite(device, A3XX_CP_PFP_UCODE_ADDR, 0x0); + for (i = 0; i < size; i++) + adreno_regread(device, A3XX_CP_PFP_UCODE_DATA, &data[i]); + + return DEBUG_SECTION_SZ(size); +} + +#define CP_ROQ_SIZE 128 + +static int a3xx_snapshot_cp_roq(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(CP_ROQ_SIZE)) { + SNAPSHOT_ERR_NOMEM(device, "CP ROQ DEBUG"); + return 0; + } + + header->type = SNAPSHOT_DEBUG_CP_ROQ; + header->size = CP_ROQ_SIZE; + + adreno_regwrite(device, A3XX_CP_ROQ_ADDR, 0x0); + for (i = 0; i < CP_ROQ_SIZE; i++) + adreno_regread(device, A3XX_CP_ROQ_DATA, &data[i]); + + return DEBUG_SECTION_SZ(CP_ROQ_SIZE); +} + +#define DEBUGFS_BLOCK_SIZE 0x40 + +static int a3xx_snapshot_debugbus_block(struct kgsl_device *device, + void *snapshot, int remain, void *priv) +{ + struct kgsl_snapshot_debugbus *header = snapshot; + unsigned int id = (unsigned int) priv; + unsigned int val; + int i; + unsigned int *data = snapshot + sizeof(*header); + int size = + (DEBUGFS_BLOCK_SIZE * sizeof(unsigned int)) + sizeof(*header); + + if (remain < size) { + SNAPSHOT_ERR_NOMEM(device, "DEBUGBUS"); + return 0; + } + + val = (id << 8) | (1 << 16); + + header->id = id; + header->count = DEBUGFS_BLOCK_SIZE; + + for (i = 0; i < DEBUGFS_BLOCK_SIZE; i++) { + adreno_regwrite(device, A3XX_RBBM_DEBUG_BUS_CTL, val | i); + adreno_regread(device, A3XX_RBBM_DEBUG_BUS_DATA_STATUS, + &data[i]); + } + + return size; +} + +static unsigned int debugbus_blocks[] = { + RBBM_BLOCK_ID_CP, + RBBM_BLOCK_ID_RBBM, + RBBM_BLOCK_ID_VBIF, + RBBM_BLOCK_ID_HLSQ, + RBBM_BLOCK_ID_UCHE, + RBBM_BLOCK_ID_PC, + RBBM_BLOCK_ID_VFD, + RBBM_BLOCK_ID_VPC, + RBBM_BLOCK_ID_TSE, + RBBM_BLOCK_ID_RAS, + RBBM_BLOCK_ID_VSC, + RBBM_BLOCK_ID_SP_0, + RBBM_BLOCK_ID_SP_1, + RBBM_BLOCK_ID_SP_2, + RBBM_BLOCK_ID_SP_3, + RBBM_BLOCK_ID_TPL1_0, + RBBM_BLOCK_ID_TPL1_1, + RBBM_BLOCK_ID_TPL1_2, + RBBM_BLOCK_ID_TPL1_3, + RBBM_BLOCK_ID_RB_0, + RBBM_BLOCK_ID_RB_1, + RBBM_BLOCK_ID_RB_2, + RBBM_BLOCK_ID_RB_3, + RBBM_BLOCK_ID_MARB_0, + RBBM_BLOCK_ID_MARB_1, + RBBM_BLOCK_ID_MARB_2, + RBBM_BLOCK_ID_MARB_3, +}; + +static void *a3xx_snapshot_debugbus(struct kgsl_device *device, + void *snapshot, int *remain) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(debugbus_blocks); i++) { + snapshot = kgsl_snapshot_add_section(device, + KGSL_SNAPSHOT_SECTION_DEBUGBUS, snapshot, remain, + a3xx_snapshot_debugbus_block, + (void *) debugbus_blocks[i]); + } + + return snapshot; +} + +/* A3XX GPU snapshot function - this is where all of the A3XX specific + * bits and pieces are grabbed into the snapshot memory + */ + +void *a3xx_snapshot(struct adreno_device *adreno_dev, void *snapshot, + int *remain, int hang) +{ + struct kgsl_device *device = &adreno_dev->dev; + struct kgsl_snapshot_registers regs; + + regs.regs = (unsigned int *) a3xx_registers; + regs.count = a3xx_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 = kgsl_snapshot_indexed_registers(device, snapshot, + remain, REG_CP_STATE_DEBUG_INDEX, + REG_CP_STATE_DEBUG_DATA, 0x0, 0x14); + + /* CP_ME indexed registers */ + snapshot = kgsl_snapshot_indexed_registers(device, snapshot, + remain, REG_CP_ME_CNTL, REG_CP_ME_STATUS, + 64, 44); + + /* Disable Clock gating temporarily for the debug bus to work */ + adreno_regwrite(device, A3XX_RBBM_CLOCK_CTL, 0x00); + + /* VPC memory */ + snapshot = kgsl_snapshot_add_section(device, + KGSL_SNAPSHOT_SECTION_DEBUG, snapshot, remain, + a3xx_snapshot_vpc_memory, NULL); + + /* CP MEQ */ + snapshot = kgsl_snapshot_add_section(device, + KGSL_SNAPSHOT_SECTION_DEBUG, snapshot, remain, + a3xx_snapshot_cp_meq, NULL); + + /* Shader working/shadow memory */ + snapshot = kgsl_snapshot_add_section(device, + KGSL_SNAPSHOT_SECTION_DEBUG, snapshot, remain, + a3xx_snapshot_shader_memory, NULL); + + + /* CP PFP and PM4 */ + /* Reading these will hang the GPU if it isn't already hung */ + + if (hang) { + snapshot = kgsl_snapshot_add_section(device, + KGSL_SNAPSHOT_SECTION_DEBUG, snapshot, remain, + a3xx_snapshot_cp_pfp_ram, NULL); + + snapshot = kgsl_snapshot_add_section(device, + KGSL_SNAPSHOT_SECTION_DEBUG, snapshot, remain, + a3xx_snapshot_cp_pm4_ram, NULL); + } + + /* CP ROQ */ + snapshot = kgsl_snapshot_add_section(device, + KGSL_SNAPSHOT_SECTION_DEBUG, snapshot, remain, + a3xx_snapshot_cp_roq, NULL); + + snapshot = a3xx_snapshot_debugbus(device, snapshot, remain); + + /* Enable Clock gating */ + adreno_regwrite(device, A3XX_RBBM_CLOCK_CTL, + A3XX_RBBM_CLOCK_CTL_DEFAULT); + + return snapshot; +} diff --git a/drivers/gpu/msm/adreno_a3xx_trace.c b/drivers/gpu/msm/adreno_a3xx_trace.c new file mode 100644 index 00000000000..8b4a80dde8e --- /dev/null +++ b/drivers/gpu/msm/adreno_a3xx_trace.c @@ -0,0 +1,20 @@ +/* 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" + +/* Instantiate tracepoints */ +#define CREATE_TRACE_POINTS +#include "a3xx_reg.h" +#include "adreno_a3xx_trace.h" diff --git a/drivers/gpu/msm/adreno_a3xx_trace.h b/drivers/gpu/msm/adreno_a3xx_trace.h new file mode 100644 index 00000000000..44483a8e7aa --- /dev/null +++ b/drivers/gpu/msm/adreno_a3xx_trace.h @@ -0,0 +1,89 @@ +/* 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. + * + */ + +#if !defined(_ADRENO_A3XX_TRACE_H) || defined(TRACE_HEADER_MULTI_READ) +#define _ADRENO_A3XX_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_a3xx_trace + +#include + +struct kgsl_device; + +/* + * Tracepoint for a3xx irq. Includes status info + */ +TRACE_EVENT(kgsl_a3xx_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, "|", + { 1 << A3XX_INT_RBBM_AHB_ERROR, "RBBM_GPU_IDLE" }, + { 1 << A3XX_INT_RBBM_AHB_ERROR, "RBBM_AHB_ERR" }, + { 1 << A3XX_INT_RBBM_REG_TIMEOUT, "RBBM_REG_TIMEOUT" }, + { 1 << A3XX_INT_RBBM_ME_MS_TIMEOUT, + "RBBM_ME_MS_TIMEOUT" }, + { 1 << A3XX_INT_RBBM_PFP_MS_TIMEOUT, + "RBBM_PFP_MS_TIMEOUT" }, + { 1 << A3XX_INT_RBBM_ATB_BUS_OVERFLOW, + "RBBM_ATB_BUS_OVERFLOW" }, + { 1 << A3XX_INT_VFD_ERROR, "RBBM_VFD_ERROR" }, + { 1 << A3XX_INT_CP_SW_INT, "CP_SW" }, + { 1 << A3XX_INT_CP_T0_PACKET_IN_IB, + "CP_T0_PACKET_IN_IB" }, + { 1 << A3XX_INT_CP_OPCODE_ERROR, "CP_OPCODE_ERROR" }, + { 1 << A3XX_INT_CP_RESERVED_BIT_ERROR, + "CP_RESERVED_BIT_ERROR" }, + { 1 << A3XX_INT_CP_HW_FAULT, "CP_HW_FAULT" }, + { 1 << A3XX_INT_CP_DMA, "CP_DMA" }, + { 1 << A3XX_INT_CP_IB2_INT, "CP_IB2_INT" }, + { 1 << A3XX_INT_CP_IB1_INT, "CP_IB1_INT" }, + { 1 << A3XX_INT_CP_RB_INT, "CP_RB_INT" }, + { 1 << A3XX_INT_CP_REG_PROTECT_FAULT, + "CP_REG_PROTECT_FAULT" }, + { 1 << A3XX_INT_CP_RB_DONE_TS, "CP_RB_DONE_TS" }, + { 1 << A3XX_INT_CP_VS_DONE_TS, "CP_VS_DONE_TS" }, + { 1 << A3XX_INT_CP_PS_DONE_TS, "CP_PS_DONE_TS" }, + { 1 << A3XX_INT_CACHE_FLUSH_TS, "CACHE_FLUSH_TS" }, + { 1 << A3XX_INT_CP_AHB_ERROR_HALT, + "CP_AHB_ERROR_HALT" }, + { 1 << A3XX_INT_MISC_HANG_DETECT, "MISC_HANG_DETECT" }, + { 1 << A3XX_INT_UCHE_OOB_ACCESS, "UCHE_OOB_ACCESS" }) + : "None" + ) +); + +#endif /* _ADRENO_A3XX_TRACE_H */ + +/* This part must be outside protection */ +#include diff --git a/drivers/gpu/msm/adreno_drawctxt.c b/drivers/gpu/msm/adreno_drawctxt.c index 990af5f076a..451db5c5853 100644 --- a/drivers/gpu/msm/adreno_drawctxt.c +++ b/drivers/gpu/msm/adreno_drawctxt.c @@ -192,7 +192,8 @@ void adreno_drawctxt_destroy(struct kgsl_device *device, adreno_drawctxt_switch(adreno_dev, NULL, 0); } - adreno_idle(device, KGSL_TIMEOUT_DEFAULT); + if (device->state != KGSL_STATE_HUNG) + adreno_idle(device, KGSL_TIMEOUT_DEFAULT); kgsl_sharedmem_free(&drawctxt->gpustate); kgsl_sharedmem_free(&drawctxt->context_gmem_shadow.gmemshadow); diff --git a/drivers/gpu/msm/adreno_ringbuffer.c b/drivers/gpu/msm/adreno_ringbuffer.c index f9d9df1a03d..c56f5fd580b 100644 --- a/drivers/gpu/msm/adreno_ringbuffer.c +++ b/drivers/gpu/msm/adreno_ringbuffer.c @@ -529,7 +529,7 @@ adreno_ringbuffer_addcmds(struct adreno_ringbuffer *rb, 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->flags & CTXT_FLAGS_GPU_HANG) { + 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); diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c index 7a2857c380f..8d841d69c99 100644 --- a/drivers/gpu/msm/kgsl.c +++ b/drivers/gpu/msm/kgsl.c @@ -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) @@ -255,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; } @@ -271,6 +280,7 @@ 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); @@ -301,6 +311,14 @@ static void kgsl_timestamp_expired(struct work_struct *work) kfree(event); } + /* Mark the next pending event */ + if (!list_empty(&device->events) && device->ftbl->next_event) { + event = list_first_entry(&device->events, struct kgsl_event, + list); + + device->ftbl->next_event(device, event); + } + mutex_unlock(&device->mutex); } @@ -1844,6 +1862,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; } @@ -1914,6 +1937,12 @@ 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); + + nr = _IOC_NR(cmd); nr = _IOC_NR(cmd); @@ -2369,22 +2398,30 @@ kgsl_ptdata_init(void) static void kgsl_core_exit(void) { - unregister_chrdev_region(kgsl_driver.major, KGSL_DEVICE_MAX); - - kgsl_mmu_ptpool_destroy(&kgsl_driver.ptpool); + kgsl_mmu_ptpool_destroy(kgsl_driver.ptpool); kgsl_driver.ptpool = NULL; - device_unregister(&kgsl_driver.virtdev); + kgsl_drm_exit(); + kgsl_cffdump_destroy(); + kgsl_core_debugfs_close(); + + /* + * We call kgsl_sharedmem_uninit_sysfs() and device_unregister() + * only if kgsl_driver.virtdev has been populated. + * We check at least one member of kgsl_driver.virtdev to + * see if it is not NULL (and thus, has been populated). + */ + if (kgsl_driver.virtdev.class) { + kgsl_sharedmem_uninit_sysfs(); + device_unregister(&kgsl_driver.virtdev); + } if (kgsl_driver.class) { class_destroy(kgsl_driver.class); kgsl_driver.class = NULL; } - kgsl_drm_exit(); - kgsl_cffdump_destroy(); - kgsl_core_debugfs_close(); - kgsl_sharedmem_uninit_sysfs(); + unregister_chrdev_region(kgsl_driver.major, KGSL_DEVICE_MAX); } static int __init kgsl_core_init(void) diff --git a/drivers/gpu/msm/kgsl.h b/drivers/gpu/msm/kgsl.h index 25c4827e136..c2229cfcf12 100644 --- a/drivers/gpu/msm/kgsl.h +++ b/drivers/gpu/msm/kgsl.h @@ -242,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_device.h b/drivers/gpu/msm/kgsl_device.h index fdb4a6e2e07..28b96a0c8c1 100644 --- a/drivers/gpu/msm/kgsl_device.h +++ b/drivers/gpu/msm/kgsl_device.h @@ -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 @@ -105,6 +107,8 @@ struct kgsl_functable { struct kgsl_context *context); long (*ioctl) (struct kgsl_device_private *dev_priv, unsigned int cmd, void *data); + void (*next_event)(struct kgsl_device *device, + struct kgsl_event *event); }; struct kgsl_memregion { @@ -128,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; }; @@ -211,6 +215,12 @@ 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 { diff --git a/drivers/gpu/msm/kgsl_iommu.h b/drivers/gpu/msm/kgsl_iommu.h new file mode 100644 index 00000000000..f14db935b72 --- /dev/null +++ b/drivers/gpu/msm/kgsl_iommu.h @@ -0,0 +1,121 @@ +/* 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_IOMMU_H +#define __KGSL_IOMMU_H + +#include + +/* IOMMU registers and masks */ +#define KGSL_IOMMU_TTBR0 0x10 +#define KGSL_IOMMU_TTBR1 0x14 +#define KGSL_IOMMU_FSR 0x20 + +#define KGSL_IOMMU_TTBR0_PA_MASK 0x0003FFFF +#define KGSL_IOMMU_TTBR0_PA_SHIFT 14 +#define KGSL_IOMMU_CTX_TLBIALL 0x800 +#define KGSL_IOMMU_CTX_SHIFT 12 + +/* + * Max number of iommu units that the gpu core can have + * On APQ8064, KGSL can control a maximum of 2 IOMMU units. + */ +#define KGSL_IOMMU_MAX_UNITS 2 + +/* Max number of iommu contexts per IOMMU unit */ +#define KGSL_IOMMU_MAX_DEVS_PER_UNIT 2 + +/* Macros to read/write IOMMU registers */ +#define KGSL_IOMMU_SET_IOMMU_REG(base_addr, ctx, REG, val) \ + writel_relaxed(val, base_addr + \ + (ctx << KGSL_IOMMU_CTX_SHIFT) + \ + KGSL_IOMMU_##REG) + +#define KGSL_IOMMU_GET_IOMMU_REG(base_addr, ctx, REG) \ + readl_relaxed(base_addr + \ + (ctx << KGSL_IOMMU_CTX_SHIFT) + \ + KGSL_IOMMU_##REG) + +/* Gets the lsb value of pagetable */ +#define KGSL_IOMMMU_PT_LSB(pt_val) \ + (pt_val & ~(KGSL_IOMMU_TTBR0_PA_MASK << \ + KGSL_IOMMU_TTBR0_PA_SHIFT)) + +/* offset at which a nop command is placed in setstate_memory */ +#define KGSL_IOMMU_SETSTATE_NOP_OFFSET 1024 + +/* + * struct kgsl_iommu_device - Structure holding data about iommu contexts + * @dev: Device pointer to iommu context + * @attached: Indicates whether this iommu context is presently attached to + * a pagetable/domain or not + * @pt_lsb: The LSB of IOMMU_TTBR0 register which is the pagetable + * register + * @ctx_id: This iommu units context id. It can be either 0 or 1 + * @clk_enabled: If set indicates that iommu clocks of this iommu context + * are on, else the clocks are off + */ +struct kgsl_iommu_device { + struct device *dev; + bool attached; + unsigned int pt_lsb; + enum kgsl_iommu_context_id ctx_id; + bool clk_enabled; + struct kgsl_device *kgsldev; +}; + +/* + * struct kgsl_iommu_unit - Structure holding data about iommu units. An IOMMU + * units is basically a separte IOMMU h/w block with it's own IOMMU contexts + * @dev: Pointer to array of struct kgsl_iommu_device which has information + * about the IOMMU contexts under this IOMMU unit + * @dev_count: Number of IOMMU contexts that are valid in the previous feild + * @reg_map: Memory descriptor which holds the mapped address of this IOMMU + * units register range + */ +struct kgsl_iommu_unit { + struct kgsl_iommu_device dev[KGSL_IOMMU_MAX_DEVS_PER_UNIT]; + unsigned int dev_count; + struct kgsl_memdesc reg_map; +}; + +/* + * struct kgsl_iommu - Structure holding iommu data for kgsl driver + * @dev: Array of kgsl_iommu_device which contain information about + * iommu contexts owned by graphics cores + * @unit_count: Number of IOMMU units that are available for this + * instance of the IOMMU driver + * @iommu_last_cmd_ts: The timestamp of last command submitted that + * aceeses iommu registers + * @clk_event_queued: Indicates whether an event to disable clocks + * is already queued or not + * @device: Pointer to kgsl device + */ +struct kgsl_iommu { + struct kgsl_iommu_unit iommu_units[KGSL_IOMMU_MAX_UNITS]; + unsigned int unit_count; + unsigned int iommu_last_cmd_ts; + bool clk_event_queued; + struct kgsl_device *device; +}; + +/* + * struct kgsl_iommu_pt - Iommu pagetable structure private to kgsl driver + * @domain: Pointer to the iommu domain that contains the iommu pagetable + * @iommu: Pointer to iommu structure + */ +struct kgsl_iommu_pt { + struct iommu_domain *domain; + struct kgsl_iommu *iommu; +}; + +#endif diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c index e9f21438eb0..fa40558f33f 100644 --- a/drivers/gpu/msm/kgsl_pwrctrl.c +++ b/drivers/gpu/msm/kgsl_pwrctrl.c @@ -126,7 +126,7 @@ static int __gpuclk_store(int max, struct device *dev, 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) + else if (!max || (NULL == device->pwrscale.policy)) kgsl_pwrctrl_pwrlevel_change(device, i); done: @@ -856,6 +856,7 @@ void kgsl_pwrctrl_wake(struct kgsl_device *device) pm_qos_update_request(&device->pm_qos_req_dma, GPU_SWFI_LATENCY); case KGSL_STATE_ACTIVE: + kgsl_pwrctrl_request_state(device, KGSL_STATE_NONE); break; default: KGSL_PWR_WARN(device, "unhandled state %s\n", diff --git a/drivers/gpu/msm/kgsl_pwrscale_msm.c b/drivers/gpu/msm/kgsl_pwrscale_msm.c new file mode 100644 index 00000000000..61d4b2d1885 --- /dev/null +++ b/drivers/gpu/msm/kgsl_pwrscale_msm.c @@ -0,0 +1,200 @@ +/* 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 "kgsl.h" +#include "kgsl_pwrscale.h" +#include "kgsl_device.h" +#include "a2xx_reg.h" + +struct msm_priv { + struct kgsl_device *device; + int enabled; + int handle; + unsigned int cur_freq; + struct msm_dcvs_idle idle_source; + struct msm_dcvs_freq freq_sink; + struct msm_dcvs_core_info *core_info; +}; + +static int msm_idle_enable(struct msm_dcvs_idle *self, + enum msm_core_control_event event) +{ + struct msm_priv *priv = container_of(self, struct msm_priv, + idle_source); + + switch (event) { + case MSM_DCVS_ENABLE_IDLE_PULSE: + priv->enabled = true; + break; + case MSM_DCVS_DISABLE_IDLE_PULSE: + priv->enabled = false; + break; + case MSM_DCVS_ENABLE_HIGH_LATENCY_MODES: + case MSM_DCVS_DISABLE_HIGH_LATENCY_MODES: + break; + } + return 0; +} + +/* Set the requested frequency if it is within 5MHz (delta) of a + * supported frequency. + */ +static int msm_set_freq(struct msm_dcvs_freq *self, + unsigned int freq) +{ + int i, delta = 5000000; + struct msm_priv *priv = container_of(self, struct msm_priv, + freq_sink); + struct kgsl_device *device = priv->device; + struct kgsl_pwrctrl *pwr = &device->pwrctrl; + + /* msm_dcvs manager uses frequencies in kHz */ + freq *= 1000; + for (i = 0; i < pwr->num_pwrlevels; i++) + if (abs(pwr->pwrlevels[i].gpu_freq - freq) < delta) + break; + if (i == pwr->num_pwrlevels) + return 0; + + mutex_lock(&device->mutex); + kgsl_pwrctrl_pwrlevel_change(device, i); + priv->cur_freq = pwr->pwrlevels[pwr->active_pwrlevel].gpu_freq; + mutex_unlock(&device->mutex); + + /* return current frequency in kHz */ + return priv->cur_freq / 1000; +} + +static unsigned int msm_get_freq(struct msm_dcvs_freq *self) +{ + struct msm_priv *priv = container_of(self, struct msm_priv, + freq_sink); + /* return current frequency in kHz */ + return priv->cur_freq / 1000; +} + +static void msm_busy(struct kgsl_device *device, + struct kgsl_pwrscale *pwrscale) +{ + struct msm_priv *priv = pwrscale->priv; + if (priv->enabled) + msm_dcvs_idle(priv->handle, MSM_DCVS_IDLE_EXIT, 0); + return; +} + +static void msm_idle(struct kgsl_device *device, + struct kgsl_pwrscale *pwrscale) +{ + struct msm_priv *priv = pwrscale->priv; + unsigned int rb_rptr, rb_wptr; + kgsl_regread(device, REG_CP_RB_RPTR, &rb_rptr); + kgsl_regread(device, REG_CP_RB_WPTR, &rb_wptr); + + if (priv->enabled && (rb_rptr == rb_wptr)) + msm_dcvs_idle(priv->handle, MSM_DCVS_IDLE_ENTER, 0); + + return; +} + +static void msm_sleep(struct kgsl_device *device, + struct kgsl_pwrscale *pwrscale) +{ + /* do we need to reset any parameters here? */ +} + +static int msm_init(struct kgsl_device *device, + struct kgsl_pwrscale *pwrscale) +{ + struct msm_priv *priv; + struct msm_dcvs_freq_entry *tbl; + int i, ret, low_level; + struct kgsl_pwrctrl *pwr = &device->pwrctrl; + struct platform_device *pdev = + container_of(device->parentdev, struct platform_device, dev); + struct kgsl_device_platform_data *pdata = pdev->dev.platform_data; + + priv = pwrscale->priv = kzalloc(sizeof(struct msm_priv), + GFP_KERNEL); + if (pwrscale->priv == NULL) + return -ENOMEM; + + priv->core_info = pdata->core_info; + tbl = priv->core_info->freq_tbl; + /* Fill in frequency table from low to high, reversing order. */ + low_level = pwr->num_pwrlevels - KGSL_PWRLEVEL_LAST_OFFSET; + for (i = 0; i <= low_level; i++) + tbl[i].freq = + pwr->pwrlevels[low_level - i].gpu_freq / 1000; + ret = msm_dcvs_register_core(device->name, 0, priv->core_info); + if (ret) { + KGSL_PWR_ERR(device, "msm_dcvs_register_core failed"); + goto err; + } + + priv->device = device; + priv->idle_source.enable = msm_idle_enable; + priv->idle_source.core_name = device->name; + priv->handle = msm_dcvs_idle_source_register(&priv->idle_source); + if (priv->handle < 0) { + ret = priv->handle; + KGSL_PWR_ERR(device, "msm_dcvs_idle_source_register failed\n"); + goto err; + } + + priv->freq_sink.core_name = device->name; + priv->freq_sink.set_frequency = msm_set_freq; + priv->freq_sink.get_frequency = msm_get_freq; + ret = msm_dcvs_freq_sink_register(&priv->freq_sink); + if (ret >= 0) { + if (device->ftbl->isidle(device)) { + device->pwrscale.gpu_busy = 0; + msm_dcvs_idle(priv->handle, MSM_DCVS_IDLE_ENTER, 0); + } else { + device->pwrscale.gpu_busy = 1; + } + return 0; + } + + KGSL_PWR_ERR(device, "msm_dcvs_freq_sink_register failed\n"); + msm_dcvs_idle_source_unregister(&priv->idle_source); + +err: + kfree(pwrscale->priv); + pwrscale->priv = NULL; + + return ret; +} + +static void msm_close(struct kgsl_device *device, + struct kgsl_pwrscale *pwrscale) +{ + struct msm_priv *priv = pwrscale->priv; + + if (pwrscale->priv == NULL) + return; + msm_dcvs_idle_source_unregister(&priv->idle_source); + msm_dcvs_freq_sink_unregister(&priv->freq_sink); + kfree(pwrscale->priv); + pwrscale->priv = NULL; +} + +struct kgsl_pwrscale_policy kgsl_pwrscale_policy_msm = { + .name = "msm", + .init = msm_init, + .idle = msm_idle, + .busy = msm_busy, + .sleep = msm_sleep, + .close = msm_close, +}; diff --git a/drivers/gpu/msm/kgsl_sync.c b/drivers/gpu/msm/kgsl_sync.c new file mode 100644 index 00000000000..11dd8716dcc --- /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 00000000000..06b3ad0d891 --- /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/include/linux/fmem.h b/include/linux/fmem.h new file mode 100644 index 00000000000..e4fa82cb5c9 --- /dev/null +++ b/include/linux/fmem.h @@ -0,0 +1,62 @@ +/* + * + * 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 + * 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 diff --git a/include/linux/ion.h b/include/linux/ion.h index d9443ff4893..22d7cc89b9a 100644 --- a/include/linux/ion.h +++ b/include/linux/ion.h @@ -64,6 +64,13 @@ enum ion_heap_type { enum ion_heap_ids { 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 */ diff --git a/include/linux/msm_adsp.h b/include/linux/msm_adsp.h new file mode 100644 index 00000000000..ca23ad8dd74 --- /dev/null +++ b/include/linux/msm_adsp.h @@ -0,0 +1,78 @@ +/* include/linux/msm_adsp.h + * + * 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 + +#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_SET_CLKRATE \ + _IOR(ADSP_IOCTL_MAGIC, 6, unsigned) + +#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_kgsl.h b/include/linux/msm_kgsl.h index baef1cc4409..0f22d4d7cd8 100644 --- a/include/linux/msm_kgsl.h +++ b/include/linux/msm_kgsl.h @@ -436,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 { @@ -447,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 */ @@ -458,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, diff --git a/include/linux/pm_qos.h b/include/linux/pm_qos.h new file mode 100644 index 00000000000..233149cb19f --- /dev/null +++ b/include/linux/pm_qos.h @@ -0,0 +1,153 @@ +#ifndef _LINUX_PM_QOS_H +#define _LINUX_PM_QOS_H +/* interface for the pm_qos_power infrastructure of the linux kernel. + * + * Mark Gross + */ +#include +#include +#include +#include +#include + +enum { + PM_QOS_RESERVED = 0, + PM_QOS_CPU_DMA_LATENCY, + PM_QOS_NETWORK_LATENCY, + PM_QOS_NETWORK_THROUGHPUT, + + /* insert new class ID */ + PM_QOS_NUM_CLASSES, +}; + +#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 +#define PM_QOS_DEV_LAT_DEFAULT_VALUE 0 + +struct pm_qos_request { + struct plist_node node; + int pm_qos_class; + struct delayed_work work; /* for pm_qos_update_request_timeout */ +}; + +struct dev_pm_qos_request { + struct plist_node node; + struct device *dev; +}; + +enum pm_qos_type { + PM_QOS_UNITIALIZED, + PM_QOS_MAX, /* return the largest value */ + 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_constraints { + struct plist_head list; + s32 target_value; /* Do not change to 64 bit */ + s32 default_value; + enum pm_qos_type type; + struct blocking_notifier_head *notifiers; +}; + +/* Action requested to pm_qos_update_target */ +enum pm_qos_req_action { + PM_QOS_ADD_REQ, /* Add a new request */ + PM_QOS_UPDATE_REQ, /* Update an existing request */ + PM_QOS_REMOVE_REQ /* Remove an existing request */ +}; + +static inline int dev_pm_qos_request_active(struct dev_pm_qos_request *req) +{ + return req->dev != 0; +} + +int pm_qos_update_target(struct pm_qos_constraints *c, struct plist_node *node, + enum pm_qos_req_action action, int value); +void pm_qos_add_request(struct pm_qos_request *req, int pm_qos_class, + s32 value); +void pm_qos_update_request(struct pm_qos_request *req, + s32 new_value); +void pm_qos_update_request_timeout(struct pm_qos_request *req, + s32 new_value, unsigned long timeout_us); +void pm_qos_remove_request(struct pm_qos_request *req); + +int pm_qos_request(int pm_qos_class); +int pm_qos_add_notifier(int pm_qos_class, struct notifier_block *notifier); +int pm_qos_remove_notifier(int pm_qos_class, struct notifier_block *notifier); +int pm_qos_request_active(struct pm_qos_request *req); +s32 pm_qos_read_value(struct pm_qos_constraints *c); + +#ifdef CONFIG_PM +s32 __dev_pm_qos_read_value(struct device *dev); +s32 dev_pm_qos_read_value(struct device *dev); +int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req, + s32 value); +int dev_pm_qos_update_request(struct dev_pm_qos_request *req, s32 new_value); +int dev_pm_qos_remove_request(struct dev_pm_qos_request *req); +int dev_pm_qos_add_notifier(struct device *dev, + struct notifier_block *notifier); +int dev_pm_qos_remove_notifier(struct device *dev, + struct notifier_block *notifier); +int dev_pm_qos_add_global_notifier(struct notifier_block *notifier); +int dev_pm_qos_remove_global_notifier(struct notifier_block *notifier); +void dev_pm_qos_constraints_init(struct device *dev); +void dev_pm_qos_constraints_destroy(struct device *dev); +int dev_pm_qos_add_ancestor_request(struct device *dev, + struct dev_pm_qos_request *req, s32 value); +#else +static inline s32 __dev_pm_qos_read_value(struct device *dev) + { return 0; } +static inline s32 dev_pm_qos_read_value(struct device *dev) + { return 0; } +static inline int dev_pm_qos_add_request(struct device *dev, + struct dev_pm_qos_request *req, + s32 value) + { return 0; } +static inline int dev_pm_qos_update_request(struct dev_pm_qos_request *req, + s32 new_value) + { return 0; } +static inline int dev_pm_qos_remove_request(struct dev_pm_qos_request *req) + { return 0; } +static inline int dev_pm_qos_add_notifier(struct device *dev, + struct notifier_block *notifier) + { return 0; } +static inline int dev_pm_qos_remove_notifier(struct device *dev, + struct notifier_block *notifier) + { return 0; } +static inline int dev_pm_qos_add_global_notifier( + struct notifier_block *notifier) + { return 0; } +static inline int dev_pm_qos_remove_global_notifier( + struct notifier_block *notifier) + { return 0; } +static inline void dev_pm_qos_constraints_init(struct device *dev) +{ + dev->power.power_state = PMSG_ON; +} +static inline void dev_pm_qos_constraints_destroy(struct device *dev) +{ + dev->power.power_state = PMSG_INVALID; +} +static inline int dev_pm_qos_add_ancestor_request(struct device *dev, + struct dev_pm_qos_request *req, s32 value) + { return 0; } +#endif + +#ifdef CONFIG_PM_RUNTIME +int dev_pm_qos_expose_latency_limit(struct device *dev, s32 value); +void dev_pm_qos_hide_latency_limit(struct device *dev); +#else +static inline int dev_pm_qos_expose_latency_limit(struct device *dev, s32 value) + { return 0; } +static inline void dev_pm_qos_hide_latency_limit(struct device *dev) {} +#endif + +#endif diff --git a/include/linux/sync.h b/include/linux/sync.h new file mode 100644 index 00000000000..f057a4d4b3d --- /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 bde8b356d053b0f0c62e6db5595d20d52c8ad021 Mon Sep 17 00:00:00 2001 From: Matt Filetto Date: Sun, 14 Apr 2013 12:32:07 -0700 Subject: [PATCH 004/111] GPU: Correctly update Adreno/KGSL drivers to jb-chocolote. Change-Id: If9c8e2ff366acc3e6c08e6342f67d32c37a43984 Conflicts: drivers/gpu/msm/a2xx_reg.h drivers/gpu/msm/adreno.c drivers/gpu/msm/adreno_a2xx.c drivers/gpu/msm/kgsl.c drivers/gpu/msm/kgsl_device.h --- drivers/gpu/msm/Makefile | 1 - drivers/gpu/msm/a2xx_reg.h | 3 +- drivers/gpu/msm/a3xx_reg.h | 523 ----- drivers/gpu/msm/adreno.c | 260 ++- drivers/gpu/msm/adreno.h | 17 +- drivers/gpu/msm/adreno_a2xx.c | 15 +- drivers/gpu/msm/adreno_a3xx.c | 2774 ------------------------ drivers/gpu/msm/adreno_a3xx_snapshot.c | 332 --- drivers/gpu/msm/adreno_a3xx_trace.c | 20 - drivers/gpu/msm/adreno_a3xx_trace.h | 89 - drivers/gpu/msm/adreno_debugfs.c | 5 + drivers/gpu/msm/adreno_drawctxt.c | 3 +- drivers/gpu/msm/adreno_postmortem.c | 2 +- drivers/gpu/msm/adreno_ringbuffer.c | 47 +- drivers/gpu/msm/kgsl.c | 59 +- drivers/gpu/msm/kgsl.h | 6 - drivers/gpu/msm/kgsl_device.h | 19 +- drivers/gpu/msm/kgsl_gpummu.c | 5 +- drivers/gpu/msm/kgsl_iommu.c | 6 +- drivers/gpu/msm/kgsl_iommu.h | 121 -- drivers/gpu/msm/kgsl_mmu.c | 2 +- drivers/gpu/msm/kgsl_pwrctrl.c | 45 +- drivers/gpu/msm/kgsl_pwrscale_msm.c | 200 -- drivers/gpu/msm/kgsl_sync.c | 211 -- drivers/gpu/msm/kgsl_sync.h | 75 - drivers/gpu/msm/z180.c | 47 +- drivers/gpu/msm/z180.h | 3 + include/linux/msm_kgsl.h | 16 +- 28 files changed, 322 insertions(+), 4584 deletions(-) delete mode 100644 drivers/gpu/msm/a3xx_reg.h delete mode 100644 drivers/gpu/msm/adreno_a3xx.c delete mode 100644 drivers/gpu/msm/adreno_a3xx_snapshot.c delete mode 100644 drivers/gpu/msm/adreno_a3xx_trace.c delete mode 100644 drivers/gpu/msm/adreno_a3xx_trace.h delete mode 100644 drivers/gpu/msm/kgsl_iommu.h delete mode 100644 drivers/gpu/msm/kgsl_pwrscale_msm.c delete mode 100644 drivers/gpu/msm/kgsl_sync.c delete mode 100644 drivers/gpu/msm/kgsl_sync.h diff --git a/drivers/gpu/msm/Makefile b/drivers/gpu/msm/Makefile index 65774c34ae1..7b8f3e633d1 100644 --- a/drivers/gpu/msm/Makefile +++ b/drivers/gpu/msm/Makefile @@ -16,7 +16,6 @@ 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 50b2745bed0..28b8dac57e1 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-2013, 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 @@ -355,6 +355,7 @@ union reg_cp_rb_cntl { #define REG_RB_MODECONTROL 0x2208 #define REG_RB_SURFACE_INFO 0x2000 #define REG_RB_SAMPLE_POS 0x220a +#define REG_RB_BC_CONTROL 0x0F01 #define REG_SCRATCH_ADDR 0x01DD #define REG_SCRATCH_REG0 0x0578 diff --git a/drivers/gpu/msm/a3xx_reg.h b/drivers/gpu/msm/a3xx_reg.h deleted file mode 100644 index 8ec94318067..00000000000 --- a/drivers/gpu/msm/a3xx_reg.h +++ /dev/null @@ -1,523 +0,0 @@ -/* 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 _A300_REG_H -#define _A300_REG_H - -/* Interrupt bit positions within RBBM_INT_0 */ - -#define A3XX_INT_RBBM_GPU_IDLE 0 -#define A3XX_INT_RBBM_AHB_ERROR 1 -#define A3XX_INT_RBBM_REG_TIMEOUT 2 -#define A3XX_INT_RBBM_ME_MS_TIMEOUT 3 -#define A3XX_INT_RBBM_PFP_MS_TIMEOUT 4 -#define A3XX_INT_RBBM_ATB_BUS_OVERFLOW 5 -#define A3XX_INT_VFD_ERROR 6 -#define A3XX_INT_CP_SW_INT 7 -#define A3XX_INT_CP_T0_PACKET_IN_IB 8 -#define A3XX_INT_CP_OPCODE_ERROR 9 -#define A3XX_INT_CP_RESERVED_BIT_ERROR 10 -#define A3XX_INT_CP_HW_FAULT 11 -#define A3XX_INT_CP_DMA 12 -#define A3XX_INT_CP_IB2_INT 13 -#define A3XX_INT_CP_IB1_INT 14 -#define A3XX_INT_CP_RB_INT 15 -#define A3XX_INT_CP_REG_PROTECT_FAULT 16 -#define A3XX_INT_CP_RB_DONE_TS 17 -#define A3XX_INT_CP_VS_DONE_TS 18 -#define A3XX_INT_CP_PS_DONE_TS 19 -#define A3XX_INT_CACHE_FLUSH_TS 20 -#define A3XX_INT_CP_AHB_ERROR_HALT 21 -#define A3XX_INT_MISC_HANG_DETECT 24 -#define A3XX_INT_UCHE_OOB_ACCESS 25 - -/* Register definitions */ - -#define A3XX_RBBM_HW_VERSION 0x000 -#define A3XX_RBBM_HW_RELEASE 0x001 -#define A3XX_RBBM_HW_CONFIGURATION 0x002 -#define A3XX_RBBM_CLOCK_CTL 0x010 -#define A3XX_RBBM_SP_HYST_CNT 0x012 -#define A3XX_RBBM_SW_RESET_CMD 0x018 -#define A3XX_RBBM_AHB_CTL0 0x020 -#define A3XX_RBBM_AHB_CTL1 0x021 -#define A3XX_RBBM_AHB_CMD 0x022 -#define A3XX_RBBM_AHB_ERROR_STATUS 0x027 -#define A3XX_RBBM_GPR0_CTL 0x02E -/* This the same register as on A2XX, just in a different place */ -#define A3XX_RBBM_STATUS 0x030 -#define A3XX_RBBM_WAIT_IDLE_CLOCKS_CTL 0x33 -#define A3XX_RBBM_INTERFACE_HANG_INT_CTL 0x50 -#define A3XX_RBBM_INTERFACE_HANG_MASK_CTL0 0x51 -#define A3XX_RBBM_INTERFACE_HANG_MASK_CTL1 0x54 -#define A3XX_RBBM_INTERFACE_HANG_MASK_CTL2 0x57 -#define A3XX_RBBM_INTERFACE_HANG_MASK_CTL3 0x5A -#define A3XX_RBBM_INT_CLEAR_CMD 0x061 -#define A3XX_RBBM_INT_0_MASK 0x063 -#define A3XX_RBBM_INT_0_STATUS 0x064 -#define A3XX_RBBM_GPU_BUSY_MASKED 0x88 -#define A3XX_RBBM_RBBM_CTL 0x100 -#define A3XX_RBBM_RBBM_CTL 0x100 -#define A3XX_RBBM_PERFCTR_PWR_1_LO 0x0EC -#define A3XX_RBBM_PERFCTR_PWR_1_HI 0x0ED -#define A3XX_RBBM_DEBUG_BUS_CTL 0x111 -#define A3XX_RBBM_DEBUG_BUS_DATA_STATUS 0x112 -/* Following two are same as on A2XX, just in a different place */ -#define A3XX_CP_PFP_UCODE_ADDR 0x1C9 -#define A3XX_CP_PFP_UCODE_DATA 0x1CA -#define A3XX_CP_ROQ_ADDR 0x1CC -#define A3XX_CP_ROQ_DATA 0x1CD -#define A3XX_CP_MEQ_ADDR 0x1DA -#define A3XX_CP_MEQ_DATA 0x1DB -#define A3XX_CP_HW_FAULT 0x45C -#define A3XX_CP_AHB_FAULT 0x54D -#define A3XX_CP_PROTECT_CTRL 0x45E -#define A3XX_CP_PROTECT_STATUS 0x45F -#define A3XX_CP_PROTECT_REG_0 0x460 -#define A3XX_CP_PROTECT_REG_1 0x461 -#define A3XX_CP_PROTECT_REG_2 0x462 -#define A3XX_CP_PROTECT_REG_3 0x463 -#define A3XX_CP_PROTECT_REG_4 0x464 -#define A3XX_CP_PROTECT_REG_5 0x465 -#define A3XX_CP_PROTECT_REG_6 0x466 -#define A3XX_CP_PROTECT_REG_7 0x467 -#define A3XX_CP_PROTECT_REG_8 0x468 -#define A3XX_CP_PROTECT_REG_9 0x469 -#define A3XX_CP_PROTECT_REG_A 0x46A -#define A3XX_CP_PROTECT_REG_B 0x46B -#define A3XX_CP_PROTECT_REG_C 0x46C -#define A3XX_CP_PROTECT_REG_D 0x46D -#define A3XX_CP_PROTECT_REG_E 0x46E -#define A3XX_CP_PROTECT_REG_F 0x46F -#define A3XX_CP_SCRATCH_REG2 0x57A -#define A3XX_CP_SCRATCH_REG3 0x57B -#define A3XX_VSC_BIN_SIZE 0xC01 -#define A3XX_VSC_SIZE_ADDRESS 0xC02 -#define A3XX_VSC_PIPE_CONFIG_0 0xC06 -#define A3XX_VSC_PIPE_DATA_ADDRESS_0 0xC07 -#define A3XX_VSC_PIPE_DATA_LENGTH_0 0xC08 -#define A3XX_VSC_PIPE_CONFIG_1 0xC09 -#define A3XX_VSC_PIPE_DATA_ADDRESS_1 0xC0A -#define A3XX_VSC_PIPE_DATA_LENGTH_1 0xC0B -#define A3XX_VSC_PIPE_CONFIG_2 0xC0C -#define A3XX_VSC_PIPE_DATA_ADDRESS_2 0xC0D -#define A3XX_VSC_PIPE_DATA_LENGTH_2 0xC0E -#define A3XX_VSC_PIPE_CONFIG_3 0xC0F -#define A3XX_VSC_PIPE_DATA_ADDRESS_3 0xC10 -#define A3XX_VSC_PIPE_DATA_LENGTH_3 0xC11 -#define A3XX_VSC_PIPE_CONFIG_4 0xC12 -#define A3XX_VSC_PIPE_DATA_ADDRESS_4 0xC13 -#define A3XX_VSC_PIPE_DATA_LENGTH_4 0xC14 -#define A3XX_VSC_PIPE_CONFIG_5 0xC15 -#define A3XX_VSC_PIPE_DATA_ADDRESS_5 0xC16 -#define A3XX_VSC_PIPE_DATA_LENGTH_5 0xC17 -#define A3XX_VSC_PIPE_CONFIG_6 0xC18 -#define A3XX_VSC_PIPE_DATA_ADDRESS_6 0xC19 -#define A3XX_VSC_PIPE_DATA_LENGTH_6 0xC1A -#define A3XX_VSC_PIPE_CONFIG_7 0xC1B -#define A3XX_VSC_PIPE_DATA_ADDRESS_7 0xC1C -#define A3XX_VSC_PIPE_DATA_LENGTH_7 0xC1D -#define A3XX_GRAS_CL_USER_PLANE_X0 0xCA0 -#define A3XX_GRAS_CL_USER_PLANE_Y0 0xCA1 -#define A3XX_GRAS_CL_USER_PLANE_Z0 0xCA2 -#define A3XX_GRAS_CL_USER_PLANE_W0 0xCA3 -#define A3XX_GRAS_CL_USER_PLANE_X1 0xCA4 -#define A3XX_GRAS_CL_USER_PLANE_Y1 0xCA5 -#define A3XX_GRAS_CL_USER_PLANE_Z1 0xCA6 -#define A3XX_GRAS_CL_USER_PLANE_W1 0xCA7 -#define A3XX_GRAS_CL_USER_PLANE_X2 0xCA8 -#define A3XX_GRAS_CL_USER_PLANE_Y2 0xCA9 -#define A3XX_GRAS_CL_USER_PLANE_Z2 0xCAA -#define A3XX_GRAS_CL_USER_PLANE_W2 0xCAB -#define A3XX_GRAS_CL_USER_PLANE_X3 0xCAC -#define A3XX_GRAS_CL_USER_PLANE_Y3 0xCAD -#define A3XX_GRAS_CL_USER_PLANE_Z3 0xCAE -#define A3XX_GRAS_CL_USER_PLANE_W3 0xCAF -#define A3XX_GRAS_CL_USER_PLANE_X4 0xCB0 -#define A3XX_GRAS_CL_USER_PLANE_Y4 0xCB1 -#define A3XX_GRAS_CL_USER_PLANE_Z4 0xCB2 -#define A3XX_GRAS_CL_USER_PLANE_W4 0xCB3 -#define A3XX_GRAS_CL_USER_PLANE_X5 0xCB4 -#define A3XX_GRAS_CL_USER_PLANE_Y5 0xCB5 -#define A3XX_GRAS_CL_USER_PLANE_Z5 0xCB6 -#define A3XX_GRAS_CL_USER_PLANE_W5 0xCB7 -#define A3XX_VFD_PERFCOUNTER0_SELECT 0xE44 -#define A3XX_VPC_VPC_DEBUG_RAM_SEL 0xE61 -#define A3XX_VPC_VPC_DEBUG_RAM_READ 0xE62 -#define A3XX_UCHE_CACHE_INVALIDATE0_REG 0xEA0 -#define A3XX_GRAS_CL_CLIP_CNTL 0x2040 -#define A3XX_GRAS_CL_GB_CLIP_ADJ 0x2044 -#define A3XX_GRAS_CL_VPORT_XOFFSET 0x2048 -#define A3XX_GRAS_CL_VPORT_ZOFFSET 0x204C -#define A3XX_GRAS_CL_VPORT_ZSCALE 0x204D -#define A3XX_GRAS_SU_POINT_MINMAX 0x2068 -#define A3XX_GRAS_SU_POINT_SIZE 0x2069 -#define A3XX_GRAS_SU_POLY_OFFSET_SCALE 0x206C -#define A3XX_GRAS_SU_POLY_OFFSET_OFFSET 0x206D -#define A3XX_GRAS_SU_MODE_CONTROL 0x2070 -#define A3XX_GRAS_SC_CONTROL 0x2072 -#define A3XX_GRAS_SC_SCREEN_SCISSOR_TL 0x2074 -#define A3XX_GRAS_SC_SCREEN_SCISSOR_BR 0x2075 -#define A3XX_GRAS_SC_WINDOW_SCISSOR_TL 0x2079 -#define A3XX_GRAS_SC_WINDOW_SCISSOR_BR 0x207A -#define A3XX_RB_MODE_CONTROL 0x20C0 -#define A3XX_RB_RENDER_CONTROL 0x20C1 -#define A3XX_RB_MSAA_CONTROL 0x20C2 -#define A3XX_RB_MRT_CONTROL0 0x20C4 -#define A3XX_RB_MRT_BUF_INFO0 0x20C5 -#define A3XX_RB_MRT_BLEND_CONTROL0 0x20C7 -#define A3XX_RB_MRT_BLEND_CONTROL1 0x20CB -#define A3XX_RB_MRT_BLEND_CONTROL2 0x20CF -#define A3XX_RB_MRT_BLEND_CONTROL3 0x20D3 -#define A3XX_RB_BLEND_RED 0x20E4 -#define A3XX_RB_COPY_CONTROL 0x20EC -#define A3XX_RB_COPY_DEST_INFO 0x20EF -#define A3XX_RB_DEPTH_CONTROL 0x2100 -#define A3XX_RB_STENCIL_CONTROL 0x2104 -#define A3XX_PC_VSTREAM_CONTROL 0x21E4 -#define A3XX_PC_VERTEX_REUSE_BLOCK_CNTL 0x21EA -#define A3XX_PC_PRIM_VTX_CNTL 0x21EC -#define A3XX_PC_RESTART_INDEX 0x21ED -#define A3XX_HLSQ_CONTROL_0_REG 0x2200 -#define A3XX_HLSQ_VS_CONTROL_REG 0x2204 -#define A3XX_HLSQ_CONST_FSPRESV_RANGE_REG 0x2207 -#define A3XX_HLSQ_CL_NDRANGE_0_REG 0x220A -#define A3XX_HLSQ_CL_NDRANGE_2_REG 0x220C -#define A3XX_HLSQ_CL_CONTROL_0_REG 0x2211 -#define A3XX_HLSQ_CL_CONTROL_1_REG 0x2212 -#define A3XX_HLSQ_CL_KERNEL_CONST_REG 0x2214 -#define A3XX_HLSQ_CL_KERNEL_GROUP_X_REG 0x2215 -#define A3XX_HLSQ_CL_KERNEL_GROUP_Z_REG 0x2217 -#define A3XX_HLSQ_CL_WG_OFFSET_REG 0x221A -#define A3XX_VFD_CONTROL_0 0x2240 -#define A3XX_VFD_INDEX_MIN 0x2242 -#define A3XX_VFD_INDEX_MAX 0x2243 -#define A3XX_VFD_FETCH_INSTR_0_0 0x2246 -#define A3XX_VFD_FETCH_INSTR_0_4 0x224E -#define A3XX_VFD_FETCH_INSTR_1_F 0x2265 -#define A3XX_VFD_DECODE_INSTR_0 0x2266 -#define A3XX_VFD_VS_THREADING_THRESHOLD 0x227E -#define A3XX_VPC_ATTR 0x2280 -#define A3XX_VPC_VARY_CYLWRAP_ENABLE_1 0x228B -#define A3XX_SP_SP_CTRL_REG 0x22C0 -#define A3XX_SP_VS_CTRL_REG0 0x22C4 -#define A3XX_SP_VS_CTRL_REG1 0x22C5 -#define A3XX_SP_VS_PARAM_REG 0x22C6 -#define A3XX_SP_VS_OUT_REG_7 0x22CE -#define A3XX_SP_VS_VPC_DST_REG_0 0x22D0 -#define A3XX_SP_VS_OBJ_OFFSET_REG 0x22D4 -#define A3XX_SP_VS_PVT_MEM_ADDR_REG 0x22D7 -#define A3XX_SP_VS_PVT_MEM_SIZE_REG 0x22D8 -#define A3XX_SP_VS_LENGTH_REG 0x22DF -#define A3XX_SP_FS_CTRL_REG0 0x22E0 -#define A3XX_SP_FS_CTRL_REG1 0x22E1 -#define A3XX_SP_FS_OBJ_OFFSET_REG 0x22E2 -#define A3XX_SP_FS_PVT_MEM_ADDR_REG 0x22E5 -#define A3XX_SP_FS_PVT_MEM_SIZE_REG 0x22E6 -#define A3XX_SP_FS_FLAT_SHAD_MODE_REG_0 0x22E8 -#define A3XX_SP_FS_FLAT_SHAD_MODE_REG_1 0x22E9 -#define A3XX_SP_FS_OUTPUT_REG 0x22EC -#define A3XX_SP_FS_MRT_REG_0 0x22F0 -#define A3XX_SP_FS_IMAGE_OUTPUT_REG_0 0x22F4 -#define A3XX_SP_FS_IMAGE_OUTPUT_REG_3 0x22F7 -#define A3XX_SP_FS_LENGTH_REG 0x22FF -#define A3XX_TPL1_TP_VS_TEX_OFFSET 0x2340 -#define A3XX_TPL1_TP_FS_TEX_OFFSET 0x2342 -#define A3XX_TPL1_TP_FS_BORDER_COLOR_BASE_ADDR 0x2343 -#define A3XX_VBIF_FIXED_SORT_EN 0x300C -#define A3XX_VBIF_FIXED_SORT_SEL0 0x300D -#define A3XX_VBIF_FIXED_SORT_SEL1 0x300E -#define A3XX_VBIF_ABIT_SORT 0x301C -#define A3XX_VBIF_ABIT_SORT_CONF 0x301D -#define A3XX_VBIF_GATE_OFF_WRREQ_EN 0x302A -#define A3XX_VBIF_IN_RD_LIM_CONF0 0x302C -#define A3XX_VBIF_IN_RD_LIM_CONF1 0x302D -#define A3XX_VBIF_IN_WR_LIM_CONF0 0x3030 -#define A3XX_VBIF_IN_WR_LIM_CONF1 0x3031 -#define A3XX_VBIF_OUT_RD_LIM_CONF0 0x3034 -#define A3XX_VBIF_OUT_WR_LIM_CONF0 0x3035 -#define A3XX_VBIF_DDR_OUT_MAX_BURST 0x3036 -#define A3XX_VBIF_ARB_CTL 0x303C -#define A3XX_VBIF_OUT_AXI_AOOO_EN 0x305E -#define A3XX_VBIF_OUT_AXI_AOOO 0x305F - -/* Bit flags for RBBM_CTL */ -#define RBBM_RBBM_CTL_RESET_PWR_CTR1 (1 << 1) -#define RBBM_RBBM_CTL_ENABLE_PWR_CTR1 (1 << 17) - -/* Various flags used by the context switch code */ - -#define SP_MULTI 0 -#define SP_BUFFER_MODE 1 -#define SP_TWO_VTX_QUADS 0 -#define SP_PIXEL_BASED 0 -#define SP_R8G8B8A8_UNORM 8 -#define SP_FOUR_PIX_QUADS 1 - -#define HLSQ_DIRECT 0 -#define HLSQ_BLOCK_ID_SP_VS 4 -#define HLSQ_SP_VS_INSTR 0 -#define HLSQ_SP_FS_INSTR 0 -#define HLSQ_BLOCK_ID_SP_FS 6 -#define HLSQ_TWO_PIX_QUADS 0 -#define HLSQ_TWO_VTX_QUADS 0 -#define HLSQ_BLOCK_ID_TP_TEX 2 -#define HLSQ_TP_TEX_SAMPLERS 0 -#define HLSQ_TP_TEX_MEMOBJ 1 -#define HLSQ_BLOCK_ID_TP_MIPMAP 3 -#define HLSQ_TP_MIPMAP_BASE 1 -#define HLSQ_FOUR_PIX_QUADS 1 - -#define RB_FACTOR_ONE 1 -#define RB_BLEND_OP_ADD 0 -#define RB_FACTOR_ZERO 0 -#define RB_DITHER_DISABLE 0 -#define RB_DITHER_ALWAYS 1 -#define RB_FRAG_NEVER 0 -#define RB_ENDIAN_NONE 0 -#define RB_R8G8B8A8_UNORM 8 -#define RB_RESOLVE_PASS 2 -#define RB_CLEAR_MODE_RESOLVE 1 -#define RB_TILINGMODE_LINEAR 0 -#define RB_REF_NEVER 0 -#define RB_FRAG_LESS 1 -#define RB_REF_ALWAYS 7 -#define RB_STENCIL_KEEP 0 -#define RB_RENDERING_PASS 0 -#define RB_TILINGMODE_32X32 2 - -#define PC_DRAW_TRIANGLES 2 -#define PC_DI_PT_RECTLIST 8 -#define PC_DI_SRC_SEL_AUTO_INDEX 2 -#define PC_DI_INDEX_SIZE_16_BIT 0 -#define PC_DI_IGNORE_VISIBILITY 0 -#define PC_DI_PT_TRILIST 4 -#define PC_DI_SRC_SEL_IMMEDIATE 1 -#define PC_DI_INDEX_SIZE_32_BIT 1 - -#define UCHE_ENTIRE_CACHE 1 -#define UCHE_OP_INVALIDATE 1 - -/* - * The following are bit field shifts within some of the registers defined - * above. These are used in the context switch code in conjunction with the - * _SET macro - */ - -#define GRAS_CL_CLIP_CNTL_CLIP_DISABLE 16 -#define GRAS_CL_CLIP_CNTL_IJ_PERSP_CENTER 12 -#define GRAS_CL_CLIP_CNTL_PERSP_DIVISION_DISABLE 21 -#define GRAS_CL_CLIP_CNTL_VP_CLIP_CODE_IGNORE 19 -#define GRAS_CL_CLIP_CNTL_VP_XFORM_DISABLE 20 -#define GRAS_CL_CLIP_CNTL_ZFAR_CLIP_DISABLE 17 -#define GRAS_CL_VPORT_XSCALE_VPORT_XSCALE 0 -#define GRAS_CL_VPORT_YSCALE_VPORT_YSCALE 0 -#define GRAS_CL_VPORT_ZSCALE_VPORT_ZSCALE 0 -#define GRAS_SC_CONTROL_RASTER_MODE 12 -#define GRAS_SC_CONTROL_RENDER_MODE 4 -#define GRAS_SC_SCREEN_SCISSOR_BR_BR_X 0 -#define GRAS_SC_SCREEN_SCISSOR_BR_BR_Y 16 -#define GRAS_SC_WINDOW_SCISSOR_BR_BR_X 0 -#define GRAS_SC_WINDOW_SCISSOR_BR_BR_Y 16 -#define GRAS_SU_CTRLMODE_LINEHALFWIDTH 03 -#define HLSQ_CONSTFSPRESERVEDRANGEREG_ENDENTRY 16 -#define HLSQ_CONSTFSPRESERVEDRANGEREG_STARTENTRY 0 -#define HLSQ_CTRL0REG_CHUNKDISABLE 26 -#define HLSQ_CTRL0REG_CONSTSWITCHMODE 27 -#define HLSQ_CTRL0REG_FSSUPERTHREADENABLE 6 -#define HLSQ_CTRL0REG_FSTHREADSIZE 4 -#define HLSQ_CTRL0REG_LAZYUPDATEDISABLE 28 -#define HLSQ_CTRL0REG_RESERVED2 10 -#define HLSQ_CTRL0REG_SPCONSTFULLUPDATE 29 -#define HLSQ_CTRL0REG_SPSHADERRESTART 9 -#define HLSQ_CTRL0REG_TPFULLUPDATE 30 -#define HLSQ_CTRL1REG_RESERVED1 9 -#define HLSQ_CTRL1REG_VSSUPERTHREADENABLE 8 -#define HLSQ_CTRL1REG_VSTHREADSIZE 6 -#define HLSQ_CTRL2REG_PRIMALLOCTHRESHOLD 26 -#define HLSQ_FSCTRLREG_FSCONSTLENGTH 0 -#define HLSQ_FSCTRLREG_FSCONSTSTARTOFFSET 12 -#define HLSQ_FSCTRLREG_FSINSTRLENGTH 24 -#define HLSQ_VSCTRLREG_VSINSTRLENGTH 24 -#define PC_PRIM_VTX_CONTROL_POLYMODE_BACK_PTYPE 8 -#define PC_PRIM_VTX_CONTROL_POLYMODE_FRONT_PTYPE 5 -#define PC_PRIM_VTX_CONTROL_PROVOKING_VTX_LAST 25 -#define PC_PRIM_VTX_CONTROL_STRIDE_IN_VPC 0 -#define PC_DRAW_INITIATOR_PRIM_TYPE 0 -#define PC_DRAW_INITIATOR_SOURCE_SELECT 6 -#define PC_DRAW_INITIATOR_VISIBILITY_CULLING_MODE 9 -#define PC_DRAW_INITIATOR_INDEX_SIZE 0x0B -#define PC_DRAW_INITIATOR_SMALL_INDEX 0x0D -#define PC_DRAW_INITIATOR_PRE_DRAW_INITIATOR_ENABLE 0x0E -#define RB_COPYCONTROL_COPY_GMEM_BASE 14 -#define RB_COPYCONTROL_RESOLVE_CLEAR_MODE 4 -#define RB_COPYDESTBASE_COPY_DEST_BASE 4 -#define RB_COPYDESTINFO_COPY_COMPONENT_ENABLE 14 -#define RB_COPYDESTINFO_COPY_DEST_ENDIAN 18 -#define RB_COPYDESTINFO_COPY_DEST_FORMAT 2 -#define RB_COPYDESTINFO_COPY_DEST_TILE 0 -#define RB_COPYDESTPITCH_COPY_DEST_PITCH 0 -#define RB_DEPTHCONTROL_Z_TEST_FUNC 4 -#define RB_MODECONTROL_RENDER_MODE 8 -#define RB_MODECONTROL_MARB_CACHE_SPLIT_MODE 15 -#define RB_MODECONTROL_PACKER_TIMER_ENABLE 16 -#define RB_MRTBLENDCONTROL_ALPHA_BLEND_OPCODE 21 -#define RB_MRTBLENDCONTROL_ALPHA_DEST_FACTOR 24 -#define RB_MRTBLENDCONTROL_ALPHA_SRC_FACTOR 16 -#define RB_MRTBLENDCONTROL_CLAMP_ENABLE 29 -#define RB_MRTBLENDCONTROL_RGB_BLEND_OPCODE 5 -#define RB_MRTBLENDCONTROL_RGB_DEST_FACTOR 8 -#define RB_MRTBLENDCONTROL_RGB_SRC_FACTOR 0 -#define RB_MRTBUFBASE_COLOR_BUF_BASE 4 -#define RB_MRTBUFINFO_COLOR_BUF_PITCH 17 -#define RB_MRTBUFINFO_COLOR_FORMAT 0 -#define RB_MRTBUFINFO_COLOR_TILE_MODE 6 -#define RB_MRTCONTROL_COMPONENT_ENABLE 24 -#define RB_MRTCONTROL_DITHER_MODE 12 -#define RB_MRTCONTROL_READ_DEST_ENABLE 3 -#define RB_MRTCONTROL_ROP_CODE 8 -#define RB_MSAACONTROL_MSAA_DISABLE 10 -#define RB_MSAACONTROL_SAMPLE_MASK 16 -#define RB_RENDERCONTROL_ALPHA_TEST_FUNC 24 -#define RB_RENDERCONTROL_BIN_WIDTH 4 -#define RB_RENDERCONTROL_DISABLE_COLOR_PIPE 12 -#define RB_STENCILCONTROL_STENCIL_FAIL 11 -#define RB_STENCILCONTROL_STENCIL_FAIL_BF 23 -#define RB_STENCILCONTROL_STENCIL_FUNC 8 -#define RB_STENCILCONTROL_STENCIL_FUNC_BF 20 -#define RB_STENCILCONTROL_STENCIL_ZFAIL 17 -#define RB_STENCILCONTROL_STENCIL_ZFAIL_BF 29 -#define RB_STENCILCONTROL_STENCIL_ZPASS 14 -#define RB_STENCILCONTROL_STENCIL_ZPASS_BF 26 -#define SP_FSCTRLREG0_FSFULLREGFOOTPRINT 10 -#define SP_FSCTRLREG0_FSHALFREGFOOTPRINT 4 -#define SP_FSCTRLREG0_FSICACHEINVALID 2 -#define SP_FSCTRLREG0_FSINOUTREGOVERLAP 18 -#define SP_FSCTRLREG0_FSINSTRBUFFERMODE 1 -#define SP_FSCTRLREG0_FSLENGTH 24 -#define SP_FSCTRLREG0_FSSUPERTHREADMODE 21 -#define SP_FSCTRLREG0_FSTHREADMODE 0 -#define SP_FSCTRLREG0_FSTHREADSIZE 20 -#define SP_FSCTRLREG0_PIXLODENABLE 22 -#define SP_FSCTRLREG1_FSCONSTLENGTH 0 -#define SP_FSCTRLREG1_FSINITIALOUTSTANDING 20 -#define SP_FSCTRLREG1_HALFPRECVAROFFSET 24 -#define SP_FSMRTREG_REGID 0 -#define SP_FSMRTREG_PRECISION 8 -#define SP_FSOUTREG_PAD0 2 -#define SP_IMAGEOUTPUTREG_MRTFORMAT 0 -#define SP_IMAGEOUTPUTREG_DEPTHOUTMODE 3 -#define SP_IMAGEOUTPUTREG_PAD0 6 -#define SP_OBJOFFSETREG_CONSTOBJECTSTARTOFFSET 16 -#define SP_OBJOFFSETREG_SHADEROBJOFFSETINIC 25 -#define SP_SHADERLENGTH_LEN 0 -#define SP_SPCTRLREG_CONSTMODE 18 -#define SP_SPCTRLREG_LOMODE 22 -#define SP_SPCTRLREG_SLEEPMODE 20 -#define SP_VSCTRLREG0_VSFULLREGFOOTPRINT 10 -#define SP_VSCTRLREG0_VSICACHEINVALID 2 -#define SP_VSCTRLREG0_VSINSTRBUFFERMODE 1 -#define SP_VSCTRLREG0_VSLENGTH 24 -#define SP_VSCTRLREG0_VSSUPERTHREADMODE 21 -#define SP_VSCTRLREG0_VSTHREADMODE 0 -#define SP_VSCTRLREG0_VSTHREADSIZE 20 -#define SP_VSCTRLREG1_VSINITIALOUTSTANDING 24 -#define SP_VSOUTREG_COMPMASK0 9 -#define SP_VSPARAMREG_POSREGID 0 -#define SP_VSPARAMREG_PSIZEREGID 8 -#define SP_VSPARAMREG_TOTALVSOUTVAR 20 -#define SP_VSVPCDSTREG_OUTLOC0 0 -#define TPL1_TPTEXOFFSETREG_BASETABLEPTR 16 -#define TPL1_TPTEXOFFSETREG_MEMOBJOFFSET 8 -#define TPL1_TPTEXOFFSETREG_SAMPLEROFFSET 0 -#define UCHE_INVALIDATE1REG_OPCODE 0x1C -#define UCHE_INVALIDATE1REG_ALLORPORTION 0x1F -#define VFD_BASEADDR_BASEADDR 0 -#define VFD_CTRLREG0_PACKETSIZE 18 -#define VFD_CTRLREG0_STRMDECINSTRCNT 22 -#define VFD_CTRLREG0_STRMFETCHINSTRCNT 27 -#define VFD_CTRLREG0_TOTALATTRTOVS 0 -#define VFD_CTRLREG1_MAXSTORAGE 0 -#define VFD_CTRLREG1_REGID4INST 24 -#define VFD_CTRLREG1_REGID4VTX 16 -#define VFD_DECODEINSTRUCTIONS_CONSTFILL 4 -#define VFD_DECODEINSTRUCTIONS_FORMAT 6 -#define VFD_DECODEINSTRUCTIONS_LASTCOMPVALID 29 -#define VFD_DECODEINSTRUCTIONS_REGID 12 -#define VFD_DECODEINSTRUCTIONS_SHIFTCNT 24 -#define VFD_DECODEINSTRUCTIONS_SWITCHNEXT 30 -#define VFD_DECODEINSTRUCTIONS_WRITEMASK 0 -#define VFD_FETCHINSTRUCTIONS_BUFSTRIDE 7 -#define VFD_FETCHINSTRUCTIONS_FETCHSIZE 0 -#define VFD_FETCHINSTRUCTIONS_INDEXDECODE 18 -#define VFD_FETCHINSTRUCTIONS_STEPRATE 24 -#define VFD_FETCHINSTRUCTIONS_SWITCHNEXT 17 -#define VFD_THREADINGTHRESHOLD_REGID_VTXCNT 8 -#define VFD_THREADINGTHRESHOLD_REGID_THRESHOLD 0 -#define VFD_THREADINGTHRESHOLD_RESERVED6 4 -#define VPC_VPCATTR_LMSIZE 28 -#define VPC_VPCATTR_THRHDASSIGN 12 -#define VPC_VPCATTR_TOTALATTR 0 -#define VPC_VPCPACK_NUMFPNONPOSVAR 8 -#define VPC_VPCPACK_NUMNONPOSVSVAR 16 -#define VPC_VPCVARPSREPLMODE_COMPONENT08 0 -#define VPC_VPCVARPSREPLMODE_COMPONENT09 2 -#define VPC_VPCVARPSREPLMODE_COMPONENT0A 4 -#define VPC_VPCVARPSREPLMODE_COMPONENT0B 6 -#define VPC_VPCVARPSREPLMODE_COMPONENT0C 8 -#define VPC_VPCVARPSREPLMODE_COMPONENT0D 10 -#define VPC_VPCVARPSREPLMODE_COMPONENT0E 12 -#define VPC_VPCVARPSREPLMODE_COMPONENT0F 14 -#define VPC_VPCVARPSREPLMODE_COMPONENT10 16 -#define VPC_VPCVARPSREPLMODE_COMPONENT11 18 -#define VPC_VPCVARPSREPLMODE_COMPONENT12 20 -#define VPC_VPCVARPSREPLMODE_COMPONENT13 22 -#define VPC_VPCVARPSREPLMODE_COMPONENT14 24 -#define VPC_VPCVARPSREPLMODE_COMPONENT15 26 -#define VPC_VPCVARPSREPLMODE_COMPONENT16 28 -#define VPC_VPCVARPSREPLMODE_COMPONENT17 30 - -/* RBBM Debug bus block IDs */ -#define RBBM_BLOCK_ID_NONE 0x0 -#define RBBM_BLOCK_ID_CP 0x1 -#define RBBM_BLOCK_ID_RBBM 0x2 -#define RBBM_BLOCK_ID_VBIF 0x3 -#define RBBM_BLOCK_ID_HLSQ 0x4 -#define RBBM_BLOCK_ID_UCHE 0x5 -#define RBBM_BLOCK_ID_PC 0x8 -#define RBBM_BLOCK_ID_VFD 0x9 -#define RBBM_BLOCK_ID_VPC 0xa -#define RBBM_BLOCK_ID_TSE 0xb -#define RBBM_BLOCK_ID_RAS 0xc -#define RBBM_BLOCK_ID_VSC 0xd -#define RBBM_BLOCK_ID_SP_0 0x10 -#define RBBM_BLOCK_ID_SP_1 0x11 -#define RBBM_BLOCK_ID_SP_2 0x12 -#define RBBM_BLOCK_ID_SP_3 0x13 -#define RBBM_BLOCK_ID_TPL1_0 0x18 -#define RBBM_BLOCK_ID_TPL1_1 0x19 -#define RBBM_BLOCK_ID_TPL1_2 0x1a -#define RBBM_BLOCK_ID_TPL1_3 0x1b -#define RBBM_BLOCK_ID_RB_0 0x20 -#define RBBM_BLOCK_ID_RB_1 0x21 -#define RBBM_BLOCK_ID_RB_2 0x22 -#define RBBM_BLOCK_ID_RB_3 0x23 -#define RBBM_BLOCK_ID_MARB_0 0x28 -#define RBBM_BLOCK_ID_MARB_1 0x29 -#define RBBM_BLOCK_ID_MARB_2 0x2a -#define RBBM_BLOCK_ID_MARB_3 0x2b - -/* RBBM_CLOCK_CTL default value */ -#define A3XX_RBBM_CLOCK_CTL_DEFAULT 0xBFFFFFFF - -#endif diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c index da19b65b4a1..e01bb605a34 100644 --- a/drivers/gpu/msm/adreno.c +++ b/drivers/gpu/msm/adreno.c @@ -113,10 +113,25 @@ static struct adreno_device device_3d0 = { }, .pfp_fw = NULL, .pm4_fw = NULL, - .wait_timeout = 10000, /* in milliseconds */ + .wait_timeout = 0, /* in milliseconds, 0 means disabled */ .ib_check_level = 0, }; +/* This set of registers are used for Hang detection + * If the values of these registers are same after + * KGSL_TIMEOUT_PART time, GPU hang is reported in + * kernel log. + */ +unsigned int hang_detect_regs[] = { + REG_RBBM_STATUS, + REG_CP_RB_RPTR, + REG_CP_IB1_BASE, + REG_CP_IB1_BUFSZ, + REG_CP_IB2_BASE, + REG_CP_IB2_BUFSZ, +}; + +const unsigned int hang_detect_regs_count = ARRAY_SIZE(hang_detect_regs); /* * This is the master list of all GPU cores that are supported by this @@ -277,6 +292,12 @@ static void adreno_setstate(struct kgsl_device *device, struct kgsl_context *context; struct adreno_context *adreno_ctx = NULL; + /* + * Fix target freeze issue by adding TLB flush for each submit + * on A20X based targets. + */ + if (adreno_is_a20x(adreno_dev)) + flags |= KGSL_MMUFLAGS_TLBFLUSH; /* * If possible, then set the state via the command stream to avoid * a CPU idle. Otherwise, use the default setstate which uses register @@ -530,7 +551,9 @@ static int adreno_start(struct kgsl_device *device, unsigned int init_ram) } kgsl_mh_start(device); - + /* Assign correct RBBM status register to hang detect regs + */ + hang_detect_regs[0] = adreno_dev->gpudev->reg_rbbm_status; if (kgsl_mmu_start(device)) goto error_clk_off; @@ -555,7 +578,10 @@ static int adreno_start(struct kgsl_device *device, unsigned int init_ram) adreno_regwrite(device, REG_RBBM_SOFT_RESET, 0x00000000); - adreno_regwrite(device, REG_RBBM_CNTL, 0x00004442); + if (adreno_is_a200(adreno_dev)) + adreno_regwrite(device, REG_RBBM_CNTL, 0x0000FFFF); + else + adreno_regwrite(device, REG_RBBM_CNTL, 0x00004442); if (adreno_is_a225(adreno_dev)) { /* Enable large instruction store for A225 */ @@ -718,7 +744,7 @@ adreno_recover_hang(struct kgsl_device *device, rb->timestamp = timestamp; /* wait for idle */ - ret = adreno_idle(device, KGSL_TIMEOUT_DEFAULT); + ret = adreno_idle(device); done: kgsl_sharedmem_writel(&device->memstore, KGSL_DEVICE_MEMSTORE_OFFSET(eoptimestamp), @@ -930,61 +956,93 @@ static inline void adreno_poke(struct kgsl_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) +static int adreno_ringbuffer_drain(struct kgsl_device *device, + unsigned int *regs) { struct adreno_device *adreno_dev = ADRENO_DEVICE(device); struct adreno_ringbuffer *rb = &adreno_dev->ringbuffer; + unsigned long wait; + unsigned long timeout = jiffies + msecs_to_jiffies(ADRENO_IDLE_TIMEOUT); + + if (!(rb->flags & KGSL_FLAGS_STARTED)) + return 0; + + /* + * The first time into the loop, wait for 100 msecs and kick wptr again + * to ensure that the hardware has updated correctly. After that, kick + * it periodically every KGSL_TIMEOUT_PART msecs until the timeout + * expires + */ + + wait = jiffies + msecs_to_jiffies(100); + + adreno_poke(device); + + do { + if (time_after(jiffies, wait)) { + adreno_poke(device); + + /* Check to see if the core is hung */ + if (adreno_hang_detect(device, regs)) + return -ETIMEDOUT; + + wait = jiffies + msecs_to_jiffies(KGSL_TIMEOUT_PART); + } + GSL_RB_GET_READPTR(rb, &rb->rptr); + + if (time_after(jiffies, timeout)) { + KGSL_DRV_ERR(device, "rptr: %x, wptr: %x\n", + rb->rptr, rb->wptr); + return -ETIMEDOUT; + } + } while (rb->rptr != rb->wptr); + + return 0; +} + +/* Caller must hold the device mutex. */ +int adreno_idle(struct kgsl_device *device) +{ unsigned int rbbm_status; - unsigned long wait_timeout = - msecs_to_jiffies(adreno_dev->wait_timeout); unsigned long wait_time; unsigned long wait_time_part; - unsigned int msecs; - unsigned int msecs_first; - unsigned int msecs_part; + unsigned int prev_reg_val[hang_detect_regs_count]; + + memset(prev_reg_val, 0, sizeof(prev_reg_val)); 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) { - 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 { - 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", - rb->rptr, rb->wptr); - goto err; - } - } while (rb->rptr != rb->wptr); - } + /* First, wait for the ringbuffer to drain */ + if (adreno_ringbuffer_drain(device, prev_reg_val)) + goto err; /* now, wait for the GPU to finish its operations */ - wait_time = jiffies + wait_timeout; + wait_time = jiffies + msecs_to_jiffies(ADRENO_IDLE_TIMEOUT); + wait_time_part = jiffies + msecs_to_jiffies(KGSL_TIMEOUT_PART); + while (time_before(jiffies, wait_time)) { adreno_regread(device, REG_RBBM_STATUS, &rbbm_status); if (rbbm_status == 0x110) return 0; } + /* Dont wait for timeout, detect hang faster. + */ + if (time_after(jiffies, wait_time_part)) { + wait_time_part = jiffies + + msecs_to_jiffies(KGSL_TIMEOUT_PART); + if ((adreno_hang_detect(device, prev_reg_val))) + goto err; +} + + err: KGSL_DRV_ERR(device, "spun too long waiting for RB to idle\n"); if (KGSL_STATE_DUMP_AND_RECOVER != device->state && !adreno_dump_and_recover(device)) { - wait_time = jiffies + wait_timeout; + wait_time = jiffies + msecs_to_jiffies(ADRENO_IDLE_TIMEOUT); goto retry; } return -ETIMEDOUT; @@ -1024,7 +1082,7 @@ static int adreno_suspend_context(struct kgsl_device *device) /* switch to NULL ctxt */ if (adreno_dev->drawctxt_active != NULL) { adreno_drawctxt_switch(adreno_dev, NULL, 0); - status = adreno_idle(device, KGSL_TIMEOUT_DEFAULT); + status = adreno_idle(device); } return status; @@ -1145,53 +1203,6 @@ void adreno_regwrite(struct kgsl_device *device, unsigned int offsetwords, __raw_writel(value, reg); } -static void 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); - } - } -} - static int kgsl_check_interrupt_timestamp(struct kgsl_device *device, unsigned int timestamp) { @@ -1264,6 +1275,30 @@ static int kgsl_check_interrupt_timestamp(struct kgsl_device *device, __ret; \ }) + +unsigned int adreno_hang_detect(struct kgsl_device *device, + unsigned int *prev_reg_val) +{ + struct adreno_device *adreno_dev = ADRENO_DEVICE(device); + unsigned int curr_reg_val[hang_detect_regs_count]; + unsigned int hang_detected = 1; + unsigned int i; + + if (!adreno_dev->fast_hang_detect) + return 0; + + for (i = 0; i < hang_detect_regs_count; i++) { + adreno_regread(device, hang_detect_regs[i], + &curr_reg_val[i]); + if (curr_reg_val[i] != prev_reg_val[i]) { + prev_reg_val[i] = curr_reg_val[i]; + hang_detected = 0; + } + } + + return hang_detected; +} + /* MUST be called with the device mutex held */ static int adreno_waittimestamp(struct kgsl_device *device, unsigned int timestamp, @@ -1274,12 +1309,16 @@ static int adreno_waittimestamp(struct kgsl_device *device, 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; + int retries = 0; + + unsigned int time_elapsed = 0; + unsigned int prev_reg_val[hang_detect_regs_count]; + unsigned int wait; + + memset(prev_reg_val, 0, sizeof(prev_reg_val)); /* Don't wait forever, set a max value for now */ - if (msecs == -1) + if (msecs == KGSL_TIMEOUT_DEFAULT) msecs = adreno_dev->wait_timeout; if (timestamp_cmp(timestamp, adreno_dev->ringbuffer.timestamp) > 0) { @@ -1290,13 +1329,18 @@ static int adreno_waittimestamp(struct kgsl_device *device, goto done; } - /* Keep the first timeout as 100msecs before rewriting - * the WPTR. Less visible impact if the WPTR has not - * been updated properly. + /* + * Make the first timeout interval 100 msecs and then try to kick the + * wptr again. This helps to ensure the wptr is updated properly. If + * the requested timeout is less than 100 msecs, then wait 20msecs which + * is the minimum amount of time we can safely wait at 100HZ */ - msecs_first = (msecs <= 100) ? ((msecs + 4) / 5) : 100; - msecs_part = (msecs - msecs_first + 3) / 4; - for (retries = 0; retries < 5; retries++) { + if (msecs == 0 || msecs >= 100) + wait = 100; + else + wait = 20; + + do { if (kgsl_check_timestamp(device, timestamp)) { /* if the timestamp happens while we're not * waiting, there's a chance that an interrupt @@ -1312,6 +1356,11 @@ static int adreno_waittimestamp(struct kgsl_device *device, if (io_cnt < pwr->pwrlevels[pwr->active_pwrlevel].io_fraction) io = 0; + + if ((retries > 0) && + (adreno_hang_detect(device, prev_reg_val))) + goto hang_dump; + mutex_unlock(&device->mutex); /* We need to make sure that the process is * placed in wait-q before its condition is called @@ -1319,9 +1368,9 @@ static int adreno_waittimestamp(struct kgsl_device *device, status = kgsl_wait_event_interruptible_timeout( device->wait_queue, kgsl_check_interrupt_timestamp(device, - timestamp), - msecs_to_jiffies(retries ? - msecs_part : msecs_first), io); + timestamp), + msecs_to_jiffies(wait), io); + mutex_lock(&device->mutex); if (status > 0) { @@ -1333,7 +1382,15 @@ static int adreno_waittimestamp(struct kgsl_device *device, goto done; } /*this wait timed out*/ - } + + time_elapsed += wait; + wait = KGSL_TIMEOUT_PART; + + retries++; + + } while (!msecs || time_elapsed < msecs); + +hang_dump: /* Check if timestamp has retired here because we may have hit * recovery which can take some time and cause waiting threads @@ -1409,8 +1466,8 @@ 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; - return ticks / gpu_freq; + s64 ticksus = (s64)ticks*1000000; + return div_u64(ticksus, gpu_freq); } static void adreno_power_stats(struct kgsl_device *device, @@ -1498,7 +1555,6 @@ 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 9c75f6dc6fe..d50eec6cbeb 100644 --- a/drivers/gpu/msm/adreno.h +++ b/drivers/gpu/msm/adreno.h @@ -49,6 +49,12 @@ #define ADRENO_NUM_CTX_SWITCH_ALLOWED_BEFORE_DRAW 50 +/* One cannot wait forever for the core to idle, so set an upper limit to the + * amount of time to wait for the core to go idle + */ + +#define ADRENO_IDLE_TIMEOUT (20 * 1000) + enum adreno_gpurev { ADRENO_REV_UNKNOWN = 0, ADRENO_REV_A200 = 200, @@ -78,10 +84,12 @@ struct adreno_device { unsigned int istore_size; unsigned int pix_shader_start; unsigned int ib_check_level; + unsigned int fast_hang_detect; }; struct adreno_gpudev { /* keeps track of when we need to execute the draw workaround code */ + unsigned int reg_rbbm_status; int ctx_switches_since_last_draw; int (*ctxt_create)(struct adreno_device *, struct adreno_context *); void (*ctxt_save)(struct adreno_device *, struct adreno_context *); @@ -124,7 +132,11 @@ 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); +extern unsigned int hang_detect_regs[]; +extern const unsigned int hang_detect_regs_count; + + +int adreno_idle(struct kgsl_device *device); void adreno_regread(struct kgsl_device *device, unsigned int offsetwords, unsigned int *value); void adreno_regwrite(struct kgsl_device *device, unsigned int offsetwords, @@ -143,6 +155,9 @@ void *adreno_snapshot(struct kgsl_device *device, void *snapshot, int *remain, int adreno_dump_and_recover(struct kgsl_device *device); +unsigned int adreno_hang_detect(struct kgsl_device *device, + unsigned int *prev_reg_val); + 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 9efd587fa16..9b9fcc8b00a 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-2013, 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 @@ -581,6 +581,12 @@ static void build_regsave_cmds(struct adreno_device *adreno_dev, *cmd++ = REG_TP0_CHICKEN; *cmd++ = tmp_ctx.reg_values[1]; + if (adreno_is_a20x(adreno_dev)) { + *cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2); + *cmd++ = REG_RB_BC_CONTROL; + *cmd++ = tmp_ctx.reg_values[2]; + } + if (adreno_is_a22x(adreno_dev)) { unsigned int i; unsigned int j = 2; @@ -1107,6 +1113,12 @@ static void build_regrestore_cmds(struct adreno_device *adreno_dev, tmp_ctx.reg_values[1] = virt2gpu(cmd, &drawctxt->gpustate); *cmd++ = 0x00000000; + if (adreno_is_a20x(adreno_dev)) { + *cmd++ = cp_type0_packet(REG_RB_BC_CONTROL, 1); + tmp_ctx.reg_values[2] = virt2gpu(cmd, &drawctxt->gpustate); + *cmd++ = 0x00000000; + } + if (adreno_is_a22x(adreno_dev)) { unsigned int i; unsigned int j = 2; @@ -1780,6 +1792,7 @@ void *a2xx_snapshot(struct adreno_device *adreno_dev, void *snapshot, int *remain, int hang); struct adreno_gpudev adreno_a2xx_gpudev = { + .reg_rbbm_status=REG_RBBM_STATUS, .ctxt_create = a2xx_drawctxt_create, .ctxt_save = a2xx_drawctxt_save, .ctxt_restore = a2xx_drawctxt_restore, diff --git a/drivers/gpu/msm/adreno_a3xx.c b/drivers/gpu/msm/adreno_a3xx.c deleted file mode 100644 index 27146927095..00000000000 --- a/drivers/gpu/msm/adreno_a3xx.c +++ /dev/null @@ -1,2774 +0,0 @@ -/* 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 "kgsl.h" -#include "adreno.h" -#include "kgsl_sharedmem.h" -#include "kgsl_cffdump.h" -#include "a3xx_reg.h" -#include "adreno_a3xx_trace.h" - -/* - * Set of registers to dump for A3XX on postmortem and snapshot. - * Registers in pairs - first value is the start offset, second - * is the stop offset (inclusive) - */ - -const unsigned int a3xx_registers[] = { - 0x0000, 0x0002, 0x0010, 0x0012, 0x0018, 0x0018, 0x0020, 0x0027, - 0x0029, 0x002b, 0x002e, 0x0033, 0x0040, 0x0042, 0x0050, 0x005c, - 0x0060, 0x006c, 0x0080, 0x0082, 0x0084, 0x0088, 0x0090, 0x00e5, - 0x00ea, 0x00ed, 0x0100, 0x0100, 0x0110, 0x0123, 0x01c0, 0x01c1, - 0x01c3, 0x01c5, 0x01c7, 0x01c7, 0x01d5, 0x01d9, 0x01dc, 0x01dd, - 0x01ea, 0x01ea, 0x01ee, 0x01f1, 0x01f5, 0x01f5, 0x01fc, 0x01ff, - 0x0440, 0x0440, 0x0443, 0x0443, 0x0445, 0x0445, 0x044d, 0x044f, - 0x0452, 0x0452, 0x0454, 0x046f, 0x047c, 0x047c, 0x047f, 0x047f, - 0x0578, 0x057f, 0x0600, 0x0602, 0x0605, 0x0607, 0x060a, 0x060e, - 0x0612, 0x0614, 0x0c01, 0x0c02, 0x0c06, 0x0c1d, 0x0c3d, 0x0c3f, - 0x0c48, 0x0c4b, 0x0c80, 0x0c80, 0x0c88, 0x0c8b, 0x0ca0, 0x0cb7, - 0x0cc0, 0x0cc1, 0x0cc6, 0x0cc7, 0x0ce4, 0x0ce5, 0x0e00, 0x0e05, - 0x0e0c, 0x0e0c, 0x0e22, 0x0e23, 0x0e41, 0x0e45, 0x0e64, 0x0e65, - 0x0e80, 0x0e82, 0x0e84, 0x0e89, 0x0ea0, 0x0ea1, 0x0ea4, 0x0ea7, - 0x0ec4, 0x0ecb, 0x0ee0, 0x0ee0, 0x0f00, 0x0f01, 0x0f03, 0x0f09, - 0x2040, 0x2040, 0x2044, 0x2044, 0x2048, 0x204d, 0x2068, 0x2069, - 0x206c, 0x206d, 0x2070, 0x2070, 0x2072, 0x2072, 0x2074, 0x2075, - 0x2079, 0x207a, 0x20c0, 0x20d3, 0x20e4, 0x20ef, 0x2100, 0x2109, - 0x210c, 0x210c, 0x210e, 0x210e, 0x2110, 0x2111, 0x2114, 0x2115, - 0x21e4, 0x21e4, 0x21ea, 0x21ea, 0x21ec, 0x21ed, 0x21f0, 0x21f0, - 0x2200, 0x2212, 0x2214, 0x2217, 0x221a, 0x221a, 0x2240, 0x227e, - 0x2280, 0x228b, 0x22c0, 0x22c0, 0x22c4, 0x22ce, 0x22d0, 0x22d8, - 0x22df, 0x22e6, 0x22e8, 0x22e9, 0x22ec, 0x22ec, 0x22f0, 0x22f7, - 0x22ff, 0x22ff, 0x2340, 0x2343, 0x2348, 0x2349, 0x2350, 0x2356, - 0x2360, 0x2360, 0x2440, 0x2440, 0x2444, 0x2444, 0x2448, 0x244d, - 0x2468, 0x2469, 0x246c, 0x246d, 0x2470, 0x2470, 0x2472, 0x2472, - 0x2474, 0x2475, 0x2479, 0x247a, 0x24c0, 0x24d3, 0x24e4, 0x24ef, - 0x2500, 0x2509, 0x250c, 0x250c, 0x250e, 0x250e, 0x2510, 0x2511, - 0x2514, 0x2515, 0x25e4, 0x25e4, 0x25ea, 0x25ea, 0x25ec, 0x25ed, - 0x25f0, 0x25f0, 0x2600, 0x2612, 0x2614, 0x2617, 0x261a, 0x261a, - 0x2640, 0x267e, 0x2680, 0x268b, 0x26c0, 0x26c0, 0x26c4, 0x26ce, - 0x26d0, 0x26d8, 0x26df, 0x26e6, 0x26e8, 0x26e9, 0x26ec, 0x26ec, - 0x26f0, 0x26f7, 0x26ff, 0x26ff, 0x2740, 0x2743, 0x2748, 0x2749, - 0x2750, 0x2756, 0x2760, 0x2760, 0x300C, 0x300E, 0x301C, 0x301D, - 0x302A, 0x302A, 0x302C, 0x302D, 0x3030, 0x3031, 0x3034, 0x3036, - 0x303C, 0x303C, 0x305E, 0x305F, -}; - -const unsigned int a3xx_registers_count = ARRAY_SIZE(a3xx_registers) / 2; - -/* Simple macro to facilitate bit setting in the gmem2sys and sys2gmem - * functions. - */ - -#define _SET(_shift, _val) ((_val) << (_shift)) - -/* - **************************************************************************** - * - * Context state shadow structure: - * - * +---------------------+------------+-------------+---------------------+---+ - * | ALU Constant Shadow | Reg Shadow | C&V Buffers | Shader Instr Shadow |Tex| - * +---------------------+------------+-------------+---------------------+---+ - * - * 8K - ALU Constant Shadow (8K aligned) - * 4K - H/W Register Shadow (8K aligned) - * 5K - Command and Vertex Buffers - * 8K - Shader Instruction Shadow - * ~6K - Texture Constant Shadow - * - * - *************************************************************************** - */ - -/* Sizes of all sections in state shadow memory */ -#define ALU_SHADOW_SIZE (8*1024) /* 8KB */ -#define REG_SHADOW_SIZE (4*1024) /* 4KB */ -#define CMD_BUFFER_SIZE (5*1024) /* 5KB */ -#define TEX_SIZE_MEM_OBJECTS 896 /* bytes */ -#define TEX_SIZE_MIPMAP 1936 /* bytes */ -#define TEX_SIZE_SAMPLER_OBJ 256 /* bytes */ -#define TEX_SHADOW_SIZE \ - ((TEX_SIZE_MEM_OBJECTS + TEX_SIZE_MIPMAP + \ - TEX_SIZE_SAMPLER_OBJ)*2) /* ~6KB */ -#define SHADER_SHADOW_SIZE (8*1024) /* 8KB */ - -/* Total context size, excluding GMEM shadow */ -#define CONTEXT_SIZE \ - (ALU_SHADOW_SIZE+REG_SHADOW_SIZE + \ - CMD_BUFFER_SIZE+SHADER_SHADOW_SIZE + \ - TEX_SHADOW_SIZE) - -/* Offsets to different sections in context shadow memory */ -#define REG_OFFSET ALU_SHADOW_SIZE -#define CMD_OFFSET (REG_OFFSET+REG_SHADOW_SIZE) -#define SHADER_OFFSET (CMD_OFFSET+CMD_BUFFER_SIZE) -#define TEX_OFFSET (SHADER_OFFSET+SHADER_SHADOW_SIZE) -#define VS_TEX_OFFSET_MEM_OBJECTS TEX_OFFSET -#define VS_TEX_OFFSET_MIPMAP (VS_TEX_OFFSET_MEM_OBJECTS+TEX_SIZE_MEM_OBJECTS) -#define VS_TEX_OFFSET_SAMPLER_OBJ (VS_TEX_OFFSET_MIPMAP+TEX_SIZE_MIPMAP) -#define FS_TEX_OFFSET_MEM_OBJECTS \ - (VS_TEX_OFFSET_SAMPLER_OBJ+TEX_SIZE_SAMPLER_OBJ) -#define FS_TEX_OFFSET_MIPMAP (FS_TEX_OFFSET_MEM_OBJECTS+TEX_SIZE_MEM_OBJECTS) -#define FS_TEX_OFFSET_SAMPLER_OBJ (FS_TEX_OFFSET_MIPMAP+TEX_SIZE_MIPMAP) - -/* The offset for fragment shader data in HLSQ context */ -#define SSIZE (16*1024) - -#define HLSQ_SAMPLER_OFFSET 0x000 -#define HLSQ_MEMOBJ_OFFSET 0x400 -#define HLSQ_MIPMAP_OFFSET 0x800 - -/* Use shadow RAM */ -#define HLSQ_SHADOW_BASE (0x10000+SSIZE*2) - -#define REG_TO_MEM_LOOP_COUNT_SHIFT 18 - -#define BUILD_PC_DRAW_INITIATOR(prim_type, source_select, index_size, \ - vis_cull_mode) \ - (((prim_type) << PC_DRAW_INITIATOR_PRIM_TYPE) | \ - ((source_select) << PC_DRAW_INITIATOR_SOURCE_SELECT) | \ - ((index_size & 1) << PC_DRAW_INITIATOR_INDEX_SIZE) | \ - ((index_size >> 1) << PC_DRAW_INITIATOR_SMALL_INDEX) | \ - ((vis_cull_mode) << PC_DRAW_INITIATOR_VISIBILITY_CULLING_MODE) | \ - (1 << PC_DRAW_INITIATOR_PRE_DRAW_INITIATOR_ENABLE)) - -/* - * List of context registers (starting from dword offset 0x2000). - * Each line contains start and end of a range of registers. - */ -static const unsigned int context_register_ranges[] = { - A3XX_GRAS_CL_CLIP_CNTL, A3XX_GRAS_CL_CLIP_CNTL, - A3XX_GRAS_CL_GB_CLIP_ADJ, A3XX_GRAS_CL_GB_CLIP_ADJ, - A3XX_GRAS_CL_VPORT_XOFFSET, A3XX_GRAS_CL_VPORT_ZSCALE, - A3XX_GRAS_SU_POINT_MINMAX, A3XX_GRAS_SU_POINT_SIZE, - A3XX_GRAS_SU_POLY_OFFSET_SCALE, A3XX_GRAS_SU_POLY_OFFSET_OFFSET, - A3XX_GRAS_SU_MODE_CONTROL, A3XX_GRAS_SU_MODE_CONTROL, - A3XX_GRAS_SC_CONTROL, A3XX_GRAS_SC_CONTROL, - A3XX_GRAS_SC_SCREEN_SCISSOR_TL, A3XX_GRAS_SC_SCREEN_SCISSOR_BR, - A3XX_GRAS_SC_WINDOW_SCISSOR_TL, A3XX_GRAS_SC_WINDOW_SCISSOR_BR, - A3XX_RB_MODE_CONTROL, A3XX_RB_MRT_BLEND_CONTROL3, - A3XX_RB_BLEND_RED, A3XX_RB_COPY_DEST_INFO, - A3XX_RB_DEPTH_CONTROL, A3XX_RB_DEPTH_CONTROL, - A3XX_PC_VSTREAM_CONTROL, A3XX_PC_VSTREAM_CONTROL, - A3XX_PC_VERTEX_REUSE_BLOCK_CNTL, A3XX_PC_VERTEX_REUSE_BLOCK_CNTL, - A3XX_PC_PRIM_VTX_CNTL, A3XX_PC_RESTART_INDEX, - A3XX_HLSQ_CONTROL_0_REG, A3XX_HLSQ_CONST_FSPRESV_RANGE_REG, - A3XX_HLSQ_CL_NDRANGE_0_REG, A3XX_HLSQ_CL_NDRANGE_0_REG, - A3XX_HLSQ_CL_NDRANGE_2_REG, A3XX_HLSQ_CL_CONTROL_1_REG, - A3XX_HLSQ_CL_KERNEL_CONST_REG, A3XX_HLSQ_CL_KERNEL_GROUP_Z_REG, - A3XX_HLSQ_CL_WG_OFFSET_REG, A3XX_HLSQ_CL_WG_OFFSET_REG, - A3XX_VFD_CONTROL_0, A3XX_VFD_VS_THREADING_THRESHOLD, - A3XX_SP_SP_CTRL_REG, A3XX_SP_SP_CTRL_REG, - A3XX_SP_VS_CTRL_REG0, A3XX_SP_VS_OUT_REG_7, - A3XX_SP_VS_VPC_DST_REG_0, A3XX_SP_VS_PVT_MEM_SIZE_REG, - A3XX_SP_VS_LENGTH_REG, A3XX_SP_FS_PVT_MEM_SIZE_REG, - A3XX_SP_FS_FLAT_SHAD_MODE_REG_0, A3XX_SP_FS_FLAT_SHAD_MODE_REG_1, - A3XX_SP_FS_OUTPUT_REG, A3XX_SP_FS_OUTPUT_REG, - A3XX_SP_FS_MRT_REG_0, A3XX_SP_FS_IMAGE_OUTPUT_REG_3, - A3XX_SP_FS_LENGTH_REG, A3XX_SP_FS_LENGTH_REG, - A3XX_TPL1_TP_VS_TEX_OFFSET, A3XX_TPL1_TP_FS_BORDER_COLOR_BASE_ADDR, - A3XX_VPC_ATTR, A3XX_VPC_VARY_CYLWRAP_ENABLE_1, -}; - -/* Global registers that need to be saved separately */ -static const unsigned int global_registers[] = { - A3XX_GRAS_CL_USER_PLANE_X0, A3XX_GRAS_CL_USER_PLANE_Y0, - A3XX_GRAS_CL_USER_PLANE_Z0, A3XX_GRAS_CL_USER_PLANE_W0, - A3XX_GRAS_CL_USER_PLANE_X1, A3XX_GRAS_CL_USER_PLANE_Y1, - A3XX_GRAS_CL_USER_PLANE_Z1, A3XX_GRAS_CL_USER_PLANE_W1, - A3XX_GRAS_CL_USER_PLANE_X2, A3XX_GRAS_CL_USER_PLANE_Y2, - A3XX_GRAS_CL_USER_PLANE_Z2, A3XX_GRAS_CL_USER_PLANE_W2, - A3XX_GRAS_CL_USER_PLANE_X3, A3XX_GRAS_CL_USER_PLANE_Y3, - A3XX_GRAS_CL_USER_PLANE_Z3, A3XX_GRAS_CL_USER_PLANE_W3, - A3XX_GRAS_CL_USER_PLANE_X4, A3XX_GRAS_CL_USER_PLANE_Y4, - A3XX_GRAS_CL_USER_PLANE_Z4, A3XX_GRAS_CL_USER_PLANE_W4, - A3XX_GRAS_CL_USER_PLANE_X5, A3XX_GRAS_CL_USER_PLANE_Y5, - A3XX_GRAS_CL_USER_PLANE_Z5, A3XX_GRAS_CL_USER_PLANE_W5, - A3XX_VSC_BIN_SIZE, - A3XX_VSC_PIPE_CONFIG_0, A3XX_VSC_PIPE_CONFIG_1, - A3XX_VSC_PIPE_CONFIG_2, A3XX_VSC_PIPE_CONFIG_3, - A3XX_VSC_PIPE_CONFIG_4, A3XX_VSC_PIPE_CONFIG_5, - A3XX_VSC_PIPE_CONFIG_6, A3XX_VSC_PIPE_CONFIG_7, - A3XX_VSC_PIPE_DATA_ADDRESS_0, A3XX_VSC_PIPE_DATA_ADDRESS_1, - A3XX_VSC_PIPE_DATA_ADDRESS_2, A3XX_VSC_PIPE_DATA_ADDRESS_3, - A3XX_VSC_PIPE_DATA_ADDRESS_4, A3XX_VSC_PIPE_DATA_ADDRESS_5, - A3XX_VSC_PIPE_DATA_ADDRESS_6, A3XX_VSC_PIPE_DATA_ADDRESS_7, - A3XX_VSC_PIPE_DATA_LENGTH_0, A3XX_VSC_PIPE_DATA_LENGTH_1, - A3XX_VSC_PIPE_DATA_LENGTH_2, A3XX_VSC_PIPE_DATA_LENGTH_3, - A3XX_VSC_PIPE_DATA_LENGTH_4, A3XX_VSC_PIPE_DATA_LENGTH_5, - A3XX_VSC_PIPE_DATA_LENGTH_6, A3XX_VSC_PIPE_DATA_LENGTH_7, - A3XX_VSC_SIZE_ADDRESS -}; - -#define GLOBAL_REGISTER_COUNT ARRAY_SIZE(global_registers) - -/* A scratchpad used to build commands during context create */ -static struct tmp_ctx { - unsigned int *cmd; /* Next available dword in C&V buffer */ - - /* Addresses in comamnd buffer where registers are saved */ - uint32_t reg_values[GLOBAL_REGISTER_COUNT]; - uint32_t gmem_base; /* Base GPU address of GMEM */ -} tmp_ctx; - -#ifndef GSL_CONTEXT_SWITCH_CPU_SYNC -/* - * Function for executing dest = ( (reg & and) ROL rol ) | or - */ -static unsigned int *rmw_regtomem(unsigned int *cmd, - unsigned int reg, unsigned int and, - unsigned int rol, unsigned int or, - unsigned int dest) -{ - /* CP_SCRATCH_REG2 = (CP_SCRATCH_REG2 & 0x00000000) | reg */ - *cmd++ = cp_type3_packet(CP_REG_RMW, 3); - *cmd++ = (1 << 30) | A3XX_CP_SCRATCH_REG2; - *cmd++ = 0x00000000; /* AND value */ - *cmd++ = reg; /* OR address */ - - /* CP_SCRATCH_REG2 = ( (CP_SCRATCH_REG2 & and) ROL rol ) | or */ - *cmd++ = cp_type3_packet(CP_REG_RMW, 3); - *cmd++ = (rol << 24) | A3XX_CP_SCRATCH_REG2; - *cmd++ = and; /* AND value */ - *cmd++ = or; /* OR value */ - - *cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2); - *cmd++ = A3XX_CP_SCRATCH_REG2; - *cmd++ = dest; - - return cmd; -} -#endif - -static void build_regconstantsave_cmds(struct adreno_device *adreno_dev, - struct adreno_context *drawctxt) -{ - unsigned int *cmd = tmp_ctx.cmd; - unsigned int *start; - unsigned int i; - - drawctxt->constant_save_commands[0].hostptr = cmd; - drawctxt->constant_save_commands[0].gpuaddr = - virt2gpu(cmd, &drawctxt->gpustate); - cmd++; - - start = cmd; - - *cmd++ = cp_type3_packet(CP_WAIT_FOR_IDLE, 1); - *cmd++ = 0; - -#ifndef CONFIG_MSM_KGSL_DISABLE_SHADOW_WRITES - /* - * Context 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 */ - -#else - /* - * Make sure the HW context has the correct register values before - * reading them. - */ - - /* Write context registers into shadow */ - for (i = 0; i < ARRAY_SIZE(context_register_ranges) / 2; i++) { - unsigned int start = context_register_ranges[i * 2]; - unsigned int end = context_register_ranges[i * 2 + 1]; - *cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2); - *cmd++ = ((end - start + 1) << REG_TO_MEM_LOOP_COUNT_SHIFT) | - start; - *cmd++ = ((drawctxt->gpustate.gpuaddr + REG_OFFSET) - & 0xFFFFE000) + (start - 0x2000) * 4; - } -#endif - - /* Need to handle some of the global registers separately */ - for (i = 0; i < ARRAY_SIZE(global_registers); i++) { - *cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2); - *cmd++ = global_registers[i]; - *cmd++ = tmp_ctx.reg_values[i]; - } - - /* Save vertex shader constants */ - *cmd++ = cp_type3_packet(CP_COND_EXEC, 4); - *cmd++ = drawctxt->cond_execs[2].gpuaddr >> 2; - *cmd++ = drawctxt->cond_execs[2].gpuaddr >> 2; - *cmd++ = 0x0000FFFF; - *cmd++ = 3; /* EXEC_COUNT */ - *cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2); - drawctxt->constant_save_commands[1].hostptr = cmd; - drawctxt->constant_save_commands[1].gpuaddr = - virt2gpu(cmd, &drawctxt->gpustate); - /* - From fixup: - - dwords = SP_VS_CTRL_REG1.VSCONSTLENGTH / 4 - src = (HLSQ_SHADOW_BASE + 0x2000) / 4 - - From register spec: - SP_VS_CTRL_REG1.VSCONSTLENGTH [09:00]: 0-512, unit = 128bits. - */ - *cmd++ = 0; /* (dwords << REG_TO_MEM_LOOP_COUNT_SHIFT) | src */ - /* ALU constant shadow base */ - *cmd++ = drawctxt->gpustate.gpuaddr & 0xfffffffc; - - /* Save fragment shader constants */ - *cmd++ = cp_type3_packet(CP_COND_EXEC, 4); - *cmd++ = drawctxt->cond_execs[3].gpuaddr >> 2; - *cmd++ = drawctxt->cond_execs[3].gpuaddr >> 2; - *cmd++ = 0x0000FFFF; - *cmd++ = 3; /* EXEC_COUNT */ - *cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2); - drawctxt->constant_save_commands[2].hostptr = cmd; - drawctxt->constant_save_commands[2].gpuaddr = - virt2gpu(cmd, &drawctxt->gpustate); - /* - From fixup: - - dwords = SP_FS_CTRL_REG1.FSCONSTLENGTH / 4 - src = (HLSQ_SHADOW_BASE + 0x2000 + SSIZE) / 4 - - From register spec: - SP_FS_CTRL_REG1.FSCONSTLENGTH [09:00]: 0-512, unit = 128bits. - */ - *cmd++ = 0; /* (dwords << REG_TO_MEM_LOOP_COUNT_SHIFT) | src */ - - /* - From fixup: - - base = drawctxt->gpustate.gpuaddr (ALU constant shadow base) - offset = SP_FS_OBJ_OFFSET_REG.CONSTOBJECTSTARTOFFSET - - From register spec: - SP_FS_OBJ_OFFSET_REG.CONSTOBJECTSTARTOFFSET [16:24]: Constant object - start offset in on chip RAM, - 128bit aligned - - dst = base + offset - Because of the base alignment we can use - dst = base | offset - */ - *cmd++ = 0; /* dst */ - - /* Save VS texture memory objects */ - *cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2); - *cmd++ = - ((TEX_SIZE_MEM_OBJECTS / 4) << REG_TO_MEM_LOOP_COUNT_SHIFT) | - ((HLSQ_SHADOW_BASE + HLSQ_MEMOBJ_OFFSET) / 4); - *cmd++ = - (drawctxt->gpustate.gpuaddr + - VS_TEX_OFFSET_MEM_OBJECTS) & 0xfffffffc; - - /* Save VS texture mipmap pointers */ - *cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2); - *cmd++ = - ((TEX_SIZE_MIPMAP / 4) << REG_TO_MEM_LOOP_COUNT_SHIFT) | - ((HLSQ_SHADOW_BASE + HLSQ_MIPMAP_OFFSET) / 4); - *cmd++ = - (drawctxt->gpustate.gpuaddr + VS_TEX_OFFSET_MIPMAP) & 0xfffffffc; - - /* Save VS texture sampler objects */ - *cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2); - *cmd++ = ((TEX_SIZE_SAMPLER_OBJ / 4) << REG_TO_MEM_LOOP_COUNT_SHIFT) | - ((HLSQ_SHADOW_BASE + HLSQ_SAMPLER_OFFSET) / 4); - *cmd++ = - (drawctxt->gpustate.gpuaddr + - VS_TEX_OFFSET_SAMPLER_OBJ) & 0xfffffffc; - - /* Save FS texture memory objects */ - *cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2); - *cmd++ = - ((TEX_SIZE_MEM_OBJECTS / 4) << REG_TO_MEM_LOOP_COUNT_SHIFT) | - ((HLSQ_SHADOW_BASE + HLSQ_MEMOBJ_OFFSET + SSIZE) / 4); - *cmd++ = - (drawctxt->gpustate.gpuaddr + - FS_TEX_OFFSET_MEM_OBJECTS) & 0xfffffffc; - - /* Save FS texture mipmap pointers */ - *cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2); - *cmd++ = - ((TEX_SIZE_MIPMAP / 4) << REG_TO_MEM_LOOP_COUNT_SHIFT) | - ((HLSQ_SHADOW_BASE + HLSQ_MIPMAP_OFFSET + SSIZE) / 4); - *cmd++ = - (drawctxt->gpustate.gpuaddr + FS_TEX_OFFSET_MIPMAP) & 0xfffffffc; - - /* Save FS texture sampler objects */ - *cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2); - *cmd++ = - ((TEX_SIZE_SAMPLER_OBJ / 4) << REG_TO_MEM_LOOP_COUNT_SHIFT) | - ((HLSQ_SHADOW_BASE + HLSQ_SAMPLER_OFFSET + SSIZE) / 4); - *cmd++ = - (drawctxt->gpustate.gpuaddr + - FS_TEX_OFFSET_SAMPLER_OBJ) & 0xfffffffc; - - /* Create indirect buffer command for above command sequence */ - create_ib1(drawctxt, drawctxt->regconstant_save, start, cmd); - - tmp_ctx.cmd = cmd; -} - -/* Copy GMEM contents to system memory shadow. */ -static unsigned int *build_gmem2sys_cmds(struct adreno_device *adreno_dev, - struct adreno_context *drawctxt, - struct gmem_shadow_t *shadow) -{ - unsigned int *cmds = tmp_ctx.cmd; - unsigned int *start = cmds; - - *cmds++ = cp_type0_packet(A3XX_RBBM_CLOCK_CTL, 1); - *cmds++ = A3XX_RBBM_CLOCK_CTL_DEFAULT; - - *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3); - *cmds++ = CP_REG(A3XX_RB_MODE_CONTROL); - - /* RB_MODE_CONTROL */ - *cmds++ = _SET(RB_MODECONTROL_RENDER_MODE, RB_RESOLVE_PASS) | - _SET(RB_MODECONTROL_MARB_CACHE_SPLIT_MODE, 1) | - _SET(RB_MODECONTROL_PACKER_TIMER_ENABLE, 1); - /* RB_RENDER_CONTROL */ - *cmds++ = _SET(RB_RENDERCONTROL_BIN_WIDTH, shadow->width >> 5) | - _SET(RB_RENDERCONTROL_DISABLE_COLOR_PIPE, 1); - - *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 5); - *cmds++ = CP_REG(A3XX_RB_COPY_CONTROL); - /* RB_COPY_CONTROL */ - *cmds++ = _SET(RB_COPYCONTROL_RESOLVE_CLEAR_MODE, - RB_CLEAR_MODE_RESOLVE) | - _SET(RB_COPYCONTROL_COPY_GMEM_BASE, - tmp_ctx.gmem_base >> 14); - /* RB_COPY_DEST_BASE */ - *cmds++ = _SET(RB_COPYDESTBASE_COPY_DEST_BASE, - shadow->gmemshadow.gpuaddr >> 5); - /* RB_COPY_DEST_PITCH */ - *cmds++ = _SET(RB_COPYDESTPITCH_COPY_DEST_PITCH, - (shadow->pitch * 4) / 32); - /* RB_COPY_DEST_INFO */ - *cmds++ = _SET(RB_COPYDESTINFO_COPY_DEST_TILE, - RB_TILINGMODE_LINEAR) | - _SET(RB_COPYDESTINFO_COPY_DEST_FORMAT, RB_R8G8B8A8_UNORM) | - _SET(RB_COPYDESTINFO_COPY_COMPONENT_ENABLE, 0X0F) | - _SET(RB_COPYDESTINFO_COPY_DEST_ENDIAN, RB_ENDIAN_NONE); - - *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); - *cmds++ = CP_REG(A3XX_GRAS_SC_CONTROL); - /* GRAS_SC_CONTROL */ - *cmds++ = _SET(GRAS_SC_CONTROL_RENDER_MODE, 2); - - *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3); - *cmds++ = CP_REG(A3XX_VFD_CONTROL_0); - /* VFD_CONTROL_0 */ - *cmds++ = _SET(VFD_CTRLREG0_TOTALATTRTOVS, 4) | - _SET(VFD_CTRLREG0_PACKETSIZE, 2) | - _SET(VFD_CTRLREG0_STRMDECINSTRCNT, 1) | - _SET(VFD_CTRLREG0_STRMFETCHINSTRCNT, 1); - /* VFD_CONTROL_1 */ - *cmds++ = _SET(VFD_CTRLREG1_MAXSTORAGE, 1) | - _SET(VFD_CTRLREG1_REGID4VTX, 252) | - _SET(VFD_CTRLREG1_REGID4INST, 252); - - *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3); - *cmds++ = CP_REG(A3XX_VFD_FETCH_INSTR_0_0); - /* VFD_FETCH_INSTR_0_0 */ - *cmds++ = _SET(VFD_FETCHINSTRUCTIONS_FETCHSIZE, 11) | - _SET(VFD_FETCHINSTRUCTIONS_BUFSTRIDE, 12) | - _SET(VFD_FETCHINSTRUCTIONS_STEPRATE, 1); - /* VFD_FETCH_INSTR_1_0 */ - *cmds++ = _SET(VFD_BASEADDR_BASEADDR, - shadow->quad_vertices.gpuaddr); - - *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); - *cmds++ = CP_REG(A3XX_VFD_DECODE_INSTR_0); - /* VFD_DECODE_INSTR_0 */ - *cmds++ = _SET(VFD_DECODEINSTRUCTIONS_WRITEMASK, 0x0F) | - _SET(VFD_DECODEINSTRUCTIONS_CONSTFILL, 1) | - _SET(VFD_DECODEINSTRUCTIONS_FORMAT, 2) | - _SET(VFD_DECODEINSTRUCTIONS_SHIFTCNT, 12) | - _SET(VFD_DECODEINSTRUCTIONS_LASTCOMPVALID, 1); - - *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 5); - *cmds++ = CP_REG(A3XX_HLSQ_CONTROL_0_REG); - /* HLSQ_CONTROL_0_REG */ - *cmds++ = _SET(HLSQ_CTRL0REG_FSTHREADSIZE, HLSQ_FOUR_PIX_QUADS) | - _SET(HLSQ_CTRL0REG_FSSUPERTHREADENABLE, 1) | - _SET(HLSQ_CTRL0REG_RESERVED2, 1) | - _SET(HLSQ_CTRL0REG_SPCONSTFULLUPDATE, 1); - /* HLSQ_CONTROL_1_REG */ - *cmds++ = _SET(HLSQ_CTRL1REG_VSTHREADSIZE, HLSQ_TWO_VTX_QUADS) | - _SET(HLSQ_CTRL1REG_VSSUPERTHREADENABLE, 1); - /* HLSQ_CONTROL_2_REG */ - *cmds++ = _SET(HLSQ_CTRL2REG_PRIMALLOCTHRESHOLD, 31); - /* HLSQ_CONTROL_3_REG */ - *cmds++ = 0x00000000; - - *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 5); - *cmds++ = CP_REG(A3XX_HLSQ_VS_CONTROL_REG); - /* HLSQ_VS_CONTROL_REG */ - *cmds++ = _SET(HLSQ_VSCTRLREG_VSINSTRLENGTH, 1); - /* HLSQ_FS_CONTROL_REG */ - *cmds++ = _SET(HLSQ_FSCTRLREG_FSCONSTLENGTH, 1) | - _SET(HLSQ_FSCTRLREG_FSCONSTSTARTOFFSET, 128) | - _SET(HLSQ_FSCTRLREG_FSINSTRLENGTH, 1); - /* HLSQ_CONST_VSPRESV_RANGE_REG */ - *cmds++ = 0x00000000; - /* HLSQ_CONST_FSPRESV_RANGE_REQ */ - *cmds++ = _SET(HLSQ_CONSTFSPRESERVEDRANGEREG_STARTENTRY, 32) | - _SET(HLSQ_CONSTFSPRESERVEDRANGEREG_ENDENTRY, 32); - - *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); - *cmds++ = CP_REG(A3XX_SP_FS_LENGTH_REG); - /* SP_FS_LENGTH_REG */ - *cmds++ = _SET(SP_SHADERLENGTH_LEN, 1); - - *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); - *cmds++ = CP_REG(A3XX_SP_SP_CTRL_REG); - /* SP_SP_CTRL_REG */ - *cmds++ = _SET(SP_SPCTRLREG_SLEEPMODE, 1) | - _SET(SP_SPCTRLREG_LOMODE, 1); - - *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 12); - *cmds++ = CP_REG(A3XX_SP_VS_CTRL_REG0); - /* SP_VS_CTRL_REG0 */ - *cmds++ = _SET(SP_VSCTRLREG0_VSTHREADMODE, SP_MULTI) | - _SET(SP_VSCTRLREG0_VSINSTRBUFFERMODE, SP_BUFFER_MODE) | - _SET(SP_VSCTRLREG0_VSICACHEINVALID, 1) | - _SET(SP_VSCTRLREG0_VSFULLREGFOOTPRINT, 1) | - _SET(SP_VSCTRLREG0_VSTHREADSIZE, SP_TWO_VTX_QUADS) | - _SET(SP_VSCTRLREG0_VSSUPERTHREADMODE, 1) | - _SET(SP_VSCTRLREG0_VSLENGTH, 1); - /* SP_VS_CTRL_REG1 */ - *cmds++ = _SET(SP_VSCTRLREG1_VSINITIALOUTSTANDING, 4); - /* SP_VS_PARAM_REG */ - *cmds++ = _SET(SP_VSPARAMREG_PSIZEREGID, 252); - /* SP_VS_OUT_REG_0 */ - *cmds++ = 0x00000000; - /* SP_VS_OUT_REG_1 */ - *cmds++ = 0x00000000; - /* SP_VS_OUT_REG_2 */ - *cmds++ = 0x00000000; - /* SP_VS_OUT_REG_3 */ - *cmds++ = 0x00000000; - /* SP_VS_OUT_REG_4 */ - *cmds++ = 0x00000000; - /* SP_VS_OUT_REG_5 */ - *cmds++ = 0x00000000; - /* SP_VS_OUT_REG_6 */ - *cmds++ = 0x00000000; - /* SP_VS_OUT_REG_7 */ - *cmds++ = 0x00000000; - - *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 7); - *cmds++ = CP_REG(A3XX_SP_VS_VPC_DST_REG_0); - /* SP_VS_VPC_DST_REG_0 */ - *cmds++ = 0x00000000; - /* SP_VS_VPC_DST_REG_1 */ - *cmds++ = 0x00000000; - /* SP_VS_VPC_DST_REG_2 */ - *cmds++ = 0x00000000; - /* SP_VS_VPC_DST_REG_3 */ - *cmds++ = 0x00000000; - /* SP_VS_OBJ_OFFSET_REG */ - *cmds++ = 0x00000000; - /* SP_VS_OBJ_START_REG */ - *cmds++ = 0x00000000; - - *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 6); - *cmds++ = CP_REG(A3XX_SP_VS_LENGTH_REG); - /* SP_VS_LENGTH_REG */ - *cmds++ = _SET(SP_SHADERLENGTH_LEN, 1); - /* SP_FS_CTRL_REG0 */ - *cmds++ = _SET(SP_FSCTRLREG0_FSTHREADMODE, SP_MULTI) | - _SET(SP_FSCTRLREG0_FSINSTRBUFFERMODE, SP_BUFFER_MODE) | - _SET(SP_FSCTRLREG0_FSICACHEINVALID, 1) | - _SET(SP_FSCTRLREG0_FSHALFREGFOOTPRINT, 1) | - _SET(SP_FSCTRLREG0_FSINOUTREGOVERLAP, 1) | - _SET(SP_FSCTRLREG0_FSTHREADSIZE, SP_FOUR_PIX_QUADS) | - _SET(SP_FSCTRLREG0_FSSUPERTHREADMODE, 1) | - _SET(SP_FSCTRLREG0_FSLENGTH, 1); - /* SP_FS_CTRL_REG1 */ - *cmds++ = _SET(SP_FSCTRLREG1_FSCONSTLENGTH, 1) | - _SET(SP_FSCTRLREG1_HALFPRECVAROFFSET, 63); - /* SP_FS_OBJ_OFFSET_REG */ - *cmds++ = _SET(SP_OBJOFFSETREG_CONSTOBJECTSTARTOFFSET, 128) | - _SET(SP_OBJOFFSETREG_SHADEROBJOFFSETINIC, 127); - /* SP_FS_OBJ_START_REG */ - *cmds++ = 0x00000000; - - *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3); - *cmds++ = CP_REG(A3XX_SP_FS_FLAT_SHAD_MODE_REG_0); - /* SP_FS_FLAT_SHAD_MODE_REG_0 */ - *cmds++ = 0x00000000; - /* SP_FS_FLAT_SHAD_MODE_REG_1 */ - *cmds++ = 0x00000000; - - *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); - *cmds++ = CP_REG(A3XX_SP_FS_OUTPUT_REG); - /* SP_FS_OUTPUT_REG */ - *cmds++ = _SET(SP_IMAGEOUTPUTREG_DEPTHOUTMODE, SP_PIXEL_BASED); - - *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 5); - *cmds++ = CP_REG(A3XX_SP_FS_MRT_REG_0); - /* SP_FS_MRT_REG_0 */ - *cmds++ = _SET(SP_FSMRTREG_PRECISION, 1); - - /* SP_FS_MRT_REG_1 */ - *cmds++ = 0x00000000; - /* SP_FS_MRT_REG_2 */ - *cmds++ = 0x00000000; - /* SP_FS_MRT_REG_3 */ - *cmds++ = 0x00000000; - - *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 11); - *cmds++ = CP_REG(A3XX_VPC_ATTR); - /* VPC_ATTR */ - *cmds++ = _SET(VPC_VPCATTR_THRHDASSIGN, 1) | - _SET(VPC_VPCATTR_LMSIZE, 1); - /* VPC_PACK */ - *cmds++ = 0x00000000; - /* VPC_VARRYING_INTERUPT_MODE_0 */ - *cmds++ = 0x00000000; - /* VPC_VARRYING_INTERUPT_MODE_1 */ - *cmds++ = 0x00000000; - /* VPC_VARRYING_INTERUPT_MODE_2 */ - *cmds++ = 0x00000000; - /* VPC_VARRYING_INTERUPT_MODE_3 */ - *cmds++ = 0x00000000; - /* VPC_VARYING_PS_REPL_MODE_0 */ - *cmds++ = 0x00000000; - /* VPC_VARYING_PS_REPL_MODE_1 */ - *cmds++ = 0x00000000; - /* VPC_VARYING_PS_REPL_MODE_2 */ - *cmds++ = 0x00000000; - /* VPC_VARYING_PS_REPL_MODE_3 */ - *cmds++ = 0x00000000; - - *cmds++ = cp_type3_packet(CP_LOAD_STATE, 10); - *cmds++ = (0 << CP_LOADSTATE_DSTOFFSET_SHIFT) - | (HLSQ_DIRECT << CP_LOADSTATE_STATESRC_SHIFT) - | (HLSQ_BLOCK_ID_SP_VS << CP_LOADSTATE_STATEBLOCKID_SHIFT) - | (1 << CP_LOADSTATE_NUMOFUNITS_SHIFT); - *cmds++ = (HLSQ_SP_VS_INSTR << CP_LOADSTATE_STATETYPE_SHIFT) - | (0 << CP_LOADSTATE_EXTSRCADDR_SHIFT); - - /* (sy)(rpt3)mov.f32f32 r0.y, (r)r1.y; */ - *cmds++ = 0x00000000; *cmds++ = 0x13001000; - /* end; */ - *cmds++ = 0x00000000; *cmds++ = 0x00000000; - /* nop; */ - *cmds++ = 0x00000000; *cmds++ = 0x00000000; - /* nop; */ - *cmds++ = 0x00000000; *cmds++ = 0x00000000; - - - *cmds++ = cp_type0_packet(A3XX_VFD_PERFCOUNTER0_SELECT, 1); - *cmds++ = 0x00000000; - - *cmds++ = cp_type3_packet(CP_WAIT_FOR_IDLE, 1); - *cmds++ = 0x00000000; - - *cmds++ = cp_type3_packet(CP_LOAD_STATE, 10); - *cmds++ = (0 << CP_LOADSTATE_DSTOFFSET_SHIFT) - | (HLSQ_DIRECT << CP_LOADSTATE_STATESRC_SHIFT) - | (HLSQ_BLOCK_ID_SP_FS << CP_LOADSTATE_STATEBLOCKID_SHIFT) - | (1 << CP_LOADSTATE_NUMOFUNITS_SHIFT); - *cmds++ = (HLSQ_SP_FS_INSTR << CP_LOADSTATE_STATETYPE_SHIFT) - | (0 << CP_LOADSTATE_EXTSRCADDR_SHIFT); - - /* (sy)(rpt3)mov.f32f32 r0.y, (r)c0.x; */ - *cmds++ = 0x00000000; *cmds++ = 0x30201b00; - /* end; */ - *cmds++ = 0x00000000; *cmds++ = 0x03000000; - /* nop; */ - *cmds++ = 0x00000000; *cmds++ = 0x00000000; - /* nop; */ - *cmds++ = 0x00000000; *cmds++ = 0x00000000; - - - - *cmds++ = cp_type0_packet(A3XX_VFD_PERFCOUNTER0_SELECT, 1); - *cmds++ = 0x00000000; - - *cmds++ = cp_type3_packet(CP_WAIT_FOR_IDLE, 1); - *cmds++ = 0x00000000; - - - *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); - *cmds++ = CP_REG(A3XX_RB_MSAA_CONTROL); - /* RB_MSAA_CONTROL */ - *cmds++ = _SET(RB_MSAACONTROL_MSAA_DISABLE, 1) | - _SET(RB_MSAACONTROL_SAMPLE_MASK, 0xFFFF); - - *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); - *cmds++ = CP_REG(A3XX_RB_DEPTH_CONTROL); - /* RB_DEPTH_CONTROL */ - *cmds++ = _SET(RB_DEPTHCONTROL_Z_TEST_FUNC, RB_FRAG_NEVER); - - *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); - *cmds++ = CP_REG(A3XX_RB_STENCIL_CONTROL); - /* RB_STENCIL_CONTROL */ - *cmds++ = _SET(RB_STENCILCONTROL_STENCIL_FUNC, RB_REF_NEVER) | - _SET(RB_STENCILCONTROL_STENCIL_FAIL, RB_STENCIL_KEEP) | - _SET(RB_STENCILCONTROL_STENCIL_ZPASS, RB_STENCIL_KEEP) | - _SET(RB_STENCILCONTROL_STENCIL_ZFAIL, RB_STENCIL_KEEP) | - _SET(RB_STENCILCONTROL_STENCIL_FUNC_BF, RB_REF_NEVER) | - _SET(RB_STENCILCONTROL_STENCIL_FAIL_BF, RB_STENCIL_KEEP) | - _SET(RB_STENCILCONTROL_STENCIL_ZPASS_BF, RB_STENCIL_KEEP) | - _SET(RB_STENCILCONTROL_STENCIL_ZFAIL_BF, RB_STENCIL_KEEP); - - *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); - *cmds++ = CP_REG(A3XX_GRAS_SU_MODE_CONTROL); - /* GRAS_SU_MODE_CONTROL */ - *cmds++ = 0x00000000; - - *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); - *cmds++ = CP_REG(A3XX_RB_MRT_CONTROL0); - /* RB_MRT_CONTROL0 */ - *cmds++ = _SET(RB_MRTCONTROL_READ_DEST_ENABLE, 1) | - _SET(RB_MRTCONTROL_ROP_CODE, 12) | - _SET(RB_MRTCONTROL_DITHER_MODE, RB_DITHER_ALWAYS) | - _SET(RB_MRTCONTROL_COMPONENT_ENABLE, 0xF); - - *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3); - *cmds++ = CP_REG(A3XX_RB_MRT_BLEND_CONTROL0); - /* RB_MRT_BLEND_CONTROL0 */ - *cmds++ = _SET(RB_MRTBLENDCONTROL_RGB_SRC_FACTOR, RB_FACTOR_ONE) | - _SET(RB_MRTBLENDCONTROL_RGB_BLEND_OPCODE, RB_BLEND_OP_ADD) | - _SET(RB_MRTBLENDCONTROL_RGB_DEST_FACTOR, RB_FACTOR_ZERO) | - _SET(RB_MRTBLENDCONTROL_ALPHA_SRC_FACTOR, RB_FACTOR_ONE) | - _SET(RB_MRTBLENDCONTROL_ALPHA_DEST_FACTOR, RB_FACTOR_ZERO) | - _SET(RB_MRTBLENDCONTROL_CLAMP_ENABLE, 1); - /* RB_MRT_CONTROL1 */ - *cmds++ = _SET(RB_MRTCONTROL_READ_DEST_ENABLE, 1) | - _SET(RB_MRTCONTROL_DITHER_MODE, RB_DITHER_DISABLE) | - _SET(RB_MRTCONTROL_COMPONENT_ENABLE, 0xF); - - *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3); - *cmds++ = CP_REG(A3XX_RB_MRT_BLEND_CONTROL1); - /* RB_MRT_BLEND_CONTROL1 */ - *cmds++ = _SET(RB_MRTBLENDCONTROL_RGB_SRC_FACTOR, RB_FACTOR_ONE) | - _SET(RB_MRTBLENDCONTROL_RGB_BLEND_OPCODE, RB_BLEND_OP_ADD) | - _SET(RB_MRTBLENDCONTROL_RGB_DEST_FACTOR, RB_FACTOR_ZERO) | - _SET(RB_MRTBLENDCONTROL_ALPHA_SRC_FACTOR, RB_FACTOR_ONE) | - _SET(RB_MRTBLENDCONTROL_ALPHA_DEST_FACTOR, RB_FACTOR_ZERO) | - _SET(RB_MRTBLENDCONTROL_CLAMP_ENABLE, 1); - /* RB_MRT_CONTROL2 */ - *cmds++ = _SET(RB_MRTCONTROL_READ_DEST_ENABLE, 1) | - _SET(RB_MRTCONTROL_DITHER_MODE, RB_DITHER_DISABLE) | - _SET(RB_MRTCONTROL_COMPONENT_ENABLE, 0xF); - - *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3); - *cmds++ = CP_REG(A3XX_RB_MRT_BLEND_CONTROL2); - /* RB_MRT_BLEND_CONTROL2 */ - *cmds++ = _SET(RB_MRTBLENDCONTROL_RGB_SRC_FACTOR, RB_FACTOR_ONE) | - _SET(RB_MRTBLENDCONTROL_RGB_BLEND_OPCODE, RB_BLEND_OP_ADD) | - _SET(RB_MRTBLENDCONTROL_RGB_DEST_FACTOR, RB_FACTOR_ZERO) | - _SET(RB_MRTBLENDCONTROL_ALPHA_SRC_FACTOR, RB_FACTOR_ONE) | - _SET(RB_MRTBLENDCONTROL_ALPHA_DEST_FACTOR, RB_FACTOR_ZERO) | - _SET(RB_MRTBLENDCONTROL_CLAMP_ENABLE, 1); - /* RB_MRT_CONTROL3 */ - *cmds++ = _SET(RB_MRTCONTROL_READ_DEST_ENABLE, 1) | - _SET(RB_MRTCONTROL_DITHER_MODE, RB_DITHER_DISABLE) | - _SET(RB_MRTCONTROL_COMPONENT_ENABLE, 0xF); - - *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); - *cmds++ = CP_REG(A3XX_RB_MRT_BLEND_CONTROL3); - /* RB_MRT_BLEND_CONTROL3 */ - *cmds++ = _SET(RB_MRTBLENDCONTROL_RGB_SRC_FACTOR, RB_FACTOR_ONE) | - _SET(RB_MRTBLENDCONTROL_RGB_BLEND_OPCODE, RB_BLEND_OP_ADD) | - _SET(RB_MRTBLENDCONTROL_RGB_DEST_FACTOR, RB_FACTOR_ZERO) | - _SET(RB_MRTBLENDCONTROL_ALPHA_SRC_FACTOR, RB_FACTOR_ONE) | - _SET(RB_MRTBLENDCONTROL_ALPHA_DEST_FACTOR, RB_FACTOR_ZERO) | - _SET(RB_MRTBLENDCONTROL_CLAMP_ENABLE, 1); - - *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 5); - *cmds++ = CP_REG(A3XX_VFD_INDEX_MIN); - /* VFD_INDEX_MIN */ - *cmds++ = 0x00000000; - /* VFD_INDEX_MAX */ - *cmds++ = 0x155; - /* VFD_INSTANCEID_OFFSET */ - *cmds++ = 0x00000000; - /* VFD_INDEX_OFFSET */ - *cmds++ = 0x00000000; - - *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); - *cmds++ = CP_REG(A3XX_VFD_VS_THREADING_THRESHOLD); - /* VFD_VS_THREADING_THRESHOLD */ - *cmds++ = _SET(VFD_THREADINGTHRESHOLD_REGID_THRESHOLD, 15) | - _SET(VFD_THREADINGTHRESHOLD_REGID_VTXCNT, 252); - - *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); - *cmds++ = CP_REG(A3XX_TPL1_TP_VS_TEX_OFFSET); - /* TPL1_TP_VS_TEX_OFFSET */ - *cmds++ = 0; - - *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); - *cmds++ = CP_REG(A3XX_TPL1_TP_FS_TEX_OFFSET); - /* TPL1_TP_FS_TEX_OFFSET */ - *cmds++ = _SET(TPL1_TPTEXOFFSETREG_SAMPLEROFFSET, 16) | - _SET(TPL1_TPTEXOFFSETREG_MEMOBJOFFSET, 16) | - _SET(TPL1_TPTEXOFFSETREG_BASETABLEPTR, 224); - - *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); - *cmds++ = CP_REG(A3XX_PC_PRIM_VTX_CNTL); - /* PC_PRIM_VTX_CNTL */ - *cmds++ = _SET(PC_PRIM_VTX_CONTROL_POLYMODE_FRONT_PTYPE, - PC_DRAW_TRIANGLES) | - _SET(PC_PRIM_VTX_CONTROL_POLYMODE_BACK_PTYPE, - PC_DRAW_TRIANGLES) | - _SET(PC_PRIM_VTX_CONTROL_PROVOKING_VTX_LAST, 1); - - *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3); - *cmds++ = CP_REG(A3XX_GRAS_SC_WINDOW_SCISSOR_TL); - /* GRAS_SC_WINDOW_SCISSOR_TL */ - *cmds++ = 0x00000000; - /* GRAS_SC_WINDOW_SCISSOR_BR */ - *cmds++ = _SET(GRAS_SC_WINDOW_SCISSOR_BR_BR_X, shadow->width - 1) | - _SET(GRAS_SC_WINDOW_SCISSOR_BR_BR_Y, shadow->height - 1); - - *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3); - *cmds++ = CP_REG(A3XX_GRAS_SC_SCREEN_SCISSOR_TL); - /* GRAS_SC_SCREEN_SCISSOR_TL */ - *cmds++ = 0x00000000; - /* GRAS_SC_SCREEN_SCISSOR_BR */ - *cmds++ = _SET(GRAS_SC_SCREEN_SCISSOR_BR_BR_X, shadow->width - 1) | - _SET(GRAS_SC_SCREEN_SCISSOR_BR_BR_Y, shadow->height - 1); - - *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 5); - *cmds++ = CP_REG(A3XX_GRAS_CL_VPORT_XOFFSET); - /* GRAS_CL_VPORT_XOFFSET */ - *cmds++ = 0x00000000; - /* GRAS_CL_VPORT_XSCALE */ - *cmds++ = _SET(GRAS_CL_VPORT_XSCALE_VPORT_XSCALE, 0x3f800000); - /* GRAS_CL_VPORT_YOFFSET */ - *cmds++ = 0x00000000; - /* GRAS_CL_VPORT_YSCALE */ - *cmds++ = _SET(GRAS_CL_VPORT_YSCALE_VPORT_YSCALE, 0x3f800000); - - *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3); - *cmds++ = CP_REG(A3XX_GRAS_CL_VPORT_ZOFFSET); - /* GRAS_CL_VPORT_ZOFFSET */ - *cmds++ = 0x00000000; - /* GRAS_CL_VPORT_ZSCALE */ - *cmds++ = _SET(GRAS_CL_VPORT_ZSCALE_VPORT_ZSCALE, 0x3f800000); - - *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); - *cmds++ = CP_REG(A3XX_GRAS_CL_CLIP_CNTL); - /* GRAS_CL_CLIP_CNTL */ - *cmds++ = _SET(GRAS_CL_CLIP_CNTL_CLIP_DISABLE, 1) | - _SET(GRAS_CL_CLIP_CNTL_ZFAR_CLIP_DISABLE, 1) | - _SET(GRAS_CL_CLIP_CNTL_VP_CLIP_CODE_IGNORE, 1) | - _SET(GRAS_CL_CLIP_CNTL_VP_XFORM_DISABLE, 1) | - _SET(GRAS_CL_CLIP_CNTL_PERSP_DIVISION_DISABLE, 1); - - *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); - *cmds++ = CP_REG(A3XX_GRAS_CL_GB_CLIP_ADJ); - /* GRAS_CL_GB_CLIP_ADJ */ - *cmds++ = 0x00000000; - - *cmds++ = cp_type3_packet(CP_WAIT_FOR_IDLE, 1); - *cmds++ = 0x00000000; - - - /* oxili_generate_context_roll_packets */ - *cmds++ = cp_type0_packet(A3XX_SP_VS_CTRL_REG0, 1); - *cmds++ = 0x00000400; - - *cmds++ = cp_type0_packet(A3XX_SP_FS_CTRL_REG0, 1); - *cmds++ = 0x00000400; - - *cmds++ = cp_type0_packet(A3XX_SP_VS_PVT_MEM_SIZE_REG, 1); - *cmds++ = 0x00008000; /* SP_VS_MEM_SIZE_REG */ - - *cmds++ = cp_type0_packet(A3XX_SP_FS_PVT_MEM_SIZE_REG, 1); - *cmds++ = 0x00008000; /* SP_FS_MEM_SIZE_REG */ - - /* Clear cache invalidate bit when re-loading the shader control regs */ - *cmds++ = cp_type0_packet(A3XX_SP_VS_CTRL_REG0, 1); - *cmds++ = _SET(SP_VSCTRLREG0_VSTHREADMODE, SP_MULTI) | - _SET(SP_VSCTRLREG0_VSINSTRBUFFERMODE, SP_BUFFER_MODE) | - _SET(SP_VSCTRLREG0_VSFULLREGFOOTPRINT, 1) | - _SET(SP_VSCTRLREG0_VSTHREADSIZE, SP_TWO_VTX_QUADS) | - _SET(SP_VSCTRLREG0_VSSUPERTHREADMODE, 1) | - _SET(SP_VSCTRLREG0_VSLENGTH, 1); - - *cmds++ = cp_type0_packet(A3XX_SP_FS_CTRL_REG0, 1); - *cmds++ = _SET(SP_FSCTRLREG0_FSTHREADMODE, SP_MULTI) | - _SET(SP_FSCTRLREG0_FSINSTRBUFFERMODE, SP_BUFFER_MODE) | - _SET(SP_FSCTRLREG0_FSHALFREGFOOTPRINT, 1) | - _SET(SP_FSCTRLREG0_FSINOUTREGOVERLAP, 1) | - _SET(SP_FSCTRLREG0_FSTHREADSIZE, SP_FOUR_PIX_QUADS) | - _SET(SP_FSCTRLREG0_FSSUPERTHREADMODE, 1) | - _SET(SP_FSCTRLREG0_FSLENGTH, 1); - - *cmds++ = cp_type0_packet(A3XX_SP_VS_PVT_MEM_SIZE_REG, 1); - *cmds++ = 0x00000000; /* SP_VS_MEM_SIZE_REG */ - - *cmds++ = cp_type0_packet(A3XX_SP_FS_PVT_MEM_SIZE_REG, 1); - *cmds++ = 0x00000000; /* SP_FS_MEM_SIZE_REG */ - - /* end oxili_generate_context_roll_packets */ - - /* - * Resolve using two draw calls with a dummy register - * write in between. This is a HLM workaround - * that should be removed later. - */ - *cmds++ = cp_type3_packet(CP_DRAW_INDX_2, 6); - *cmds++ = 0x00000000; /* Viz query info */ - *cmds++ = BUILD_PC_DRAW_INITIATOR(PC_DI_PT_TRILIST, - PC_DI_SRC_SEL_IMMEDIATE, - PC_DI_INDEX_SIZE_32_BIT, - PC_DI_IGNORE_VISIBILITY); - *cmds++ = 0x00000003; /* Num indices */ - *cmds++ = 0x00000000; /* Index 0 */ - *cmds++ = 0x00000001; /* Index 1 */ - *cmds++ = 0x00000002; /* Index 2 */ - - *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); - *cmds++ = CP_REG(A3XX_HLSQ_CL_CONTROL_0_REG); - *cmds++ = 0x00000000; - - *cmds++ = cp_type3_packet(CP_DRAW_INDX_2, 6); - *cmds++ = 0x00000000; /* Viz query info */ - *cmds++ = BUILD_PC_DRAW_INITIATOR(PC_DI_PT_TRILIST, - PC_DI_SRC_SEL_IMMEDIATE, - PC_DI_INDEX_SIZE_32_BIT, - PC_DI_IGNORE_VISIBILITY); - *cmds++ = 0x00000003; /* Num indices */ - *cmds++ = 0x00000002; /* Index 0 */ - *cmds++ = 0x00000001; /* Index 1 */ - *cmds++ = 0x00000003; /* Index 2 */ - - *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); - *cmds++ = CP_REG(A3XX_HLSQ_CL_CONTROL_0_REG); - *cmds++ = 0x00000000; - - *cmds++ = cp_type3_packet(CP_WAIT_FOR_IDLE, 1); - *cmds++ = 0x00000000; - - /* Create indirect buffer command for above command sequence */ - create_ib1(drawctxt, shadow->gmem_save, start, cmds); - - return cmds; -} -static void build_shader_save_cmds(struct adreno_device *adreno_dev, - struct adreno_context *drawctxt) -{ - unsigned int *cmd = tmp_ctx.cmd; - unsigned int *start; - - /* Reserve space for boolean values used for COND_EXEC packet */ - drawctxt->cond_execs[0].hostptr = cmd; - drawctxt->cond_execs[0].gpuaddr = virt2gpu(cmd, &drawctxt->gpustate); - *cmd++ = 0; - drawctxt->cond_execs[1].hostptr = cmd; - drawctxt->cond_execs[1].gpuaddr = virt2gpu(cmd, &drawctxt->gpustate); - *cmd++ = 0; - - drawctxt->shader_save_commands[0].hostptr = cmd; - drawctxt->shader_save_commands[0].gpuaddr = - virt2gpu(cmd, &drawctxt->gpustate); - *cmd++ = 0; - drawctxt->shader_save_commands[1].hostptr = cmd; - drawctxt->shader_save_commands[1].gpuaddr = - virt2gpu(cmd, &drawctxt->gpustate); - *cmd++ = 0; - - start = cmd; - - /* Save vertex shader */ - - *cmd++ = cp_type3_packet(CP_COND_EXEC, 4); - *cmd++ = drawctxt->cond_execs[0].gpuaddr >> 2; - *cmd++ = drawctxt->cond_execs[0].gpuaddr >> 2; - *cmd++ = 0x0000FFFF; - *cmd++ = 3; /* EXEC_COUNT */ - - *cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2); - drawctxt->shader_save_commands[2].hostptr = cmd; - drawctxt->shader_save_commands[2].gpuaddr = - virt2gpu(cmd, &drawctxt->gpustate); - /* - From fixup: - - dwords = SP_VS_CTRL_REG0.VS_LENGTH * 8 - - From regspec: - SP_VS_CTRL_REG0.VS_LENGTH [31:24]: VS length, unit = 256bits. - If bit31 is 1, it means overflow - or any long shader. - - src = (HLSQ_SHADOW_BASE + 0x1000)/4 - */ - *cmd++ = 0; /*(dwords << REG_TO_MEM_LOOP_COUNT_SHIFT) | src */ - *cmd++ = (drawctxt->gpustate.gpuaddr + SHADER_OFFSET) & 0xfffffffc; - - /* Save fragment shader */ - *cmd++ = cp_type3_packet(CP_COND_EXEC, 4); - *cmd++ = drawctxt->cond_execs[1].gpuaddr >> 2; - *cmd++ = drawctxt->cond_execs[1].gpuaddr >> 2; - *cmd++ = 0x0000FFFF; - *cmd++ = 3; /* EXEC_COUNT */ - - *cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2); - drawctxt->shader_save_commands[3].hostptr = cmd; - drawctxt->shader_save_commands[3].gpuaddr = - virt2gpu(cmd, &drawctxt->gpustate); - /* - From fixup: - - dwords = SP_FS_CTRL_REG0.FS_LENGTH * 8 - - From regspec: - SP_FS_CTRL_REG0.FS_LENGTH [31:24]: FS length, unit = 256bits. - If bit31 is 1, it means overflow - or any long shader. - - fs_offset = SP_FS_OBJ_OFFSET_REG.SHADEROBJOFFSETINIC * 32 - From regspec: - - SP_FS_OBJ_OFFSET_REG.SHADEROBJOFFSETINIC [31:25]: - First instruction of the whole shader will be stored from - the offset in instruction cache, unit = 256bits, a cache line. - It can start from 0 if no VS available. - - src = (HLSQ_SHADOW_BASE + 0x1000 + SSIZE + fs_offset)/4 - */ - *cmd++ = 0; /*(dwords << REG_TO_MEM_LOOP_COUNT_SHIFT) | src */ - *cmd++ = (drawctxt->gpustate.gpuaddr + SHADER_OFFSET - + (SHADER_SHADOW_SIZE / 2)) & 0xfffffffc; - - /* Create indirect buffer command for above command sequence */ - create_ib1(drawctxt, drawctxt->shader_save, start, cmd); - - tmp_ctx.cmd = cmd; -} - -/* - * Make an IB to modify context save IBs with the correct shader instruction - * and constant sizes and offsets. - */ - -static void build_save_fixup_cmds(struct adreno_device *adreno_dev, - struct adreno_context *drawctxt) -{ - unsigned int *cmd = tmp_ctx.cmd; - unsigned int *start = cmd; - - /* Flush HLSQ lazy updates */ - *cmd++ = cp_type3_packet(CP_EVENT_WRITE, 1); - *cmd++ = 0x7; /* HLSQ_FLUSH */ - *cmd++ = cp_type3_packet(CP_WAIT_FOR_IDLE, 1); - *cmd++ = 0; - - *cmd++ = cp_type0_packet(A3XX_UCHE_CACHE_INVALIDATE0_REG, 2); - *cmd++ = 0x00000000; /* No start addr for full invalidate */ - *cmd++ = (unsigned int) - UCHE_ENTIRE_CACHE << UCHE_INVALIDATE1REG_ALLORPORTION | - UCHE_OP_INVALIDATE << UCHE_INVALIDATE1REG_OPCODE | - 0; /* No end addr for full invalidate */ - - /* Make sure registers are flushed */ - *cmd++ = cp_type3_packet(CP_CONTEXT_UPDATE, 1); - *cmd++ = 0; - -#ifdef GSL_CONTEXT_SWITCH_CPU_SYNC - - /* Save shader sizes */ - *cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2); - *cmd++ = A3XX_SP_VS_CTRL_REG0; - *cmd++ = drawctxt->shader_save_commands[2].gpuaddr; - - *cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2); - *cmd++ = A3XX_SP_FS_CTRL_REG0; - *cmd++ = drawctxt->shader_save_commands[3].gpuaddr; - - /* Save shader offsets */ - *cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2); - *cmd++ = A3XX_SP_FS_OBJ_OFFSET_REG; - *cmd++ = drawctxt->shader_save_commands[1].gpuaddr; - - /* Save constant sizes */ - *cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2); - *cmd++ = A3XX_SP_VS_CTRL_REG1; - *cmd++ = drawctxt->constant_save_commands[1].gpuaddr; - *cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2); - *cmd++ = A3XX_SP_FS_CTRL_REG1; - *cmd++ = drawctxt->constant_save_commands[2].gpuaddr; - - /* Save FS constant offset */ - *cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2); - *cmd++ = A3XX_SP_FS_OBJ_OFFSET_REG; - *cmd++ = drawctxt->constant_save_commands[0].gpuaddr; - - - /* Save VS instruction store mode */ - *cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2); - *cmd++ = A3XX_SP_VS_CTRL_REG0; - *cmd++ = drawctxt->cond_execs[0].gpuaddr; - - /* Save FS instruction store mode */ - *cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2); - *cmd++ = A3XX_SP_FS_CTRL_REG0; - *cmd++ = drawctxt->cond_execs[1].gpuaddr; -#else - - /* Shader save */ - cmd = rmw_regtomem(cmd, A3XX_SP_VS_CTRL_REG0, 0x7f000000, - 11+REG_TO_MEM_LOOP_COUNT_SHIFT, - (HLSQ_SHADOW_BASE + 0x1000) / 4, - drawctxt->shader_save_commands[2].gpuaddr); - - /* CP_SCRATCH_REG2 = (CP_SCRATCH_REG2 & 0x00000000) | SP_FS_CTRL_REG0 */ - *cmd++ = cp_type3_packet(CP_REG_RMW, 3); - *cmd++ = (1 << 30) | A3XX_CP_SCRATCH_REG2; - *cmd++ = 0x00000000; /* AND value */ - *cmd++ = A3XX_SP_FS_CTRL_REG0; /* OR address */ - /* CP_SCRATCH_REG2 = ( (CP_SCRATCH_REG2 & 0x7f000000) >> 21 ) - | ((HLSQ_SHADOW_BASE+0x1000+SSIZE)/4) */ - *cmd++ = cp_type3_packet(CP_REG_RMW, 3); - *cmd++ = ((11 + REG_TO_MEM_LOOP_COUNT_SHIFT) << 24) | - A3XX_CP_SCRATCH_REG2; - *cmd++ = 0x7f000000; /* AND value */ - *cmd++ = (HLSQ_SHADOW_BASE + 0x1000 + SSIZE) / 4; /* OR value */ - - /* - * CP_SCRATCH_REG3 = (CP_SCRATCH_REG3 & 0x00000000) | - * SP_FS_OBJ_OFFSET_REG - */ - - *cmd++ = cp_type3_packet(CP_REG_RMW, 3); - *cmd++ = (1 << 30) | A3XX_CP_SCRATCH_REG3; - *cmd++ = 0x00000000; /* AND value */ - *cmd++ = A3XX_SP_FS_OBJ_OFFSET_REG; /* OR address */ - /* - * CP_SCRATCH_REG3 = ( (CP_SCRATCH_REG3 & 0xfe000000) >> 25 ) | - * 0x00000000 - */ - *cmd++ = cp_type3_packet(CP_REG_RMW, 3); - *cmd++ = A3XX_CP_SCRATCH_REG3; - *cmd++ = 0xfe000000; /* AND value */ - *cmd++ = 0x00000000; /* OR value */ - /* - * CP_SCRATCH_REG2 = (CP_SCRATCH_REG2 & 0xffffffff) | CP_SCRATCH_REG3 - */ - *cmd++ = cp_type3_packet(CP_REG_RMW, 3); - *cmd++ = (1 << 30) | A3XX_CP_SCRATCH_REG2; - *cmd++ = 0xffffffff; /* AND value */ - *cmd++ = A3XX_CP_SCRATCH_REG3; /* OR address */ - - *cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2); - *cmd++ = A3XX_CP_SCRATCH_REG2; - *cmd++ = drawctxt->shader_save_commands[3].gpuaddr; - - /* Constant save */ - cmd = rmw_regtomem(cmd, A3XX_SP_VS_CTRL_REG1, 0x000003ff, - 2 + REG_TO_MEM_LOOP_COUNT_SHIFT, - (HLSQ_SHADOW_BASE + 0x2000) / 4, - drawctxt->constant_save_commands[1].gpuaddr); - - cmd = rmw_regtomem(cmd, A3XX_SP_FS_CTRL_REG1, 0x000003ff, - 2 + REG_TO_MEM_LOOP_COUNT_SHIFT, - (HLSQ_SHADOW_BASE + 0x2000 + SSIZE) / 4, - drawctxt->constant_save_commands[2].gpuaddr); - - cmd = rmw_regtomem(cmd, A3XX_SP_FS_OBJ_OFFSET_REG, 0x00ff0000, - 18, drawctxt->gpustate.gpuaddr & 0xfffffe00, - drawctxt->constant_save_commands[2].gpuaddr - + sizeof(unsigned int)); - - /* Modify constant save conditionals */ - cmd = rmw_regtomem(cmd, A3XX_SP_VS_CTRL_REG1, 0x000003ff, - 0, 0, drawctxt->cond_execs[2].gpuaddr); - - cmd = rmw_regtomem(cmd, A3XX_SP_FS_CTRL_REG1, 0x000003ff, - 0, 0, drawctxt->cond_execs[3].gpuaddr); - - /* Save VS instruction store mode */ - - cmd = rmw_regtomem(cmd, A3XX_SP_VS_CTRL_REG0, 0x00000002, - 31, 0, drawctxt->cond_execs[0].gpuaddr); - - /* Save FS instruction store mode */ - cmd = rmw_regtomem(cmd, A3XX_SP_FS_CTRL_REG0, 0x00000002, - 31, 0, drawctxt->cond_execs[1].gpuaddr); - -#endif - - create_ib1(drawctxt, drawctxt->save_fixup, start, cmd); - - tmp_ctx.cmd = cmd; -} - -/****************************************************************************/ -/* Functions to build context restore IBs */ -/****************************************************************************/ - -static unsigned int *build_sys2gmem_cmds(struct adreno_device *adreno_dev, - struct adreno_context *drawctxt, - struct gmem_shadow_t *shadow) -{ - unsigned int *cmds = tmp_ctx.cmd; - unsigned int *start = cmds; - - *cmds++ = cp_type0_packet(A3XX_RBBM_CLOCK_CTL, 1); - *cmds++ = A3XX_RBBM_CLOCK_CTL_DEFAULT; - - *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 5); - *cmds++ = CP_REG(A3XX_HLSQ_CONTROL_0_REG); - /* HLSQ_CONTROL_0_REG */ - *cmds++ = _SET(HLSQ_CTRL0REG_FSTHREADSIZE, HLSQ_FOUR_PIX_QUADS) | - _SET(HLSQ_CTRL0REG_FSSUPERTHREADENABLE, 1) | - _SET(HLSQ_CTRL0REG_SPSHADERRESTART, 1) | - _SET(HLSQ_CTRL0REG_CHUNKDISABLE, 1) | - _SET(HLSQ_CTRL0REG_SPCONSTFULLUPDATE, 1); - /* HLSQ_CONTROL_1_REG */ - *cmds++ = _SET(HLSQ_CTRL1REG_VSTHREADSIZE, HLSQ_TWO_VTX_QUADS) | - _SET(HLSQ_CTRL1REG_VSSUPERTHREADENABLE, 1); - /* HLSQ_CONTROL_2_REG */ - *cmds++ = _SET(HLSQ_CTRL2REG_PRIMALLOCTHRESHOLD, 31); - /* HLSQ_CONTROL3_REG */ - *cmds++ = 0x00000000; - - *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3); - *cmds++ = CP_REG(A3XX_RB_MRT_BUF_INFO0); - /* RB_MRT_BUF_INFO0 */ - *cmds++ = _SET(RB_MRTBUFINFO_COLOR_FORMAT, RB_R8G8B8A8_UNORM) | - _SET(RB_MRTBUFINFO_COLOR_TILE_MODE, RB_TILINGMODE_32X32) | - _SET(RB_MRTBUFINFO_COLOR_BUF_PITCH, - (shadow->gmem_pitch * 4 * 8) / 256); - /* RB_MRT_BUF_BASE0 */ - *cmds++ = _SET(RB_MRTBUFBASE_COLOR_BUF_BASE, tmp_ctx.gmem_base >> 5); - - /* Texture samplers */ - *cmds++ = cp_type3_packet(CP_LOAD_STATE, 4); - *cmds++ = (16 << CP_LOADSTATE_DSTOFFSET_SHIFT) - | (HLSQ_DIRECT << CP_LOADSTATE_STATESRC_SHIFT) - | (HLSQ_BLOCK_ID_TP_TEX << CP_LOADSTATE_STATEBLOCKID_SHIFT) - | (1 << CP_LOADSTATE_NUMOFUNITS_SHIFT); - *cmds++ = (HLSQ_TP_TEX_SAMPLERS << CP_LOADSTATE_STATETYPE_SHIFT) - | (0 << CP_LOADSTATE_EXTSRCADDR_SHIFT); - *cmds++ = 0x00000240; - *cmds++ = 0x00000000; - - *cmds++ = cp_type0_packet(A3XX_VFD_PERFCOUNTER0_SELECT, 1); - *cmds++ = 0x00000000; - - /* Texture memobjs */ - *cmds++ = cp_type3_packet(CP_LOAD_STATE, 6); - *cmds++ = (16 << CP_LOADSTATE_DSTOFFSET_SHIFT) - | (HLSQ_DIRECT << CP_LOADSTATE_STATESRC_SHIFT) - | (HLSQ_BLOCK_ID_TP_TEX << CP_LOADSTATE_STATEBLOCKID_SHIFT) - | (1 << CP_LOADSTATE_NUMOFUNITS_SHIFT); - *cmds++ = (HLSQ_TP_TEX_MEMOBJ << CP_LOADSTATE_STATETYPE_SHIFT) - | (0 << CP_LOADSTATE_EXTSRCADDR_SHIFT); - *cmds++ = 0x4cc06880; - *cmds++ = shadow->height | (shadow->width << 14); - *cmds++ = (shadow->pitch*4*8) << 9; - *cmds++ = 0x00000000; - - *cmds++ = cp_type0_packet(A3XX_VFD_PERFCOUNTER0_SELECT, 1); - *cmds++ = 0x00000000; - - /* Mipmap bases */ - *cmds++ = cp_type3_packet(CP_LOAD_STATE, 16); - *cmds++ = (224 << CP_LOADSTATE_DSTOFFSET_SHIFT) - | (HLSQ_DIRECT << CP_LOADSTATE_STATESRC_SHIFT) - | (HLSQ_BLOCK_ID_TP_MIPMAP << CP_LOADSTATE_STATEBLOCKID_SHIFT) - | (14 << CP_LOADSTATE_NUMOFUNITS_SHIFT); - *cmds++ = (HLSQ_TP_MIPMAP_BASE << CP_LOADSTATE_STATETYPE_SHIFT) - | (0 << CP_LOADSTATE_EXTSRCADDR_SHIFT); - *cmds++ = shadow->gmemshadow.gpuaddr; - *cmds++ = 0x00000000; - *cmds++ = 0x00000000; - *cmds++ = 0x00000000; - *cmds++ = 0x00000000; - *cmds++ = 0x00000000; - *cmds++ = 0x00000000; - *cmds++ = 0x00000000; - *cmds++ = 0x00000000; - *cmds++ = 0x00000000; - *cmds++ = 0x00000000; - *cmds++ = 0x00000000; - *cmds++ = 0x00000000; - *cmds++ = 0x00000000; - - *cmds++ = cp_type0_packet(A3XX_VFD_PERFCOUNTER0_SELECT, 1); - *cmds++ = 0x00000000; - - *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 5); - *cmds++ = CP_REG(A3XX_HLSQ_VS_CONTROL_REG); - /* HLSQ_VS_CONTROL_REG */ - *cmds++ = _SET(HLSQ_VSCTRLREG_VSINSTRLENGTH, 1); - /* HLSQ_FS_CONTROL_REG */ - *cmds++ = _SET(HLSQ_FSCTRLREG_FSCONSTLENGTH, 1) | - _SET(HLSQ_FSCTRLREG_FSCONSTSTARTOFFSET, 128) | - _SET(HLSQ_FSCTRLREG_FSINSTRLENGTH, 2); - /* HLSQ_CONST_VSPRESV_RANGE_REG */ - *cmds++ = 0x00000000; - /* HLSQ_CONST_FSPRESV_RANGE_REG */ - *cmds++ = 0x00000000; - - *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); - *cmds++ = CP_REG(A3XX_SP_FS_LENGTH_REG); - /* SP_FS_LENGTH_REG */ - *cmds++ = _SET(SP_SHADERLENGTH_LEN, 2); - - *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 12); - *cmds++ = CP_REG(A3XX_SP_VS_CTRL_REG0); - /* SP_VS_CTRL_REG0 */ - *cmds++ = _SET(SP_VSCTRLREG0_VSTHREADMODE, SP_MULTI) | - _SET(SP_VSCTRLREG0_VSINSTRBUFFERMODE, SP_BUFFER_MODE) | - _SET(SP_VSCTRLREG0_VSICACHEINVALID, 1) | - _SET(SP_VSCTRLREG0_VSFULLREGFOOTPRINT, 2) | - _SET(SP_VSCTRLREG0_VSTHREADSIZE, SP_TWO_VTX_QUADS) | - _SET(SP_VSCTRLREG0_VSLENGTH, 1); - /* SP_VS_CTRL_REG1 */ - *cmds++ = _SET(SP_VSCTRLREG1_VSINITIALOUTSTANDING, 8); - /* SP_VS_PARAM_REG */ - *cmds++ = _SET(SP_VSPARAMREG_POSREGID, 4) | - _SET(SP_VSPARAMREG_PSIZEREGID, 252) | - _SET(SP_VSPARAMREG_TOTALVSOUTVAR, 1); - /* SP_VS_OUT_REG0 */ - *cmds++ = _SET(SP_VSOUTREG_COMPMASK0, 3); - /* SP_VS_OUT_REG1 */ - *cmds++ = 0x00000000; - /* SP_VS_OUT_REG2 */ - *cmds++ = 0x00000000; - /* SP_VS_OUT_REG3 */ - *cmds++ = 0x00000000; - /* SP_VS_OUT_REG4 */ - *cmds++ = 0x00000000; - /* SP_VS_OUT_REG5 */ - *cmds++ = 0x00000000; - /* SP_VS_OUT_REG6 */ - *cmds++ = 0x00000000; - /* SP_VS_OUT_REG7 */ - *cmds++ = 0x00000000; - - *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 7); - *cmds++ = CP_REG(A3XX_SP_VS_VPC_DST_REG_0); - /* SP_VS_VPC_DST_REG0 */ - *cmds++ = _SET(SP_VSVPCDSTREG_OUTLOC0, 8); - /* SP_VS_VPC_DST_REG1 */ - *cmds++ = 0x00000000; - /* SP_VS_VPC_DST_REG2 */ - *cmds++ = 0x00000000; - /* SP_VS_VPC_DST_REG3 */ - *cmds++ = 0x00000000; - /* SP_VS_OBJ_OFFSET_REG */ - *cmds++ = 0x00000000; - /* SP_VS_OBJ_START_REG */ - *cmds++ = 0x00000000; - - *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 6); - *cmds++ = CP_REG(A3XX_SP_VS_LENGTH_REG); - /* SP_VS_LENGTH_REG */ - *cmds++ = _SET(SP_SHADERLENGTH_LEN, 1); - /* SP_FS_CTRL_REG0 */ - *cmds++ = _SET(SP_FSCTRLREG0_FSTHREADMODE, SP_MULTI) | - _SET(SP_FSCTRLREG0_FSINSTRBUFFERMODE, SP_BUFFER_MODE) | - _SET(SP_FSCTRLREG0_FSICACHEINVALID, 1) | - _SET(SP_FSCTRLREG0_FSHALFREGFOOTPRINT, 1) | - _SET(SP_FSCTRLREG0_FSFULLREGFOOTPRINT, 1) | - _SET(SP_FSCTRLREG0_FSINOUTREGOVERLAP, 1) | - _SET(SP_FSCTRLREG0_FSTHREADSIZE, SP_FOUR_PIX_QUADS) | - _SET(SP_FSCTRLREG0_FSSUPERTHREADMODE, 1) | - _SET(SP_FSCTRLREG0_PIXLODENABLE, 1) | - _SET(SP_FSCTRLREG0_FSLENGTH, 2); - /* SP_FS_CTRL_REG1 */ - *cmds++ = _SET(SP_FSCTRLREG1_FSCONSTLENGTH, 1) | - _SET(SP_FSCTRLREG1_FSINITIALOUTSTANDING, 2) | - _SET(SP_FSCTRLREG1_HALFPRECVAROFFSET, 63); - /* SP_FS_OBJ_OFFSET_REG */ - *cmds++ = _SET(SP_OBJOFFSETREG_CONSTOBJECTSTARTOFFSET, 128) | - _SET(SP_OBJOFFSETREG_SHADEROBJOFFSETINIC, 126); - /* SP_FS_OBJ_START_REG */ - *cmds++ = 0x00000000; - - *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3); - *cmds++ = CP_REG(A3XX_SP_FS_FLAT_SHAD_MODE_REG_0); - /* SP_FS_FLAT_SHAD_MODE_REG0 */ - *cmds++ = 0x00000000; - /* SP_FS_FLAT_SHAD_MODE_REG1 */ - *cmds++ = 0x00000000; - - *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); - *cmds++ = CP_REG(A3XX_SP_FS_OUTPUT_REG); - /* SP_FS_OUT_REG */ - *cmds++ = _SET(SP_FSOUTREG_PAD0, SP_PIXEL_BASED); - - *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 5); - *cmds++ = CP_REG(A3XX_SP_FS_MRT_REG_0); - /* SP_FS_MRT_REG0 */ - *cmds++ = _SET(SP_FSMRTREG_PRECISION, 1); - /* SP_FS_MRT_REG1 */ - *cmds++ = 0; - /* SP_FS_MRT_REG2 */ - *cmds++ = 0; - /* SP_FS_MRT_REG3 */ - *cmds++ = 0; - - *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 11); - *cmds++ = CP_REG(A3XX_VPC_ATTR); - /* VPC_ATTR */ - *cmds++ = _SET(VPC_VPCATTR_TOTALATTR, 2) | - _SET(VPC_VPCATTR_THRHDASSIGN, 1) | - _SET(VPC_VPCATTR_LMSIZE, 1); - /* VPC_PACK */ - *cmds++ = _SET(VPC_VPCPACK_NUMFPNONPOSVAR, 2) | - _SET(VPC_VPCPACK_NUMNONPOSVSVAR, 2); - /* VPC_VARYING_INTERP_MODE_0 */ - *cmds++ = 0x00000000; - /* VPC_VARYING_INTERP_MODE1 */ - *cmds++ = 0x00000000; - /* VPC_VARYING_INTERP_MODE2 */ - *cmds++ = 0x00000000; - /* VPC_VARYING_IINTERP_MODE3 */ - *cmds++ = 0x00000000; - /* VPC_VARRYING_PS_REPL_MODE_0 */ - *cmds++ = _SET(VPC_VPCVARPSREPLMODE_COMPONENT08, 1) | - _SET(VPC_VPCVARPSREPLMODE_COMPONENT09, 2) | - _SET(VPC_VPCVARPSREPLMODE_COMPONENT0A, 1) | - _SET(VPC_VPCVARPSREPLMODE_COMPONENT0B, 2) | - _SET(VPC_VPCVARPSREPLMODE_COMPONENT0C, 1) | - _SET(VPC_VPCVARPSREPLMODE_COMPONENT0D, 2) | - _SET(VPC_VPCVARPSREPLMODE_COMPONENT0E, 1) | - _SET(VPC_VPCVARPSREPLMODE_COMPONENT0F, 2) | - _SET(VPC_VPCVARPSREPLMODE_COMPONENT10, 1) | - _SET(VPC_VPCVARPSREPLMODE_COMPONENT11, 2) | - _SET(VPC_VPCVARPSREPLMODE_COMPONENT12, 1) | - _SET(VPC_VPCVARPSREPLMODE_COMPONENT13, 2) | - _SET(VPC_VPCVARPSREPLMODE_COMPONENT14, 1) | - _SET(VPC_VPCVARPSREPLMODE_COMPONENT15, 2) | - _SET(VPC_VPCVARPSREPLMODE_COMPONENT16, 1) | - _SET(VPC_VPCVARPSREPLMODE_COMPONENT17, 2); - /* VPC_VARRYING_PS_REPL_MODE_1 */ - *cmds++ = _SET(VPC_VPCVARPSREPLMODE_COMPONENT08, 1) | - _SET(VPC_VPCVARPSREPLMODE_COMPONENT09, 2) | - _SET(VPC_VPCVARPSREPLMODE_COMPONENT0A, 1) | - _SET(VPC_VPCVARPSREPLMODE_COMPONENT0B, 2) | - _SET(VPC_VPCVARPSREPLMODE_COMPONENT0C, 1) | - _SET(VPC_VPCVARPSREPLMODE_COMPONENT0D, 2) | - _SET(VPC_VPCVARPSREPLMODE_COMPONENT0E, 1) | - _SET(VPC_VPCVARPSREPLMODE_COMPONENT0F, 2) | - _SET(VPC_VPCVARPSREPLMODE_COMPONENT10, 1) | - _SET(VPC_VPCVARPSREPLMODE_COMPONENT11, 2) | - _SET(VPC_VPCVARPSREPLMODE_COMPONENT12, 1) | - _SET(VPC_VPCVARPSREPLMODE_COMPONENT13, 2) | - _SET(VPC_VPCVARPSREPLMODE_COMPONENT14, 1) | - _SET(VPC_VPCVARPSREPLMODE_COMPONENT15, 2) | - _SET(VPC_VPCVARPSREPLMODE_COMPONENT16, 1) | - _SET(VPC_VPCVARPSREPLMODE_COMPONENT17, 2); - /* VPC_VARRYING_PS_REPL_MODE_2 */ - *cmds++ = _SET(VPC_VPCVARPSREPLMODE_COMPONENT08, 1) | - _SET(VPC_VPCVARPSREPLMODE_COMPONENT09, 2) | - _SET(VPC_VPCVARPSREPLMODE_COMPONENT0A, 1) | - _SET(VPC_VPCVARPSREPLMODE_COMPONENT0B, 2) | - _SET(VPC_VPCVARPSREPLMODE_COMPONENT0C, 1) | - _SET(VPC_VPCVARPSREPLMODE_COMPONENT0D, 2) | - _SET(VPC_VPCVARPSREPLMODE_COMPONENT0E, 1) | - _SET(VPC_VPCVARPSREPLMODE_COMPONENT0F, 2) | - _SET(VPC_VPCVARPSREPLMODE_COMPONENT10, 1) | - _SET(VPC_VPCVARPSREPLMODE_COMPONENT11, 2) | - _SET(VPC_VPCVARPSREPLMODE_COMPONENT12, 1) | - _SET(VPC_VPCVARPSREPLMODE_COMPONENT13, 2) | - _SET(VPC_VPCVARPSREPLMODE_COMPONENT14, 1) | - _SET(VPC_VPCVARPSREPLMODE_COMPONENT15, 2) | - _SET(VPC_VPCVARPSREPLMODE_COMPONENT16, 1) | - _SET(VPC_VPCVARPSREPLMODE_COMPONENT17, 2); - /* VPC_VARRYING_PS_REPL_MODE_3 */ - *cmds++ = _SET(VPC_VPCVARPSREPLMODE_COMPONENT08, 1) | - _SET(VPC_VPCVARPSREPLMODE_COMPONENT09, 2) | - _SET(VPC_VPCVARPSREPLMODE_COMPONENT0A, 1) | - _SET(VPC_VPCVARPSREPLMODE_COMPONENT0B, 2) | - _SET(VPC_VPCVARPSREPLMODE_COMPONENT0C, 1) | - _SET(VPC_VPCVARPSREPLMODE_COMPONENT0D, 2) | - _SET(VPC_VPCVARPSREPLMODE_COMPONENT0E, 1) | - _SET(VPC_VPCVARPSREPLMODE_COMPONENT0F, 2) | - _SET(VPC_VPCVARPSREPLMODE_COMPONENT10, 1) | - _SET(VPC_VPCVARPSREPLMODE_COMPONENT11, 2) | - _SET(VPC_VPCVARPSREPLMODE_COMPONENT12, 1) | - _SET(VPC_VPCVARPSREPLMODE_COMPONENT13, 2) | - _SET(VPC_VPCVARPSREPLMODE_COMPONENT14, 1) | - _SET(VPC_VPCVARPSREPLMODE_COMPONENT15, 2) | - _SET(VPC_VPCVARPSREPLMODE_COMPONENT16, 1) | - _SET(VPC_VPCVARPSREPLMODE_COMPONENT17, 2); - - *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); - *cmds++ = CP_REG(A3XX_SP_SP_CTRL_REG); - /* SP_SP_CTRL_REG */ - *cmds++ = _SET(SP_SPCTRLREG_SLEEPMODE, 1) | - _SET(SP_SPCTRLREG_LOMODE, 1); - - /* Load vertex shader */ - *cmds++ = cp_type3_packet(CP_LOAD_STATE, 10); - *cmds++ = (0 << CP_LOADSTATE_DSTOFFSET_SHIFT) - | (HLSQ_DIRECT << CP_LOADSTATE_STATESRC_SHIFT) - | (HLSQ_BLOCK_ID_SP_VS << CP_LOADSTATE_STATEBLOCKID_SHIFT) - | (1 << CP_LOADSTATE_NUMOFUNITS_SHIFT); - *cmds++ = (HLSQ_SP_VS_INSTR << CP_LOADSTATE_STATETYPE_SHIFT) - | (0 << CP_LOADSTATE_EXTSRCADDR_SHIFT); - /* (sy)end; */ - *cmds++ = 0x00000000; *cmds++ = 0x13001000; - /* nop; */ - *cmds++ = 0x00000000; *cmds++ = 0x00000000; - /* nop; */ - *cmds++ = 0x00000000; *cmds++ = 0x00000000; - /* nop; */ - *cmds++ = 0x00000000; *cmds++ = 0x00000000; - - *cmds++ = cp_type0_packet(A3XX_VFD_PERFCOUNTER0_SELECT, 1); - *cmds++ = 0x00000000; - - *cmds++ = cp_type3_packet(CP_WAIT_FOR_IDLE, 1); - *cmds++ = 0x00000000; - - - /* Load fragment shader */ - *cmds++ = cp_type3_packet(CP_LOAD_STATE, 18); - *cmds++ = (0 << CP_LOADSTATE_DSTOFFSET_SHIFT) - | (HLSQ_DIRECT << CP_LOADSTATE_STATESRC_SHIFT) - | (HLSQ_BLOCK_ID_SP_FS << CP_LOADSTATE_STATEBLOCKID_SHIFT) - | (2 << CP_LOADSTATE_NUMOFUNITS_SHIFT); - *cmds++ = (HLSQ_SP_FS_INSTR << CP_LOADSTATE_STATETYPE_SHIFT) - | (0 << CP_LOADSTATE_EXTSRCADDR_SHIFT); - /* (sy)(rpt1)bary.f (ei)r0.z, (r)0, r0.x; */ - *cmds++ = 0x00002000; *cmds++ = 0x57309902; - /* (rpt5)nop; */ - *cmds++ = 0x00000000; *cmds++ = 0x00000500; - /* sam (f32)r0.xyzw, r0.z, s#0, t#0; */ - *cmds++ = 0x00000005; *cmds++ = 0xa0c01f00; - /* (sy)mov.f32f32 r1.x, r0.x; */ - *cmds++ = 0x00000000; *cmds++ = 0x30040b00; - /* mov.f32f32 r1.y, r0.y; */ - *cmds++ = 0x00000000; *cmds++ = 0x03000000; - /* mov.f32f32 r1.z, r0.z; */ - *cmds++ = 0x00000000; *cmds++ = 0x00000000; - /* mov.f32f32 r1.w, r0.w; */ - *cmds++ = 0x00000000; *cmds++ = 0x00000000; - /* end; */ - *cmds++ = 0x00000000; *cmds++ = 0x00000000; - - *cmds++ = cp_type0_packet(A3XX_VFD_PERFCOUNTER0_SELECT, 1); - *cmds++ = 0x00000000; - - *cmds++ = cp_type3_packet(CP_WAIT_FOR_IDLE, 1); - *cmds++ = 0x00000000; - - *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3); - *cmds++ = CP_REG(A3XX_VFD_CONTROL_0); - /* VFD_CONTROL_0 */ - *cmds++ = _SET(VFD_CTRLREG0_TOTALATTRTOVS, 8) | - _SET(VFD_CTRLREG0_PACKETSIZE, 2) | - _SET(VFD_CTRLREG0_STRMDECINSTRCNT, 2) | - _SET(VFD_CTRLREG0_STRMFETCHINSTRCNT, 2); - /* VFD_CONTROL_1 */ - *cmds++ = _SET(VFD_CTRLREG1_MAXSTORAGE, 2) | - _SET(VFD_CTRLREG1_REGID4VTX, 252) | - _SET(VFD_CTRLREG1_REGID4INST, 252); - - *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 5); - *cmds++ = CP_REG(A3XX_VFD_FETCH_INSTR_0_0); - /* VFD_FETCH_INSTR_0_0 */ - *cmds++ = _SET(VFD_FETCHINSTRUCTIONS_FETCHSIZE, 7) | - _SET(VFD_FETCHINSTRUCTIONS_BUFSTRIDE, 8) | - _SET(VFD_FETCHINSTRUCTIONS_SWITCHNEXT, 1) | - _SET(VFD_FETCHINSTRUCTIONS_STEPRATE, 1); - /* VFD_FETCH_INSTR_1_0 */ - *cmds++ = _SET(VFD_BASEADDR_BASEADDR, - shadow->quad_vertices_restore.gpuaddr); - /* VFD_FETCH_INSTR_0_1 */ - *cmds++ = _SET(VFD_FETCHINSTRUCTIONS_FETCHSIZE, 11) | - _SET(VFD_FETCHINSTRUCTIONS_BUFSTRIDE, 12) | - _SET(VFD_FETCHINSTRUCTIONS_INDEXDECODE, 1) | - _SET(VFD_FETCHINSTRUCTIONS_STEPRATE, 1); - /* VFD_FETCH_INSTR_1_1 */ - *cmds++ = _SET(VFD_BASEADDR_BASEADDR, - shadow->quad_vertices_restore.gpuaddr + 16); - - *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3); - *cmds++ = CP_REG(A3XX_VFD_DECODE_INSTR_0); - /* VFD_DECODE_INSTR_0 */ - *cmds++ = _SET(VFD_DECODEINSTRUCTIONS_WRITEMASK, 0x0F) | - _SET(VFD_DECODEINSTRUCTIONS_CONSTFILL, 1) | - _SET(VFD_DECODEINSTRUCTIONS_FORMAT, 1) | - _SET(VFD_DECODEINSTRUCTIONS_SHIFTCNT, 8) | - _SET(VFD_DECODEINSTRUCTIONS_LASTCOMPVALID, 1) | - _SET(VFD_DECODEINSTRUCTIONS_SWITCHNEXT, 1); - /* VFD_DECODE_INSTR_1 */ - *cmds++ = _SET(VFD_DECODEINSTRUCTIONS_WRITEMASK, 0x0F) | - _SET(VFD_DECODEINSTRUCTIONS_CONSTFILL, 1) | - _SET(VFD_DECODEINSTRUCTIONS_FORMAT, 2) | - _SET(VFD_DECODEINSTRUCTIONS_REGID, 4) | - _SET(VFD_DECODEINSTRUCTIONS_SHIFTCNT, 12) | - _SET(VFD_DECODEINSTRUCTIONS_LASTCOMPVALID, 1); - - *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); - *cmds++ = CP_REG(A3XX_RB_DEPTH_CONTROL); - /* RB_DEPTH_CONTROL */ - *cmds++ = _SET(RB_DEPTHCONTROL_Z_TEST_FUNC, RB_FRAG_LESS); - - *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); - *cmds++ = CP_REG(A3XX_RB_STENCIL_CONTROL); - /* RB_STENCIL_CONTROL */ - *cmds++ = _SET(RB_STENCILCONTROL_STENCIL_FUNC, RB_REF_ALWAYS) | - _SET(RB_STENCILCONTROL_STENCIL_FAIL, RB_STENCIL_KEEP) | - _SET(RB_STENCILCONTROL_STENCIL_ZPASS, RB_STENCIL_KEEP) | - _SET(RB_STENCILCONTROL_STENCIL_ZFAIL, RB_STENCIL_KEEP) | - _SET(RB_STENCILCONTROL_STENCIL_FUNC_BF, RB_REF_ALWAYS) | - _SET(RB_STENCILCONTROL_STENCIL_FAIL_BF, RB_STENCIL_KEEP) | - _SET(RB_STENCILCONTROL_STENCIL_ZPASS_BF, RB_STENCIL_KEEP) | - _SET(RB_STENCILCONTROL_STENCIL_ZFAIL_BF, RB_STENCIL_KEEP); - - *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); - *cmds++ = CP_REG(A3XX_RB_MODE_CONTROL); - /* RB_MODE_CONTROL */ - *cmds++ = _SET(RB_MODECONTROL_RENDER_MODE, RB_RENDERING_PASS) | - _SET(RB_MODECONTROL_MARB_CACHE_SPLIT_MODE, 1); - - *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); - *cmds++ = CP_REG(A3XX_RB_RENDER_CONTROL); - /* RB_RENDER_CONTROL */ - *cmds++ = _SET(RB_RENDERCONTROL_BIN_WIDTH, shadow->width >> 5) | - _SET(RB_RENDERCONTROL_ALPHA_TEST_FUNC, 7); - - *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); - *cmds++ = CP_REG(A3XX_RB_MSAA_CONTROL); - /* RB_MSAA_CONTROL */ - *cmds++ = _SET(RB_MSAACONTROL_MSAA_DISABLE, 1) | - _SET(RB_MSAACONTROL_SAMPLE_MASK, 0xFFFF); - - *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); - *cmds++ = CP_REG(A3XX_RB_MRT_CONTROL0); - /* RB_MRT_CONTROL0 */ - *cmds++ = _SET(RB_MRTCONTROL_ROP_CODE, 12) | - _SET(RB_MRTCONTROL_DITHER_MODE, RB_DITHER_DISABLE) | - _SET(RB_MRTCONTROL_COMPONENT_ENABLE, 0xF); - - *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3); - *cmds++ = CP_REG(A3XX_RB_MRT_BLEND_CONTROL0); - /* RB_MRT_BLENDCONTROL0 */ - *cmds++ = _SET(RB_MRTBLENDCONTROL_RGB_SRC_FACTOR, RB_FACTOR_ONE) | - _SET(RB_MRTBLENDCONTROL_RGB_BLEND_OPCODE, RB_BLEND_OP_ADD) | - _SET(RB_MRTBLENDCONTROL_RGB_DEST_FACTOR, RB_FACTOR_ZERO) | - _SET(RB_MRTBLENDCONTROL_ALPHA_SRC_FACTOR, RB_FACTOR_ONE) | - _SET(RB_MRTBLENDCONTROL_ALPHA_BLEND_OPCODE, RB_BLEND_OP_ADD) | - _SET(RB_MRTBLENDCONTROL_ALPHA_DEST_FACTOR, RB_FACTOR_ZERO) | - _SET(RB_MRTBLENDCONTROL_CLAMP_ENABLE, 1); - /* RB_MRT_CONTROL1 */ - *cmds++ = _SET(RB_MRTCONTROL_READ_DEST_ENABLE, 1) | - _SET(RB_MRTCONTROL_ROP_CODE, 12) | - _SET(RB_MRTCONTROL_DITHER_MODE, RB_DITHER_ALWAYS) | - _SET(RB_MRTCONTROL_COMPONENT_ENABLE, 0xF); - - *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3); - *cmds++ = CP_REG(A3XX_RB_MRT_BLEND_CONTROL1); - /* RB_MRT_BLENDCONTROL1 */ - *cmds++ = _SET(RB_MRTBLENDCONTROL_RGB_SRC_FACTOR, RB_FACTOR_ONE) | - _SET(RB_MRTBLENDCONTROL_RGB_BLEND_OPCODE, RB_BLEND_OP_ADD) | - _SET(RB_MRTBLENDCONTROL_RGB_DEST_FACTOR, RB_FACTOR_ZERO) | - _SET(RB_MRTBLENDCONTROL_ALPHA_SRC_FACTOR, RB_FACTOR_ONE) | - _SET(RB_MRTBLENDCONTROL_ALPHA_BLEND_OPCODE, RB_BLEND_OP_ADD) | - _SET(RB_MRTBLENDCONTROL_ALPHA_DEST_FACTOR, RB_FACTOR_ZERO) | - _SET(RB_MRTBLENDCONTROL_CLAMP_ENABLE, 1); - /* RB_MRT_CONTROL2 */ - *cmds++ = _SET(RB_MRTCONTROL_READ_DEST_ENABLE, 1) | - _SET(RB_MRTCONTROL_ROP_CODE, 12) | - _SET(RB_MRTCONTROL_DITHER_MODE, RB_DITHER_ALWAYS) | - _SET(RB_MRTCONTROL_COMPONENT_ENABLE, 0xF); - - *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3); - *cmds++ = CP_REG(A3XX_RB_MRT_BLEND_CONTROL2); - /* RB_MRT_BLENDCONTROL2 */ - *cmds++ = _SET(RB_MRTBLENDCONTROL_RGB_SRC_FACTOR, RB_FACTOR_ONE) | - _SET(RB_MRTBLENDCONTROL_RGB_BLEND_OPCODE, RB_BLEND_OP_ADD) | - _SET(RB_MRTBLENDCONTROL_RGB_DEST_FACTOR, RB_FACTOR_ZERO) | - _SET(RB_MRTBLENDCONTROL_ALPHA_SRC_FACTOR, RB_FACTOR_ONE) | - _SET(RB_MRTBLENDCONTROL_ALPHA_BLEND_OPCODE, RB_BLEND_OP_ADD) | - _SET(RB_MRTBLENDCONTROL_ALPHA_DEST_FACTOR, RB_FACTOR_ZERO) | - _SET(RB_MRTBLENDCONTROL_CLAMP_ENABLE, 1); - /* RB_MRT_CONTROL3 */ - *cmds++ = _SET(RB_MRTCONTROL_READ_DEST_ENABLE, 1) | - _SET(RB_MRTCONTROL_ROP_CODE, 12) | - _SET(RB_MRTCONTROL_DITHER_MODE, RB_DITHER_ALWAYS) | - _SET(RB_MRTCONTROL_COMPONENT_ENABLE, 0xF); - - *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); - *cmds++ = CP_REG(A3XX_RB_MRT_BLEND_CONTROL3); - /* RB_MRT_BLENDCONTROL3 */ - *cmds++ = _SET(RB_MRTBLENDCONTROL_RGB_SRC_FACTOR, RB_FACTOR_ONE) | - _SET(RB_MRTBLENDCONTROL_RGB_BLEND_OPCODE, RB_BLEND_OP_ADD) | - _SET(RB_MRTBLENDCONTROL_RGB_DEST_FACTOR, RB_FACTOR_ZERO) | - _SET(RB_MRTBLENDCONTROL_ALPHA_SRC_FACTOR, RB_FACTOR_ONE) | - _SET(RB_MRTBLENDCONTROL_ALPHA_BLEND_OPCODE, RB_BLEND_OP_ADD) | - _SET(RB_MRTBLENDCONTROL_ALPHA_DEST_FACTOR, RB_FACTOR_ZERO) | - _SET(RB_MRTBLENDCONTROL_CLAMP_ENABLE, 1); - - *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 5); - *cmds++ = CP_REG(A3XX_VFD_INDEX_MIN); - /* VFD_INDEX_MIN */ - *cmds++ = 0x00000000; - /* VFD_INDEX_MAX */ - *cmds++ = 340; - /* VFD_INDEX_OFFSET */ - *cmds++ = 0x00000000; - /* TPL1_TP_VS_TEX_OFFSET */ - *cmds++ = 0x00000000; - - *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); - *cmds++ = CP_REG(A3XX_VFD_VS_THREADING_THRESHOLD); - /* VFD_VS_THREADING_THRESHOLD */ - *cmds++ = _SET(VFD_THREADINGTHRESHOLD_REGID_THRESHOLD, 15) | - _SET(VFD_THREADINGTHRESHOLD_REGID_VTXCNT, 252); - - *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); - *cmds++ = CP_REG(A3XX_TPL1_TP_VS_TEX_OFFSET); - /* TPL1_TP_VS_TEX_OFFSET */ - *cmds++ = 0x00000000; - - *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); - *cmds++ = CP_REG(A3XX_TPL1_TP_FS_TEX_OFFSET); - /* TPL1_TP_FS_TEX_OFFSET */ - *cmds++ = _SET(TPL1_TPTEXOFFSETREG_SAMPLEROFFSET, 16) | - _SET(TPL1_TPTEXOFFSETREG_MEMOBJOFFSET, 16) | - _SET(TPL1_TPTEXOFFSETREG_BASETABLEPTR, 224); - - *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); - *cmds++ = CP_REG(A3XX_GRAS_SC_CONTROL); - /* GRAS_SC_CONTROL */ - /*cmds++ = _SET(GRAS_SC_CONTROL_RASTER_MODE, 1); - *cmds++ = _SET(GRAS_SC_CONTROL_RASTER_MODE, 1) |*/ - *cmds++ = 0x04001000; - - *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); - *cmds++ = CP_REG(A3XX_GRAS_SU_MODE_CONTROL); - /* GRAS_SU_MODE_CONTROL */ - *cmds++ = _SET(GRAS_SU_CTRLMODE_LINEHALFWIDTH, 2); - - *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3); - *cmds++ = CP_REG(A3XX_GRAS_SC_WINDOW_SCISSOR_TL); - /* GRAS_SC_WINDOW_SCISSOR_TL */ - *cmds++ = 0x00000000; - /* GRAS_SC_WINDOW_SCISSOR_BR */ - *cmds++ = _SET(GRAS_SC_WINDOW_SCISSOR_BR_BR_X, shadow->width - 1) | - _SET(GRAS_SC_WINDOW_SCISSOR_BR_BR_Y, shadow->height - 1); - - *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3); - *cmds++ = CP_REG(A3XX_GRAS_SC_SCREEN_SCISSOR_TL); - /* GRAS_SC_SCREEN_SCISSOR_TL */ - *cmds++ = 0x00000000; - /* GRAS_SC_SCREEN_SCISSOR_BR */ - *cmds++ = _SET(GRAS_SC_SCREEN_SCISSOR_BR_BR_X, shadow->width - 1) | - _SET(GRAS_SC_SCREEN_SCISSOR_BR_BR_Y, shadow->height - 1); - - *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 5); - *cmds++ = CP_REG(A3XX_GRAS_CL_VPORT_XOFFSET); - /* GRAS_CL_VPORT_XOFFSET */ - *cmds++ = 0x00000000; - /* GRAS_CL_VPORT_XSCALE */ - *cmds++ = _SET(GRAS_CL_VPORT_XSCALE_VPORT_XSCALE, 0x3F800000); - /* GRAS_CL_VPORT_YOFFSET */ - *cmds++ = 0x00000000; - /* GRAS_CL_VPORT_YSCALE */ - *cmds++ = _SET(GRAS_CL_VPORT_YSCALE_VPORT_YSCALE, 0x3F800000); - - *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3); - *cmds++ = CP_REG(A3XX_GRAS_CL_VPORT_ZOFFSET); - /* GRAS_CL_VPORT_ZOFFSET */ - *cmds++ = 0x00000000; - /* GRAS_CL_VPORT_ZSCALE */ - *cmds++ = _SET(GRAS_CL_VPORT_ZSCALE_VPORT_ZSCALE, 0x3F800000); - - *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); - *cmds++ = CP_REG(A3XX_GRAS_CL_CLIP_CNTL); - /* GRAS_CL_CLIP_CNTL */ - *cmds++ = _SET(GRAS_CL_CLIP_CNTL_IJ_PERSP_CENTER, 1); - - *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); - *cmds++ = CP_REG(A3XX_SP_FS_IMAGE_OUTPUT_REG_0); - /* SP_FS_IMAGE_OUTPUT_REG_0 */ - *cmds++ = _SET(SP_IMAGEOUTPUTREG_MRTFORMAT, SP_R8G8B8A8_UNORM); - - *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); - *cmds++ = CP_REG(A3XX_PC_PRIM_VTX_CNTL); - /* PC_PRIM_VTX_CONTROL */ - *cmds++ = _SET(PC_PRIM_VTX_CONTROL_STRIDE_IN_VPC, 2) | - _SET(PC_PRIM_VTX_CONTROL_POLYMODE_FRONT_PTYPE, - PC_DRAW_TRIANGLES) | - _SET(PC_PRIM_VTX_CONTROL_POLYMODE_BACK_PTYPE, - PC_DRAW_TRIANGLES) | - _SET(PC_PRIM_VTX_CONTROL_PROVOKING_VTX_LAST, 1); - - - /* oxili_generate_context_roll_packets */ - *cmds++ = cp_type0_packet(A3XX_SP_VS_CTRL_REG0, 1); - *cmds++ = 0x00000400; - - *cmds++ = cp_type0_packet(A3XX_SP_FS_CTRL_REG0, 1); - *cmds++ = 0x00000400; - - *cmds++ = cp_type0_packet(A3XX_SP_VS_PVT_MEM_SIZE_REG, 1); - *cmds++ = 0x00008000; /* SP_VS_MEM_SIZE_REG */ - - *cmds++ = cp_type0_packet(A3XX_SP_FS_PVT_MEM_SIZE_REG, 1); - *cmds++ = 0x00008000; /* SP_FS_MEM_SIZE_REG */ - - /* Clear cache invalidate bit when re-loading the shader control regs */ - *cmds++ = cp_type0_packet(A3XX_SP_VS_CTRL_REG0, 1); - *cmds++ = _SET(SP_VSCTRLREG0_VSTHREADMODE, SP_MULTI) | - _SET(SP_VSCTRLREG0_VSINSTRBUFFERMODE, SP_BUFFER_MODE) | - _SET(SP_VSCTRLREG0_VSFULLREGFOOTPRINT, 2) | - _SET(SP_VSCTRLREG0_VSTHREADSIZE, SP_TWO_VTX_QUADS) | - _SET(SP_VSCTRLREG0_VSLENGTH, 1); - - *cmds++ = cp_type0_packet(A3XX_SP_FS_CTRL_REG0, 1); - *cmds++ = _SET(SP_FSCTRLREG0_FSTHREADMODE, SP_MULTI) | - _SET(SP_FSCTRLREG0_FSINSTRBUFFERMODE, SP_BUFFER_MODE) | - _SET(SP_FSCTRLREG0_FSHALFREGFOOTPRINT, 1) | - _SET(SP_FSCTRLREG0_FSFULLREGFOOTPRINT, 1) | - _SET(SP_FSCTRLREG0_FSINOUTREGOVERLAP, 1) | - _SET(SP_FSCTRLREG0_FSTHREADSIZE, SP_FOUR_PIX_QUADS) | - _SET(SP_FSCTRLREG0_FSSUPERTHREADMODE, 1) | - _SET(SP_FSCTRLREG0_FSLENGTH, 2); - - *cmds++ = cp_type0_packet(A3XX_SP_VS_PVT_MEM_SIZE_REG, 1); - *cmds++ = 0x00000000; /* SP_VS_MEM_SIZE_REG */ - - *cmds++ = cp_type0_packet(A3XX_SP_FS_PVT_MEM_SIZE_REG, 1); - *cmds++ = 0x00000000; /* SP_FS_MEM_SIZE_REG */ - - /* end oxili_generate_context_roll_packets */ - - *cmds++ = cp_type3_packet(CP_DRAW_INDX, 3); - *cmds++ = 0x00000000; /* Viz query info */ - *cmds++ = BUILD_PC_DRAW_INITIATOR(PC_DI_PT_RECTLIST, - PC_DI_SRC_SEL_AUTO_INDEX, - PC_DI_INDEX_SIZE_16_BIT, - PC_DI_IGNORE_VISIBILITY); - *cmds++ = 0x00000002; /* Num indices */ - - /* 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 *lcc_start; - - int i; - - /* Flush HLSQ lazy updates */ - *cmd++ = cp_type3_packet(CP_EVENT_WRITE, 1); - *cmd++ = 0x7; /* HLSQ_FLUSH */ - *cmd++ = cp_type3_packet(CP_WAIT_FOR_IDLE, 1); - *cmd++ = 0; - - *cmd++ = cp_type0_packet(A3XX_UCHE_CACHE_INVALIDATE0_REG, 2); - *cmd++ = 0x00000000; /* No start addr for full invalidate */ - *cmd++ = (unsigned int) - UCHE_ENTIRE_CACHE << UCHE_INVALIDATE1REG_ALLORPORTION | - UCHE_OP_INVALIDATE << UCHE_INVALIDATE1REG_OPCODE | - 0; /* No end addr for full invalidate */ - - lcc_start = cmd; - - /* 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 - - for (i = 0; i < ARRAY_SIZE(context_register_ranges) / 2; i++) { - cmd = reg_range(cmd, context_register_ranges[i * 2], - context_register_ranges[i * 2 + 1]); - } - - lcc_start[0] = cp_type3_packet(CP_LOAD_CONSTANT_CONTEXT, - (cmd - lcc_start) - 1); - -#ifdef CONFIG_MSM_KGSL_DISABLE_SHADOW_WRITES - lcc_start[2] |= (0 << 24) | (4 << 16); /* Disable shadowing. */ -#else - lcc_start[2] |= (1 << 24) | (4 << 16); -#endif - - for (i = 0; i < ARRAY_SIZE(global_registers); i++) { - *cmd++ = cp_type0_packet(global_registers[i], 1); - tmp_ctx.reg_values[i] = virt2gpu(cmd, &drawctxt->gpustate); - *cmd++ = 0x00000000; - } - - create_ib1(drawctxt, drawctxt->reg_restore, start, cmd); - tmp_ctx.cmd = cmd; -} - -static void build_constantrestore_cmds(struct adreno_device *adreno_dev, - struct adreno_context *drawctxt) -{ - unsigned int *cmd = tmp_ctx.cmd; - unsigned int *start = cmd; - unsigned int mode = 4; /* Indirect mode */ - unsigned int stateblock; - unsigned int numunits; - unsigned int statetype; - - drawctxt->cond_execs[2].hostptr = cmd; - drawctxt->cond_execs[2].gpuaddr = virt2gpu(cmd, &drawctxt->gpustate); - *cmd++ = 0; - drawctxt->cond_execs[3].hostptr = cmd; - drawctxt->cond_execs[3].gpuaddr = virt2gpu(cmd, &drawctxt->gpustate); - *cmd++ = 0; - -#ifndef CONFIG_MSM_KGSL_DISABLE_SHADOW_WRITES - *cmd++ = cp_type3_packet(CP_LOAD_CONSTANT_CONTEXT, 3); - *cmd++ = (drawctxt->gpustate.gpuaddr + REG_OFFSET) & 0xFFFFE000; - *cmd++ = 4 << 16; - *cmd++ = 0x0; -#endif - /* HLSQ full update */ - *cmd++ = cp_type3_packet(CP_SET_CONSTANT, 2); - *cmd++ = CP_REG(A3XX_HLSQ_CONTROL_0_REG); - *cmd++ = 0x68000240; /* A3XX_HLSQ_CONTROL_0_REG */ - -#ifndef CONFIG_MSM_KGSL_DISABLE_SHADOW_WRITES - /* Re-enable shadowing */ - *cmd++ = cp_type3_packet(CP_LOAD_CONSTANT_CONTEXT, 3); - *cmd++ = (drawctxt->gpustate.gpuaddr + REG_OFFSET) & 0xFFFFE000; - *cmd++ = (4 << 16) | (1 << 24); - *cmd++ = 0x0; -#endif - - /* Load vertex shader constants */ - *cmd++ = cp_type3_packet(CP_COND_EXEC, 4); - *cmd++ = drawctxt->cond_execs[2].gpuaddr >> 2; - *cmd++ = drawctxt->cond_execs[2].gpuaddr >> 2; - *cmd++ = 0x0000ffff; - *cmd++ = 3; /* EXEC_COUNT */ - *cmd++ = cp_type3_packet(CP_LOAD_STATE, 2); - drawctxt->constant_load_commands[0].hostptr = cmd; - drawctxt->constant_load_commands[0].gpuaddr = virt2gpu(cmd, - &drawctxt->gpustate); - - /* - From fixup: - - mode = 4 (indirect) - stateblock = 4 (Vertex constants) - numunits = SP_VS_CTRL_REG1.VSCONSTLENGTH * 2; (256bit units) - - From register spec: - SP_VS_CTRL_REG1.VSCONSTLENGTH [09:00]: 0-512, unit = 128bits. - - ord1 = (numunits<<22) | (stateblock<<19) | (mode<<16); - */ - - *cmd++ = 0; /* ord1 */ - *cmd++ = ((drawctxt->gpustate.gpuaddr) & 0xfffffffc) | 1; - - /* Load fragment shader constants */ - *cmd++ = cp_type3_packet(CP_COND_EXEC, 4); - *cmd++ = drawctxt->cond_execs[3].gpuaddr >> 2; - *cmd++ = drawctxt->cond_execs[3].gpuaddr >> 2; - *cmd++ = 0x0000ffff; - *cmd++ = 3; /* EXEC_COUNT */ - *cmd++ = cp_type3_packet(CP_LOAD_STATE, 2); - drawctxt->constant_load_commands[1].hostptr = cmd; - drawctxt->constant_load_commands[1].gpuaddr = - virt2gpu(cmd, &drawctxt->gpustate); - /* - From fixup: - - mode = 4 (indirect) - stateblock = 6 (Fragment constants) - numunits = SP_FS_CTRL_REG1.FSCONSTLENGTH * 2; (256bit units) - - From register spec: - SP_FS_CTRL_REG1.FSCONSTLENGTH [09:00]: 0-512, unit = 128bits. - - ord1 = (numunits<<22) | (stateblock<<19) | (mode<<16); - */ - - *cmd++ = 0; /* ord1 */ - drawctxt->constant_load_commands[2].hostptr = cmd; - drawctxt->constant_load_commands[2].gpuaddr = - virt2gpu(cmd, &drawctxt->gpustate); - /* - From fixup: - base = drawctxt->gpustate.gpuaddr (ALU constant shadow base) - offset = SP_FS_OBJ_OFFSET_REG.CONSTOBJECTSTARTOFFSET - - From register spec: - SP_FS_OBJ_OFFSET_REG.CONSTOBJECTSTARTOFFSET [16:24]: Constant object - start offset in on chip RAM, - 128bit aligned - - ord2 = base + offset | 1 - Because of the base alignment we can use - ord2 = base | offset | 1 - */ - *cmd++ = 0; /* ord2 */ - - /* Restore VS texture memory objects */ - stateblock = 0; - statetype = 1; - numunits = (TEX_SIZE_MEM_OBJECTS / 7) / 4; - - *cmd++ = cp_type3_packet(CP_LOAD_STATE, 2); - *cmd++ = (numunits << 22) | (stateblock << 19) | (mode << 16); - *cmd++ = ((drawctxt->gpustate.gpuaddr + VS_TEX_OFFSET_MEM_OBJECTS) - & 0xfffffffc) | statetype; - - /* Restore VS texture mipmap addresses */ - stateblock = 1; - statetype = 1; - numunits = TEX_SIZE_MIPMAP / 4; - *cmd++ = cp_type3_packet(CP_LOAD_STATE, 2); - *cmd++ = (numunits << 22) | (stateblock << 19) | (mode << 16); - *cmd++ = ((drawctxt->gpustate.gpuaddr + VS_TEX_OFFSET_MIPMAP) - & 0xfffffffc) | statetype; - - /* Restore VS texture sampler objects */ - stateblock = 0; - statetype = 0; - numunits = (TEX_SIZE_SAMPLER_OBJ / 2) / 4; - *cmd++ = cp_type3_packet(CP_LOAD_STATE, 2); - *cmd++ = (numunits << 22) | (stateblock << 19) | (mode << 16); - *cmd++ = ((drawctxt->gpustate.gpuaddr + VS_TEX_OFFSET_SAMPLER_OBJ) - & 0xfffffffc) | statetype; - - /* Restore FS texture memory objects */ - stateblock = 2; - statetype = 1; - numunits = (TEX_SIZE_MEM_OBJECTS / 7) / 4; - *cmd++ = cp_type3_packet(CP_LOAD_STATE, 2); - *cmd++ = (numunits << 22) | (stateblock << 19) | (mode << 16); - *cmd++ = ((drawctxt->gpustate.gpuaddr + FS_TEX_OFFSET_MEM_OBJECTS) - & 0xfffffffc) | statetype; - - /* Restore FS texture mipmap addresses */ - stateblock = 3; - statetype = 1; - numunits = TEX_SIZE_MIPMAP / 4; - *cmd++ = cp_type3_packet(CP_LOAD_STATE, 2); - *cmd++ = (numunits << 22) | (stateblock << 19) | (mode << 16); - *cmd++ = ((drawctxt->gpustate.gpuaddr + FS_TEX_OFFSET_MIPMAP) - & 0xfffffffc) | statetype; - - /* Restore FS texture sampler objects */ - stateblock = 2; - statetype = 0; - numunits = (TEX_SIZE_SAMPLER_OBJ / 2) / 4; - *cmd++ = cp_type3_packet(CP_LOAD_STATE, 2); - *cmd++ = (numunits << 22) | (stateblock << 19) | (mode << 16); - *cmd++ = ((drawctxt->gpustate.gpuaddr + FS_TEX_OFFSET_SAMPLER_OBJ) - & 0xfffffffc) | statetype; - - create_ib1(drawctxt, drawctxt->constant_restore, start, cmd); - tmp_ctx.cmd = cmd; -} - -static void build_shader_restore_cmds(struct adreno_device *adreno_dev, - struct adreno_context *drawctxt) -{ - unsigned int *cmd = tmp_ctx.cmd; - unsigned int *start = cmd; - - /* Vertex shader */ - *cmd++ = cp_type3_packet(CP_COND_EXEC, 4); - *cmd++ = drawctxt->cond_execs[0].gpuaddr >> 2; - *cmd++ = drawctxt->cond_execs[0].gpuaddr >> 2; - *cmd++ = 1; - *cmd++ = 3; /* EXEC_COUNT */ - - *cmd++ = cp_type3_packet(CP_LOAD_STATE, 2); - drawctxt->shader_load_commands[0].hostptr = cmd; - drawctxt->shader_load_commands[0].gpuaddr = - virt2gpu(cmd, &drawctxt->gpustate); - /* - From fixup: - - mode = 4 (indirect) - stateblock = 4 (Vertex shader) - numunits = SP_VS_CTRL_REG0.VS_LENGTH - - From regspec: - SP_VS_CTRL_REG0.VS_LENGTH [31:24]: VS length, unit = 256bits. - If bit31 is 1, it means overflow - or any long shader. - - ord1 = (numunits<<22) | (stateblock<<19) | (mode<<11) - */ - *cmd++ = 0; /*ord1 */ - *cmd++ = (drawctxt->gpustate.gpuaddr + SHADER_OFFSET) & 0xfffffffc; - - /* Fragment shader */ - *cmd++ = cp_type3_packet(CP_COND_EXEC, 4); - *cmd++ = drawctxt->cond_execs[1].gpuaddr >> 2; - *cmd++ = drawctxt->cond_execs[1].gpuaddr >> 2; - *cmd++ = 1; - *cmd++ = 3; /* EXEC_COUNT */ - - *cmd++ = cp_type3_packet(CP_LOAD_STATE, 2); - drawctxt->shader_load_commands[1].hostptr = cmd; - drawctxt->shader_load_commands[1].gpuaddr = - virt2gpu(cmd, &drawctxt->gpustate); - /* - From fixup: - - mode = 4 (indirect) - stateblock = 6 (Fragment shader) - numunits = SP_FS_CTRL_REG0.FS_LENGTH - - From regspec: - SP_FS_CTRL_REG0.FS_LENGTH [31:24]: FS length, unit = 256bits. - If bit31 is 1, it means overflow - or any long shader. - - ord1 = (numunits<<22) | (stateblock<<19) | (mode<<11) - */ - *cmd++ = 0; /*ord1 */ - *cmd++ = (drawctxt->gpustate.gpuaddr + SHADER_OFFSET - + (SHADER_SHADOW_SIZE / 2)) & 0xfffffffc; - - create_ib1(drawctxt, drawctxt->shader_restore, start, cmd); - tmp_ctx.cmd = cmd; -} - -static void build_hlsqcontrol_restore_cmds(struct adreno_device *adreno_dev, - struct adreno_context *drawctxt) -{ - unsigned int *cmd = tmp_ctx.cmd; - unsigned int *start = cmd; - - *cmd++ = cp_type3_packet(CP_SET_CONSTANT, 2); - *cmd++ = CP_REG(A3XX_HLSQ_CONTROL_0_REG); - drawctxt->hlsqcontrol_restore_commands[0].hostptr = cmd; - drawctxt->hlsqcontrol_restore_commands[0].gpuaddr - = virt2gpu(cmd, &drawctxt->gpustate); - *cmd++ = 0; - - /* Create indirect buffer command for above command sequence */ - create_ib1(drawctxt, drawctxt->hlsqcontrol_restore, start, cmd); - - tmp_ctx.cmd = cmd; -} - -/* IB that modifies the shader and constant sizes and offsets in restore IBs. */ -static void build_restore_fixup_cmds(struct adreno_device *adreno_dev, - struct adreno_context *drawctxt) -{ - unsigned int *cmd = tmp_ctx.cmd; - unsigned int *start = cmd; - -#ifdef GSL_CONTEXT_SWITCH_CPU_SYNC - /* Save shader sizes */ - *cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2); - *cmd++ = A3XX_SP_VS_CTRL_REG0; - *cmd++ = drawctxt->shader_load_commands[0].gpuaddr; - - *cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2); - *cmd++ = A3XX_SP_FS_CTRL_REG0; - *cmd++ = drawctxt->shader_load_commands[1].gpuaddr; - - /* Save constant sizes */ - *cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2); - *cmd++ = A3XX_SP_VS_CTRL_REG1; - *cmd++ = drawctxt->constant_load_commands[0].gpuaddr; - - *cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2); - *cmd++ = A3XX_SP_FS_CTRL_REG1; - *cmd++ = drawctxt->constant_load_commands[1].gpuaddr; - - /* Save constant offsets */ - *cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2); - *cmd++ = A3XX_SP_FS_OBJ_OFFSET_REG; - *cmd++ = drawctxt->constant_load_commands[2].gpuaddr; -#else - /* Save shader sizes */ - cmd = rmw_regtomem(cmd, A3XX_SP_VS_CTRL_REG0, 0x7f000000, - 30, (4 << 19) | (4 << 16), - drawctxt->shader_load_commands[0].gpuaddr); - - cmd = rmw_regtomem(cmd, A3XX_SP_FS_CTRL_REG0, 0x7f000000, - 30, (6 << 19) | (4 << 16), - drawctxt->shader_load_commands[1].gpuaddr); - - /* Save constant sizes */ - cmd = rmw_regtomem(cmd, A3XX_SP_VS_CTRL_REG1, 0x000003ff, - 23, (4 << 19) | (4 << 16), - drawctxt->constant_load_commands[0].gpuaddr); - - cmd = rmw_regtomem(cmd, A3XX_SP_FS_CTRL_REG1, 0x000003ff, - 23, (6 << 19) | (4 << 16), - drawctxt->constant_load_commands[1].gpuaddr); - - /* Modify constant restore conditionals */ - cmd = rmw_regtomem(cmd, A3XX_SP_VS_CTRL_REG1, 0x000003ff, - 0, 0, drawctxt->cond_execs[2].gpuaddr); - - cmd = rmw_regtomem(cmd, A3XX_SP_FS_CTRL_REG1, 0x000003ff, - 0, 0, drawctxt->cond_execs[3].gpuaddr); - - /* Save fragment constant shadow offset */ - cmd = rmw_regtomem(cmd, A3XX_SP_FS_OBJ_OFFSET_REG, 0x00ff0000, - 18, (drawctxt->gpustate.gpuaddr & 0xfffffe00) | 1, - drawctxt->constant_load_commands[2].gpuaddr); -#endif - - /* Use mask value to avoid flushing HLSQ which would cause the HW to - discard all the shader data */ - - cmd = rmw_regtomem(cmd, A3XX_HLSQ_CONTROL_0_REG, 0x9ffffdff, - 0, 0, drawctxt->hlsqcontrol_restore_commands[0].gpuaddr); - - create_ib1(drawctxt, drawctxt->restore_fixup, start, cmd); - - tmp_ctx.cmd = cmd; -} - -static int a3xx_create_gpustate_shadow(struct adreno_device *adreno_dev, - struct adreno_context *drawctxt) -{ - drawctxt->flags |= CTXT_FLAGS_STATE_SHADOW; - - build_regrestore_cmds(adreno_dev, drawctxt); - build_constantrestore_cmds(adreno_dev, drawctxt); - build_hlsqcontrol_restore_cmds(adreno_dev, drawctxt); - build_regconstantsave_cmds(adreno_dev, drawctxt); - build_shader_save_cmds(adreno_dev, drawctxt); - build_shader_restore_cmds(adreno_dev, drawctxt); - build_restore_fixup_cmds(adreno_dev, drawctxt); - build_save_fixup_cmds(adreno_dev, drawctxt); - - return 0; -} - -/* create buffers for saving/restoring registers, constants, & GMEM */ -static int a3xx_create_gmem_shadow(struct adreno_device *adreno_dev, - struct adreno_context *drawctxt) -{ - int result; - - calc_gmemsize(&drawctxt->context_gmem_shadow, adreno_dev->gmem_size); - tmp_ctx.gmem_base = adreno_dev->gmem_base; - - result = kgsl_allocate(&drawctxt->context_gmem_shadow.gmemshadow, - drawctxt->pagetable, drawctxt->context_gmem_shadow.size); - - if (result) - return result; - - build_quad_vtxbuff(drawctxt, &drawctxt->context_gmem_shadow, - &tmp_ctx.cmd); - - tmp_ctx.cmd = build_gmem2sys_cmds(adreno_dev, drawctxt, - &drawctxt->context_gmem_shadow); - 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); - - drawctxt->flags |= CTXT_FLAGS_GMEM_SHADOW; - - return 0; -} - -static int a3xx_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); - - if (ret) - return ret; - - kgsl_sharedmem_set(&drawctxt->gpustate, 0, 0, CONTEXT_SIZE); - tmp_ctx.cmd = drawctxt->gpustate.hostptr + CMD_OFFSET; - - if (!(drawctxt->flags & CTXT_FLAGS_PREAMBLE)) { - ret = a3xx_create_gpustate_shadow(adreno_dev, drawctxt); - if (ret) - goto done; - - drawctxt->flags |= CTXT_FLAGS_SHADER_SAVE; - } - - if (!(drawctxt->flags & CTXT_FLAGS_NOGMEMALLOC)) - ret = a3xx_create_gmem_shadow(adreno_dev, drawctxt); - -done: - if (ret) - kgsl_sharedmem_free(&drawctxt->gpustate); - - return ret; -} - -static void a3xx_drawctxt_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"); - - if (!(context->flags & CTXT_FLAGS_PREAMBLE)) { - /* Fixup self modifying IBs for save operations */ - adreno_ringbuffer_issuecmds(device, context, - KGSL_CMD_FLAGS_NONE, context->save_fixup, 3); - - /* save registers and constants. */ - adreno_ringbuffer_issuecmds(device, context, - KGSL_CMD_FLAGS_NONE, - context->regconstant_save, 3); - - if (context->flags & CTXT_FLAGS_SHADER_SAVE) { - /* Save shader instructions */ - adreno_ringbuffer_issuecmds(device, context, - KGSL_CMD_FLAGS_PMODE, context->shader_save, 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, context, - KGSL_CMD_FLAGS_PMODE, - context->context_gmem_shadow. - gmem_save, 3); - context->flags |= CTXT_FLAGS_GMEM_RESTORE; - } -} - -static void a3xx_drawctxt_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 pagetable and thats it */ - kgsl_mmu_setstate(&device->mmu, device->mmu.defaultpagetable, - adreno_dev->drawctxt_active->id); - 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_MEMSTORE_OFFSET(KGSL_MEMSTORE_GLOBAL, current_context); - cmds[4] = context->id; - adreno_ringbuffer_issuecmds(device, context, KGSL_CMD_FLAGS_NONE, - cmds, 5); - kgsl_mmu_setstate(&device->mmu, context->pagetable, context->id); - - /* - * Restore GMEM. (note: changes shader. - * Shader must not already be restored.) - */ - - if (context->flags & CTXT_FLAGS_GMEM_RESTORE) { - adreno_ringbuffer_issuecmds(device, context, - KGSL_CMD_FLAGS_PMODE, - context->context_gmem_shadow. - gmem_restore, 3); - context->flags &= ~CTXT_FLAGS_GMEM_RESTORE; - } - - if (!(context->flags & CTXT_FLAGS_PREAMBLE)) { - adreno_ringbuffer_issuecmds(device, context, - KGSL_CMD_FLAGS_NONE, context->reg_restore, 3); - - /* Fixup self modifying IBs for restore operations */ - adreno_ringbuffer_issuecmds(device, context, - KGSL_CMD_FLAGS_NONE, - context->restore_fixup, 3); - - adreno_ringbuffer_issuecmds(device, context, - KGSL_CMD_FLAGS_NONE, - context->constant_restore, 3); - - if (context->flags & CTXT_FLAGS_SHADER_RESTORE) - adreno_ringbuffer_issuecmds(device, context, - KGSL_CMD_FLAGS_NONE, - context->shader_restore, 3); - - /* Restore HLSQ_CONTROL_0 register */ - adreno_ringbuffer_issuecmds(device, context, - KGSL_CMD_FLAGS_NONE, - context->hlsqcontrol_restore, 3); - } -} - -static void a3xx_rb_init(struct adreno_device *adreno_dev, - struct adreno_ringbuffer *rb) -{ - unsigned int *cmds, cmds_gpu; - cmds = adreno_ringbuffer_allocspace(rb, 18); - cmds_gpu = rb->buffer_desc.gpuaddr + sizeof(uint) * (rb->wptr - 18); - - GSL_RB_WRITE(cmds, cmds_gpu, cp_type3_packet(CP_ME_INIT, 17)); - GSL_RB_WRITE(cmds, cmds_gpu, 0x000003f7); - GSL_RB_WRITE(cmds, cmds_gpu, 0x00000000); - GSL_RB_WRITE(cmds, cmds_gpu, 0x00000000); - GSL_RB_WRITE(cmds, cmds_gpu, 0x00000000); - GSL_RB_WRITE(cmds, cmds_gpu, 0x00000080); - GSL_RB_WRITE(cmds, cmds_gpu, 0x00000100); - GSL_RB_WRITE(cmds, cmds_gpu, 0x00000180); - GSL_RB_WRITE(cmds, cmds_gpu, 0x00006600); - GSL_RB_WRITE(cmds, cmds_gpu, 0x00000150); - GSL_RB_WRITE(cmds, cmds_gpu, 0x0000014e); - GSL_RB_WRITE(cmds, cmds_gpu, 0x00000154); - GSL_RB_WRITE(cmds, cmds_gpu, 0x00000001); - GSL_RB_WRITE(cmds, cmds_gpu, 0x00000000); - GSL_RB_WRITE(cmds, cmds_gpu, 0x00000000); - /* Protected mode control - turned off for A3XX */ - GSL_RB_WRITE(cmds, cmds_gpu, 0x00000000); - GSL_RB_WRITE(cmds, cmds_gpu, 0x00000000); - GSL_RB_WRITE(cmds, cmds_gpu, 0x00000000); - - adreno_ringbuffer_submit(rb); -} - -static void a3xx_err_callback(struct adreno_device *adreno_dev, int bit) -{ - struct kgsl_device *device = &adreno_dev->dev; - const char *err = ""; - - switch (bit) { - case A3XX_INT_RBBM_AHB_ERROR: { - unsigned int reg; - - adreno_regread(device, A3XX_RBBM_AHB_ERROR_STATUS, ®); - - /* - * Return the word address of the erroring register so that it - * matches the register specification - */ - - KGSL_DRV_CRIT(device, - "RBBM | AHB bus error | %s | addr=%x | ports=%x:%x\n", - reg & (1 << 28) ? "WRITE" : "READ", - (reg & 0xFFFFF) >> 2, (reg >> 20) & 0x3, - (reg >> 24) & 0x3); - - /* Clear the error */ - adreno_regwrite(device, A3XX_RBBM_AHB_CMD, (1 << 3)); - return; - } - case A3XX_INT_RBBM_REG_TIMEOUT: - err = "RBBM: AHB register timeout"; - break; - case A3XX_INT_RBBM_ME_MS_TIMEOUT: - err = "RBBM: ME master split timeout"; - break; - case A3XX_INT_RBBM_PFP_MS_TIMEOUT: - err = "RBBM: PFP master split timeout"; - break; - case A3XX_INT_RBBM_ATB_BUS_OVERFLOW: - err = "RBBM: ATB bus oveflow"; - break; - case A3XX_INT_VFD_ERROR: - err = "VFD: Out of bounds access"; - break; - case A3XX_INT_CP_T0_PACKET_IN_IB: - err = "ringbuffer TO packet in IB interrupt"; - break; - case A3XX_INT_CP_OPCODE_ERROR: - err = "ringbuffer opcode error interrupt"; - break; - case A3XX_INT_CP_RESERVED_BIT_ERROR: - err = "ringbuffer reserved bit error interrupt"; - break; - case A3XX_INT_CP_HW_FAULT: - err = "ringbuffer hardware fault"; - break; - case A3XX_INT_CP_REG_PROTECT_FAULT: - err = "ringbuffer protected mode error interrupt"; - break; - case A3XX_INT_CP_AHB_ERROR_HALT: - err = "ringbuffer AHB error interrupt"; - break; - case A3XX_INT_MISC_HANG_DETECT: - err = "MISC: GPU hang detected"; - break; - case A3XX_INT_UCHE_OOB_ACCESS: - err = "UCHE: Out of bounds access"; - break; - } - - KGSL_DRV_CRIT(device, "%s\n", err); - kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_OFF); -} - -static void a3xx_cp_callback(struct adreno_device *adreno_dev, int irq) -{ - struct kgsl_device *device = &adreno_dev->dev; - - if (irq == A3XX_INT_CP_RB_INT) { - unsigned int context_id; - kgsl_sharedmem_readl(&device->memstore, &context_id, - KGSL_MEMSTORE_OFFSET(KGSL_MEMSTORE_GLOBAL, - current_context)); - if (context_id < KGSL_MEMSTORE_MAX) { - kgsl_sharedmem_writel(&device->memstore, - KGSL_MEMSTORE_OFFSET(context_id, - ts_cmp_enable), 0); - wmb(); - } - KGSL_CMD_WARN(device, "ringbuffer rb interrupt\n"); - } - - wake_up_interruptible_all(&device->wait_queue); - - /* Schedule work to free mem and issue ibs */ - queue_work(device->work_queue, &device->ts_expired_ws); - - atomic_notifier_call_chain(&device->ts_notifier_list, - device->id, NULL); -} - -#define A3XX_IRQ_CALLBACK(_c) { .func = _c } - -#define A3XX_INT_MASK \ - ((1 << A3XX_INT_RBBM_AHB_ERROR) | \ - (1 << A3XX_INT_RBBM_ATB_BUS_OVERFLOW) | \ - (1 << A3XX_INT_CP_T0_PACKET_IN_IB) | \ - (1 << A3XX_INT_CP_OPCODE_ERROR) | \ - (1 << A3XX_INT_CP_RESERVED_BIT_ERROR) | \ - (1 << A3XX_INT_CP_HW_FAULT) | \ - (1 << A3XX_INT_CP_IB1_INT) | \ - (1 << A3XX_INT_CP_IB2_INT) | \ - (1 << A3XX_INT_CP_RB_INT) | \ - (1 << A3XX_INT_CP_REG_PROTECT_FAULT) | \ - (1 << A3XX_INT_CP_AHB_ERROR_HALT) | \ - (1 << A3XX_INT_UCHE_OOB_ACCESS)) - -static struct { - void (*func)(struct adreno_device *, int); -} a3xx_irq_funcs[] = { - A3XX_IRQ_CALLBACK(NULL), /* 0 - RBBM_GPU_IDLE */ - A3XX_IRQ_CALLBACK(a3xx_err_callback), /* 1 - RBBM_AHB_ERROR */ - A3XX_IRQ_CALLBACK(a3xx_err_callback), /* 2 - RBBM_REG_TIMEOUT */ - A3XX_IRQ_CALLBACK(a3xx_err_callback), /* 3 - RBBM_ME_MS_TIMEOUT */ - A3XX_IRQ_CALLBACK(a3xx_err_callback), /* 4 - RBBM_PFP_MS_TIMEOUT */ - A3XX_IRQ_CALLBACK(a3xx_err_callback), /* 5 - RBBM_ATB_BUS_OVERFLOW */ - A3XX_IRQ_CALLBACK(a3xx_err_callback), /* 6 - RBBM_VFD_ERROR */ - A3XX_IRQ_CALLBACK(NULL), /* 7 - CP_SW */ - A3XX_IRQ_CALLBACK(a3xx_err_callback), /* 8 - CP_T0_PACKET_IN_IB */ - A3XX_IRQ_CALLBACK(a3xx_err_callback), /* 9 - CP_OPCODE_ERROR */ - A3XX_IRQ_CALLBACK(a3xx_err_callback), /* 10 - CP_RESERVED_BIT_ERROR */ - A3XX_IRQ_CALLBACK(a3xx_err_callback), /* 11 - CP_HW_FAULT */ - A3XX_IRQ_CALLBACK(NULL), /* 12 - CP_DMA */ - A3XX_IRQ_CALLBACK(a3xx_cp_callback), /* 13 - CP_IB2_INT */ - A3XX_IRQ_CALLBACK(a3xx_cp_callback), /* 14 - CP_IB1_INT */ - A3XX_IRQ_CALLBACK(a3xx_cp_callback), /* 15 - CP_RB_INT */ - A3XX_IRQ_CALLBACK(a3xx_err_callback), /* 16 - CP_REG_PROTECT_FAULT */ - A3XX_IRQ_CALLBACK(NULL), /* 17 - CP_RB_DONE_TS */ - A3XX_IRQ_CALLBACK(NULL), /* 18 - CP_VS_DONE_TS */ - A3XX_IRQ_CALLBACK(NULL), /* 19 - CP_PS_DONE_TS */ - A3XX_IRQ_CALLBACK(NULL), /* 20 - CP_CACHE_FLUSH_TS */ - A3XX_IRQ_CALLBACK(a3xx_err_callback), /* 21 - CP_AHB_ERROR_FAULT */ - A3XX_IRQ_CALLBACK(NULL), /* 22 - Unused */ - A3XX_IRQ_CALLBACK(NULL), /* 23 - Unused */ - A3XX_IRQ_CALLBACK(NULL), /* 24 - MISC_HANG_DETECT */ - A3XX_IRQ_CALLBACK(a3xx_err_callback), /* 25 - UCHE_OOB_ACCESS */ - /* 26 to 31 - Unused */ -}; - -static irqreturn_t a3xx_irq_handler(struct adreno_device *adreno_dev) -{ - struct kgsl_device *device = &adreno_dev->dev; - irqreturn_t ret = IRQ_NONE; - unsigned int status, tmp; - int i; - - adreno_regread(&adreno_dev->dev, A3XX_RBBM_INT_0_STATUS, &status); - - for (tmp = status, i = 0; tmp && i < ARRAY_SIZE(a3xx_irq_funcs); i++) { - if (tmp & 1) { - if (a3xx_irq_funcs[i].func != NULL) { - a3xx_irq_funcs[i].func(adreno_dev, i); - ret = IRQ_HANDLED; - } else { - KGSL_DRV_CRIT(device, - "Unhandled interrupt bit %x\n", i); - } - } - - tmp >>= 1; - } - - trace_kgsl_a3xx_irq_status(device, status); - - if (status) - adreno_regwrite(&adreno_dev->dev, A3XX_RBBM_INT_CLEAR_CMD, - status); - return ret; -} - -static void a3xx_irq_control(struct adreno_device *adreno_dev, int state) -{ - struct kgsl_device *device = &adreno_dev->dev; - - if (state) - adreno_regwrite(device, A3XX_RBBM_INT_0_MASK, A3XX_INT_MASK); - else - adreno_regwrite(device, A3XX_RBBM_INT_0_MASK, 0); -} - -static unsigned int a3xx_busy_cycles(struct adreno_device *adreno_dev) -{ - struct kgsl_device *device = &adreno_dev->dev; - unsigned int reg, val; - - /* Freeze the counter */ - adreno_regread(device, A3XX_RBBM_RBBM_CTL, ®); - reg &= ~RBBM_RBBM_CTL_ENABLE_PWR_CTR1; - adreno_regwrite(device, A3XX_RBBM_RBBM_CTL, reg); - - /* Read the value */ - adreno_regread(device, A3XX_RBBM_PERFCTR_PWR_1_LO, &val); - - /* Reset the counter */ - reg |= RBBM_RBBM_CTL_RESET_PWR_CTR1; - adreno_regwrite(device, A3XX_RBBM_RBBM_CTL, reg); - - /* Re-enable the counter */ - reg &= ~RBBM_RBBM_CTL_RESET_PWR_CTR1; - reg |= RBBM_RBBM_CTL_ENABLE_PWR_CTR1; - adreno_regwrite(device, A3XX_RBBM_RBBM_CTL, reg); - - return val; -} - -static void a3xx_start(struct adreno_device *adreno_dev) -{ - struct kgsl_device *device = &adreno_dev->dev; - - /* Set up 16 deep read/write request queues */ - - adreno_regwrite(device, A3XX_VBIF_IN_RD_LIM_CONF0, 0x10101010); - adreno_regwrite(device, A3XX_VBIF_IN_RD_LIM_CONF1, 0x10101010); - adreno_regwrite(device, A3XX_VBIF_OUT_RD_LIM_CONF0, 0x10101010); - adreno_regwrite(device, A3XX_VBIF_OUT_WR_LIM_CONF0, 0x10101010); - adreno_regwrite(device, A3XX_VBIF_DDR_OUT_MAX_BURST, 0x00000303); - adreno_regwrite(device, A3XX_VBIF_IN_WR_LIM_CONF0, 0x10101010); - adreno_regwrite(device, A3XX_VBIF_IN_WR_LIM_CONF1, 0x10101010); - - /* Enable WR-REQ */ - adreno_regwrite(device, A3XX_VBIF_GATE_OFF_WRREQ_EN, 0x000000FF); - - /* Set up round robin arbitration between both AXI ports */ - adreno_regwrite(device, A3XX_VBIF_ARB_CTL, 0x00000030); - - /* Set up AOOO */ - adreno_regwrite(device, A3XX_VBIF_OUT_AXI_AOOO_EN, 0x0000003C); - adreno_regwrite(device, A3XX_VBIF_OUT_AXI_AOOO, 0x003C003C); - - if (cpu_is_apq8064()) { - /* Enable 1K sort */ - adreno_regwrite(device, A3XX_VBIF_ABIT_SORT, 0x000000FF); - adreno_regwrite(device, A3XX_VBIF_ABIT_SORT_CONF, 0x000000A4); - } - /* Make all blocks contribute to the GPU BUSY perf counter */ - adreno_regwrite(device, A3XX_RBBM_GPU_BUSY_MASKED, 0xFFFFFFFF); - - /* Tune the hystersis counters for SP and CP idle detection */ - adreno_regwrite(device, A3XX_RBBM_SP_HYST_CNT, 0x10); - adreno_regwrite(device, A3XX_RBBM_WAIT_IDLE_CLOCKS_CTL, 0x10); - - /* Enable the RBBM error reporting bits. This lets us get - useful information on failure */ - - adreno_regwrite(device, A3XX_RBBM_AHB_CTL0, 0x00000001); - - /* Enable AHB error reporting */ - adreno_regwrite(device, A3XX_RBBM_AHB_CTL1, 0xA6FFFFFF); - - /* Turn on the power counters */ - adreno_regwrite(device, A3XX_RBBM_RBBM_CTL, 0x00030000); - - /* Turn on hang detection - this spews a lot of useful information - * into the RBBM registers on a hang */ - - adreno_regwrite(device, A3XX_RBBM_INTERFACE_HANG_INT_CTL, - (1 << 16) | 0xFFF); - - /* Enable Clock gating */ - adreno_regwrite(device, A3XX_RBBM_CLOCK_CTL, - A3XX_RBBM_CLOCK_CTL_DEFAULT); - -} - -/* Defined in adreno_a3xx_snapshot.c */ -void *a3xx_snapshot(struct adreno_device *adreno_dev, void *snapshot, - int *remain, int hang); - -struct adreno_gpudev adreno_a3xx_gpudev = { - .reg_rbbm_status = A3XX_RBBM_STATUS, - .reg_cp_pfp_ucode_addr = A3XX_CP_PFP_UCODE_ADDR, - .reg_cp_pfp_ucode_data = A3XX_CP_PFP_UCODE_DATA, - - .ctxt_create = a3xx_drawctxt_create, - .ctxt_save = a3xx_drawctxt_save, - .ctxt_restore = a3xx_drawctxt_restore, - .ctxt_draw_workaround = NULL, - .rb_init = a3xx_rb_init, - .irq_control = a3xx_irq_control, - .irq_handler = a3xx_irq_handler, - .busy_cycles = a3xx_busy_cycles, - .start = a3xx_start, - .snapshot = a3xx_snapshot, -}; diff --git a/drivers/gpu/msm/adreno_a3xx_snapshot.c b/drivers/gpu/msm/adreno_a3xx_snapshot.c deleted file mode 100644 index a3bee4dc5c1..00000000000 --- a/drivers/gpu/msm/adreno_a3xx_snapshot.c +++ /dev/null @@ -1,332 +0,0 @@ -/* 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" -#include "a3xx_reg.h" - -#define DEBUG_SECTION_SZ(_dwords) (((_dwords) * sizeof(unsigned int)) \ - + sizeof(struct kgsl_snapshot_debug)) - -#define SHADER_MEMORY_SIZE 0x4000 - -static int a3xx_snapshot_shader_memory(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(SHADER_MEMORY_SIZE)) { - SNAPSHOT_ERR_NOMEM(device, "SHADER MEMORY"); - return 0; - } - - header->type = SNAPSHOT_DEBUG_SHADER_MEMORY; - header->size = SHADER_MEMORY_SIZE; - - for (i = 0; i < SHADER_MEMORY_SIZE; i++) - adreno_regread(device, 0x4000 + i, &data[i]); - - return DEBUG_SECTION_SZ(SHADER_MEMORY_SIZE); -} - -#define VPC_MEMORY_BANKS 4 -#define VPC_MEMORY_SIZE 512 - -static int a3xx_snapshot_vpc_memory(struct kgsl_device *device, void *snapshot, - int remain, void *priv) -{ - struct kgsl_snapshot_debug *header = snapshot; - unsigned int *data = snapshot + sizeof(*header); - int size = VPC_MEMORY_BANKS * VPC_MEMORY_SIZE; - int bank, addr, i = 0; - - if (remain < DEBUG_SECTION_SZ(size)) { - SNAPSHOT_ERR_NOMEM(device, "VPC MEMORY"); - return 0; - } - - header->type = SNAPSHOT_DEBUG_VPC_MEMORY; - header->size = size; - - for (bank = 0; bank < VPC_MEMORY_BANKS; bank++) { - for (addr = 0; addr < VPC_MEMORY_SIZE; addr++) { - unsigned int val = bank | (addr << 4); - adreno_regwrite(device, - A3XX_VPC_VPC_DEBUG_RAM_SEL, val); - adreno_regread(device, - A3XX_VPC_VPC_DEBUG_RAM_READ, &data[i++]); - } - } - - return DEBUG_SECTION_SZ(size); -} - -#define CP_MEQ_SIZE 16 -static int a3xx_snapshot_cp_meq(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(CP_MEQ_SIZE)) { - SNAPSHOT_ERR_NOMEM(device, "CP MEQ DEBUG"); - return 0; - } - - header->type = SNAPSHOT_DEBUG_CP_MEQ; - header->size = CP_MEQ_SIZE; - - adreno_regwrite(device, A3XX_CP_MEQ_ADDR, 0x0); - for (i = 0; i < CP_MEQ_SIZE; i++) - adreno_regread(device, A3XX_CP_MEQ_DATA, &data[i]); - - return DEBUG_SECTION_SZ(CP_MEQ_SIZE); -} - -static int a3xx_snapshot_cp_pm4_ram(struct kgsl_device *device, void *snapshot, - int remain, void *priv) -{ - struct adreno_device *adreno_dev = ADRENO_DEVICE(device); - struct kgsl_snapshot_debug *header = snapshot; - unsigned int *data = snapshot + sizeof(*header); - int i, size = adreno_dev->pm4_fw_size - 1; - - if (remain < DEBUG_SECTION_SZ(size)) { - SNAPSHOT_ERR_NOMEM(device, "CP PM4 RAM DEBUG"); - return 0; - } - - header->type = SNAPSHOT_DEBUG_CP_PM4_RAM; - header->size = size; - - /* - * Read the firmware from the GPU rather than use our cache in order to - * try to catch mis-programming or corruption in the hardware. We do - * use the cached version of the size, however, instead of trying to - * maintain always changing hardcoded constants - */ - - adreno_regwrite(device, REG_CP_ME_RAM_RADDR, 0x0); - for (i = 0; i < size; i++) - adreno_regread(device, REG_CP_ME_RAM_DATA, &data[i]); - - return DEBUG_SECTION_SZ(size); -} - -static int a3xx_snapshot_cp_pfp_ram(struct kgsl_device *device, void *snapshot, - int remain, void *priv) -{ - struct adreno_device *adreno_dev = ADRENO_DEVICE(device); - struct kgsl_snapshot_debug *header = snapshot; - unsigned int *data = snapshot + sizeof(*header); - int i, size = adreno_dev->pfp_fw_size - 1; - - if (remain < DEBUG_SECTION_SZ(size)) { - SNAPSHOT_ERR_NOMEM(device, "CP PFP RAM DEBUG"); - return 0; - } - - header->type = SNAPSHOT_DEBUG_CP_PFP_RAM; - header->size = size; - - /* - * Read the firmware from the GPU rather than use our cache in order to - * try to catch mis-programming or corruption in the hardware. We do - * use the cached version of the size, however, instead of trying to - * maintain always changing hardcoded constants - */ - kgsl_regwrite(device, A3XX_CP_PFP_UCODE_ADDR, 0x0); - for (i = 0; i < size; i++) - adreno_regread(device, A3XX_CP_PFP_UCODE_DATA, &data[i]); - - return DEBUG_SECTION_SZ(size); -} - -#define CP_ROQ_SIZE 128 - -static int a3xx_snapshot_cp_roq(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(CP_ROQ_SIZE)) { - SNAPSHOT_ERR_NOMEM(device, "CP ROQ DEBUG"); - return 0; - } - - header->type = SNAPSHOT_DEBUG_CP_ROQ; - header->size = CP_ROQ_SIZE; - - adreno_regwrite(device, A3XX_CP_ROQ_ADDR, 0x0); - for (i = 0; i < CP_ROQ_SIZE; i++) - adreno_regread(device, A3XX_CP_ROQ_DATA, &data[i]); - - return DEBUG_SECTION_SZ(CP_ROQ_SIZE); -} - -#define DEBUGFS_BLOCK_SIZE 0x40 - -static int a3xx_snapshot_debugbus_block(struct kgsl_device *device, - void *snapshot, int remain, void *priv) -{ - struct kgsl_snapshot_debugbus *header = snapshot; - unsigned int id = (unsigned int) priv; - unsigned int val; - int i; - unsigned int *data = snapshot + sizeof(*header); - int size = - (DEBUGFS_BLOCK_SIZE * sizeof(unsigned int)) + sizeof(*header); - - if (remain < size) { - SNAPSHOT_ERR_NOMEM(device, "DEBUGBUS"); - return 0; - } - - val = (id << 8) | (1 << 16); - - header->id = id; - header->count = DEBUGFS_BLOCK_SIZE; - - for (i = 0; i < DEBUGFS_BLOCK_SIZE; i++) { - adreno_regwrite(device, A3XX_RBBM_DEBUG_BUS_CTL, val | i); - adreno_regread(device, A3XX_RBBM_DEBUG_BUS_DATA_STATUS, - &data[i]); - } - - return size; -} - -static unsigned int debugbus_blocks[] = { - RBBM_BLOCK_ID_CP, - RBBM_BLOCK_ID_RBBM, - RBBM_BLOCK_ID_VBIF, - RBBM_BLOCK_ID_HLSQ, - RBBM_BLOCK_ID_UCHE, - RBBM_BLOCK_ID_PC, - RBBM_BLOCK_ID_VFD, - RBBM_BLOCK_ID_VPC, - RBBM_BLOCK_ID_TSE, - RBBM_BLOCK_ID_RAS, - RBBM_BLOCK_ID_VSC, - RBBM_BLOCK_ID_SP_0, - RBBM_BLOCK_ID_SP_1, - RBBM_BLOCK_ID_SP_2, - RBBM_BLOCK_ID_SP_3, - RBBM_BLOCK_ID_TPL1_0, - RBBM_BLOCK_ID_TPL1_1, - RBBM_BLOCK_ID_TPL1_2, - RBBM_BLOCK_ID_TPL1_3, - RBBM_BLOCK_ID_RB_0, - RBBM_BLOCK_ID_RB_1, - RBBM_BLOCK_ID_RB_2, - RBBM_BLOCK_ID_RB_3, - RBBM_BLOCK_ID_MARB_0, - RBBM_BLOCK_ID_MARB_1, - RBBM_BLOCK_ID_MARB_2, - RBBM_BLOCK_ID_MARB_3, -}; - -static void *a3xx_snapshot_debugbus(struct kgsl_device *device, - void *snapshot, int *remain) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(debugbus_blocks); i++) { - snapshot = kgsl_snapshot_add_section(device, - KGSL_SNAPSHOT_SECTION_DEBUGBUS, snapshot, remain, - a3xx_snapshot_debugbus_block, - (void *) debugbus_blocks[i]); - } - - return snapshot; -} - -/* A3XX GPU snapshot function - this is where all of the A3XX specific - * bits and pieces are grabbed into the snapshot memory - */ - -void *a3xx_snapshot(struct adreno_device *adreno_dev, void *snapshot, - int *remain, int hang) -{ - struct kgsl_device *device = &adreno_dev->dev; - struct kgsl_snapshot_registers regs; - - regs.regs = (unsigned int *) a3xx_registers; - regs.count = a3xx_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 = kgsl_snapshot_indexed_registers(device, snapshot, - remain, REG_CP_STATE_DEBUG_INDEX, - REG_CP_STATE_DEBUG_DATA, 0x0, 0x14); - - /* CP_ME indexed registers */ - snapshot = kgsl_snapshot_indexed_registers(device, snapshot, - remain, REG_CP_ME_CNTL, REG_CP_ME_STATUS, - 64, 44); - - /* Disable Clock gating temporarily for the debug bus to work */ - adreno_regwrite(device, A3XX_RBBM_CLOCK_CTL, 0x00); - - /* VPC memory */ - snapshot = kgsl_snapshot_add_section(device, - KGSL_SNAPSHOT_SECTION_DEBUG, snapshot, remain, - a3xx_snapshot_vpc_memory, NULL); - - /* CP MEQ */ - snapshot = kgsl_snapshot_add_section(device, - KGSL_SNAPSHOT_SECTION_DEBUG, snapshot, remain, - a3xx_snapshot_cp_meq, NULL); - - /* Shader working/shadow memory */ - snapshot = kgsl_snapshot_add_section(device, - KGSL_SNAPSHOT_SECTION_DEBUG, snapshot, remain, - a3xx_snapshot_shader_memory, NULL); - - - /* CP PFP and PM4 */ - /* Reading these will hang the GPU if it isn't already hung */ - - if (hang) { - snapshot = kgsl_snapshot_add_section(device, - KGSL_SNAPSHOT_SECTION_DEBUG, snapshot, remain, - a3xx_snapshot_cp_pfp_ram, NULL); - - snapshot = kgsl_snapshot_add_section(device, - KGSL_SNAPSHOT_SECTION_DEBUG, snapshot, remain, - a3xx_snapshot_cp_pm4_ram, NULL); - } - - /* CP ROQ */ - snapshot = kgsl_snapshot_add_section(device, - KGSL_SNAPSHOT_SECTION_DEBUG, snapshot, remain, - a3xx_snapshot_cp_roq, NULL); - - snapshot = a3xx_snapshot_debugbus(device, snapshot, remain); - - /* Enable Clock gating */ - adreno_regwrite(device, A3XX_RBBM_CLOCK_CTL, - A3XX_RBBM_CLOCK_CTL_DEFAULT); - - return snapshot; -} diff --git a/drivers/gpu/msm/adreno_a3xx_trace.c b/drivers/gpu/msm/adreno_a3xx_trace.c deleted file mode 100644 index 8b4a80dde8e..00000000000 --- a/drivers/gpu/msm/adreno_a3xx_trace.c +++ /dev/null @@ -1,20 +0,0 @@ -/* 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" - -/* Instantiate tracepoints */ -#define CREATE_TRACE_POINTS -#include "a3xx_reg.h" -#include "adreno_a3xx_trace.h" diff --git a/drivers/gpu/msm/adreno_a3xx_trace.h b/drivers/gpu/msm/adreno_a3xx_trace.h deleted file mode 100644 index 44483a8e7aa..00000000000 --- a/drivers/gpu/msm/adreno_a3xx_trace.h +++ /dev/null @@ -1,89 +0,0 @@ -/* 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. - * - */ - -#if !defined(_ADRENO_A3XX_TRACE_H) || defined(TRACE_HEADER_MULTI_READ) -#define _ADRENO_A3XX_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_a3xx_trace - -#include - -struct kgsl_device; - -/* - * Tracepoint for a3xx irq. Includes status info - */ -TRACE_EVENT(kgsl_a3xx_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, "|", - { 1 << A3XX_INT_RBBM_AHB_ERROR, "RBBM_GPU_IDLE" }, - { 1 << A3XX_INT_RBBM_AHB_ERROR, "RBBM_AHB_ERR" }, - { 1 << A3XX_INT_RBBM_REG_TIMEOUT, "RBBM_REG_TIMEOUT" }, - { 1 << A3XX_INT_RBBM_ME_MS_TIMEOUT, - "RBBM_ME_MS_TIMEOUT" }, - { 1 << A3XX_INT_RBBM_PFP_MS_TIMEOUT, - "RBBM_PFP_MS_TIMEOUT" }, - { 1 << A3XX_INT_RBBM_ATB_BUS_OVERFLOW, - "RBBM_ATB_BUS_OVERFLOW" }, - { 1 << A3XX_INT_VFD_ERROR, "RBBM_VFD_ERROR" }, - { 1 << A3XX_INT_CP_SW_INT, "CP_SW" }, - { 1 << A3XX_INT_CP_T0_PACKET_IN_IB, - "CP_T0_PACKET_IN_IB" }, - { 1 << A3XX_INT_CP_OPCODE_ERROR, "CP_OPCODE_ERROR" }, - { 1 << A3XX_INT_CP_RESERVED_BIT_ERROR, - "CP_RESERVED_BIT_ERROR" }, - { 1 << A3XX_INT_CP_HW_FAULT, "CP_HW_FAULT" }, - { 1 << A3XX_INT_CP_DMA, "CP_DMA" }, - { 1 << A3XX_INT_CP_IB2_INT, "CP_IB2_INT" }, - { 1 << A3XX_INT_CP_IB1_INT, "CP_IB1_INT" }, - { 1 << A3XX_INT_CP_RB_INT, "CP_RB_INT" }, - { 1 << A3XX_INT_CP_REG_PROTECT_FAULT, - "CP_REG_PROTECT_FAULT" }, - { 1 << A3XX_INT_CP_RB_DONE_TS, "CP_RB_DONE_TS" }, - { 1 << A3XX_INT_CP_VS_DONE_TS, "CP_VS_DONE_TS" }, - { 1 << A3XX_INT_CP_PS_DONE_TS, "CP_PS_DONE_TS" }, - { 1 << A3XX_INT_CACHE_FLUSH_TS, "CACHE_FLUSH_TS" }, - { 1 << A3XX_INT_CP_AHB_ERROR_HALT, - "CP_AHB_ERROR_HALT" }, - { 1 << A3XX_INT_MISC_HANG_DETECT, "MISC_HANG_DETECT" }, - { 1 << A3XX_INT_UCHE_OOB_ACCESS, "UCHE_OOB_ACCESS" }) - : "None" - ) -); - -#endif /* _ADRENO_A3XX_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 81d3419fc68..66935951e43 100644 --- a/drivers/gpu/msm/adreno_debugfs.c +++ b/drivers/gpu/msm/adreno_debugfs.c @@ -364,6 +364,11 @@ void adreno_debugfs_init(struct kgsl_device *device) debugfs_create_u32("ib_check", 0644, device->d_debugfs, &adreno_dev->ib_check_level); + /* By Default enable fast hang detection */ + adreno_dev->fast_hang_detect = 1; + debugfs_create_u32("fast_hang_detect", 0644, device->d_debugfs, + &adreno_dev->fast_hang_detect); + /* Create post mortem control files */ pm_d_debugfs = debugfs_create_dir("postmortem", device->d_debugfs); diff --git a/drivers/gpu/msm/adreno_drawctxt.c b/drivers/gpu/msm/adreno_drawctxt.c index 451db5c5853..4fdbebc963d 100644 --- a/drivers/gpu/msm/adreno_drawctxt.c +++ b/drivers/gpu/msm/adreno_drawctxt.c @@ -192,8 +192,7 @@ void adreno_drawctxt_destroy(struct kgsl_device *device, adreno_drawctxt_switch(adreno_dev, NULL, 0); } - if (device->state != KGSL_STATE_HUNG) - adreno_idle(device, KGSL_TIMEOUT_DEFAULT); + adreno_idle(device); kgsl_sharedmem_free(&drawctxt->gpustate); kgsl_sharedmem_free(&drawctxt->context_gmem_shadow.gmemshadow); diff --git a/drivers/gpu/msm/adreno_postmortem.c b/drivers/gpu/msm/adreno_postmortem.c index af4bb85b103..f06248e9b47 100644 --- a/drivers/gpu/msm/adreno_postmortem.c +++ b/drivers/gpu/msm/adreno_postmortem.c @@ -715,7 +715,7 @@ int adreno_postmortem_dump(struct kgsl_device *device, int manual) } if (device->state == KGSL_STATE_ACTIVE) - kgsl_idle(device, KGSL_TIMEOUT_DEFAULT); + kgsl_idle(device); } KGSL_LOG_DUMP(device, "POWER: FLAGS = %08lX | ACTIVE POWERLEVEL = %08X", diff --git a/drivers/gpu/msm/adreno_ringbuffer.c b/drivers/gpu/msm/adreno_ringbuffer.c index c56f5fd580b..72f3b0bdfc1 100644 --- a/drivers/gpu/msm/adreno_ringbuffer.c +++ b/drivers/gpu/msm/adreno_ringbuffer.c @@ -47,7 +47,16 @@ #define A225_PFP_FW "a225_pfp.fw" #define A225_PM4_FW "a225_pm4.fw" -static void adreno_ringbuffer_submit(struct adreno_ringbuffer *rb) + +/* + * CP DEBUG settings for all cores: + * DYNAMIC_CLK_DISABLE [27] - turn off the dynamic clock control + * PROG_END_PTR_ENABLE [25] - Allow 128 bit writes to the VBIF + */ + +#define CP_DEBUG_DEFAULT ((1 << 27) | (1 << 25)) + +void adreno_ringbuffer_submit(struct adreno_ringbuffer *rb) { BUG_ON(rb->wptr == 0); @@ -71,9 +80,12 @@ 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; + unsigned long wait_timeout = msecs_to_jiffies(ADRENO_IDLE_TIMEOUT); + unsigned long wait_time_part; + unsigned int prev_reg_val[hang_detect_regs_count]; + + memset(prev_reg_val, 0, sizeof(prev_reg_val)); /* if wptr ahead, fill the remaining with NOPs */ if (wptr_ahead) { @@ -101,6 +113,7 @@ adreno_ringbuffer_waitspace(struct adreno_ringbuffer *rb, unsigned int numcmds, } wait_time = jiffies + wait_timeout; + wait_time_part = jiffies + msecs_to_jiffies(KGSL_TIMEOUT_PART); /* wait for space in ringbuffer */ while (1) { GSL_RB_GET_READPTR(rb, &rb->rptr); @@ -110,16 +123,34 @@ adreno_ringbuffer_waitspace(struct adreno_ringbuffer *rb, unsigned int numcmds, if (freecmds == 0 || freecmds > numcmds) break; + /* Dont wait for timeout, detect hang faster. + */ + if (time_after(jiffies, wait_time_part)) { + wait_time_part = jiffies + + msecs_to_jiffies(KGSL_TIMEOUT_PART); + if ((adreno_hang_detect(rb->device, + prev_reg_val))){ + KGSL_DRV_ERR(rb->device, + "Hang detected while waiting for freespace in" + "ringbuffer rptr: 0x%x, wptr: 0x%x\n", + rb->rptr, rb->wptr); + goto err; + } + } + 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)) + goto err; + } + continue; +err: + if (!adreno_dump_and_recover(rb->device)) wait_time = jiffies + wait_timeout; else /* GPU is hung and we cannot recover */ BUG(); - } } } @@ -241,7 +272,7 @@ static int adreno_ringbuffer_load_pm4_ucode(struct kgsl_device *device) 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_DEBUG, CP_DEBUG_DEFAULT); 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, @@ -433,7 +464,7 @@ int adreno_ringbuffer_start(struct adreno_ringbuffer *rb, unsigned int init_ram) adreno_ringbuffer_submit(rb); /* idle device to validate ME INIT */ - status = adreno_idle(device, KGSL_TIMEOUT_DEFAULT); + status = adreno_idle(device); if (status == 0) rb->flags |= KGSL_FLAGS_STARTED; @@ -882,7 +913,7 @@ adreno_ringbuffer_issueibcmds(struct kgsl_device_private *dev_priv, * this is conservative but works reliably and is ok * even for performance simulations */ - adreno_idle(device, KGSL_TIMEOUT_DEFAULT); + adreno_idle(device); #endif return 0; diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c index 8d841d69c99..39eac67e15a 100644 --- a/drivers/gpu/msm/kgsl.c +++ b/drivers/gpu/msm/kgsl.c @@ -33,7 +33,6 @@ #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." @@ -60,9 +59,9 @@ static struct ion_client *kgsl_ion_client; * @returns - 0 on success or error code on failure */ -int kgsl_add_event(struct kgsl_device *device, u32 ts, +static int kgsl_add_event(struct kgsl_device *device, u32 ts, void (*cb)(struct kgsl_device *, void *, u32), void *priv, - void *owner) + struct kgsl_device_private *owner) { struct kgsl_event *event; struct list_head *n; @@ -106,7 +105,6 @@ 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 @@ -114,8 +112,8 @@ EXPORT_SYMBOL(kgsl_add_event); * @owner - driver instance that owns the events to cancel * */ -void kgsl_cancel_events(struct kgsl_device *device, - void *owner) +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, @@ -137,7 +135,6 @@ 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) @@ -258,12 +255,6 @@ 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; } @@ -280,7 +271,6 @@ 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); @@ -311,14 +301,6 @@ static void kgsl_timestamp_expired(struct work_struct *work) kfree(event); } - /* Mark the next pending event */ - if (!list_empty(&device->events) && device->ftbl->next_event) { - event = list_first_entry(&device->events, struct kgsl_event, - list); - - device->ftbl->next_event(device, event); - } - mutex_unlock(&device->mutex); } @@ -435,7 +417,7 @@ static int kgsl_suspend_device(struct kgsl_device *device, pm_message_t state) break; case KGSL_STATE_ACTIVE: /* Wait for the device to become idle */ - device->ftbl->idle(device, KGSL_TIMEOUT_DEFAULT); + device->ftbl->idle(device); case KGSL_STATE_NAP: case KGSL_STATE_SLEEP: /* Get the completion ready to be waited upon. */ @@ -1862,11 +1844,6 @@ 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; } @@ -1937,8 +1914,6 @@ 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); @@ -2398,30 +2373,22 @@ kgsl_ptdata_init(void) static void kgsl_core_exit(void) { - kgsl_mmu_ptpool_destroy(kgsl_driver.ptpool); - kgsl_driver.ptpool = NULL; + unregister_chrdev_region(kgsl_driver.major, KGSL_DEVICE_MAX); - kgsl_drm_exit(); - kgsl_cffdump_destroy(); - kgsl_core_debugfs_close(); + kgsl_mmu_ptpool_destroy(&kgsl_driver.ptpool); + kgsl_driver.ptpool = NULL; - /* - * We call kgsl_sharedmem_uninit_sysfs() and device_unregister() - * only if kgsl_driver.virtdev has been populated. - * We check at least one member of kgsl_driver.virtdev to - * see if it is not NULL (and thus, has been populated). - */ - if (kgsl_driver.virtdev.class) { - kgsl_sharedmem_uninit_sysfs(); - device_unregister(&kgsl_driver.virtdev); - } + device_unregister(&kgsl_driver.virtdev); if (kgsl_driver.class) { class_destroy(kgsl_driver.class); kgsl_driver.class = NULL; } - unregister_chrdev_region(kgsl_driver.major, KGSL_DEVICE_MAX); + 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.h b/drivers/gpu/msm/kgsl.h index c2229cfcf12..25c4827e136 100644 --- a/drivers/gpu/msm/kgsl.h +++ b/drivers/gpu/msm/kgsl.h @@ -242,11 +242,5 @@ 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_device.h b/drivers/gpu/msm/kgsl_device.h index 28b96a0c8c1..ba3c3290c42 100644 --- a/drivers/gpu/msm/kgsl_device.h +++ b/drivers/gpu/msm/kgsl_device.h @@ -23,10 +23,10 @@ #include "kgsl_pwrctrl.h" #include "kgsl_log.h" #include "kgsl_pwrscale.h" -#include #define KGSL_TIMEOUT_NONE 0 #define KGSL_TIMEOUT_DEFAULT 0xFFFFFFFF +#define KGSL_TIMEOUT_PART 2000 /* 2 sec */ #define FIRST_TIMEOUT (HZ / 2) @@ -58,7 +58,6 @@ 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 @@ -69,7 +68,7 @@ struct kgsl_functable { 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); + int (*idle) (struct kgsl_device *device); unsigned int (*isidle) (struct kgsl_device *device); int (*suspend_context) (struct kgsl_device *device); int (*start) (struct kgsl_device *device, unsigned int init_ram); @@ -107,8 +106,6 @@ struct kgsl_functable { struct kgsl_context *context); long (*ioctl) (struct kgsl_device_private *dev_priv, unsigned int cmd, void *data); - void (*next_event)(struct kgsl_device *device, - struct kgsl_event *event); }; struct kgsl_memregion { @@ -132,7 +129,7 @@ struct kgsl_event { void (*func)(struct kgsl_device *, void *, u32); void *priv; struct list_head list; - void *owner; + struct kgsl_device_private *owner; }; @@ -215,12 +212,6 @@ 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 { @@ -272,9 +263,9 @@ static inline void kgsl_regwrite(struct kgsl_device *device, device->ftbl->regwrite(device, offsetwords, value); } -static inline int kgsl_idle(struct kgsl_device *device, unsigned int timeout) +static inline int kgsl_idle(struct kgsl_device *device) { - return device->ftbl->idle(device, timeout); + return device->ftbl->idle(device); } static inline unsigned int kgsl_gpuid(struct kgsl_device *device) diff --git a/drivers/gpu/msm/kgsl_gpummu.c b/drivers/gpu/msm/kgsl_gpummu.c index 7458cf28914..b1d7f31987d 100644 --- a/drivers/gpu/msm/kgsl_gpummu.c +++ b/drivers/gpu/msm/kgsl_gpummu.c @@ -520,7 +520,8 @@ static void kgsl_gpummu_default_setstate(struct kgsl_device *device, return; if (flags & KGSL_MMUFLAGS_PTUPDATE) { - kgsl_idle(device, KGSL_TIMEOUT_DEFAULT); + + kgsl_idle(device); gpummu_pt = device->mmu.hwpagetable->priv; kgsl_regwrite(device, MH_MMU_PT_BASE, gpummu_pt->base.gpuaddr); @@ -615,7 +616,7 @@ static int kgsl_gpummu_start(struct kgsl_device *device) kgsl_regwrite(device, MH_MMU_CONFIG, mmu->config); /* idle device */ - kgsl_idle(device, KGSL_TIMEOUT_DEFAULT); + kgsl_idle(device); /* enable axi interrupts */ kgsl_regwrite(device, MH_INTERRUPT_MASK, diff --git a/drivers/gpu/msm/kgsl_iommu.c b/drivers/gpu/msm/kgsl_iommu.c index 81e237f110d..889d679bf1e 100644 --- a/drivers/gpu/msm/kgsl_iommu.c +++ b/drivers/gpu/msm/kgsl_iommu.c @@ -154,7 +154,7 @@ static void kgsl_iommu_setstate(struct kgsl_device *device, * specified page table */ if (mmu->hwpagetable != pagetable) { - kgsl_idle(device, KGSL_TIMEOUT_DEFAULT); + kgsl_idle(device); kgsl_detach_pagetable_iommu_domain(mmu); mmu->hwpagetable = pagetable; if (mmu->hwpagetable) @@ -308,11 +308,11 @@ 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. - */ + * 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, diff --git a/drivers/gpu/msm/kgsl_iommu.h b/drivers/gpu/msm/kgsl_iommu.h deleted file mode 100644 index f14db935b72..00000000000 --- a/drivers/gpu/msm/kgsl_iommu.h +++ /dev/null @@ -1,121 +0,0 @@ -/* 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_IOMMU_H -#define __KGSL_IOMMU_H - -#include - -/* IOMMU registers and masks */ -#define KGSL_IOMMU_TTBR0 0x10 -#define KGSL_IOMMU_TTBR1 0x14 -#define KGSL_IOMMU_FSR 0x20 - -#define KGSL_IOMMU_TTBR0_PA_MASK 0x0003FFFF -#define KGSL_IOMMU_TTBR0_PA_SHIFT 14 -#define KGSL_IOMMU_CTX_TLBIALL 0x800 -#define KGSL_IOMMU_CTX_SHIFT 12 - -/* - * Max number of iommu units that the gpu core can have - * On APQ8064, KGSL can control a maximum of 2 IOMMU units. - */ -#define KGSL_IOMMU_MAX_UNITS 2 - -/* Max number of iommu contexts per IOMMU unit */ -#define KGSL_IOMMU_MAX_DEVS_PER_UNIT 2 - -/* Macros to read/write IOMMU registers */ -#define KGSL_IOMMU_SET_IOMMU_REG(base_addr, ctx, REG, val) \ - writel_relaxed(val, base_addr + \ - (ctx << KGSL_IOMMU_CTX_SHIFT) + \ - KGSL_IOMMU_##REG) - -#define KGSL_IOMMU_GET_IOMMU_REG(base_addr, ctx, REG) \ - readl_relaxed(base_addr + \ - (ctx << KGSL_IOMMU_CTX_SHIFT) + \ - KGSL_IOMMU_##REG) - -/* Gets the lsb value of pagetable */ -#define KGSL_IOMMMU_PT_LSB(pt_val) \ - (pt_val & ~(KGSL_IOMMU_TTBR0_PA_MASK << \ - KGSL_IOMMU_TTBR0_PA_SHIFT)) - -/* offset at which a nop command is placed in setstate_memory */ -#define KGSL_IOMMU_SETSTATE_NOP_OFFSET 1024 - -/* - * struct kgsl_iommu_device - Structure holding data about iommu contexts - * @dev: Device pointer to iommu context - * @attached: Indicates whether this iommu context is presently attached to - * a pagetable/domain or not - * @pt_lsb: The LSB of IOMMU_TTBR0 register which is the pagetable - * register - * @ctx_id: This iommu units context id. It can be either 0 or 1 - * @clk_enabled: If set indicates that iommu clocks of this iommu context - * are on, else the clocks are off - */ -struct kgsl_iommu_device { - struct device *dev; - bool attached; - unsigned int pt_lsb; - enum kgsl_iommu_context_id ctx_id; - bool clk_enabled; - struct kgsl_device *kgsldev; -}; - -/* - * struct kgsl_iommu_unit - Structure holding data about iommu units. An IOMMU - * units is basically a separte IOMMU h/w block with it's own IOMMU contexts - * @dev: Pointer to array of struct kgsl_iommu_device which has information - * about the IOMMU contexts under this IOMMU unit - * @dev_count: Number of IOMMU contexts that are valid in the previous feild - * @reg_map: Memory descriptor which holds the mapped address of this IOMMU - * units register range - */ -struct kgsl_iommu_unit { - struct kgsl_iommu_device dev[KGSL_IOMMU_MAX_DEVS_PER_UNIT]; - unsigned int dev_count; - struct kgsl_memdesc reg_map; -}; - -/* - * struct kgsl_iommu - Structure holding iommu data for kgsl driver - * @dev: Array of kgsl_iommu_device which contain information about - * iommu contexts owned by graphics cores - * @unit_count: Number of IOMMU units that are available for this - * instance of the IOMMU driver - * @iommu_last_cmd_ts: The timestamp of last command submitted that - * aceeses iommu registers - * @clk_event_queued: Indicates whether an event to disable clocks - * is already queued or not - * @device: Pointer to kgsl device - */ -struct kgsl_iommu { - struct kgsl_iommu_unit iommu_units[KGSL_IOMMU_MAX_UNITS]; - unsigned int unit_count; - unsigned int iommu_last_cmd_ts; - bool clk_event_queued; - struct kgsl_device *device; -}; - -/* - * struct kgsl_iommu_pt - Iommu pagetable structure private to kgsl driver - * @domain: Pointer to the iommu domain that contains the iommu pagetable - * @iommu: Pointer to iommu structure - */ -struct kgsl_iommu_pt { - struct iommu_domain *domain; - struct kgsl_iommu *iommu; -}; - -#endif diff --git a/drivers/gpu/msm/kgsl_mmu.c b/drivers/gpu/msm/kgsl_mmu.c index 975aa83781a..5dc5de31abe 100644 --- a/drivers/gpu/msm/kgsl_mmu.c +++ b/drivers/gpu/msm/kgsl_mmu.c @@ -526,7 +526,7 @@ void kgsl_mh_start(struct kgsl_device *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); + kgsl_idle(device); /* define physical memory range accessible by the core */ kgsl_regwrite(device, MH_MMU_MPU_BASE, mh->mpu_base); diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c index fa40558f33f..800edb5a26e 100644 --- a/drivers/gpu/msm/kgsl_pwrctrl.c +++ b/drivers/gpu/msm/kgsl_pwrctrl.c @@ -64,6 +64,9 @@ void kgsl_pwrctrl_pwrlevel_change(struct kgsl_device *device, new_level >= pwr->thermal_pwrlevel && new_level != pwr->active_pwrlevel) { struct kgsl_pwrlevel *pwrlevel = &pwr->pwrlevels[new_level]; + int diff = new_level - pwr->active_pwrlevel; + int d = (diff > 0) ? 1 : -1; + int level = pwr->active_pwrlevel; pwr->active_pwrlevel = new_level; if ((test_bit(KGSL_PWRFLAGS_CLK_ON, &pwr->power_flags)) || (device->state == KGSL_STATE_NAP)) { @@ -73,9 +76,16 @@ void kgsl_pwrctrl_pwrlevel_change(struct kgsl_device *device, * 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); + device->ftbl->idle(device); + + /* Don't shift by more than one level at a time to + * avoid glitches. + */ + while (level != new_level) { + level += d; + clk_set_rate(pwr->grp_clks[0], + pwr->pwrlevels[level].gpu_freq); + } } if (test_bit(KGSL_PWRFLAGS_AXI_ON, &pwr->power_flags)) { if (pwr->pcl) @@ -346,23 +356,34 @@ 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_disable(pwr->grp_clks[i]); + /* High latency clock maintenance. */ if ((pwr->pwrlevels[0].gpu_freq > 0) && - (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); + for (i = KGSL_MAX_CLKS - 1; i > 0; i--) + if (pwr->grp_clks[i]) + clk_unprepare(pwr->grp_clks[i]); + } kgsl_pwrctrl_busy_time(device, true); } } else if (state == KGSL_PWRFLAGS_ON) { if (!test_and_set_bit(KGSL_PWRFLAGS_CLK_ON, &pwr->power_flags)) { trace_kgsl_clk(device, state); - if ((pwr->pwrlevels[0].gpu_freq > 0) && - (device->state != KGSL_STATE_NAP)) - clk_set_rate(pwr->grp_clks[0], - pwr->pwrlevels[pwr->active_pwrlevel]. + /* High latency clock maintenance. */ + if (device->state != KGSL_STATE_NAP) { + for (i = KGSL_MAX_CLKS - 1; i > 0; i--) + if (pwr->grp_clks[i]) + clk_prepare(pwr->grp_clks[i]); + + if (pwr->pwrlevels[0].gpu_freq > 0) + 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--) @@ -852,11 +873,9 @@ void kgsl_pwrctrl_wake(struct kgsl_device *device) mod_timer(&device->idle_timer, jiffies + device->pwrctrl.interval_timeout); wake_lock(&device->idle_wakelock); - if (device->pwrctrl.restore_slumber == false) - pm_qos_update_request(&device->pm_qos_req_dma, - GPU_SWFI_LATENCY); + pm_qos_update_request(&device->pm_qos_req_dma, + GPU_SWFI_LATENCY); case KGSL_STATE_ACTIVE: - kgsl_pwrctrl_request_state(device, KGSL_STATE_NONE); break; default: KGSL_PWR_WARN(device, "unhandled state %s\n", diff --git a/drivers/gpu/msm/kgsl_pwrscale_msm.c b/drivers/gpu/msm/kgsl_pwrscale_msm.c deleted file mode 100644 index 61d4b2d1885..00000000000 --- a/drivers/gpu/msm/kgsl_pwrscale_msm.c +++ /dev/null @@ -1,200 +0,0 @@ -/* 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 "kgsl.h" -#include "kgsl_pwrscale.h" -#include "kgsl_device.h" -#include "a2xx_reg.h" - -struct msm_priv { - struct kgsl_device *device; - int enabled; - int handle; - unsigned int cur_freq; - struct msm_dcvs_idle idle_source; - struct msm_dcvs_freq freq_sink; - struct msm_dcvs_core_info *core_info; -}; - -static int msm_idle_enable(struct msm_dcvs_idle *self, - enum msm_core_control_event event) -{ - struct msm_priv *priv = container_of(self, struct msm_priv, - idle_source); - - switch (event) { - case MSM_DCVS_ENABLE_IDLE_PULSE: - priv->enabled = true; - break; - case MSM_DCVS_DISABLE_IDLE_PULSE: - priv->enabled = false; - break; - case MSM_DCVS_ENABLE_HIGH_LATENCY_MODES: - case MSM_DCVS_DISABLE_HIGH_LATENCY_MODES: - break; - } - return 0; -} - -/* Set the requested frequency if it is within 5MHz (delta) of a - * supported frequency. - */ -static int msm_set_freq(struct msm_dcvs_freq *self, - unsigned int freq) -{ - int i, delta = 5000000; - struct msm_priv *priv = container_of(self, struct msm_priv, - freq_sink); - struct kgsl_device *device = priv->device; - struct kgsl_pwrctrl *pwr = &device->pwrctrl; - - /* msm_dcvs manager uses frequencies in kHz */ - freq *= 1000; - for (i = 0; i < pwr->num_pwrlevels; i++) - if (abs(pwr->pwrlevels[i].gpu_freq - freq) < delta) - break; - if (i == pwr->num_pwrlevels) - return 0; - - mutex_lock(&device->mutex); - kgsl_pwrctrl_pwrlevel_change(device, i); - priv->cur_freq = pwr->pwrlevels[pwr->active_pwrlevel].gpu_freq; - mutex_unlock(&device->mutex); - - /* return current frequency in kHz */ - return priv->cur_freq / 1000; -} - -static unsigned int msm_get_freq(struct msm_dcvs_freq *self) -{ - struct msm_priv *priv = container_of(self, struct msm_priv, - freq_sink); - /* return current frequency in kHz */ - return priv->cur_freq / 1000; -} - -static void msm_busy(struct kgsl_device *device, - struct kgsl_pwrscale *pwrscale) -{ - struct msm_priv *priv = pwrscale->priv; - if (priv->enabled) - msm_dcvs_idle(priv->handle, MSM_DCVS_IDLE_EXIT, 0); - return; -} - -static void msm_idle(struct kgsl_device *device, - struct kgsl_pwrscale *pwrscale) -{ - struct msm_priv *priv = pwrscale->priv; - unsigned int rb_rptr, rb_wptr; - kgsl_regread(device, REG_CP_RB_RPTR, &rb_rptr); - kgsl_regread(device, REG_CP_RB_WPTR, &rb_wptr); - - if (priv->enabled && (rb_rptr == rb_wptr)) - msm_dcvs_idle(priv->handle, MSM_DCVS_IDLE_ENTER, 0); - - return; -} - -static void msm_sleep(struct kgsl_device *device, - struct kgsl_pwrscale *pwrscale) -{ - /* do we need to reset any parameters here? */ -} - -static int msm_init(struct kgsl_device *device, - struct kgsl_pwrscale *pwrscale) -{ - struct msm_priv *priv; - struct msm_dcvs_freq_entry *tbl; - int i, ret, low_level; - struct kgsl_pwrctrl *pwr = &device->pwrctrl; - struct platform_device *pdev = - container_of(device->parentdev, struct platform_device, dev); - struct kgsl_device_platform_data *pdata = pdev->dev.platform_data; - - priv = pwrscale->priv = kzalloc(sizeof(struct msm_priv), - GFP_KERNEL); - if (pwrscale->priv == NULL) - return -ENOMEM; - - priv->core_info = pdata->core_info; - tbl = priv->core_info->freq_tbl; - /* Fill in frequency table from low to high, reversing order. */ - low_level = pwr->num_pwrlevels - KGSL_PWRLEVEL_LAST_OFFSET; - for (i = 0; i <= low_level; i++) - tbl[i].freq = - pwr->pwrlevels[low_level - i].gpu_freq / 1000; - ret = msm_dcvs_register_core(device->name, 0, priv->core_info); - if (ret) { - KGSL_PWR_ERR(device, "msm_dcvs_register_core failed"); - goto err; - } - - priv->device = device; - priv->idle_source.enable = msm_idle_enable; - priv->idle_source.core_name = device->name; - priv->handle = msm_dcvs_idle_source_register(&priv->idle_source); - if (priv->handle < 0) { - ret = priv->handle; - KGSL_PWR_ERR(device, "msm_dcvs_idle_source_register failed\n"); - goto err; - } - - priv->freq_sink.core_name = device->name; - priv->freq_sink.set_frequency = msm_set_freq; - priv->freq_sink.get_frequency = msm_get_freq; - ret = msm_dcvs_freq_sink_register(&priv->freq_sink); - if (ret >= 0) { - if (device->ftbl->isidle(device)) { - device->pwrscale.gpu_busy = 0; - msm_dcvs_idle(priv->handle, MSM_DCVS_IDLE_ENTER, 0); - } else { - device->pwrscale.gpu_busy = 1; - } - return 0; - } - - KGSL_PWR_ERR(device, "msm_dcvs_freq_sink_register failed\n"); - msm_dcvs_idle_source_unregister(&priv->idle_source); - -err: - kfree(pwrscale->priv); - pwrscale->priv = NULL; - - return ret; -} - -static void msm_close(struct kgsl_device *device, - struct kgsl_pwrscale *pwrscale) -{ - struct msm_priv *priv = pwrscale->priv; - - if (pwrscale->priv == NULL) - return; - msm_dcvs_idle_source_unregister(&priv->idle_source); - msm_dcvs_freq_sink_unregister(&priv->freq_sink); - kfree(pwrscale->priv); - pwrscale->priv = NULL; -} - -struct kgsl_pwrscale_policy kgsl_pwrscale_policy_msm = { - .name = "msm", - .init = msm_init, - .idle = msm_idle, - .busy = msm_busy, - .sleep = msm_sleep, - .close = msm_close, -}; diff --git a/drivers/gpu/msm/kgsl_sync.c b/drivers/gpu/msm/kgsl_sync.c deleted file mode 100644 index 11dd8716dcc..00000000000 --- a/drivers/gpu/msm/kgsl_sync.c +++ /dev/null @@ -1,211 +0,0 @@ -/* 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 deleted file mode 100644 index 06b3ad0d891..00000000000 --- a/drivers/gpu/msm/kgsl_sync.h +++ /dev/null @@ -1,75 +0,0 @@ -/* 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/z180.c b/drivers/gpu/msm/z180.c index 1b1ca0ba6b5..fa53648723e 100644 --- a/drivers/gpu/msm/z180.c +++ b/drivers/gpu/msm/z180.c @@ -289,15 +289,22 @@ static int z180_setup_pt(struct kgsl_device *device, return result; } -static inline unsigned int rb_offset(unsigned int index) +static inline unsigned int rb_offset(unsigned int timestamp) { - return index*sizeof(unsigned int)*(Z180_PACKET_SIZE); + return (timestamp % Z180_PACKET_COUNT) + *sizeof(unsigned int)*(Z180_PACKET_SIZE); } -static void addmarker(struct z180_ringbuffer *rb, unsigned int index) +static inline unsigned int rb_gpuaddr(struct z180_device *z180_dev, + unsigned int timestamp) +{ + return z180_dev->ringbuffer.cmdbufdesc.gpuaddr + rb_offset(timestamp); +} + +static void addmarker(struct z180_ringbuffer *rb, unsigned int timestamp) { char *ptr = (char *)(rb->cmdbufdesc.hostptr); - unsigned int *p = (unsigned int *)(ptr + rb_offset(index)); + unsigned int *p = (unsigned int *)(ptr + rb_offset(timestamp)); *p++ = Z180_STREAM_PACKET; *p++ = (Z180_MARKER_CMD | 5); @@ -311,11 +318,11 @@ static void addmarker(struct z180_ringbuffer *rb, unsigned int index) *p++ = ADDR_VGV3_LAST << 24; } -static void addcmd(struct z180_ringbuffer *rb, unsigned int index, +static void addcmd(struct z180_ringbuffer *rb, unsigned int timestamp, unsigned int cmd, unsigned int nextcnt) { char * ptr = (char *)(rb->cmdbufdesc.hostptr); - unsigned int *p = (unsigned int *)(ptr + (rb_offset(index) + unsigned int *p = (unsigned int *)(ptr + (rb_offset(timestamp) + (Z180_MARKER_SIZE * sizeof(unsigned int)))); *p++ = Z180_STREAM_PACKET_CALL; @@ -338,7 +345,7 @@ static void z180_cmdstream_start(struct kgsl_device *device) z180_cmdwindow_write(device, ADDR_VGV3_MODE, 4); z180_cmdwindow_write(device, ADDR_VGV3_NEXTADDR, - z180_dev->ringbuffer.cmdbufdesc.gpuaddr); + rb_gpuaddr(z180_dev, z180_dev->current_timestamp)); z180_cmdwindow_write(device, ADDR_VGV3_NEXTCMD, cmd | 5); @@ -362,7 +369,7 @@ static int room_in_rb(struct z180_device *device) return ts_diff < Z180_PACKET_COUNT; } -static int z180_idle(struct kgsl_device *device, unsigned int timeout) +static int z180_idle(struct kgsl_device *device) { int status = 0; struct z180_device *z180_dev = Z180_DEVICE(device); @@ -370,7 +377,7 @@ static int z180_idle(struct kgsl_device *device, unsigned int timeout) if (timestamp_cmp(z180_dev->current_timestamp, z180_dev->timestamp) > 0) status = z180_wait(device, z180_dev->current_timestamp, - timeout); + Z180_IDLE_TIMEOUT); if (status) KGSL_DRV_ERR(device, "z180_waittimestamp() timed out\n"); @@ -389,9 +396,7 @@ z180_cmdstream_issueibcmds(struct kgsl_device_private *dev_priv, long 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 old_timestamp = 0; unsigned int nextcnt = Z180_STREAM_END_CMD | 5; struct kgsl_mem_entry *entry = NULL; unsigned int cmd; @@ -461,26 +466,22 @@ z180_cmdstream_issueibcmds(struct kgsl_device_private *dev_priv, } result = 0; - index = z180_dev->current_timestamp % Z180_PACKET_COUNT; + old_timestamp = z180_dev->current_timestamp; 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); + addcmd(&z180_dev->ringbuffer, old_timestamp, cmd + ofs, cnt); kgsl_pwrscale_busy(device); /* Make sure the next ringbuffer entry has a marker */ - addmarker(&z180_dev->ringbuffer, nextindex); - - nextaddr = z180_dev->ringbuffer.cmdbufdesc.gpuaddr - + rb_offset(nextindex); + addmarker(&z180_dev->ringbuffer, z180_dev->current_timestamp); /* monkey patch the IB so that it jumps back to the ringbuffer */ kgsl_sharedmem_writel(&entry->memdesc, - ((sizedwords + 1) * sizeof(unsigned int)), - nextaddr); + ((sizedwords + 1) * sizeof(unsigned int)), + rb_gpuaddr(z180_dev, z180_dev->current_timestamp)); kgsl_sharedmem_writel(&entry->memdesc, ((sizedwords + 2) * sizeof(unsigned int)), nextcnt); @@ -592,7 +593,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); + z180_idle(device); del_timer_sync(&device->idle_timer); @@ -859,7 +860,7 @@ z180_drawctxt_destroy(struct kgsl_device *device, { struct z180_device *z180_dev = Z180_DEVICE(device); - z180_idle(device, KGSL_TIMEOUT_DEFAULT); + z180_idle(device); if (z180_dev->ringbuffer.prevctx == context->id) { z180_dev->ringbuffer.prevctx = Z180_INVALID_CONTEXT; diff --git a/drivers/gpu/msm/z180.h b/drivers/gpu/msm/z180.h index e5c5ef303c5..2962ccd82e6 100644 --- a/drivers/gpu/msm/z180.h +++ b/drivers/gpu/msm/z180.h @@ -21,6 +21,9 @@ #define Z180_DEFAULT_PWRSCALE_POLICY NULL +/* Wait a maximum of 10 seconds when trying to idle the core */ +#define Z180_IDLE_TIMEOUT (10 * 1000) + struct z180_ringbuffer { unsigned int prevctx; struct kgsl_memdesc cmdbufdesc; diff --git a/include/linux/msm_kgsl.h b/include/linux/msm_kgsl.h index 0f22d4d7cd8..baef1cc4409 100644 --- a/include/linux/msm_kgsl.h +++ b/include/linux/msm_kgsl.h @@ -436,8 +436,7 @@ struct kgsl_cff_syncmem { /* * A timestamp event allows the user space to register an action following an - * 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. + * expired timestamp. */ struct kgsl_timestamp_event { @@ -448,7 +447,7 @@ struct kgsl_timestamp_event { size_t len; /* Size of the event specific blob */ }; -#define IOCTL_KGSL_TIMESTAMP_EVENT_OLD \ +#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 */ @@ -459,17 +458,6 @@ 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 e0176044a906cc0e21243724dde07dea54f42222 Mon Sep 17 00:00:00 2001 From: Matt Filetto Date: Mon, 15 Apr 2013 15:22:34 -0700 Subject: [PATCH 005/111] vidc: updated headers Change-Id: I853f9dc3c5e2c28a441367ec0f044dc317ba9930 --- include/linux/msm_vidc_dec.h | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/include/linux/msm_vidc_dec.h b/include/linux/msm_vidc_dec.h index 0c03e136c3c..cfaca7b0ee9 100644 --- a/include/linux/msm_vidc_dec.h +++ b/include/linux/msm_vidc_dec.h @@ -76,6 +76,10 @@ #define VDEC_EXTRADATA_VUI 0x020 #define VDEC_EXTRADATA_VC1 0x040 +#define VDEC_EXTRADATA_EXT_DATA 0x0800 +#define VDEC_EXTRADATA_USER_DATA 0x1000 +#define VDEC_EXTRADATA_EXT_BUFFER 0x2000 + #define VDEC_CMDBASE 0x800 #define VDEC_CMD_SET_INTF_VERSION (VDEC_CMDBASE) @@ -207,6 +211,12 @@ struct vdec_ioctl_msg { #define VDEC_IOCTL_GET_DISABLE_DMX_SUPPORT \ _IOR(VDEC_IOCTL_MAGIC, 37, struct vdec_ioctl_msg) +#define VDEC_IOCTL_SET_META_BUFFERS \ + _IOW(VDEC_IOCTL_MAGIC, 38, struct vdec_ioctl_msg) + +#define VDEC_IOCTL_FREE_META_BUFFERS \ + _IO(VDEC_IOCTL_MAGIC, 39) + enum vdec_picture { PICTURE_TYPE_I, PICTURE_TYPE_P, @@ -230,6 +240,7 @@ struct vdec_allocatorproperty { size_t buffer_size; uint32_t alignment; uint32_t buf_poolid; + size_t meta_buffer_size; }; struct vdec_bufferpayload { @@ -520,6 +531,11 @@ struct vdec_aspectratioinfo { uint32_t par_height; }; +struct vdec_sep_metadatainfo { + void __user *metabufaddr; + uint32_t size; +}; + struct vdec_output_frameinfo { void __user *bufferaddr; size_t offset; @@ -532,6 +548,7 @@ struct vdec_output_frameinfo { struct vdec_framesize framesize; enum vdec_interlaced_format interlaced_format; struct vdec_aspectratioinfo aspect_ratio_info; + struct vdec_sep_metadatainfo metadata_info; }; union vdec_msgdata { @@ -565,4 +582,12 @@ struct vdec_mv_buff_size{ int alignment; }; +struct vdec_meta_buffers { + size_t size; + int count; + int pmem_fd; + int pmem_fd_iommu; + int offset; +}; + #endif /* end of macro _VDECDECODER_H_ */ From 079ecc1a9be143ab062d19860ad6e5d47cc97215 Mon Sep 17 00:00:00 2001 From: David Hays Date: Mon, 29 Apr 2013 14:59:52 -0500 Subject: [PATCH 006/111] Update to qdsp6 version 3 Change-Id: I589c8ebb854a2f883b7996be7795f4069b8f3760 --- arch/arm/mach-msm/Makefile | 2 +- arch/arm/mach-msm/board-vigor-audio.c | 17 +- arch/arm/mach-msm/clock-8x60.h | 253 ++ arch/arm/mach-msm/include/mach/qdsp6v3/apr.h | 189 ++ .../mach-msm/include/mach/qdsp6v3/apr_audio.h | 1012 ++++++ .../include/mach/qdsp6v3/audio_dev_ctl.h | 240 ++ .../arm/mach-msm/include/mach/qdsp6v3/q6afe.h | 67 + .../arm/mach-msm/include/mach/qdsp6v3/q6asm.h | 264 ++ .../mach-msm/include/mach/qdsp6v3/q6voice.h | 757 +++++ .../include/mach/qdsp6v3/snddev_ecodec.h | 48 + .../include/mach/qdsp6v3/snddev_hdmi.h | 40 + .../include/mach/qdsp6v3/snddev_icodec.h | 98 + arch/arm/mach-msm/qdsp6v3/Makefile | 15 + arch/arm/mach-msm/qdsp6v3/aac_in.c | 435 +++ arch/arm/mach-msm/qdsp6v3/amrnb_in.c | 330 ++ arch/arm/mach-msm/qdsp6v3/apr.c | 681 ++++ arch/arm/mach-msm/qdsp6v3/apr_tal.c | 288 ++ arch/arm/mach-msm/qdsp6v3/apr_tal.h | 71 + arch/arm/mach-msm/qdsp6v3/audio_aac.c | 71 + arch/arm/mach-msm/qdsp6v3/audio_acdb.c | 780 +++++ arch/arm/mach-msm/qdsp6v3/audio_acdb.h | 63 + arch/arm/mach-msm/qdsp6v3/audio_dev_ctl.c | 1757 ++++++++++ arch/arm/mach-msm/qdsp6v3/audio_lpa.c | 1435 +++++++++ arch/arm/mach-msm/qdsp6v3/audio_lpa.h | 129 + arch/arm/mach-msm/qdsp6v3/audio_mvs.c | 998 ++++++ arch/arm/mach-msm/qdsp6v3/audio_utils.c | 644 ++++ arch/arm/mach-msm/qdsp6v3/audio_utils.h | 109 + arch/arm/mach-msm/qdsp6v3/audio_wma.c | 1585 ++++++++++ arch/arm/mach-msm/qdsp6v3/audio_wmapro.c | 1644 ++++++++++ .../mach-msm/qdsp6v3/board-msm8x60-audio.c | 1901 +++++++++++ arch/arm/mach-msm/qdsp6v3/dsp_debug.c | 202 ++ arch/arm/mach-msm/qdsp6v3/dsp_debug.h | 38 + arch/arm/mach-msm/qdsp6v3/evrc_in.c | 337 ++ arch/arm/mach-msm/qdsp6v3/fm.c | 259 ++ arch/arm/mach-msm/qdsp6v3/pcm_in.c | 504 +++ arch/arm/mach-msm/qdsp6v3/pcm_out.c | 491 +++ arch/arm/mach-msm/qdsp6v3/q6adm.c | 651 ++++ arch/arm/mach-msm/qdsp6v3/q6adm.h | 56 + arch/arm/mach-msm/qdsp6v3/q6afe.c | 687 ++++ arch/arm/mach-msm/qdsp6v3/q6asm.c | 2548 +++++++++++++++ arch/arm/mach-msm/qdsp6v3/q6core.c | 348 ++ arch/arm/mach-msm/qdsp6v3/q6voice.c | 2812 +++++++++++++++++ arch/arm/mach-msm/qdsp6v3/qcelp_in.c | 334 ++ arch/arm/mach-msm/qdsp6v3/rtac.h | 45 + arch/arm/mach-msm/qdsp6v3/snddev_ecodec.c | 390 +++ arch/arm/mach-msm/qdsp6v3/snddev_hdmi.c | 182 ++ arch/arm/mach-msm/qdsp6v3/snddev_icodec.c | 1189 +++++++ arch/arm/mach-msm/qdsp6v3/snddev_mi2s.c | 472 +++ arch/arm/mach-msm/qdsp6v3/snddev_mi2s.h | 46 + arch/arm/mach-msm/qdsp6v3/snddev_virtual.c | 172 + arch/arm/mach-msm/qdsp6v3/snddev_virtual.h | 20 + .../mach-msm/qdsp6v3/timpani_profile_8x60.h | 2269 +++++++++++++ .../qdsp6v3/timpani_profile_8x60_lead.h | 699 ++++ .../qdsp6v3/timpani_profile_8x60_vigor.h | 641 ++++ include/linux/msm_audio_mvs.h | 113 + 55 files changed, 31416 insertions(+), 12 deletions(-) create mode 100644 arch/arm/mach-msm/clock-8x60.h create mode 100644 arch/arm/mach-msm/include/mach/qdsp6v3/apr.h create mode 100644 arch/arm/mach-msm/include/mach/qdsp6v3/apr_audio.h create mode 100644 arch/arm/mach-msm/include/mach/qdsp6v3/audio_dev_ctl.h create mode 100644 arch/arm/mach-msm/include/mach/qdsp6v3/q6afe.h create mode 100644 arch/arm/mach-msm/include/mach/qdsp6v3/q6asm.h create mode 100644 arch/arm/mach-msm/include/mach/qdsp6v3/q6voice.h create mode 100644 arch/arm/mach-msm/include/mach/qdsp6v3/snddev_ecodec.h create mode 100644 arch/arm/mach-msm/include/mach/qdsp6v3/snddev_hdmi.h create mode 100644 arch/arm/mach-msm/include/mach/qdsp6v3/snddev_icodec.h create mode 100644 arch/arm/mach-msm/qdsp6v3/Makefile create mode 100644 arch/arm/mach-msm/qdsp6v3/aac_in.c create mode 100644 arch/arm/mach-msm/qdsp6v3/amrnb_in.c create mode 100644 arch/arm/mach-msm/qdsp6v3/apr.c create mode 100644 arch/arm/mach-msm/qdsp6v3/apr_tal.c create mode 100644 arch/arm/mach-msm/qdsp6v3/apr_tal.h create mode 100644 arch/arm/mach-msm/qdsp6v3/audio_aac.c create mode 100644 arch/arm/mach-msm/qdsp6v3/audio_acdb.c create mode 100644 arch/arm/mach-msm/qdsp6v3/audio_acdb.h create mode 100644 arch/arm/mach-msm/qdsp6v3/audio_dev_ctl.c create mode 100644 arch/arm/mach-msm/qdsp6v3/audio_lpa.c create mode 100644 arch/arm/mach-msm/qdsp6v3/audio_lpa.h create mode 100644 arch/arm/mach-msm/qdsp6v3/audio_mvs.c create mode 100644 arch/arm/mach-msm/qdsp6v3/audio_utils.c create mode 100644 arch/arm/mach-msm/qdsp6v3/audio_utils.h create mode 100644 arch/arm/mach-msm/qdsp6v3/audio_wma.c create mode 100644 arch/arm/mach-msm/qdsp6v3/audio_wmapro.c create mode 100644 arch/arm/mach-msm/qdsp6v3/board-msm8x60-audio.c create mode 100644 arch/arm/mach-msm/qdsp6v3/dsp_debug.c create mode 100644 arch/arm/mach-msm/qdsp6v3/dsp_debug.h create mode 100644 arch/arm/mach-msm/qdsp6v3/evrc_in.c create mode 100644 arch/arm/mach-msm/qdsp6v3/fm.c create mode 100644 arch/arm/mach-msm/qdsp6v3/pcm_in.c create mode 100644 arch/arm/mach-msm/qdsp6v3/pcm_out.c create mode 100644 arch/arm/mach-msm/qdsp6v3/q6adm.c create mode 100644 arch/arm/mach-msm/qdsp6v3/q6adm.h create mode 100644 arch/arm/mach-msm/qdsp6v3/q6afe.c create mode 100644 arch/arm/mach-msm/qdsp6v3/q6asm.c create mode 100644 arch/arm/mach-msm/qdsp6v3/q6core.c create mode 100644 arch/arm/mach-msm/qdsp6v3/q6voice.c create mode 100644 arch/arm/mach-msm/qdsp6v3/qcelp_in.c create mode 100644 arch/arm/mach-msm/qdsp6v3/rtac.h create mode 100644 arch/arm/mach-msm/qdsp6v3/snddev_ecodec.c create mode 100644 arch/arm/mach-msm/qdsp6v3/snddev_hdmi.c create mode 100644 arch/arm/mach-msm/qdsp6v3/snddev_icodec.c create mode 100644 arch/arm/mach-msm/qdsp6v3/snddev_mi2s.c create mode 100644 arch/arm/mach-msm/qdsp6v3/snddev_mi2s.h create mode 100644 arch/arm/mach-msm/qdsp6v3/snddev_virtual.c create mode 100644 arch/arm/mach-msm/qdsp6v3/snddev_virtual.h create mode 100644 arch/arm/mach-msm/qdsp6v3/timpani_profile_8x60.h create mode 100644 arch/arm/mach-msm/qdsp6v3/timpani_profile_8x60_lead.h create mode 100644 arch/arm/mach-msm/qdsp6v3/timpani_profile_8x60_vigor.h create mode 100644 include/linux/msm_audio_mvs.h diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index f7de361bbb5..776edcc7d1a 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -169,7 +169,7 @@ obj-$(CONFIG_MSM7KV2_AUDIO) += qdsp5v2_2x/ obj-$(CONFIG_MSM7KV2_AUDIO) += htc_acoustic_7x30.o htc_acdb_7x30.o obj-$(CONFIG_MSM_QDSP6) += qdsp6/ -obj-$(CONFIG_MSM8X60_AUDIO) += qdsp6v2_1x/ +obj-$(CONFIG_MSM8X60_AUDIO) += qdsp6v3/ obj-$(CONFIG_MSM_AUDIO_QDSP6) += qdsp6v2/ obj-$(CONFIG_MSM_HW3D) += hw3d.o ifdef CONFIG_PM diff --git a/arch/arm/mach-msm/board-vigor-audio.c b/arch/arm/mach-msm/board-vigor-audio.c index beac20b8758..15fc1c36dbc 100644 --- a/arch/arm/mach-msm/board-vigor-audio.c +++ b/arch/arm/mach-msm/board-vigor-audio.c @@ -23,12 +23,12 @@ #include #include #include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include #include #include @@ -404,10 +404,6 @@ static struct dev_ctrl_ops dops = { .support_opendsp = vigor_support_opendsp, }; -static struct q6asm_ops qops = { - .get_q6_effect = vigor_get_q6_effect_mode, -}; - void __init vigor_audio_init(void) { int i = 0; @@ -419,7 +415,6 @@ void __init vigor_audio_init(void) htc_8x60_register_ecodec_ops(&eops); htc_8x60_register_icodec_ops(&iops); htc_8x60_register_dev_ctrl_ops(&dops); - htc_8x60_register_q6asm_ops(&qops); acoustic_register_ops(&acoustic); /* PMIC GPIO Init (See board-vigor.c) */ diff --git a/arch/arm/mach-msm/clock-8x60.h b/arch/arm/mach-msm/clock-8x60.h new file mode 100644 index 00000000000..e9effae0ac8 --- /dev/null +++ b/arch/arm/mach-msm/clock-8x60.h @@ -0,0 +1,253 @@ +/* Copyright (c) 2009-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 __ARCH_ARM_MACH_MSM_CLOCK_8X60_H +#define __ARCH_ARM_MACH_MSM_CLOCK_8X60_H + +#include "clock-local.h" + +enum { + /* Peripheral Clocks */ + L_GSBI1_UART_CLK, //0 + L_GSBI2_UART_CLK, + L_GSBI3_UART_CLK, + L_GSBI4_UART_CLK, + L_GSBI5_UART_CLK, + L_GSBI6_UART_CLK, + L_GSBI7_UART_CLK, + L_GSBI8_UART_CLK, + L_GSBI9_UART_CLK, + L_GSBI10_UART_CLK, + L_GSBI11_UART_CLK, //10 + L_GSBI12_UART_CLK, + L_GSBI1_QUP_CLK, + L_GSBI2_QUP_CLK, + L_GSBI3_QUP_CLK, + L_GSBI4_QUP_CLK, + L_GSBI5_QUP_CLK, + L_GSBI6_QUP_CLK, + L_GSBI7_QUP_CLK, + L_GSBI8_QUP_CLK, + L_GSBI9_QUP_CLK, //20 + L_GSBI10_QUP_CLK, + L_GSBI11_QUP_CLK, + L_GSBI12_QUP_CLK, + L_PDM_CLK, + L_PMEM_CLK, + L_PRNG_CLK, + L_SDC1_CLK, + L_SDC2_CLK, + L_SDC3_CLK, + L_SDC4_CLK, //30 + L_SDC5_CLK, + L_TSIF_REF_CLK, + L_TSSC_CLK, + L_USB_HS1_XCVR_CLK, + L_USB_PHY0_CLK, + L_USB_FS1_SRC_CLK, + L_USB_FS1_XCVR_CLK, + L_USB_FS1_SYS_CLK, + L_USB_FS2_SRC_CLK, + L_USB_FS2_XCVR_CLK, //40 + L_USB_FS2_SYS_CLK, + + /* HW-Voteable Clocks */ + L_ADM0_CLK, + L_ADM0_P_CLK, + L_ADM1_CLK, + L_ADM1_P_CLK, + L_MODEM_AHB1_P_CLK, + L_MODEM_AHB2_P_CLK, + L_PMIC_ARB0_P_CLK, + L_PMIC_ARB1_P_CLK, + L_PMIC_SSBI2_CLK, //50 + L_RPM_MSG_RAM_P_CLK, + + /* Fast Peripheral Bus Clocks */ + L_CE2_P_CLK, + L_GSBI1_P_CLK, + L_GSBI2_P_CLK, + L_GSBI3_P_CLK, + L_GSBI4_P_CLK, + L_GSBI5_P_CLK, + L_GSBI6_P_CLK, + L_GSBI7_P_CLK, + L_GSBI8_P_CLK, //60 + L_GSBI9_P_CLK, + L_GSBI10_P_CLK, + L_GSBI11_P_CLK, + L_GSBI12_P_CLK, + L_PPSS_P_CLK, + L_TSIF_P_CLK, + L_USB_FS1_P_CLK, + L_USB_FS2_P_CLK, + L_USB_HS1_P_CLK, + L_SDC1_P_CLK, //70 + L_SDC2_P_CLK, + L_SDC3_P_CLK, + L_SDC4_P_CLK, + L_SDC5_P_CLK, + + /* Multimedia Clocks */ + L_AMP_CLK, + L_CAM_CLK, + L_CSI_SRC_CLK, + L_CSI0_CLK, + L_CSI1_CLK, + L_DSI_BYTE_CLK, //80 + L_DSI_ESC_CLK, + L_GFX2D0_CLK, + L_GFX2D1_CLK, + L_GFX3D_CLK, + L_IJPEG_CLK, + L_JPEGD_CLK, + L_MDP_CLK, + L_MDP_VSYNC_CLK, + L_PIXEL_SRC_CLK, + L_PIXEL_MDP_CLK, //90 + L_PIXEL_LCDC_CLK, + L_ROT_CLK, + L_TV_SRC_CLK, + L_TV_ENC_CLK, + L_TV_DAC_CLK, + L_VCODEC_CLK, + L_MDP_TV_CLK, + L_HDMI_TV_CLK, + L_HDMI_APP_CLK, + L_VPE_CLK, //100 + L_VFE_CLK, + L_CSI0_VFE_CLK, + L_CSI1_VFE_CLK, + L_GMEM_AXI_CLK, + L_IJPEG_AXI_CLK, + L_IMEM_AXI_CLK, + L_JPEGD_AXI_CLK, + L_VCODEC_AXI_CLK, + L_VFE_AXI_CLK, + L_MDP_AXI_CLK, //110 + L_ROT_AXI_CLK, + L_VPE_AXI_CLK, + + /* Multimedia Fast Peripheral Bus Clocks */ + L_AMP_P_CLK, + L_CSI0_P_CLK, + L_CSI1_P_CLK, + L_DSI_M_P_CLK, + L_DSI_S_P_CLK, + L_GFX2D0_P_CLK, + L_GFX2D1_P_CLK, + L_GFX3D_P_CLK, //120 + L_HDMI_M_P_CLK, + L_HDMI_S_P_CLK, + L_IJPEG_P_CLK, + L_IMEM_P_CLK, + L_JPEGD_P_CLK, + L_MDP_P_CLK, + L_ROT_P_CLK, + L_SMMU_P_CLK, + L_TV_ENC_P_CLK, + L_VCODEC_P_CLK, //130 + L_VFE_P_CLK, + L_VPE_P_CLK, + + /* LPA Clocks */ + L_MI2S_SRC_CLK, + L_MI2S_OSR_CLK, + L_MI2S_BIT_CLK, + L_CODEC_I2S_MIC_OSR_CLK, + L_CODEC_I2S_MIC_BIT_CLK, + L_SPARE_I2S_MIC_OSR_CLK, + L_SPARE_I2S_MIC_BIT_CLK, + L_CODEC_I2S_SPKR_OSR_CLK, //140 + L_CODEC_I2S_SPKR_BIT_CLK, + L_SPARE_I2S_SPKR_OSR_CLK, + L_SPARE_I2S_SPKR_BIT_CLK, + L_PCM_CLK, + + /* Measurement-only Clocks */ + L_SC0_DIV2_M_CLK, + L_SC1_DIV2_M_CLK, + L_L2_DIV2_M_CLK, + L_AFAB_M_CLK, + L_SFAB_M_CLK, + L_EBI1_2X_M_CLK, + L_CFPB0_M_CLK, + L_CFPB1_M_CLK, + L_CFPB2_M_CLK, + L_DFAB_M_CLK, + L_SFPB_M_CLK, + L_MMFAB_M_CLK, + L_SMI_DDR2X_M_CLK, + L_MMFPB_M_CLK, + + L_NR_CLKS //145 +}; + +enum clk_sources { + PLL_0 = 0, + PLL_1, + PLL_2, + PLL_3, + PLL_4, + PLL_6, + PLL_7, + PLL_8, + PXO, + CXO, + NUM_SRC +}; + +/*extern struct clk_local soc_clk_local_tbl_mxo[];*/ + +struct pll_rate { + const uint32_t l_val; + const uint32_t m_val; + const uint32_t n_val; + const uint32_t vco; + const uint32_t post_div; + const uint32_t i_bits; +}; +#define PLL_RATE(l, m, n, v, d, i) { l, m, n, v, (d>>1), i } + +extern struct clk_ops soc_clk_ops_8x60; +#define CLK_8X60(clk_name, clk_id, clk_dev, clk_flags) { \ + .con_id = clk_name, \ + .dev_id = clk_dev, \ + .clk = &(struct clk){ \ + .id = L_##clk_id, \ + .ops = &soc_clk_ops_8x60, \ + .flags = clk_flags, \ + .dbg_name = #clk_id, \ + .name = clk_name, \ + }, \ + } + +void soc_clk_src_votes_show(void); + +#endif diff --git a/arch/arm/mach-msm/include/mach/qdsp6v3/apr.h b/arch/arm/mach-msm/include/mach/qdsp6v3/apr.h new file mode 100644 index 00000000000..19f1860aa48 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdsp6v3/apr.h @@ -0,0 +1,189 @@ +/* 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 __APR_H_ +#define __APR_H_ + +#define APR_Q6_NOIMG 0 +#define APR_Q6_LOADING 1 +#define APR_Q6_LOADED 2 + +struct apr_q6 { + void *pil; + uint32_t state; + struct mutex lock; +}; + +struct apr_hdr { + uint16_t hdr_field; + uint16_t pkt_size; + uint8_t src_svc; + uint8_t src_domain; + uint16_t src_port; + uint8_t dest_svc; + uint8_t dest_domain; + uint16_t dest_port; + uint32_t token; + uint32_t opcode; +}; + +#define APR_HDR_LEN(hdr_len) ((hdr_len)/4) +#define APR_PKT_SIZE(hdr_len, payload_len) ((hdr_len) + (payload_len)) +#define APR_HDR_FIELD(msg_type, hdr_len, ver)\ + (((msg_type & 0x3) << 8) | ((hdr_len & 0xF) << 4) | (ver & 0xF)) + +#define APR_HDR_SIZE sizeof(struct apr_hdr) + +/* Version */ +#define APR_PKT_VER 0x0 + +/* Command and Response Types */ +#define APR_MSG_TYPE_EVENT 0x0 +#define APR_MSG_TYPE_CMD_RSP 0x1 +#define APR_MSG_TYPE_SEQ_CMD 0x2 +#define APR_MSG_TYPE_NSEQ_CMD 0x3 +#define APR_MSG_TYPE_MAX 0x04 + +/* APR Basic Response Message */ +#define APR_BASIC_RSP_RESULT 0x000110E8 +#define APR_RSP_ACCEPTED 0x000100BE + +/* Domain IDs */ +#define APR_DOMAIN_SIM 0x1 +#define APR_DOMAIN_PC 0x2 +#define APR_DOMAIN_MODEM 0x3 +#define APR_DOMAIN_ADSP 0x4 +#define APR_DOMAIN_APPS 0x5 +#define APR_DOMAIN_MAX 0x6 + +/* ADSP service IDs */ +#define APR_SVC_TEST_CLIENT 0x2 +#define APR_SVC_ADSP_CORE 0x3 +#define APR_SVC_AFE 0x4 +#define APR_SVC_VSM 0x5 +#define APR_SVC_VPM 0x6 +#define APR_SVC_ASM 0x7 +#define APR_SVC_ADM 0x8 +#define APR_SVC_ADSP_MVM 0x09 +#define APR_SVC_ADSP_CVS 0x0A +#define APR_SVC_ADSP_CVP 0x0B +#define APR_SVC_MAX 0x0C + +/* Modem Service IDs */ +#define APR_SVC_MVS 0x3 +#define APR_SVC_MVM 0x4 +#define APR_SVC_CVS 0x5 +#define APR_SVC_CVP 0x6 +#define APR_SVC_SRD 0x7 + +/* APR Port IDs */ +#define APR_MAX_PORTS 0x40 + +#define APR_NAME_MAX 0x40 + +#define RESET_EVENTS 0xFFFFFFFF + +#define LPASS_RESTART_EVENT 0x1000 +#define LPASS_RESTART_READY 0x1001 + +struct apr_client_data { + uint16_t reset_event; + uint16_t reset_proc; + uint16_t payload_size; + uint16_t hdr_len; + uint16_t msg_type; + uint16_t src; + uint16_t dest_svc; + uint16_t src_port; + uint16_t dest_port; + uint32_t token; + uint32_t opcode; + void *payload; +}; + +typedef int32_t (*apr_fn)(struct apr_client_data *data, void *priv); + +struct apr_svc { + uint16_t id; + uint16_t dest_id; + uint16_t client_id; + uint8_t rvd; + uint8_t port_cnt; + uint8_t svc_cnt; + uint8_t need_reset; + apr_fn port_fn[APR_MAX_PORTS]; + void *port_priv[APR_MAX_PORTS]; + apr_fn fn; + void *priv; + struct mutex m_lock; + spinlock_t w_lock; +}; + +struct apr_client { + uint8_t id; + uint8_t svc_cnt; + uint8_t rvd; + struct mutex m_lock; + struct apr_svc_ch_dev *handle; + struct apr_svc svc[APR_SVC_MAX]; +}; + +#define ADSP_GET_VERSION 0x00011152 +#define ADSP_GET_VERSION_RSP 0x00011153 + +struct adsp_get_version { + uint32_t build_id; + uint32_t svc_cnt; +}; + +struct adsp_service_info { + uint32_t svc_id; + uint32_t svc_ver; +}; + +#define ADSP_CMD_SET_POWER_COLLAPSE_STATE 0x0001115C +struct adsp_power_collapse { + struct apr_hdr hdr; + uint32_t power_collapse; +}; + +struct apr_svc *apr_register(char *dest, char *svc_name, apr_fn svc_fn, + uint32_t src_port, void *priv); +inline int apr_fill_hdr(void *handle, uint32_t *buf, uint16_t src_port, + uint16_t msg_type, uint16_t dest_port, + uint32_t token, uint32_t opcode, uint16_t len); + +int apr_send_pkt(void *handle, uint32_t *buf); +int apr_deregister(void *handle); +void change_q6_state(int state); +void q6audio_dsp_not_responding(void); +uint32_t core_get_adsp_version(void); +void *core_open(void); +int32_t core_close(void); +void apr_reset(void *handle); +#endif diff --git a/arch/arm/mach-msm/include/mach/qdsp6v3/apr_audio.h b/arch/arm/mach-msm/include/mach/qdsp6v3/apr_audio.h new file mode 100644 index 00000000000..a6657cffbd5 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdsp6v3/apr_audio.h @@ -0,0 +1,1012 @@ +/* + * + * 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. + * + * 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 _APR_AUDIO_H_ +#define _APR_AUDIO_H_ + +/* ASM opcodes without APR payloads*/ +#include + +/* + * Audio Front End (AFE) + */ + +/* Port ID. Update afe_get_port_index when a new port is added here. */ +#define PRIMARY_I2S_RX 0 /* index = 0 */ +#define PRIMARY_I2S_TX 1 /* index = 1 */ +#define PCM_RX 2 /* index = 2 */ +#define PCM_TX 3 /* index = 3 */ +#define SECONDARY_I2S_RX 4 /* index = 4 */ +#define SECONDARY_I2S_TX 5 /* index = 5 */ +#define MI2S_RX 6 /* index = 6 */ +#define MI2S_TX 7 /* index = 7 */ +#define HDMI_RX 8 /* index = 8 */ +#define RSVD_2 9 /* index = 9 */ +#define RSVD_3 10 /* index = 10 */ +#define DIGI_MIC_TX 11 /* index = 11 */ +#define VOICE_RECORD_RX 0x8003 /* index = 12 */ +#define VOICE_RECORD_TX 0x8004 /* index = 13 */ +#define VOICE_PLAYBACK_TX 0x8005 /* index = 14 */ +#define AFE_PORT_INVALID 0xFFFF + +#define AFE_PORT_CMD_START 0x000100ca +struct afe_port_start_command { + struct apr_hdr hdr; + u16 port_id; + u16 gain; /* Q13 */ + u32 sample_rate; /* 8 , 16, 48khz */ +} __attribute__ ((packed)); + +#define AFE_PORT_CMD_STOP 0x000100cb +struct afe_port_stop_command { + struct apr_hdr hdr; + u16 port_id; + u16 reserved; +} __attribute__ ((packed)); + +#define AFE_PORT_CMD_APPLY_GAIN 0x000100cc +struct afe_port_gain_command { + struct apr_hdr hdr; + u16 port_id; + u16 gain;/* Q13 */ +} __attribute__ ((packed)); + +#define AFE_PORT_CMD_SIDETONE_CTL 0x000100cd +struct afe_port_sidetone_command { + struct apr_hdr hdr; + u16 rx_port_id; /* Primary i2s tx = 1 */ + /* PCM tx = 3 */ + /* Secondary i2s tx = 5 */ + /* Mi2s tx = 7 */ + /* Digital mic tx = 11 */ + u16 tx_port_id; /* Primary i2s rx = 0 */ + /* PCM rx = 2 */ + /* Secondary i2s rx = 4 */ + /* Mi2S rx = 6 */ + /* HDMI rx = 8 */ + u16 gain; /* Q13 */ + u16 enable; /* 1 = enable, 0 = disable */ +} __attribute__ ((packed)); + +#define AFE_PORT_CMD_LOOPBACK 0x000100ce +struct afe_loopback_command { + struct apr_hdr hdr; + u16 tx_port_id; /* Primary i2s rx = 0 */ + /* PCM rx = 2 */ + /* Secondary i2s rx = 4 */ + /* Mi2S rx = 6 */ + /* HDMI rx = 8 */ + u16 rx_port_id; /* Primary i2s tx = 1 */ + /* PCM tx = 3 */ + /* Secondary i2s tx = 5 */ + /* Mi2s tx = 7 */ + /* Digital mic tx = 11 */ + u16 mode; /* Default -1, DSP will conver + the tx to rx format */ + u16 enable; /* 1 = enable, 0 = disable */ +} __attribute__ ((packed)); + +#define AFE_PSEUDOPORT_CMD_START 0x000100cf +struct afe_pseudoport_start_command { + struct apr_hdr hdr; + u16 port_id; /* Pseudo Port 1 = 0x8000 */ + /* Pseudo Port 2 = 0x8001 */ + /* Pseudo Port 3 = 0x8002 */ + u16 timing; /* FTRT = 0 , AVTimer = 1, */ +} __attribute__ ((packed)); + +#define AFE_PSEUDOPORT_CMD_STOP 0x000100d0 +struct afe_pseudoport_stop_command { + struct apr_hdr hdr; + u16 port_id; /* Pseudo Port 1 = 0x8000 */ + /* Pseudo Port 2 = 0x8001 */ + /* Pseudo Port 3 = 0x8002 */ + u16 reserved; +} __attribute__ ((packed)); + +#define AFE_CMD_GET_ACTIVE_PORTS 0x000100d1 + + +#define AFE_CMD_GET_ACTIVE_HANDLES_FOR_PORT 0x000100d2 +struct afe_get_active_handles_command { + struct apr_hdr hdr; + u16 port_id; + u16 reserved; +} __attribute__ ((packed)); + +#define AFE_PCM_CFG_MODE_PCM 0x0 +#define AFE_PCM_CFG_MODE_AUX 0x1 +#define AFE_PCM_CFG_SYNC_EXT 0x0 +#define AFE_PCM_CFG_SYNC_INT 0x1 +#define AFE_PCM_CFG_FRM_8BPF 0x0 +#define AFE_PCM_CFG_FRM_16BPF 0x1 +#define AFE_PCM_CFG_FRM_32BPF 0x2 +#define AFE_PCM_CFG_FRM_64BPF 0x3 +#define AFE_PCM_CFG_FRM_128BPF 0x4 +#define AFE_PCM_CFG_FRM_256BPF 0x5 +#define AFE_PCM_CFG_QUANT_ALAW_NOPAD 0x0 +#define AFE_PCM_CFG_QUANT_MULAW_NOPAD 0x1 +#define AFE_PCM_CFG_QUANT_LINEAR_NOPAD 0x2 +#define AFE_PCM_CFG_QUANT_ALAW_PAD 0x3 +#define AFE_PCM_CFG_QUANT_MULAW_PAD 0x4 +#define AFE_PCM_CFG_QUANT_LINEAR_PAD 0x5 +#define AFE_PCM_CFG_CDATAOE_MASTER 0x0 +#define AFE_PCM_CFG_CDATAOE_SHARE 0x1 + +struct afe_port_pcm_cfg { + u16 mode; /* PCM (short sync) = 0, AUXPCM (long sync) = 1 */ + u16 sync; /* external = 0 , internal = 1 */ + u16 frame; /* 8 bpf = 0 */ + /* 16 bpf = 1 */ + /* 32 bpf = 2 */ + /* 64 bpf = 3 */ + /* 128 bpf = 4 */ + /* 256 bpf = 5 */ + u16 quant; + u16 slot; /* Slot for PCM stream , 0 - 31 */ + u16 data; /* 0, PCM block is the only master */ + /* 1, PCM block is shares to driver data out signal */ + /* other master */ + u16 reserved; +} __attribute__ ((packed)); + +enum { + AFE_I2S_SD0 = 1, + AFE_I2S_SD1, + AFE_I2S_SD2, + AFE_I2S_SD3, + AFE_I2S_QUAD01, + AFE_I2S_QUAD23, + AFE_I2S_6CHS, + AFE_I2S_8CHS, +}; + +#define AFE_MI2S_MONO 0 +#define AFE_MI2S_STEREO 3 +#define AFE_MI2S_4CHANNELS 4 +#define AFE_MI2S_6CHANNELS 6 +#define AFE_MI2S_8CHANNELS 8 + +struct afe_port_mi2s_cfg { + u16 bitwidth; /* 16,24,32 */ + u16 line; /* Called ChannelMode in documentation */ + /* i2s_sd0 = 1 */ + /* i2s_sd1 = 2 */ + /* i2s_sd2 = 3 */ + /* i2s_sd3 = 4 */ + /* i2s_quad01 = 5 */ + /* i2s_quad23 = 6 */ + /* i2s_6chs = 7 */ + /* i2s_8chs = 8 */ + u16 channel; /* Called MonoStereo in documentation */ + /* i2s mono = 0 */ + /* i2s mono right = 1 */ + /* i2s mono left = 2 */ + /* i2s stereo = 3 */ + u16 ws; /* 0, word select signal from external source */ + /* 1, word select signal from internal source */ + u16 reserved; +} __attribute__ ((packed)); + +struct afe_port_hdmi_cfg { + u16 bitwidth; /* 16,24,32 */ + u16 channel_mode; /* HDMI Stereo = 0 */ + /* HDMI_3Point1 (4-ch) = 1 */ + /* HDMI_5Point1 (6-ch) = 2 */ + /* HDMI_6Point1 (8-ch) = 3 */ + u16 data_type; /* HDMI_Linear = 0 */ + /* HDMI_non_Linaer = 1 */ +} __attribute__ ((packed)); + +#define AFE_PORT_AUDIO_IF_CONFIG 0x000100d3 + +union afe_port_config { + struct afe_port_pcm_cfg pcm; + struct afe_port_mi2s_cfg mi2s; + struct afe_port_hdmi_cfg hdmi; +} __attribute__((packed)); + +struct afe_audioif_config_command { + struct apr_hdr hdr; + u16 port_id; + union afe_port_config port; +} __attribute__ ((packed)); + +#define AFE_TEST_CODEC_LOOPBACK_CTL 0x000100d5 +struct afe_codec_loopback_command { + u16 port_inf; /* Primary i2s = 0 */ + /* PCM = 2 */ + /* Secondary i2s = 4 */ + /* Mi2s = 6 */ + u16 enable; /* 0, disable. 1, enable */ +} __attribute__ ((packed)); + + +#define AFE_PARAM_ID_SIDETONE_GAIN 0x00010300 +struct afe_param_sidetone_gain { + u16 gain; + u16 reserved; +} __attribute__ ((packed)); + +#define AFE_PARAM_ID_SAMPLING_RATE 0x00010301 +struct afe_param_sampling_rate { + u32 sampling_rate; +} __attribute__ ((packed)); + + +#define AFE_PARAM_ID_CHANNELS 0x00010302 +struct afe_param_channels { + u16 channels; + u16 reserved; +} __attribute__ ((packed)); + + +#define AFE_PARAM_ID_LOOPBACK_GAIN 0x00010303 +struct afe_param_loopback_gain { + u16 gain; + u16 reserved; +} __attribute__ ((packed)); + + +#define AFE_MODULE_ID_PORT_INFO 0x00010200 +struct afe_param_payload { + u32 module_id; + u32 param_id; + u16 param_size; + u16 reserved; + union { + struct afe_param_sidetone_gain sidetone_gain; + struct afe_param_sampling_rate sampling_rate; + struct afe_param_channels channels; + struct afe_param_loopback_gain loopback_gain; + } __attribute__((packed)) param; +} __attribute__ ((packed)); + +#define AFE_PORT_CMD_SET_PARAM 0x000100dc + +struct afe_port_cmd_set_param { + struct apr_hdr hdr; + u16 port_id; + u16 payload_size; + u32 payload_address; + struct afe_param_payload payload; +} __attribute__ ((packed)); + + +#define AFE_EVENT_GET_ACTIVE_PORTS 0x00010100 +struct afe_get_active_ports_rsp { + u16 num_ports; + u16 port_id; +} __attribute__ ((packed)); + + +#define AFE_EVENT_GET_ACTIVE_HANDLES 0x00010102 +struct afe_get_active_handles_rsp { + u16 port_id; + u16 num_handles; + u16 mode; /* 0, voice rx */ + /* 1, voice tx */ + /* 2, audio rx */ + /* 3, audio tx */ + u16 handle; +} __attribute__ ((packed)); + +#define ADM_MAX_COPPS 5 + +#define ADM_SERVICE_CMD_GET_COPP_HANDLES 0x00010300 +struct adm_get_copp_handles_command { + struct apr_hdr hdr; +} __attribute__ ((packed)); + +#define ADM_CMD_MATRIX_MAP_ROUTINGS 0x00010301 +struct adm_routings_session { + u16 id; + u16 num_copps; + u16 copp_id[ADM_MAX_COPPS+1]; /*Padding if numCopps is odd */ +} __packed; + +struct adm_routings_command { + struct apr_hdr hdr; + u32 path; /* 0 = Rx, 1 Tx */ + u32 num_sessions; + struct adm_routings_session session[8]; +} __attribute__ ((packed)); + + +#define ADM_CMD_MATRIX_RAMP_GAINS 0x00010302 +struct adm_ramp_gain { + struct apr_hdr hdr; + u16 session_id; + u16 copp_id; + u16 initial_gain; + u16 gain_increment; + u16 ramp_duration; + u16 reserved; +} __attribute__ ((packed)); + +struct adm_ramp_gains_command { + struct apr_hdr hdr; + u32 id; + u32 num_gains; + struct adm_ramp_gain gains[ADM_MAX_COPPS]; +} __attribute__ ((packed)); + + +#define ADM_CMD_COPP_OPEN 0x00010304 +struct adm_copp_open_command { + struct apr_hdr hdr; + u16 flags; + u16 mode; /* 1-RX, 2-Live TX, 3-Non Live TX */ + u16 endpoint_id1; + u16 endpoint_id2; + u32 topology_id; + u16 channel_config; + u16 reserved; + u32 rate; +} __attribute__ ((packed)); + +#define ADM_CMD_COPP_CLOSE 0x00010305 + +#define ADM_CMD_MEMORY_MAP 0x00010C30 +struct adm_cmd_memory_map{ + struct apr_hdr hdr; + u32 buf_add; + u32 buf_size; + u16 mempool_id; + u16 reserved; +} __attribute__((packed)); + +#define ADM_CMD_MEMORY_UNMAP 0x00010C31 +struct adm_cmd_memory_unmap{ + struct apr_hdr hdr; + u32 buf_add; +} __attribute__((packed)); + +#define ADM_CMD_MEMORY_MAP_REGIONS 0x00010C47 +struct adm_memory_map_regions{ + u32 phys; + u32 buf_size; +} __attribute__((packed)); + +struct adm_cmd_memory_map_regions{ + struct apr_hdr hdr; + u16 mempool_id; + u16 nregions; +} __attribute__((packed)); + +#define ADM_CMD_MEMORY_UNMAP_REGIONS 0x00010C48 +struct adm_memory_unmap_regions{ + u32 phys; +} __attribute__((packed)); + +struct adm_cmd_memory_unmap_regions{ + struct apr_hdr hdr; + u16 nregions; + u16 reserved; +} __attribute__((packed)); + +#define DEFAULT_COPP_TOPOLOGY 0x00010be3 +#define DEFAULT_POPP_TOPOLOGY 0x00010be4 +#define VPM_TX_SM_ECNS_COPP_TOPOLOGY 0x00010F71 +#define VPM_TX_DM_FLUENCE_COPP_TOPOLOGY 0x00010F72 +#define HTC_STEREO_RECORD_TOPOLOGY 0x10000000 +#define HTC_COPP_TOPOLOGY 0x10000001 + +#define ASM_MAX_EQ_BANDS 12 + +struct asm_eq_band { + u32 band_idx; /* The band index, 0 .. 11 */ + u32 filter_type; /* Filter band type */ + u32 center_freq_hz; /* Filter band center frequency */ + u32 filter_gain; /* Filter band initial gain (dB) */ + /* Range is +12 dB to -12 dB with 1dB increments. */ + u32 q_factor; +} __attribute__ ((packed)); + +struct asm_equalizer_params { + u32 enable; + u32 num_bands; + struct asm_eq_band eq_bands[ASM_MAX_EQ_BANDS]; +} __attribute__ ((packed)); + +struct asm_master_gain_params { + u16 master_gain; + u16 padding; +} __attribute__ ((packed)); + +struct asm_lrchannel_gain_params { + u16 left_gain; + u16 right_gain; +} __attribute__ ((packed)); + +struct asm_mute_params { + u32 muteflag; +} __attribute__ ((packed)); + +struct asm_softvolume_params { + u32 period; + u32 step; + u32 rampingcurve; +} __attribute__ ((packed)); + +struct asm_softpause_params { + u32 enable; + u32 period; + u32 step; + u32 rampingcurve; +} __packed; + +struct asm_pp_param_data_hdr { + u32 module_id; + u32 param_id; + u16 param_size; + u16 reserved; +} __attribute__ ((packed)); + +struct asm_pp_params_command { + struct apr_hdr hdr; + u32 *payload; + u32 payload_size; + struct asm_pp_param_data_hdr params; +} __attribute__ ((packed)); + +#define EQUALIZER_MODULE_ID 0x00010c27 +#define EQUALIZER_PARAM_ID 0x00010c28 + +#define VOLUME_CONTROL_MODULE_ID 0x00010bfe +#define MASTER_GAIN_PARAM_ID 0x00010bff +#define L_R_CHANNEL_GAIN_PARAM_ID 0x00010c00 +#define MUTE_CONFIG_PARAM_ID 0x00010c01 +#define SOFT_PAUSE_PARAM_ID 0x00010D6A + +#define IIR_FILTER_ENABLE_PARAM_ID 0x00010c03 +#define IIR_FILTER_PREGAIN_PARAM_ID 0x00010c04 +#define IIR_FILTER_CONFIG_PARAM_ID 0x00010c05 + +#define MBADRC_MODULE_ID 0x00010c06 +#define MBADRC_ENABLE_PARAM_ID 0x00010c07 +#define MBADRC_CONFIG_PARAM_ID 0x00010c08 + + +#define ADM_CMD_SET_PARAMS 0x00010306 +#define ADM_CMD_GET_PARAMS 0x0001030B +#define ADM_CMDRSP_GET_PARAMS 0x0001030C +struct adm_set_params_command { + struct apr_hdr hdr; + u32 payload; + u32 payload_size; +} __attribute__ ((packed)); + + +#define ADM_CMD_TAP_COPP_PCM 0x00010307 +struct adm_tap_copp_pcm_command { + struct apr_hdr hdr; +} __attribute__ ((packed)); + + +/* QDSP6 to Client messages +*/ +#define ADM_SERVICE_CMDRSP_GET_COPP_HANDLES 0x00010308 +struct adm_get_copp_handles_respond { + struct apr_hdr hdr; + u32 handles; + u32 copp_id; +} __attribute__ ((packed)); + +#define ADM_CMDRSP_COPP_OPEN 0x0001030A +struct adm_copp_open_respond { + u32 status; + u16 copp_id; + u16 reserved; +} __attribute__ ((packed)); + +#define ASM_STREAM_PRIORITY_NORMAL 0 +#define ASM_STREAM_PRIORITY_LOW 1 +#define ASM_STREAM_PRIORITY_HIGH 2 +#define ASM_STREAM_PRIORITY_RESERVED 3 + +#define ASM_END_POINT_DEVICE_MATRIX 0 +#define ASM_END_POINT_STREAM 1 + +#define AAC_ENC_MODE_AAC_LC 0x02 +#define AAC_ENC_MODE_AAC_P 0x05 +#define AAC_ENC_MODE_EAAC_P 0x1D + +#define ASM_STREAM_CMD_CLOSE 0x00010BCD +#define ASM_STREAM_CMD_FLUSH 0x00010BCE +#define ASM_STREAM_CMD_SET_PP_PARAMS 0x00010BCF +#define ASM_STREAM_CMD_GET_PP_PARAMS 0x00010BD0 +#define ASM_STREAM_CMDRSP_GET_PP_PARAMS 0x00010BD1 +#define ASM_SESSION_CMD_PAUSE 0x00010BD3 +#define ASM_SESSION_CMD_GET_SESSION_TIME 0x00010BD4 +#define ASM_DATA_CMD_EOS 0x00010BDB +#define ASM_DATA_EVENT_EOS 0x00010BDD + +#define ASM_SERVICE_CMD_GET_STREAM_HANDLES 0x00010C0B +#define ASM_STREAM_CMD_FLUSH_READBUFS 0x00010C09 + +#define ASM_SESSION_EVENT_RX_UNDERFLOW 0x00010C17 +#define ASM_SESSION_EVENT_TX_OVERFLOW 0x00010C18 +#define ASM_SERVICE_CMD_GET_WALLCLOCK_TIME 0x00010C19 +#define ASM_DATA_CMDRSP_EOS 0x00010C1C + +/* ASM Data structures */ + +/* common declarations */ +struct asm_pcm_cfg { + u16 ch_cfg; + u16 bits_per_sample; + u32 sample_rate; + u16 is_signed; + u16 interleaved; +}; + +struct asm_adpcm_cfg { + u16 ch_cfg; + u16 bits_per_sample; + u32 sample_rate; + u32 block_size; +}; + +struct asm_yadpcm_cfg { + u16 ch_cfg; + u16 bits_per_sample; + u32 sample_rate; +}; + +struct asm_midi_cfg { + u32 nMode; +}; + +struct asm_wma_cfg { + u16 format_tag; + u16 ch_cfg; + u32 sample_rate; + u32 avg_bytes_per_sec; + u16 block_align; + u16 valid_bits_per_sample; + u32 ch_mask; + u16 encode_opt; + u16 adv_encode_opt; + u32 adv_encode_opt2; + u32 drc_peak_ref; + u32 drc_peak_target; + u32 drc_ave_ref; + u32 drc_ave_target; +}; + +struct asm_wmapro_cfg { + u16 format_tag; + u16 ch_cfg; + u32 sample_rate; + u32 avg_bytes_per_sec; + u16 block_align; + u16 valid_bits_per_sample; + u32 ch_mask; + u16 encode_opt; + u16 adv_encode_opt; + u32 adv_encode_opt2; + u32 drc_peak_ref; + u32 drc_peak_target; + u32 drc_ave_ref; + u32 drc_ave_target; +}; + +struct asm_aac_cfg { + u16 format; + u16 aot; + u16 ep_config; + u16 section_data_resilience; + u16 scalefactor_data_resilience; + u16 spectral_data_resilience; + u16 ch_cfg; + u16 reserved; + u32 sample_rate; +}; + +struct asm_flac_cfg { + u16 stream_info_present; + u16 min_blk_size; + u16 max_blk_size; + u16 ch_cfg; + u16 sample_size; + u16 sample_rate; + u16 md5_sum; + u32 ext_sample_rate; + u32 min_frame_size; + u32 max_frame_size; +}; + +struct asm_vorbis_cfg { + u32 ch_cfg; + u32 bit_rate; + u32 min_bit_rate; + u32 max_bit_rate; + u16 bit_depth_pcm_sample; + u16 bit_stream_format; +}; + +struct asm_aac_read_cfg { + u32 bitrate; + u32 enc_mode; + u16 format; + u16 ch_cfg; + u32 sample_rate; +}; + +struct asm_amrnb_read_cfg { + u16 mode; + u16 dtx_mode; +}; + +struct asm_evrc_read_cfg { + u16 max_rate; + u16 min_rate; + u16 rate_modulation_cmd; + u16 reserved; +}; + +struct asm_qcelp13_read_cfg { + u16 max_rate; + u16 min_rate; + u16 reduced_rate_level; + u16 rate_modulation_cmd; +}; + +struct asm_sbc_read_cfg { + u32 subband; + u32 block_len; + u32 ch_mode; + u32 alloc_method; + u32 bit_rate; + u32 sample_rate; +}; + +struct asm_sbc_bitrate { + u32 bitrate; +}; + +struct asm_immed_decode { + u32 mode; +}; + +struct asm_sbr_ps { + u32 enable; +}; + +struct asm_encode_cfg_blk { + u32 frames_per_buf; + u32 format_id; + u32 cfg_size; + union { + struct asm_pcm_cfg pcm; + struct asm_aac_read_cfg aac; + struct asm_amrnb_read_cfg amrnb; + struct asm_evrc_read_cfg evrc; + struct asm_qcelp13_read_cfg qcelp13; + struct asm_sbc_read_cfg sbc; + } __attribute__((packed)) cfg; +}; + +struct asm_frame_meta_info { + u32 offset_to_frame; + u32 frame_size; + u32 encoded_pcm_samples; + u32 msw_ts; + u32 lsw_ts; + u32 nflags; +}; + +/* Stream level commands */ +#define ASM_STREAM_CMD_OPEN_READ 0x00010BCB +struct asm_stream_cmd_open_read { + struct apr_hdr hdr; + u32 uMode; + u32 src_endpoint; + u32 pre_proc_top; + u32 format; +} __attribute__((packed)); + +/* Supported formats */ +#define LINEAR_PCM 0x00010BE5 +#define DTMF 0x00010BE6 +#define ADPCM 0x00010BE7 +#define YADPCM 0x00010BE8 +#define MP3 0x00010BE9 +#define MPEG4_AAC 0x00010BEA +#define AMRNB_FS 0x00010BEB +#define V13K_FS 0x00010BED +#define EVRC_FS 0x00010BEE +#define EVRCB_FS 0x00010BEF +#define EVRCWB_FS 0x00010BF0 +#define MIDI 0x00010BF1 +#define SBC 0x00010BF2 +#define WMA_V10PRO 0x00010BF3 +#define WMA_V9 0x00010BF4 +#define AMR_WB_PLUS 0x00010BF5 +#define AC3_DECODER 0x00010BF6 +#define G711_ALAW_FS 0x00010BF7 +#define G711_MLAW_FS 0x00010BF8 +#define G711_PCM_FS 0x00010BF9 + +#define ASM_ENCDEC_SBCRATE 0x00010C13 +#define ASM_ENCDEC_IMMDIATE_DECODE 0x00010C14 +#define ASM_ENCDEC_CFG_BLK 0x00010C2C + +#define ASM_ENCDEC_SBCRATE 0x00010C13 +#define ASM_ENCDEC_IMMDIATE_DECODE 0x00010C14 +#define ASM_ENCDEC_CFG_BLK 0x00010C2C + +#define ASM_STREAM_CMD_OPEN_WRITE 0x00010BCA +struct asm_stream_cmd_open_write { + struct apr_hdr hdr; + u32 uMode; + u16 sink_endpoint; + u16 stream_handle; + u32 post_proc_top; + u32 format; +} __attribute__((packed)); + +#define ASM_STREAM_CMD_OPEN_READWRITE 0x00010BCC + +struct asm_stream_cmd_open_read_write { + struct apr_hdr hdr; + u32 uMode; + u32 post_proc_top; + u32 write_format; + u32 read_format; +} __attribute__((packed)); + +#define ASM_STREAM_CMD_SET_ENCDEC_PARAM 0x00010C10 +#define ASM_STREAM_CMD_GET_ENCDEC_PARAM 0x00010C11 +#define ASM_ENCDEC_CFG_BLK_ID 0x00010C2C +#define ASM_ENABLE_SBR_PS 0x00010C63 +struct asm_stream_cmd_encdec_cfg_blk{ + struct apr_hdr hdr; + u32 param_id; + u32 param_size; + struct asm_encode_cfg_blk enc_blk; +} __attribute__((packed)); + +struct asm_stream_cmd_encdec_sbc_bitrate{ + struct apr_hdr hdr; + u32 param_id; + struct asm_sbc_bitrate sbc_bitrate; +} __attribute__((packed)); + +struct asm_stream_cmd_encdec_immed_decode{ + struct apr_hdr hdr; + u32 param_id; + u32 param_size; + struct asm_immed_decode dec; +} __attribute__((packed)); + +struct asm_stream_cmd_encdec_sbr{ + struct apr_hdr hdr; + u32 param_id; + u32 param_size; + struct asm_sbr_ps sbr_ps; +} __attribute__((packed)); + +#define ASM_STREAM _CMD_ADJUST_SAMPLES 0x00010C0A +struct asm_stream_cmd_adjust_samples{ + struct apr_hdr hdr; + u16 nsamples; + u16 reserved; +} __attribute__((packed)); + +#define ASM_STREAM_CMD_TAP_POPP_PCM 0x00010BF9 +struct asm_stream_cmd_tap_popp_pcm{ + struct apr_hdr hdr; + u16 enable; + u16 reserved; + u32 module_id; +} __attribute__((packed)); + +/* Session Level commands */ +#define ASM_SESSION_CMD_MEMORY_MAP 0x00010C32 +struct asm_stream_cmd_memory_map{ + struct apr_hdr hdr; + u32 buf_add; + u32 buf_size; + u16 mempool_id; + u16 reserved; +} __attribute__((packed)); + +#define ASM_SESSION_CMD_MEMORY_UNMAP 0x00010C33 +struct asm_stream_cmd_memory_unmap{ + struct apr_hdr hdr; + u32 buf_add; +} __attribute__((packed)); + +#define ASM_SESSION_CMD_MEMORY_MAP_REGIONS 0x00010C45 +struct asm_memory_map_regions{ + u32 phys; + u32 buf_size; +} __attribute__((packed)); + +struct asm_stream_cmd_memory_map_regions{ + struct apr_hdr hdr; + u16 mempool_id; + u16 nregions; +} __attribute__((packed)); + +#define ASM_SESSION_CMD_MEMORY_UNMAP_REGIONS 0x00010C46 +struct asm_memory_unmap_regions{ + u32 phys; +} __attribute__((packed)); + +struct asm_stream_cmd_memory_unmap_regions{ + struct apr_hdr hdr; + u16 nregions; + u16 reserved; +} __attribute__((packed)); + +#define ASM_SESSION_CMD_RUN 0x00010BD2 +struct asm_stream_cmd_run{ + struct apr_hdr hdr; + u32 flags; + u32 msw_ts; + u32 lsw_ts; +} __attribute__((packed)); + +/* Session level events */ +#define ASM_SESSION_CMD_REGISTER_FOR_RX_UNDERFLOW_EVENTS 0x00010BD5 +struct asm_stream_cmd_reg_rx_underflow_event{ + struct apr_hdr hdr; + u16 enable; + u16 reserved; +} __attribute__((packed)); + +#define ASM_SESSION_CMD_REGISTER_FOR_TX_OVERFLOW_EVENTS 0x00010BD6 +struct asm_stream_cmd_reg_tx_overflow_event{ + struct apr_hdr hdr; + u16 enable; + u16 reserved; +} __attribute__((packed)); + +/* Data Path commands */ +#define ASM_DATA_CMD_WRITE 0x00010BD9 +struct asm_stream_cmd_write{ + struct apr_hdr hdr; + u32 buf_add; + u32 avail_bytes; + u32 uid; + u32 msw_ts; + u32 lsw_ts; + u32 uflags; +} __attribute__((packed)); + +#define ASM_DATA_CMD_READ 0x00010BDA +struct asm_stream_cmd_read{ + struct apr_hdr hdr; + u32 buf_add; + u32 buf_size; + u32 uid; +} __attribute__((packed)); + +#define ASM_DATA_CMD_MEDIA_FORMAT_UPDATE 0x00010BDC +#define ASM_DATA_EVENT_MEDIA_FORMAT_UPDATE 0x00010BDE +struct asm_stream_media_format_update{ + struct apr_hdr hdr; + u32 format; + u32 cfg_size; + union { + struct asm_pcm_cfg pcm_cfg; + struct asm_adpcm_cfg adpcm_cfg; + struct asm_yadpcm_cfg yadpcm_cfg; + struct asm_midi_cfg midi_cfg; + struct asm_wma_cfg wma_cfg; + struct asm_wmapro_cfg wmapro_cfg; + struct asm_aac_cfg aac_cfg; + struct asm_flac_cfg flac_cfg; + struct asm_vorbis_cfg vorbis_cfg; + } __attribute__((packed)) write_cfg; +} __attribute__((packed)); + + +/* Command Responses */ +#define ASM_STREAM_CMDRSP_GET_ENCDEC_PARAM 0x00010C12 +struct asm_stream_cmdrsp_get_readwrite_param{ + struct apr_hdr hdr; + u32 status; + u32 param_id; + u16 param_size; + u16 padding; + union { + struct asm_sbc_bitrate sbc_bitrate; + struct asm_immed_decode aac_dec; + } __attribute__((packed)) read_write_cfg; +} __attribute__((packed)); + + +#define ASM_SESSION_CMDRSP_GET_SESSION_TIME 0x00010BD8 +struct asm_stream_cmdrsp_get_session_time{ + struct apr_hdr hdr; + u32 status; + u32 msw_ts; + u32 lsw_ts; +} __attribute__((packed)); + +#define ASM_DATA_EVENT_WRITE_DONE 0x00010BDF +struct asm_data_event_write_done{ + u32 buf_add; + u32 status; +} __attribute__((packed)); + +#define ASM_DATA_EVENT_READ_DONE 0x00010BE0 +struct asm_data_event_read_done{ + u32 status; + u32 buffer_add; + u32 enc_frame_size; + u32 offset; + u32 msw_ts; + u32 lsw_ts; + u32 flags; + u32 num_frames; + u32 id; +} __attribute__((packed)); + + +/* service level events */ + +#define ASM_SERVICE_CMDRSP_GET_STREAM_HANDLES 0x00010C1B +struct asm_svc_cmdrsp_get_strm_handles{ + struct apr_hdr hdr; + u32 num_handles; + u32 stream_handles; +} __attribute__((packed)); + + +#define ASM_SERVICE_CMDRSP_GET_WALLCLOCK_TIME 0x00010C1A +struct asm_svc_cmdrsp_get_wallclock_time{ + struct apr_hdr hdr; + u32 status; + u32 msw_ts; + u32 lsw_ts; +} __attribute__((packed)); + +/* + * Error code +*/ +#define ADSP_EOK 0x00000000 /* Success / completed / no errors. */ +#define ADSP_EFAILED 0x00000001 /* General failure. */ +#define ADSP_EBADPARAM 0x00000002 /* Bad operation parameter(s). */ +#define ADSP_EUNSUPPORTED 0x00000003 /* Unsupported routine/operation. */ +#define ADSP_EVERSION 0x00000004 /* Unsupported version. */ +#define ADSP_EUNEXPECTED 0x00000005 /* Unexpected problem encountered. */ +#define ADSP_EPANIC 0x00000006 /* Unhandled problem occurred. */ +#define ADSP_ENORESOURCE 0x00000007 /* Unable to allocate resource(s). */ +#define ADSP_EHANDLE 0x00000008 /* Invalid handle. */ +#define ADSP_EALREADY 0x00000009 /* Operation is already processed. */ +#define ADSP_ENOTREADY 0x0000000A /* Operation not ready to be processed*/ +#define ADSP_EPENDING 0x0000000B /* Operation is pending completion*/ +#define ADSP_EBUSY 0x0000000C /* Operation could not be accepted or + processed. */ +#define ADSP_EABORTED 0x0000000D /* Operation aborted due to an error. */ +#define ADSP_EPREEMPTED 0x0000000E /* Operation preempted by higher priority*/ +#define ADSP_ECONTINUE 0x0000000F /* Operation requests intervention + to complete. */ +#define ADSP_EIMMEDIATE 0x00000010 /* Operation requests immediate + intervention to complete. */ +#define ADSP_ENOTIMPL 0x00000011 /* Operation is not implemented. */ +#define ADSP_ENEEDMORE 0x00000012 /* Operation needs more data or resources*/ + +#endif /*_APR_AUDIO_H_*/ diff --git a/arch/arm/mach-msm/include/mach/qdsp6v3/audio_dev_ctl.h b/arch/arm/mach-msm/include/mach/qdsp6v3/audio_dev_ctl.h new file mode 100644 index 00000000000..d8ea0d45326 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdsp6v3/audio_dev_ctl.h @@ -0,0 +1,240 @@ +/* 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 __MACH_QDSP6_V2_SNDDEV_H +#define __MACH_QDSP6_V2_SNDDEV_H +#include + +#define AUDIO_DEV_CTL_MAX_DEV 64 +#define DIR_TX 2 +#define DIR_RX 1 + +#define DEVICE_IGNORE 0xffffffff +#define SESSION_IGNORE 0x0UL + +/* 8 concurrent sessions with Q6 possible, session:0 + reserved in DSP */ +#define MAX_SESSIONS 0x09 + +/* This represents Maximum bit needed for representing sessions + per clients, MAX_BIT_PER_CLIENT >= MAX_SESSIONS */ +#define MAX_BIT_PER_CLIENT 16 + +#define VOICE_STATE_INVALID 0x0 +#define VOICE_STATE_INCALL 0x1 +#define VOICE_STATE_OFFCALL 0x2 +#define ONE_TO_MANY 1 +#define MANY_TO_ONE 2 + +struct msm_snddev_info { + const char *name; + u32 capability; + u32 copp_id; + u32 acdb_id; + u32 dev_volume; + struct msm_snddev_ops { + int (*open)(struct msm_snddev_info *); + int (*close)(struct msm_snddev_info *); + int (*set_freq)(struct msm_snddev_info *, u32); + int (*enable_sidetone)(struct msm_snddev_info *, u32, uint16_t); + int (*set_device_volume)(struct msm_snddev_info *, u32); + int (*enable_anc)(struct msm_snddev_info *, u32); + } dev_ops; + u8 opened; + void *private_data; + bool state; + u32 sample_rate; + u32 channel_mode; + u32 set_sample_rate; + u64 sessions; + int usage_count; + s32 max_voc_rx_vol[VOC_RX_VOL_ARRAY_NUM]; /* [0] is for NB,[1] for WB */ + s32 min_voc_rx_vol[VOC_RX_VOL_ARRAY_NUM]; +}; + +struct msm_volume { + int volume; /* Volume parameter, in % Scale */ + int pan; +}; + +extern struct msm_volume msm_vol_ctl; + +void msm_snddev_register(struct msm_snddev_info *); +void msm_snddev_unregister(struct msm_snddev_info *); +int msm_snddev_devcount(void); +int msm_snddev_query(int dev_id); +unsigned short msm_snddev_route_dec(int popp_id); +unsigned short msm_snddev_route_enc(int enc_id); + +int msm_snddev_set_dec(int popp_id, int copp_id, int set, + int rate, int channel_mode); +int msm_snddev_set_enc(int popp_id, int copp_id, int set, + int rate, int channel_mode); + +int msm_snddev_is_set(int popp_id, int copp_id); +int msm_get_voc_route(u32 *rx_id, u32 *tx_id); +int msm_set_voc_route(struct msm_snddev_info *dev_info, int stream_type, + int dev_id); +int msm_snddev_enable_sidetone(u32 dev_id, u32 enable, uint16_t gain); + +int msm_set_copp_id(int session_id, int copp_id); + +int msm_clear_copp_id(int session_id, int copp_id); + +int msm_clear_session_id(int session_id); + +int msm_reset_all_device(void); + +int msm_clear_all_session(void); + +struct msm_snddev_info *audio_dev_ctrl_find_dev(u32 dev_id); + +void msm_release_voc_thread(void); + +int snddev_voice_set_volume(int vol, int path); + +int msm_get_call_state(void); + +struct auddev_evt_voc_devinfo { + u32 dev_type; /* Rx or Tx */ + u32 acdb_dev_id; /* acdb id of device */ + u32 dev_sample; /* Sample rate of device */ + s32 max_rx_vol[VOC_RX_VOL_ARRAY_NUM]; /* unit is mb (milibel), + [0] is for NB, other for WB */ + s32 min_rx_vol[VOC_RX_VOL_ARRAY_NUM]; /* unit is mb */ + u32 dev_id; /* registered device id */ + u32 dev_port_id; +}; + +struct auddev_evt_audcal_info { + u32 dev_id; + u32 acdb_id; + u32 sample_rate; + u32 dev_type; + u32 sessions; +}; + +union msm_vol_mute { + int vol; + bool mute; +}; + +struct auddev_evt_voc_mute_info { + u32 dev_type; + u32 acdb_dev_id; + union msm_vol_mute dev_vm_val; +}; + +struct auddev_evt_freq_info { + u32 dev_type; + u32 acdb_dev_id; + u32 sample_rate; +}; + +union auddev_evt_data { + struct auddev_evt_voc_devinfo voc_devinfo; + struct auddev_evt_voc_mute_info voc_vm_info; + struct auddev_evt_freq_info freq_info; + u32 routing_id; + s32 session_vol; + s32 voice_state; + struct auddev_evt_audcal_info audcal_info; +}; + +struct message_header { + uint32_t id; + uint32_t data_len; +}; + +#define AUDDEV_EVT_DEV_CHG_VOICE 0x01 /* device change event */ +#define AUDDEV_EVT_DEV_RDY 0x02 /* device ready event */ +#define AUDDEV_EVT_DEV_RLS 0x04 /* device released event */ +#define AUDDEV_EVT_REL_PENDING 0x08 /* device release pending */ +#define AUDDEV_EVT_DEVICE_VOL_MUTE_CHG 0x10 /* device volume changed */ +#define AUDDEV_EVT_START_VOICE 0x20 /* voice call start */ +#define AUDDEV_EVT_END_VOICE 0x40 /* voice call end */ +#define AUDDEV_EVT_STREAM_VOL_CHG 0x80 /* device volume changed */ +#define AUDDEV_EVT_FREQ_CHG 0x100 /* Change in freq */ +#define AUDDEV_EVT_VOICE_STATE_CHG 0x200 /* Change in voice state */ + +#define AUDDEV_CLNT_VOC 0x1 /*Vocoder clients*/ +#define AUDDEV_CLNT_DEC 0x2 /*Decoder clients*/ +#define AUDDEV_CLNT_ENC 0x3 /* Encoder clients */ +#define AUDDEV_CLNT_AUDIOCAL 0x4 /* AudioCalibration client */ + +#define AUDIO_DEV_CTL_MAX_LISTNER 20 /* Max Listeners Supported */ + +struct msm_snd_evt_listner { + uint32_t evt_id; + uint32_t clnt_type; + uint32_t clnt_id; + void *private_data; + void (*auddev_evt_listener)(u32 evt_id, + union auddev_evt_data *evt_payload, + void *private_data); + struct msm_snd_evt_listner *cb_next; + struct msm_snd_evt_listner *cb_prev; +}; + +struct event_listner { + struct msm_snd_evt_listner *cb; + u32 num_listner; + int state; /* Call state */ /* TODO remove this if not req*/ +}; + +extern struct event_listner event; +int auddev_register_evt_listner(u32 evt_id, u32 clnt_type, u32 clnt_id, + void (*listner)(u32 evt_id, + union auddev_evt_data *evt_payload, + void *private_data), + void *private_data); +int auddev_unregister_evt_listner(u32 clnt_type, u32 clnt_id); +void mixer_post_event(u32 evt_id, u32 dev_id); +void broadcast_event(u32 evt_id, u32 dev_id, u64 session_id); +int auddev_cfg_tx_copp_topology(int session_id, int cfg); +int msm_snddev_request_freq(int *freq, u32 session_id, + u32 capability, u32 clnt_type); +int msm_snddev_withdraw_freq(u32 session_id, + u32 capability, u32 clnt_type); +int msm_device_is_voice(int dev_id); +int msm_get_voc_freq(int *tx_freq, int *rx_freq); +int msm_snddev_get_enc_freq(int session_id); +int msm_set_voice_vol(int dir, s32 volume); +int msm_set_voice_mute(int dir, int mute); +int msm_get_voice_state(void); +int msm_enable_incall_recording(int popp_id, int rec_mode, int rate, + int channel_mode); +int msm_disable_incall_recording(uint32_t popp_id, uint32_t rec_mode); +void msm_set_voc_freq(int tx_freq, int rx_freq); + +struct dev_ctrl_ops { + int (*support_opendsp) (void); +}; + +void htc_8x60_register_dev_ctrl_ops(struct dev_ctrl_ops *ops); +#endif diff --git a/arch/arm/mach-msm/include/mach/qdsp6v3/q6afe.h b/arch/arm/mach-msm/include/mach/qdsp6v3/q6afe.h new file mode 100644 index 00000000000..0e977557f9d --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdsp6v3/q6afe.h @@ -0,0 +1,67 @@ +/* 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 __Q6AFE_H__ +#define __Q6AFE_H__ +#include + +#define MSM_AFE_MONO 0 +#define MSM_AFE_MONO_RIGHT 1 +#define MSM_AFE_MONO_LEFT 2 +#define MSM_AFE_STEREO 3 + +enum { + IDX_PRIMARY_I2S_RX = 0, + IDX_PRIMARY_I2S_TX = 1, + IDX_PCM_RX = 2, + IDX_PCM_TX = 3, + IDX_SECONDARY_I2S_RX = 4, + IDX_SECONDARY_I2S_TX = 5, + IDX_MI2S_RX = 6, + IDX_MI2S_TX = 7, + IDX_HDMI_RX = 8, + IDX_RSVD_2 = 9, + IDX_RSVD_3 = 10, + IDX_DIGI_MIC_TX = 11, + IDX_VOICE_RECORD_RX = 12, + IDX_VOICE_RECORD_TX = 13, + IDX_VOICE_PLAYBACK_TX = 14, + AFE_MAX_PORTS +}; + + +int afe_open(u16 port_id, union afe_port_config *afe_config, int rate); +int afe_close(int port_id); +int afe_loopback(u16 enable, u16 rx_port, u16 tx_port); +int afe_sidetone(u16 tx_port_id, u16 rx_port_id, u16 enable, uint16_t gain); +int afe_loopback_gain(u16 port_id, u16 volume); +int afe_validate_port(u16 port_id); +int afe_get_port_index(u16 port_id); +int afe_start_pseudo_port(u16 port_id); +int afe_stop_pseudo_port(u16 port_id); + +#endif /* __Q6AFE_H__ */ diff --git a/arch/arm/mach-msm/include/mach/qdsp6v3/q6asm.h b/arch/arm/mach-msm/include/mach/qdsp6v3/q6asm.h new file mode 100644 index 00000000000..afb037726a3 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdsp6v3/q6asm.h @@ -0,0 +1,264 @@ +/* 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 __Q6_ASM_H__ +#define __Q6_ASM_H__ + +#include + +#define IN 0x000 +#define OUT 0x001 +#define CH_MODE_MONO 0x001 +#define CH_MODE_STEREO 0x002 + +#define FORMAT_LINEAR_PCM 0x0000 +#define FORMAT_DTMF 0x0001 +#define FORMAT_ADPCM 0x0002 +#define FORMAT_YADPCM 0x0003 +#define FORMAT_MP3 0x0004 +#define FORMAT_MPEG4_AAC 0x0005 +#define FORMAT_AMRNB 0x0006 +#define FORMAT_AMRWB 0x0007 +#define FORMAT_V13K 0x0008 +#define FORMAT_EVRC 0x0009 +#define FORMAT_EVRCB 0x000a +#define FORMAT_EVRCWB 0x000b +#define FORMAT_MIDI 0x000c +#define FORMAT_SBC 0x000d +#define FORMAT_WMA_V10PRO 0x000e +#define FORMAT_WMA_V9 0x000f +#define FORMAT_AMR_WB_PLUS 0x0010 + +#define ENCDEC_SBCBITRATE 0x0001 +#define ENCDEC_IMMEDIATE_DECODE 0x0002 +#define ENCDEC_CFG_BLK 0x0003 + +#define CMD_PAUSE 0x0001 +#define CMD_FLUSH 0x0002 +#define CMD_EOS 0x0003 +#define CMD_CLOSE 0x0004 + +/* bit 0:1 represents priority of stream */ +#define STREAM_PRIORITY_NORMAL 0x0000 +#define STREAM_PRIORITY_LOW 0x0001 +#define STREAM_PRIORITY_HIGH 0x0002 + +/* bit 4 represents META enable of encoded data buffer */ +#define BUFFER_META_ENABLE 0x0010 + +#define ASYNC_IO_MODE 0x0002 +#define SYNC_IO_MODE 0x0001 +#define NO_TIMESTAMP 0xFF00 +#define SET_TIMESTAMP 0x0000 + +#define SOFT_PAUSE_ENABLE 1 +#define SOFT_PAUSE_DISABLE 0 + +#define SESSION_MAX 0x08 + +typedef void (*app_cb)(uint32_t opcode, uint32_t token, + uint32_t *payload, void *priv); + +struct audio_buffer { + dma_addr_t phys; + void *data; + uint32_t used; + uint32_t size;/* size of buffer */ + uint32_t actual_size; /* actual number of bytes read by DSP */ +}; + +struct audio_aio_write_param { + unsigned long paddr; + uint32_t uid; + uint32_t len; + uint32_t msw_ts; + uint32_t lsw_ts; + uint32_t flags; +}; + +struct audio_aio_read_param { + unsigned long paddr; + uint32_t len; + uint32_t uid; +}; + +struct audio_port_data { + struct audio_buffer *buf; + uint32_t max_buf_cnt; + uint32_t dsp_buf; + uint32_t cpu_buf; + /* read or write locks */ + struct mutex lock; + spinlock_t dsp_lock; +}; + +struct audio_client { + int session; + /* idx:1 out port, 0: in port*/ + struct audio_port_data port[2]; + + struct apr_svc *apr; + struct mutex cmd_lock; + + atomic_t cmd_state; + atomic_t time_flag; + wait_queue_head_t cmd_wait; + wait_queue_head_t time_wait; + + app_cb cb; + void *priv; + uint32_t io_mode; + uint64_t time_stamp; +}; + +void q6asm_audio_client_free(struct audio_client *ac); + +struct audio_client *q6asm_audio_client_alloc(app_cb cb, void *priv); + +int q6asm_audio_client_buf_alloc(unsigned int dir/* 1:Out,0:In */, + struct audio_client *ac, + unsigned int bufsz, + unsigned int bufcnt); +int q6asm_audio_client_buf_alloc_contiguous(unsigned int dir + /* 1:Out,0:In */, + struct audio_client *ac, + unsigned int bufsz, + unsigned int bufcnt); + +int q6asm_audio_client_buf_free_contiguous(unsigned int dir, + struct audio_client *ac); + +int q6asm_open_read(struct audio_client *ac, uint32_t format); + +int q6asm_open_write(struct audio_client *ac, uint32_t format); + +int q6asm_open_read_write(struct audio_client *ac, + uint32_t rd_format, + uint32_t wr_format); + +int q6asm_write(struct audio_client *ac, uint32_t len, uint32_t msw_ts, + uint32_t lsw_ts, uint32_t flags); + +int q6asm_write_nolock(struct audio_client *ac, uint32_t len, uint32_t msw_ts, + uint32_t lsw_ts, uint32_t flags); + +int q6asm_async_write(struct audio_client *ac, + struct audio_aio_write_param *param); + +int q6asm_async_read(struct audio_client *ac, + struct audio_aio_read_param *param); + +int q6asm_read(struct audio_client *ac); +int q6asm_read_nolock(struct audio_client *ac); + +int q6asm_memory_map(struct audio_client *ac, uint32_t buf_add, + int dir, uint32_t bufsz, uint32_t bufcnt); + +int q6asm_memory_unmap(struct audio_client *ac, uint32_t buf_add, + int dir); + +int q6asm_run(struct audio_client *ac, uint32_t flags, + uint32_t msw_ts, uint32_t lsw_ts); + +int q6asm_run_nowait(struct audio_client *ac, uint32_t flags, + uint32_t msw_ts, uint32_t lsw_ts); + +int q6asm_reg_tx_overflow(struct audio_client *ac, uint16_t enable); + +int q6asm_cmd(struct audio_client *ac, int cmd); + +int q6asm_cmd_nowait(struct audio_client *ac, int cmd); + +void *q6asm_is_cpu_buf_avail(int dir, struct audio_client *ac, + uint32_t *size, uint32_t *idx); + +int q6asm_is_dsp_buf_avail(int dir, struct audio_client *ac); + +/* File format specific configurations to be added below */ + +int q6asm_enc_cfg_blk_aac(struct audio_client *ac, + uint32_t frames_per_buf, + uint32_t sample_rate, uint32_t channels, + uint32_t bit_rate, + uint32_t mode, uint32_t format); + +int q6asm_enc_cfg_blk_pcm(struct audio_client *ac, + uint32_t rate, uint32_t channels); + +int q6asm_enable_sbrps(struct audio_client *ac, + uint32_t sbr_ps); + +int q6asm_enc_cfg_blk_qcelp(struct audio_client *ac, uint32_t frames_per_buf, + uint16_t min_rate, uint16_t max_rate, + uint16_t reduced_rate_level, uint16_t rate_modulation_cmd); + +int q6asm_enc_cfg_blk_evrc(struct audio_client *ac, uint32_t frames_per_buf, + uint16_t min_rate, uint16_t max_rate, + uint16_t rate_modulation_cmd); + +int q6asm_enc_cfg_blk_amrnb(struct audio_client *ac, uint32_t frames_per_buf, + uint16_t band_mode, uint16_t dtx_enable); + +int q6asm_media_format_block_pcm(struct audio_client *ac, + uint32_t rate, uint32_t channels); + +int q6asm_media_format_block_aac(struct audio_client *ac, + struct asm_aac_cfg *cfg); + +int q6asm_media_format_block_wma(struct audio_client *ac, + void *cfg); + +int q6asm_media_format_block_wmapro(struct audio_client *ac, + void *cfg); + +/* PP specific */ +int q6asm_equalizer(struct audio_client *ac, void *eq); + +/* Send Volume Command */ +int q6asm_set_volume(struct audio_client *ac, int volume); + +/* Set SoftPause Params */ +int q6asm_set_softpause(struct audio_client *ac, + struct asm_softpause_params *param); + +/* Send left-right channel gain */ +int q6asm_set_lrgain(struct audio_client *ac, int left_gain, int right_gain); + +/* Enable Mute/unmute flag */ +int q6asm_set_mute(struct audio_client *ac, int muteflag); + +uint64_t q6asm_get_session_time(struct audio_client *ac); + +/* Client can set the IO mode to either AIO/SIO mode */ +int q6asm_set_io_mode(struct audio_client *ac, uint32_t mode); + +#ifdef CONFIG_MSM8X60_RTAC +/* Get Service ID for APR communication */ +int q6asm_get_apr_service_id(int session_id); +#endif + +#endif /* __Q6_ASM_H__ */ diff --git a/arch/arm/mach-msm/include/mach/qdsp6v3/q6voice.h b/arch/arm/mach-msm/include/mach/qdsp6v3/q6voice.h new file mode 100644 index 00000000000..25d4ca3a65c --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdsp6v3/q6voice.h @@ -0,0 +1,757 @@ +/* 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 __QDSP6VOICE_H__ +#define __QDSP6VOICE_H__ + +#include + +/* Device Event */ +#define DEV_CHANGE_READY 0x1 + +#define VOICE_CALL_START 0x1 +#define VOICE_CALL_END 0 + +#define VOICE_DEV_ENABLED 0x1 +#define VOICE_DEV_DISABLED 0 + +#define MAX_VOC_PKT_SIZE 322 + +#define SESSION_NAME_LEN 20 + +struct voice_header { + uint32_t id; + uint32_t data_len; +}; + +struct voice_init { + struct voice_header hdr; + void *cb_handle; +}; + + +/* Device information payload structure */ + +struct device_data { + uint32_t dev_acdb_id; + uint32_t volume; /* in percentage */ + uint32_t mute; + uint32_t sample; + uint32_t enabled; + uint32_t dev_id; + uint32_t dev_port_id; +}; + +enum { + VOC_INIT = 0, + VOC_RUN, + VOC_CHANGE, + VOC_RELEASE, +}; + +/* TO MVM commands */ +#define VSS_IMVM_CMD_CREATE_PASSIVE_CONTROL_SESSION 0x000110FF +/**< No payload. Wait for APRV2_IBASIC_RSP_RESULT response. */ + +#define VSS_IMVM_CMD_CREATE_FULL_CONTROL_SESSION 0x000110FE +/* Create a new full control MVM session. */ + +#define APRV2_IBASIC_CMD_DESTROY_SESSION 0x0001003C +/**< No payload. Wait for APRV2_IBASIC_RSP_RESULT response. */ + +#define VSS_IMVM_CMD_ATTACH_STREAM 0x0001123C +/* Attach a stream to the MVM. */ + +#define VSS_IMVM_CMD_DETACH_STREAM 0x0001123D +/* Detach a stream from the MVM. */ + +#define VSS_IMVM_CMD_START_VOICE 0x00011190 +/**< No payload. Wait for APRV2_IBASIC_RSP_RESULT response. */ + +#define VSS_IMVM_CMD_STOP_VOICE 0x00011192 +/**< No payload. Wait for APRV2_IBASIC_RSP_RESULT response. */ + +#define VSS_ISTREAM_CMD_ATTACH_VOCPROC 0x000110F8 +/**< Wait for APRV2_IBASIC_RSP_RESULT response. */ + +#define VSS_ISTREAM_CMD_DETACH_VOCPROC 0x000110F9 +/**< Wait for APRV2_IBASIC_RSP_RESULT response. */ + + +#define VSS_ISTREAM_CMD_SET_TTY_MODE 0x00011196 +/**< Wait for APRV2_IBASIC_RSP_RESULT response. */ + +#define VSS_ICOMMON_CMD_SET_NETWORK 0x0001119C +/* Set the network type. */ + +#define VSS_ICOMMON_CMD_SET_VOICE_TIMING 0x000111E0 +/* Set the voice timing parameters. */ + +struct vss_imvm_cmd_create_control_session_t { + char name[SESSION_NAME_LEN]; /* + * A variable-sized stream name. + * + * The stream name size is the payload size minus the size of the other + * fields. + */ +} __packed; + +struct vss_istream_cmd_set_tty_mode_t { + uint32_t mode; + /**< + * TTY mode. + * + * 0 : TTY disabled + * 1 : HCO + * 2 : VCO + * 3 : FULL + */ +} __attribute__((packed)); + +struct vss_istream_cmd_attach_vocproc_t { + uint16_t handle; + /**< Handle of vocproc being attached. */ +} __attribute__((packed)); + +struct vss_istream_cmd_detach_vocproc_t { + uint16_t handle; + /**< Handle of vocproc being detached. */ +} __attribute__((packed)); + +struct vss_imvm_cmd_attach_stream_t { + uint16_t handle; + /* The stream handle to attach. */ +} __attribute__((packed)); + +struct vss_imvm_cmd_detach_stream_t { + uint16_t handle; + /* The stream handle to detach. */ +} __attribute__((packed)); + +struct vss_icommon_cmd_set_network_t { + uint32_t network_id; + /* Network ID. (Refer to VSS_NETWORK_ID_XXX). */ +} __attribute__((packed)); + +struct vss_icommon_cmd_set_voice_timing_t { + uint16_t mode; + /* + * The vocoder frame synchronization mode. + * + * 0 : No frame sync. + * 1 : Hard VFR (20ms Vocoder Frame Reference interrupt). + */ + uint16_t enc_offset; + /* + * The offset in microseconds from the VFR to deliver a Tx vocoder + * packet. The offset should be less than 20000us. + */ + uint16_t dec_req_offset; + /* + * The offset in microseconds from the VFR to request for an Rx vocoder + * packet. The offset should be less than 20000us. + */ + uint16_t dec_offset; + /* + * The offset in microseconds from the VFR to indicate the deadline to + * receive an Rx vocoder packet. The offset should be less than 20000us. + * Rx vocoder packets received after this deadline are not guaranteed to + * be processed. + */ +} __attribute__((packed)); + +struct mvm_attach_vocproc_cmd { + struct apr_hdr hdr; + struct vss_istream_cmd_attach_vocproc_t mvm_attach_cvp_handle; +} __attribute__((packed)); + +struct mvm_detach_vocproc_cmd { + struct apr_hdr hdr; + struct vss_istream_cmd_detach_vocproc_t mvm_detach_cvp_handle; +} __attribute__((packed)); + +struct mvm_create_ctl_session_cmd { + struct apr_hdr hdr; + struct vss_imvm_cmd_create_control_session_t mvm_session; +} __packed; + +struct mvm_set_tty_mode_cmd { + struct apr_hdr hdr; + struct vss_istream_cmd_set_tty_mode_t tty_mode; +} __attribute__((packed)); + +struct mvm_attach_stream_cmd { + struct apr_hdr hdr; + struct vss_imvm_cmd_attach_stream_t attach_stream; +} __attribute__((packed)); + +struct mvm_detach_stream_cmd { + struct apr_hdr hdr; + struct vss_imvm_cmd_detach_stream_t detach_stream; +} __attribute__((packed)); + +struct mvm_set_network_cmd { + struct apr_hdr hdr; + struct vss_icommon_cmd_set_network_t network; +} __attribute__((packed)); + +struct mvm_set_voice_timing_cmd { + struct apr_hdr hdr; + struct vss_icommon_cmd_set_voice_timing_t timing; +} __attribute__((packed)); + +/* TO CVS commands */ +#define VSS_ISTREAM_CMD_CREATE_PASSIVE_CONTROL_SESSION 0x00011140 +/**< Wait for APRV2_IBASIC_RSP_RESULT response. */ + +#define VSS_ISTREAM_CMD_CREATE_FULL_CONTROL_SESSION 0x000110F7 +/* Create a new full control stream session. */ + +#define APRV2_IBASIC_CMD_DESTROY_SESSION 0x0001003C + +#define VSS_ISTREAM_CMD_CACHE_CALIBRATION_DATA 0x000110FB + +#define VSS_ISTREAM_CMD_SET_MUTE 0x00011022 + +#define VSS_ISTREAM_CMD_SET_MEDIA_TYPE 0x00011186 +/* Set media type on the stream. */ + +#define VSS_ISTREAM_EVT_SEND_ENC_BUFFER 0x00011015 +/* Event sent by the stream to its client to provide an encoded packet. */ + +#define VSS_ISTREAM_EVT_REQUEST_DEC_BUFFER 0x00011017 +/* Event sent by the stream to its client requesting for a decoder packet. + * The client should respond with a VSS_ISTREAM_EVT_SEND_DEC_BUFFER event. + */ + +#define VSS_ISTREAM_EVT_SEND_DEC_BUFFER 0x00011016 +/* Event sent by the client to the stream in response to a + * VSS_ISTREAM_EVT_REQUEST_DEC_BUFFER event, providing a decoder packet. + */ + +#define VSS_ISTREAM_CMD_VOC_AMR_SET_ENC_RATE 0x0001113E +/* Set AMR encoder rate. */ + +#define VSS_ISTREAM_CMD_VOC_AMRWB_SET_ENC_RATE 0x0001113F +/* Set AMR-WB encoder rate. */ + +#define VSS_ISTREAM_CMD_CDMA_SET_ENC_MINMAX_RATE 0x00011019 +/* Set encoder minimum and maximum rate. */ + +#define VSS_ISTREAM_CMD_SET_ENC_DTX_MODE 0x0001101D +/* Set encoder DTX mode. */ + +#define VSS_ISTREAM_CMD_START_RECORD 0x00011236 +/* Start in-call conversation recording. */ + +#define VSS_ISTREAM_CMD_STOP_RECORD 0x00011237 +/* Stop in-call conversation recording. */ + +#define VSS_ISTREAM_CMD_START_PLAYBACK 0x00011238 +/* Start in-call music delivery on the Tx voice path. */ + +#define VSS_ISTREAM_CMD_STOP_PLAYBACK 0x00011239 +/* Stop the in-call music delivery on the Tx voice path. */ + +struct vss_istream_cmd_create_passive_control_session_t { + char name[SESSION_NAME_LEN]; + /**< + * A variable-sized stream name. + * + * The stream name size is the payload size minus the size of the other + * fields. + */ +} __attribute__((packed)); + +struct vss_istream_cmd_set_mute_t { + uint16_t direction; + /**< + * 0 : TX only + * 1 : RX only + * 2 : TX and Rx + */ + uint16_t mute_flag; + /**< + * Mute, un-mute. + * + * 0 : Silence disable + * 1 : Silence enable + * 2 : CNG enable. Applicable to TX only. If set on RX behavior + * will be the same as 1 + */ +} __attribute__((packed)); + +struct vss_istream_cmd_create_full_control_session_t { + uint16_t direction; + /* + * Stream direction. + * + * 0 : TX only + * 1 : RX only + * 2 : TX and RX + * 3 : TX and RX loopback + */ + uint32_t enc_media_type; + /* Tx vocoder type. (Refer to VSS_MEDIA_ID_XXX). */ + uint32_t dec_media_type; + /* Rx vocoder type. (Refer to VSS_MEDIA_ID_XXX). */ + uint32_t network_id; + /* Network ID. (Refer to VSS_NETWORK_ID_XXX). */ + char name[SESSION_NAME_LEN]; + /* + * A variable-sized stream name. + * + * The stream name size is the payload size minus the size of the other + * fields. + */ +} __attribute__((packed)); + +struct vss_istream_cmd_set_media_type_t { + uint32_t rx_media_id; + /* Set the Rx vocoder type. (Refer to VSS_MEDIA_ID_XXX). */ + uint32_t tx_media_id; + /* Set the Tx vocoder type. (Refer to VSS_MEDIA_ID_XXX). */ +} __attribute__((packed)); + +struct vss_istream_evt_send_enc_buffer_t { + uint32_t media_id; + /* Media ID of the packet. */ + uint8_t packet_data[MAX_VOC_PKT_SIZE]; + /* Packet data buffer. */ +} __attribute__((packed)); + +struct vss_istream_evt_send_dec_buffer_t { + uint32_t media_id; + /* Media ID of the packet. */ + uint8_t packet_data[MAX_VOC_PKT_SIZE]; + /* Packet data. */ +} __attribute__((packed)); + +struct vss_istream_cmd_voc_amr_set_enc_rate_t { + uint32_t mode; + /* Set the AMR encoder rate. + * + * 0x00000000 : 4.75 kbps + * 0x00000001 : 5.15 kbps + * 0x00000002 : 5.90 kbps + * 0x00000003 : 6.70 kbps + * 0x00000004 : 7.40 kbps + * 0x00000005 : 7.95 kbps + * 0x00000006 : 10.2 kbps + * 0x00000007 : 12.2 kbps + */ +} __attribute__((packed)); + +struct vss_istream_cmd_voc_amrwb_set_enc_rate_t { + uint32_t mode; + /* Set the AMR-WB encoder rate. + * + * 0x00000000 : 6.60 kbps + * 0x00000001 : 8.85 kbps + * 0x00000002 : 12.65 kbps + * 0x00000003 : 14.25 kbps + * 0x00000004 : 15.85 kbps + * 0x00000005 : 18.25 kbps + * 0x00000006 : 19.85 kbps + * 0x00000007 : 23.05 kbps + * 0x00000008 : 23.85 kbps + */ +} __attribute__((packed)); + +struct vss_istream_cmd_cdma_set_enc_minmax_rate_t { + uint16_t min_rate; + /* Set the lower bound encoder rate. + * + * 0x0000 : Blank frame + * 0x0001 : Eighth rate + * 0x0002 : Quarter rate + * 0x0003 : Half rate + * 0x0004 : Full rate + */ + uint16_t max_rate; + /* Set the upper bound encoder rate. + * + * 0x0000 : Blank frame + * 0x0001 : Eighth rate + * 0x0002 : Quarter rate + * 0x0003 : Half rate + * 0x0004 : Full rate + */ +} __attribute__((packed)); + +struct vss_istream_cmd_set_enc_dtx_mode_t { + uint32_t enable; + /* Toggle DTX on or off. + * + * 0 : Disables DTX + * 1 : Enables DTX + */ +} __attribute__((packed)); + +#define VSS_TAP_POINT_NONE 0x00010F78 +/* Indicates no tapping for specified path. */ + +#define VSS_TAP_POINT_STREAM_END 0x00010F79 +/* Indicates that specified path should be tapped at the end of the stream. */ + +struct vss_istream_cmd_start_record_t { + uint32_t rx_tap_point; + /* Tap point to use on the Rx path. Supported values are: + * VSS_TAP_POINT_NONE : Do not record Rx path. + * VSS_TAP_POINT_STREAM_END : Rx tap point is at the end of the stream. + */ + uint32_t tx_tap_point; + /* Tap point to use on the Tx path. Supported values are: + * VSS_TAP_POINT_NONE : Do not record tx path. + * VSS_TAP_POINT_STREAM_END : Tx tap point is at the end of the stream. + */ +} __attribute__((packed)); + +struct cvs_create_passive_ctl_session_cmd { + struct apr_hdr hdr; + struct vss_istream_cmd_create_passive_control_session_t cvs_session; +} __attribute__((packed)); + +struct cvs_create_full_ctl_session_cmd { + struct apr_hdr hdr; + struct vss_istream_cmd_create_full_control_session_t cvs_session; +} __attribute__((packed)); + +struct cvs_destroy_session_cmd { + struct apr_hdr hdr; +} __attribute__((packed)); + +struct cvs_cache_calibration_data_cmd { + struct apr_hdr hdr; +} __attribute__ ((packed)); + +struct cvs_set_mute_cmd { + struct apr_hdr hdr; + struct vss_istream_cmd_set_mute_t cvs_set_mute; +} __attribute__((packed)); + +struct cvs_set_media_type_cmd { + struct apr_hdr hdr; + struct vss_istream_cmd_set_media_type_t media_type; +} __attribute__((packed)); + +struct cvs_send_dec_buf_cmd { + struct apr_hdr hdr; + struct vss_istream_evt_send_dec_buffer_t dec_buf; +} __attribute__((packed)); + +struct cvs_set_amr_enc_rate_cmd { + struct apr_hdr hdr; + struct vss_istream_cmd_voc_amr_set_enc_rate_t amr_rate; +} __attribute__((packed)); + +struct cvs_set_amrwb_enc_rate_cmd { + struct apr_hdr hdr; + struct vss_istream_cmd_voc_amrwb_set_enc_rate_t amrwb_rate; +} __attribute__((packed)); + +struct cvs_set_cdma_enc_minmax_rate_cmd { + struct apr_hdr hdr; + struct vss_istream_cmd_cdma_set_enc_minmax_rate_t cdma_rate; +} __attribute__((packed)); + +struct cvs_set_enc_dtx_mode_cmd { + struct apr_hdr hdr; + struct vss_istream_cmd_set_enc_dtx_mode_t dtx_mode; +} __attribute__((packed)); + +struct cvs_start_record_cmd { + struct apr_hdr hdr; + struct vss_istream_cmd_start_record_t rec_mode; +} __attribute__((packed)); + +/* TO CVP commands */ + +#define VSS_IVOCPROC_CMD_CREATE_FULL_CONTROL_SESSION 0x000100C3 +/**< Wait for APRV2_IBASIC_RSP_RESULT response. */ + +#define APRV2_IBASIC_CMD_DESTROY_SESSION 0x0001003C + +#define VSS_IVOCPROC_CMD_SET_DEVICE 0x000100C4 + +#define VSS_IVOCPROC_CMD_CACHE_CALIBRATION_DATA 0x000110E3 + +#define VSS_IVOCPROC_CMD_CACHE_VOLUME_CALIBRATION_TABLE 0x000110E4 + +#define VSS_IVOCPROC_CMD_SET_VP3_DATA 0x000110EB + +#define VSS_IVOCPROC_CMD_SET_RX_VOLUME_INDEX 0x000110EE + +#define VSS_IVOCPROC_CMD_ENABLE 0x000100C6 +/**< No payload. Wait for APRV2_IBASIC_RSP_RESULT response. */ + +#define VSS_IVOCPROC_CMD_DISABLE 0x000110E1 +/**< No payload. Wait for APRV2_IBASIC_RSP_RESULT response. */ + +#define VSS_IVOCPROC_TOPOLOGY_ID_NONE 0x00010F70 +#define VSS_IVOCPROC_TOPOLOGY_ID_TX_SM_ECNS 0x00010F71 +#define VSS_IVOCPROC_TOPOLOGY_ID_TX_DM_FLUENCE 0x00010F72 + +#define VSS_IVOCPROC_TOPOLOGY_ID_RX_DEFAULT 0x00010F77 + +/* Newtwork IDs */ +#define VSS_NETWORK_ID_DEFAULT 0x00010037 +#define VSS_NETWORK_ID_VOIP_NB 0x00011240 +#define VSS_NETWORK_ID_VOIP_WB 0x00011241 +#define VSS_NETWORK_ID_VOIP_WV 0x00011242 + +/* Media types */ +#define VSS_MEDIA_ID_EVRC_MODEM 0x00010FC2 +/* 80-VF690-47 CDMA enhanced variable rate vocoder modem format. */ +#define VSS_MEDIA_ID_AMR_NB_MODEM 0x00010FC6 +/* 80-VF690-47 UMTS AMR-NB vocoder modem format. */ +#define VSS_MEDIA_ID_AMR_WB_MODEM 0x00010FC7 +/* 80-VF690-47 UMTS AMR-WB vocoder modem format. */ +#define VSS_MEDIA_ID_PCM_NB 0x00010FCB +/* Linear PCM (16-bit, little-endian). */ +#define VSS_MEDIA_ID_G711_ALAW 0x00010FCD +/* G.711 a-law (contains two 10ms vocoder frames). */ +#define VSS_MEDIA_ID_G711_MULAW 0x00010FCE +/* G.711 mu-law (contains two 10ms vocoder frames). */ +#define VSS_MEDIA_ID_G729 0x00010FD0 +/* G.729AB (contains two 10ms vocoder frames. */ + +#define VOICE_CMD_SET_PARAM 0x00011006 +#define VOICE_CMD_GET_PARAM 0x00011007 +#define VOICE_EVT_GET_PARAM_ACK 0x00011008 + +struct vss_ivocproc_cmd_create_full_control_session_t { + uint16_t direction; + /* + * stream direction. + * 0 : TX only + * 1 : RX only + * 2 : TX and RX + */ + uint32_t tx_port_id; + /* + * TX device port ID which vocproc will connect to. If not supplying a + * port ID set to VSS_IVOCPROC_PORT_ID_NONE. + */ + uint32_t tx_topology_id; + /* + * Tx leg topology ID. If not supplying a topology ID set to + * VSS_IVOCPROC_TOPOLOGY_ID_NONE. + */ + uint32_t rx_port_id; + /* + * RX device port ID which vocproc will connect to. If not supplying a + * port ID set to VSS_IVOCPROC_PORT_ID_NONE. + */ + uint32_t rx_topology_id; + /* + * Rx leg topology ID. If not supplying a topology ID set to + * VSS_IVOCPROC_TOPOLOGY_ID_NONE. + */ + int32_t network_id; + /* + * Network ID. (Refer to VSS_NETWORK_ID_XXX). If not supplying a network + * ID set to VSS_NETWORK_ID_DEFAULT. + */ +} __attribute__((packed)); + +struct vss_ivocproc_cmd_set_device_t { + uint32_t tx_port_id; + /**< + * TX device port ID which vocproc will connect to. + * VSS_IVOCPROC_PORT_ID_NONE means vocproc will not connect to any port. + */ + uint32_t tx_topology_id; + /**< + * TX leg topology ID. + * VSS_IVOCPROC_TOPOLOGY_ID_NONE means vocproc does not contain any + * pre/post-processing blocks and is pass-through. + */ + int32_t rx_port_id; + /**< + * RX device port ID which vocproc will connect to. + * VSS_IVOCPROC_PORT_ID_NONE means vocproc will not connect to any port. + */ + uint32_t rx_topology_id; + /**< + * RX leg topology ID. + * VSS_IVOCPROC_TOPOLOGY_ID_NONE means vocproc does not contain any + * pre/post-processing blocks and is pass-through. + */ +} __attribute__((packed)); + +struct vss_ivocproc_cmd_set_volume_index_t { + uint16_t vol_index; + /**< + * Volume index utilized by the vocproc to index into the volume table + * provided in VSS_IVOCPROC_CMD_CACHE_VOLUME_CALIBRATION_TABLE and set + * volume on the VDSP. + */ +} __attribute__((packed)); + +struct cvp_create_full_ctl_session_cmd { + struct apr_hdr hdr; + struct vss_ivocproc_cmd_create_full_control_session_t cvp_session; +} __attribute__ ((packed)); + +struct cvp_command { + struct apr_hdr hdr; +} __attribute__((packed)); + +struct cvp_set_device_cmd { + struct apr_hdr hdr; + struct vss_ivocproc_cmd_set_device_t cvp_set_device; +} __attribute__ ((packed)); + +struct cvp_cache_calibration_data_cmd { + struct apr_hdr hdr; +} __attribute__((packed)); + +struct cvp_cache_volume_calibration_table_cmd { + struct apr_hdr hdr; +} __attribute__((packed)); + +struct cvp_set_vp3_data_cmd { + struct apr_hdr hdr; +} __attribute__((packed)); + +struct cvp_set_rx_volume_index_cmd { + struct apr_hdr hdr; + struct vss_ivocproc_cmd_set_volume_index_t cvp_set_vol_idx; +} __attribute__((packed)); + +/* CB for up-link packets. */ +typedef void (*ul_cb_fn)(uint8_t *voc_pkt, + uint32_t pkt_len, + void *private_data); + +/* CB for down-link packets. */ +typedef void (*dl_cb_fn)(uint8_t *voc_pkt, + uint32_t *pkt_len, + void *private_data); + + +struct mvs_driver_info { + uint32_t media_type; + uint32_t rate; + uint32_t network_type; + uint32_t dtx_mode; + ul_cb_fn ul_cb; + dl_cb_fn dl_cb; + void *private_data; +}; + +struct incall_rec_info { + uint32_t pending; + uint32_t rec_mode; +}; + +struct incall_music_info { + uint32_t pending; + uint32_t playing; +}; + +struct voice_data { + int voc_state;/*INIT, CHANGE, RELEASE, RUN */ + uint32_t voc_path; + uint32_t adsp_version; + + wait_queue_head_t mvm_wait; + wait_queue_head_t cvs_wait; + wait_queue_head_t cvp_wait; + + uint32_t device_events; + + /* cache the values related to Rx and Tx */ + struct device_data dev_rx; + struct device_data dev_tx; + + /* these default values are for all devices */ + uint32_t default_mute_val; + uint32_t default_vol_val; + uint32_t default_sample_val; + + /* call status */ + int v_call_status; /* Start or End */ + + /* APR to MVM in the modem */ + void *apr_mvm; + /* APR to CVS in the modem */ + void *apr_cvs; + /* APR to CVP in the modem */ + void *apr_cvp; + + /* APR to MVM in the Q6 */ + void *apr_q6_mvm; + /* APR to CVS in the Q6 */ + void *apr_q6_cvs; + /* APR to CVP in the Q6 */ + void *apr_q6_cvp; + + u32 mvm_state; + u32 cvs_state; + u32 cvp_state; + + /* Handle to MVM in the modem */ + u16 mvm_handle; + /* Handle to CVS in the modem */ + u16 cvs_handle; + /* Handle to CVP in the modem */ + u16 cvp_handle; + + /* Handle to MVM in the Q6 */ + u16 mvm_q6_handle; + /* Handle to CVS in the Q6 */ + u16 cvs_q6_handle; + /* Handle to CVP in the Q6 */ + u16 cvp_q6_handle; + + struct mutex lock; + + struct mvs_driver_info mvs_info; + + struct incall_rec_info rec_info; + + struct incall_music_info music_info; +}; + +int voice_set_voc_path_full(uint32_t set); + +void voice_register_mvs_cb(ul_cb_fn ul_cb, + dl_cb_fn dl_cb, + void *private_data); + +void voice_config_vocoder(uint32_t media_type, + uint32_t rate, + uint32_t network_type, + uint32_t dtx_mode); + +int voice_start_record(uint32_t rec_mode, uint32_t set); + +int voice_start_playback(uint32_t set); +#endif diff --git a/arch/arm/mach-msm/include/mach/qdsp6v3/snddev_ecodec.h b/arch/arm/mach-msm/include/mach/qdsp6v3/snddev_ecodec.h new file mode 100644 index 00000000000..e07ad02505c --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdsp6v3/snddev_ecodec.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 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 __MACH_QDSP6V2_SNDDEV_ECODEC_H +#define __MACH_QDSP6V2_SNDDEV_ECODEC_H +#include + +struct snddev_ecodec_data { + u32 capability; /* RX or TX */ + const char *name; + u32 copp_id; /* audpp routing */ + u8 channel_mode; + u32 conf_pcm_ctl_val; + u32 conf_aux_codec_intf; + u32 conf_data_format_padding_val; +}; + +struct q6v2audio_ecodec_ops { + void (*bt_sco_enable)(int en); +}; + +void htc_8x60_register_ecodec_ops(struct q6v2audio_ecodec_ops *ops); +#endif diff --git a/arch/arm/mach-msm/include/mach/qdsp6v3/snddev_hdmi.h b/arch/arm/mach-msm/include/mach/qdsp6v3/snddev_hdmi.h new file mode 100644 index 00000000000..cdc81a1f03f --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdsp6v3/snddev_hdmi.h @@ -0,0 +1,40 @@ +/* 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 __MACH_QDSP6_V2_SNDDEV_HDMI_H +#define __MACH_QDSP6_V2_SNDDEV_HDMI_H + +struct snddev_hdmi_data { + u32 capability; /* RX or TX */ + const char *name; + u32 copp_id; /* audpp routing */ + u32 acdb_id; /* Audio Cal purpose */ + u8 channel_mode; + u32 default_sample_rate; +}; +#endif diff --git a/arch/arm/mach-msm/include/mach/qdsp6v3/snddev_icodec.h b/arch/arm/mach-msm/include/mach/qdsp6v3/snddev_icodec.h new file mode 100644 index 00000000000..06f31f569dd --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdsp6v3/snddev_icodec.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 __MACH_QDSP6V2_SNDDEV_ICODEC_H +#define __MACH_QDSP6V2_SNDDEV_ICODEC_H +#include +#include +#include +#include + +struct snddev_icodec_data { + u32 capability; /* RX or TX */ + const char *name; + u32 copp_id; /* audpp routing */ + /* Adie profile */ + struct adie_codec_dev_profile *profile; + /* Afe setting */ + u8 channel_mode; + u32 default_sample_rate; + void (*pamp_on) (int on); + void (*voltage_on) (int on); + u32 dev_vol_type; + u32 aic3254_id; + u32 aic3254_voc_id; + u32 default_aic3254_id; +}; + +/* Context for each internal codec sound device */ +struct snddev_icodec_state { + struct snddev_icodec_data *data; + struct adie_codec_path *adie_path; + u32 sample_rate; + u32 enabled; +}; + +struct q6v2audio_analog_ops { + void (*speaker_enable)(int en); + void (*headset_enable)(int en); + void (*handset_enable)(int en); + void (*bt_sco_enable)(int en); + void (*headset_speaker_enable)(int en); + void (*int_mic_enable)(int en); + void (*back_mic_enable)(int en); + void (*ext_mic_enable)(int en); + void (*stereo_mic_enable)(int en); + void (*usb_headset_enable)(int en); + void (*fm_headset_enable)(int en); + void (*fm_speaker_enable)(int en); + void (*voltage_on) (int on); +}; + +struct q6v2audio_icodec_ops { + int (*support_aic3254) (void); + int (*support_adie) (void); + int (*is_msm_i2s_slave) (void); + int (*support_aic3254_use_mclk) (void); +}; + +struct q6v2audio_aic3254_ops { + void (*aic3254_set_mode)(int config, int mode); +}; + +struct aic3254_info { + u32 dev_id; + u32 path_id; +}; + + +void htc_8x60_register_analog_ops(struct q6v2audio_analog_ops *ops); +void htc_8x60_register_aic3254_ops(struct q6v2audio_aic3254_ops *ops); +int update_aic3254_info(struct aic3254_info *info); +void htc_8x60_register_icodec_ops(struct q6v2audio_icodec_ops *ops); +#endif diff --git a/arch/arm/mach-msm/qdsp6v3/Makefile b/arch/arm/mach-msm/qdsp6v3/Makefile new file mode 100644 index 00000000000..a0aca30cf3c --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v3/Makefile @@ -0,0 +1,15 @@ +obj-$(CONFIG_MSM8X60_RTAC) += rtac.o +obj-y += audio_dev_ctl.o +obj-y += board-msm8x60-audio.o +obj-$(CONFIG_TIMPANI_CODEC) += snddev_icodec.o +obj-y += snddev_ecodec.o snddev_mi2s.o snddev_virtual.o +obj-y += apr.o apr_tal.o q6core.o dsp_debug.o +obj-y += audio_acdb.o +obj-y += q6asm.o q6adm.o q6afe.o +obj-y += pcm_out.o pcm_in.o fm.o +obj-y += audio_lpa.o +obj-y += q6voice.o +obj-y += snddev_hdmi.o +obj-y += aac_in.o qcelp_in.o evrc_in.o amrnb_in.o audio_utils.o +obj-y += audio_mvs.o +obj-y += audio_wma.o audio_wmapro.o audio_aac.o diff --git a/arch/arm/mach-msm/qdsp6v3/aac_in.c b/arch/arm/mach-msm/qdsp6v3/aac_in.c new file mode 100644 index 00000000000..f01a1414ba2 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v3/aac_in.c @@ -0,0 +1,435 @@ +/* + * 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 "audio_utils.h" + + +/* Buffer with meta*/ +#define PCM_BUF_SIZE (4096 + sizeof(struct meta_in)) + +/* Maximum 5 frames in buffer with meta */ +#define FRAME_SIZE (1 + ((1536+sizeof(struct meta_out_dsp)) * 5)) + +#define AAC_FORMAT_ADTS 65535 + +void q6asm_aac_in_cb(uint32_t opcode, uint32_t token, + uint32_t *payload, void *priv) +{ + struct q6audio_in * audio = (struct q6audio_in *)priv; + unsigned long flags; + + pr_debug("%s:session id %d: opcode[0x%x]\n", __func__, + audio->ac->session, opcode); + + spin_lock_irqsave(&audio->dsp_lock, flags); + switch (opcode) { + case ASM_DATA_EVENT_READ_DONE: + audio_in_get_dsp_frames(audio, token, payload); + break; + case ASM_DATA_EVENT_WRITE_DONE: + atomic_inc(&audio->in_count); + wake_up(&audio->write_wait); + break; + case ASM_DATA_CMDRSP_EOS: + audio->eos_rsp = 1; + wake_up(&audio->read_wait); + break; + case ASM_STREAM_CMDRSP_GET_ENCDEC_PARAM: + break; + case ASM_STREAM_CMDRSP_GET_PP_PARAMS: + break; + case ASM_SESSION_EVENT_TX_OVERFLOW: + pr_aud_err("%s:session id %d: ASM_SESSION_EVENT_TX_OVERFLOW\n", + __func__, audio->ac->session); + break; + default: + pr_aud_err("%s:session id %d: Ignore opcode[0x%x]\n", __func__, + audio->ac->session, opcode); + break; + } + spin_unlock_irqrestore(&audio->dsp_lock, flags); +} +/* ------------------- device --------------------- */ +static long aac_in_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct q6audio_in *audio = file->private_data; + int rc = 0; + int cnt = 0; + + switch (cmd) { + case AUDIO_START: { + struct msm_audio_aac_enc_config *enc_cfg; + struct msm_audio_aac_config *aac_config; + uint32_t aac_mode = AAC_ENC_MODE_AAC_LC; + + enc_cfg = audio->enc_cfg; + aac_config = audio->codec_cfg; + /* ENCODE CFG (after new set of API's are published )bharath*/ + pr_debug("%s:session id %d: default buf alloc[%d]\n", __func__, + audio->ac->session, audio->buf_alloc); + if (audio->enabled == 1) { + pr_aud_info("%s:AUDIO_START already over\n", __func__); + rc = 0; + break; + } + + rc = audio_in_buf_alloc(audio); + if (rc < 0) { + pr_aud_err("%s:session id %d: buffer allocation failed\n", + __func__, audio->ac->session); + break; + } + + pr_debug("%s:sbr_ps_flag = %d, sbr_flag = %d\n", __func__, + aac_config->sbr_ps_on_flag, aac_config->sbr_on_flag); + if (aac_config->sbr_ps_on_flag) + aac_mode = AAC_ENC_MODE_EAAC_P; + else if (aac_config->sbr_on_flag) + aac_mode = AAC_ENC_MODE_AAC_P; + else + aac_mode = AAC_ENC_MODE_AAC_LC; + + rc = q6asm_enc_cfg_blk_aac(audio->ac, + audio->buf_cfg.frames_per_buf, + enc_cfg->sample_rate, + enc_cfg->channels, + enc_cfg->bit_rate, + aac_mode, + enc_cfg->stream_format); + if (rc < 0) { + pr_aud_err("%s:session id %d: cmd media format block\ + failed\n", __func__, audio->ac->session); + break; + } + if (audio->feedback == NON_TUNNEL_MODE) { + rc = q6asm_media_format_block_pcm(audio->ac, + audio->pcm_cfg.sample_rate, + audio->pcm_cfg.channel_count); + if (rc < 0) { + pr_aud_err("%s:session id %d: media format block\ + failed\n", __func__, audio->ac->session); + break; + } + } + rc = audio_in_enable(audio); + if (!rc) { + audio->enabled = 1; + } else { + audio->enabled = 0; + pr_aud_err("%s:session id %d: Audio Start procedure\ + failed rc=%d\n", __func__, audio->ac->session, rc); + break; + } + while (cnt++ < audio->str_cfg.buffer_count) + q6asm_read(audio->ac); + pr_debug("%s:session id %d: AUDIO_START success enable[%d]\n", + __func__, audio->ac->session, audio->enabled); + break; + } + case AUDIO_STOP: { + pr_debug("%s:session id %d: Rxed AUDIO_STOP\n", __func__, + audio->ac->session); + rc = audio_in_disable(audio); + if (rc < 0) { + pr_aud_err("%s:session id %d: Audio Stop procedure failed\ + rc=%d\n", __func__, audio->ac->session, rc); + break; + } + break; + } + case AUDIO_GET_AAC_ENC_CONFIG: { + struct msm_audio_aac_enc_config cfg; + struct msm_audio_aac_enc_config *enc_cfg; + enc_cfg = audio->enc_cfg; + if (enc_cfg->channels == CH_MODE_MONO) + cfg.channels = 1; + else + cfg.channels = 2; + cfg.sample_rate = enc_cfg->sample_rate; + cfg.bit_rate = enc_cfg->bit_rate; + /* ADTS(-1) to ADTS(0x00), RAW(0x00) to RAW(0x03) */ + cfg.stream_format = ((enc_cfg->stream_format == \ + 0x00) ? AUDIO_AAC_FORMAT_ADTS : AUDIO_AAC_FORMAT_RAW); + pr_debug("%s:session id %d: Get-aac-cfg: format=%d sr=%d\ + bitrate=%d\n", __func__, audio->ac->session, + cfg.stream_format, cfg.sample_rate, cfg.bit_rate); + if (copy_to_user((void *)arg, &cfg, sizeof(cfg))) + rc = -EFAULT; + break; + } + case AUDIO_SET_AAC_ENC_CONFIG: { + struct msm_audio_aac_enc_config cfg; + struct msm_audio_aac_enc_config *enc_cfg; + enc_cfg = audio->enc_cfg; + if (copy_from_user(&cfg, (void *)arg, sizeof(cfg))) { + rc = -EFAULT; + break; + } + pr_debug("%s:session id %d: Set-aac-cfg: stream=%d\n", __func__, + audio->ac->session, cfg.stream_format); + + if ((cfg.stream_format != AUDIO_AAC_FORMAT_RAW) && + (cfg.stream_format != AAC_FORMAT_ADTS)) { + pr_aud_err("%s:session id %d: unsupported AAC format\n", + __func__, audio->ac->session); + rc = -EINVAL; + break; + } + + if (cfg.channels == 1) { + cfg.channels = CH_MODE_MONO; + } else if (cfg.channels == 2) { + cfg.channels = CH_MODE_STEREO; + } else { + rc = -EINVAL; + break; + } + if ((cfg.sample_rate < 8000) && (cfg.sample_rate > 48000)) { + pr_aud_err("%s: ERROR in setting samplerate = %d\n", + __func__, cfg.sample_rate); + rc = -EINVAL; + break; + } + enc_cfg->sample_rate = cfg.sample_rate; + enc_cfg->channels = cfg.channels; + enc_cfg->bit_rate = cfg.bit_rate; + enc_cfg->stream_format = + ((cfg.stream_format == AUDIO_AAC_FORMAT_RAW) ? \ + 0x03 : 0x00); + pr_debug("%s:session id %d: Set-aac-cfg:SR= 0x%x ch=0x%x\ + bitrate=0x%x, format(adts/raw) = %d\n", + __func__, audio->ac->session, enc_cfg->sample_rate, + enc_cfg->channels, enc_cfg->bit_rate, + enc_cfg->stream_format); + break; + } + case AUDIO_GET_AAC_CONFIG: { + if (copy_to_user((void *)arg, &audio->codec_cfg, + sizeof(struct msm_audio_aac_config))) { + rc = -EFAULT; + break; + } + break; + } + case AUDIO_SET_AAC_CONFIG: { + struct msm_audio_aac_config aac_cfg; + struct msm_audio_aac_config *audio_aac_cfg; + struct msm_audio_aac_enc_config *enc_cfg; + enc_cfg = audio->enc_cfg; + audio_aac_cfg = audio->codec_cfg; + + if (copy_from_user(&aac_cfg, (void *)arg, + sizeof(struct msm_audio_aac_config))) { + rc = -EFAULT; + break; + } + pr_debug("%s:session id %d: AUDIO_SET_AAC_CONFIG: sbr_flag = %d" + " sbr_ps_flag = %d\n", __func__, + audio->ac->session, aac_cfg.sbr_on_flag, + aac_cfg.sbr_ps_on_flag); + audio_aac_cfg->sbr_on_flag = aac_cfg.sbr_on_flag; + audio_aac_cfg->sbr_ps_on_flag = aac_cfg.sbr_ps_on_flag; + if ((audio_aac_cfg->sbr_on_flag == 1) || + (audio_aac_cfg->sbr_ps_on_flag == 1)) { + if (enc_cfg->sample_rate < 24000) { + pr_aud_err("%s: ERROR in setting samplerate = %d" + "\n", __func__, enc_cfg->sample_rate); + rc = -EINVAL; + break; + } + } + break; + } + default: + rc = -EINVAL; + } + return rc; +} + +static int aac_in_open(struct inode *inode, struct file *file) +{ + struct q6audio_in *audio = NULL; + struct msm_audio_aac_enc_config *enc_cfg; + struct msm_audio_aac_config *aac_config; + int rc = 0; + + audio = kzalloc(sizeof(struct q6audio_in), GFP_KERNEL); + + if (audio == NULL) { + pr_aud_err("%s:Could not allocate memory for aac\ + driver\n", __func__); + return -ENOMEM; + } + /* Allocate memory for encoder config param */ + audio->enc_cfg = kzalloc(sizeof(struct msm_audio_aac_enc_config), + GFP_KERNEL); + if (audio->enc_cfg == NULL) { + pr_aud_err("%s:session id %d: Could not allocate memory for aac\ + config param\n", __func__, audio->ac->session); + kfree(audio); + return -ENOMEM; + } + enc_cfg = audio->enc_cfg; + + audio->codec_cfg = kzalloc(sizeof(struct msm_audio_aac_config), + GFP_KERNEL); + if (audio->codec_cfg == NULL) { + pr_aud_err("%s:session id %d: Could not allocate memory for aac\ + config\n", __func__, audio->ac->session); + kfree(audio->enc_cfg); + kfree(audio); + return -ENOMEM; + } + aac_config = audio->codec_cfg; + + mutex_init(&audio->lock); + mutex_init(&audio->read_lock); + mutex_init(&audio->write_lock); + spin_lock_init(&audio->dsp_lock); + init_waitqueue_head(&audio->read_wait); + init_waitqueue_head(&audio->write_wait); + + /* Settings will be re-config at AUDIO_SET_CONFIG, + * but at least we need to have initial config + */ + audio->str_cfg.buffer_size = FRAME_SIZE; + audio->str_cfg.buffer_count = FRAME_NUM; + audio->min_frame_size = 1536; + audio->max_frames_per_buf = 5; + enc_cfg->sample_rate = 8000; + enc_cfg->channels = 1; + enc_cfg->bit_rate = 16000; + enc_cfg->stream_format = 0x00;/* 0:ADTS, 3:RAW */ + audio->buf_cfg.meta_info_enable = 0x01; + audio->buf_cfg.frames_per_buf = 0x01; + audio->pcm_cfg.buffer_count = PCM_BUF_COUNT; + audio->pcm_cfg.buffer_size = PCM_BUF_SIZE; + aac_config->format = AUDIO_AAC_FORMAT_ADTS; + aac_config->audio_object = AUDIO_AAC_OBJECT_LC; + aac_config->sbr_on_flag = 0; + aac_config->sbr_ps_on_flag = 0; + aac_config->channel_configuration = 1; + + audio->ac = q6asm_audio_client_alloc((app_cb)q6asm_aac_in_cb, + (void *)audio); + + if (!audio->ac) { + pr_aud_err("%s: Could not allocate memory for\ + audio client\n", __func__); + kfree(audio->enc_cfg); + kfree(audio->codec_cfg); + kfree(audio); + return -ENOMEM; + } + /* open aac encoder in tunnel mode */ + audio->buf_cfg.frames_per_buf = 0x01; + + if ((file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + audio->feedback = NON_TUNNEL_MODE; + rc = q6asm_open_read_write(audio->ac, FORMAT_MPEG4_AAC, + FORMAT_LINEAR_PCM); + + if (rc < 0) { + pr_aud_err("%s:session id %d: NT Open failed rc=%d\n", + __func__, audio->ac->session, rc); + rc = -ENODEV; + goto fail; + } + audio->buf_cfg.meta_info_enable = 0x01; + pr_aud_info("%s:session id %d: NT mode encoder success\n", __func__, + audio->ac->session); + } else if (!(file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + audio->feedback = TUNNEL_MODE; + rc = q6asm_open_read(audio->ac, FORMAT_MPEG4_AAC); + + if (rc < 0) { + pr_aud_err("%s:session id %d: Tunnel Open failed rc=%d\n", + __func__, audio->ac->session, rc); + rc = -ENODEV; + goto fail; + } + /* register for tx overflow (valid for tunnel mode only) */ + rc = q6asm_reg_tx_overflow(audio->ac, 0x01); + if (rc < 0) { + pr_aud_err("%s:session id %d: TX Overflow registration\ + failed rc=%d\n", __func__, + audio->ac->session, rc); + rc = -ENODEV; + goto fail; + } + audio->buf_cfg.meta_info_enable = 0x00; + pr_aud_info("%s:session id %d: T mode encoder success\n", __func__, + audio->ac->session); + } else { + pr_aud_err("%s:session id %d: Unexpected mode\n", __func__, + audio->ac->session); + rc = -EACCES; + goto fail; + } + audio->opened = 1; + atomic_set(&audio->in_count, PCM_BUF_COUNT); + atomic_set(&audio->out_count, 0x00); + audio->enc_ioctl = aac_in_ioctl; + file->private_data = audio; + + pr_aud_info("%s:session id %d: success\n", __func__, audio->ac->session); + return 0; +fail: + q6asm_audio_client_free(audio->ac); + kfree(audio->enc_cfg); + kfree(audio->codec_cfg); + kfree(audio); + return rc; +} + +static const struct file_operations audio_in_fops = { + .owner = THIS_MODULE, + .open = aac_in_open, + .release = audio_in_release, + .read = audio_in_read, + .write = audio_in_write, + .unlocked_ioctl = audio_in_ioctl, +}; + +struct miscdevice audio_aac_in_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_aac_in", + .fops = &audio_in_fops, +}; + +static int __init aac_in_init(void) +{ + return misc_register(&audio_aac_in_misc); +} +device_initcall(aac_in_init); diff --git a/arch/arm/mach-msm/qdsp6v3/amrnb_in.c b/arch/arm/mach-msm/qdsp6v3/amrnb_in.c new file mode 100644 index 00000000000..35984c38ef5 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v3/amrnb_in.c @@ -0,0 +1,330 @@ +/* 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 "audio_utils.h" + +/* Buffer with meta*/ +#define PCM_BUF_SIZE (4096 + sizeof(struct meta_in)) + +/* Maximum 10 frames in buffer with meta */ +#define FRAME_SIZE (1 + ((32+sizeof(struct meta_out_dsp)) * 10)) + +void q6asm_amrnb_in_cb(uint32_t opcode, uint32_t token, + uint32_t *payload, void *priv) +{ + struct q6audio_in * audio = (struct q6audio_in *)priv; + unsigned long flags; + + pr_debug("%s:session id %d: opcode - %d\n", __func__, + audio->ac->session, opcode); + + spin_lock_irqsave(&audio->dsp_lock, flags); + switch (opcode) { + case ASM_DATA_EVENT_READ_DONE: + audio_in_get_dsp_frames(audio, token, payload); + break; + case ASM_DATA_EVENT_WRITE_DONE: + atomic_inc(&audio->in_count); + wake_up(&audio->write_wait); + break; + case ASM_DATA_CMDRSP_EOS: + audio->eos_rsp = 1; + wake_up(&audio->read_wait); + break; + case ASM_STREAM_CMDRSP_GET_ENCDEC_PARAM: + break; + case ASM_STREAM_CMDRSP_GET_PP_PARAMS: + break; + case ASM_SESSION_EVENT_TX_OVERFLOW: + pr_aud_err("%s:session id %d: ASM_SESSION_EVENT_TX_OVERFLOW\n", + __func__, audio->ac->session); + break; + default: + pr_aud_err("%s:session id %d: Ignore opcode[0x%x]\n", __func__, + audio->ac->session, opcode); + break; + } + spin_unlock_irqrestore(&audio->dsp_lock, flags); +} + +/* ------------------- device --------------------- */ +static long amrnb_in_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct q6audio_in *audio = file->private_data; + int rc = 0; + int cnt = 0; + + switch (cmd) { + case AUDIO_START: { + struct msm_audio_amrnb_enc_config_v2 *enc_cfg; + enc_cfg = audio->enc_cfg; + pr_debug("%s:session id %d: default buf alloc[%d]\n", __func__, + audio->ac->session, audio->buf_alloc); + if (audio->enabled == 1) { + pr_aud_info("%s:AUDIO_START already over\n", __func__); + rc = 0; + break; + } + rc = audio_in_buf_alloc(audio); + if (rc < 0) { + pr_aud_err("%s:session id %d: buffer allocation failed\n", + __func__, audio->ac->session); + break; + } + + rc = q6asm_enc_cfg_blk_amrnb(audio->ac, + audio->buf_cfg.frames_per_buf, + enc_cfg->band_mode, + enc_cfg->dtx_enable); + + if (rc < 0) { + pr_aud_err("%s:session id %d: cmd amrnb media format block\ + failed\n", __func__, audio->ac->session); + break; + } + if (audio->feedback == NON_TUNNEL_MODE) { + rc = q6asm_media_format_block_pcm(audio->ac, + audio->pcm_cfg.sample_rate, + audio->pcm_cfg.channel_count); + + if (rc < 0) { + pr_aud_err("%s:session id %d: media format block\ + failed\n", __func__, audio->ac->session); + break; + } + } + pr_debug("%s:session id %d: AUDIO_START enable[%d]\n", + __func__, audio->ac->session, + audio->enabled); + rc = audio_in_enable(audio); + if (!rc) { + audio->enabled = 1; + } else { + audio->enabled = 0; + pr_aud_err("%s:session id %d: Audio Start procedure failed\ + rc=%d\n", __func__, + audio->ac->session, rc); + break; + } + while (cnt++ < audio->str_cfg.buffer_count) + q6asm_read(audio->ac); /* Push buffer to DSP */ + rc = 0; + pr_debug("%s:session id %d: AUDIO_START success enable[%d]\n", + __func__, audio->ac->session, audio->enabled); + break; + } + case AUDIO_STOP: { + pr_debug("%s:AUDIO_STOP\n", __func__); + rc = audio_in_disable(audio); + if (rc < 0) { + pr_aud_err("%s:session id %d: Audio Stop procedure failed\ + rc=%d\n", __func__, + audio->ac->session, rc); + break; + } + break; + } + case AUDIO_GET_AMRNB_ENC_CONFIG_V2: { + if (copy_to_user((void *)arg, audio->enc_cfg, + sizeof(struct msm_audio_amrnb_enc_config_v2))) + rc = -EFAULT; + break; + } + case AUDIO_SET_AMRNB_ENC_CONFIG_V2: { + struct msm_audio_amrnb_enc_config_v2 cfg; + struct msm_audio_amrnb_enc_config_v2 *enc_cfg; + enc_cfg = audio->enc_cfg; + if (copy_from_user(&cfg, (void *) arg, + sizeof(struct msm_audio_amrnb_enc_config_v2))) { + rc = -EFAULT; + break; + } + if (cfg.band_mode > 8 || + cfg.band_mode < 1) { + pr_aud_err("%s:session id %d: invalid band mode\n", + __func__, audio->ac->session); + rc = -EINVAL; + break; + } + /* AMR NB encoder accepts values between 0-7 + while openmax provides value between 1-8 + as per spec */ + enc_cfg->band_mode = (cfg.band_mode - 1); + enc_cfg->dtx_enable = (cfg.dtx_enable ? 1 : 0); + enc_cfg->frame_format = 0; + pr_debug("%s:session id %d: band_mode = 0x%x dtx_enable=0x%x\n", + __func__, audio->ac->session, + enc_cfg->band_mode, enc_cfg->dtx_enable); + break; + } + default: + rc = -EINVAL; + } + return rc; +} + +static int amrnb_in_open(struct inode *inode, struct file *file) +{ + struct q6audio_in *audio = NULL; + struct msm_audio_amrnb_enc_config_v2 *enc_cfg; + int rc = 0; + + audio = kzalloc(sizeof(struct q6audio_in), GFP_KERNEL); + + if (audio == NULL) { + pr_aud_err("%s:session id %d: Could not allocate memory for amrnb\ + driver\n", __func__, audio->ac->session); + return -ENOMEM; + } + /* Allocate memory for encoder config param */ + audio->enc_cfg = kzalloc(sizeof(struct msm_audio_amrnb_enc_config_v2), + GFP_KERNEL); + if (audio->enc_cfg == NULL) { + pr_aud_err("%s:session id %d: Could not allocate memory for aac\ + config param\n", __func__, audio->ac->session); + kfree(audio); + return -ENOMEM; + } + enc_cfg = audio->enc_cfg; + + mutex_init(&audio->lock); + mutex_init(&audio->read_lock); + mutex_init(&audio->write_lock); + spin_lock_init(&audio->dsp_lock); + init_waitqueue_head(&audio->read_wait); + init_waitqueue_head(&audio->write_wait); + + /* Settings will be re-config at AUDIO_SET_CONFIG, + * but at least we need to have initial config + */ + audio->str_cfg.buffer_size = FRAME_SIZE; + audio->str_cfg.buffer_count = FRAME_NUM; + audio->min_frame_size = 32; + audio->max_frames_per_buf = 10; + audio->pcm_cfg.buffer_size = PCM_BUF_SIZE; + audio->pcm_cfg.buffer_count = PCM_BUF_COUNT; + enc_cfg->band_mode = 7; + enc_cfg->dtx_enable = 0; + audio->pcm_cfg.channel_count = 1; + audio->pcm_cfg.sample_rate = 8000; + audio->buf_cfg.meta_info_enable = 0x01; + audio->buf_cfg.frames_per_buf = 0x01; + + audio->ac = q6asm_audio_client_alloc((app_cb)q6asm_amrnb_in_cb, + (void *)audio); + + if (!audio->ac) { + pr_aud_err("%s:session id %d: Could not allocate memory for audio\ + client\n", __func__, audio->ac->session); + kfree(audio->enc_cfg); + kfree(audio); + return -ENOMEM; + } + + /* open amrnb encoder in T/NT mode */ + if ((file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + audio->feedback = NON_TUNNEL_MODE; + rc = q6asm_open_read_write(audio->ac, FORMAT_AMRNB, + FORMAT_LINEAR_PCM); + if (rc < 0) { + pr_aud_err("%s:session id %d: NT mode Open failed rc=%d\n", + __func__, audio->ac->session, rc); + rc = -ENODEV; + goto fail; + } + pr_aud_info("%s:session id %d: NT mode encoder success\n", + __func__, audio->ac->session); + } else if (!(file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + audio->feedback = TUNNEL_MODE; + rc = q6asm_open_read(audio->ac, FORMAT_AMRNB); + if (rc < 0) { + pr_aud_err("%s:session id %d: T mode Open failed rc=%d\n", + __func__, audio->ac->session, rc); + rc = -ENODEV; + goto fail; + } + /* register for tx overflow (valid for tunnel mode only) */ + rc = q6asm_reg_tx_overflow(audio->ac, 0x01); + if (rc < 0) { + pr_aud_err("%s:session id %d: TX Overflow registration\ + failed rc=%d\n", __func__, audio->ac->session, + rc); + rc = -ENODEV; + goto fail; + } + pr_aud_info("%s:session id %d: T mode encoder success\n", + __func__, audio->ac->session); + } else { + pr_aud_err("%s:session id %d: Unexpected mode\n", __func__, + audio->ac->session); + rc = -EACCES; + goto fail; + } + + audio->opened = 1; + atomic_set(&audio->in_count, PCM_BUF_COUNT); + atomic_set(&audio->out_count, 0x00); + audio->enc_ioctl = amrnb_in_ioctl; + file->private_data = audio; + + pr_aud_info("%s:session id %d: success\n", __func__, audio->ac->session); + return 0; +fail: + q6asm_audio_client_free(audio->ac); + kfree(audio->enc_cfg); + kfree(audio); + return rc; +} + +static const struct file_operations audio_in_fops = { + .owner = THIS_MODULE, + .open = amrnb_in_open, + .release = audio_in_release, + .read = audio_in_read, + .write = audio_in_write, + .unlocked_ioctl = audio_in_ioctl, +}; + +struct miscdevice audio_amrnb_in_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_amrnb_in", + .fops = &audio_in_fops, +}; + +static int __init amrnb_in_init(void) +{ + return misc_register(&audio_amrnb_in_misc); +} + +device_initcall(amrnb_in_init); diff --git a/arch/arm/mach-msm/qdsp6v3/apr.c b/arch/arm/mach-msm/qdsp6v3/apr.c new file mode 100644 index 00000000000..8ee12c576ab --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v3/apr.c @@ -0,0 +1,681 @@ +/* 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. + * + * 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 +#include +#include +#include "apr_tal.h" +#include "dsp_debug.h" + +struct apr_q6 q6; +struct apr_client client[APR_DEST_MAX][APR_CLIENT_MAX]; +static atomic_t dsp_state; +static atomic_t modem_state; + +static wait_queue_head_t dsp_wait; +static wait_queue_head_t modem_wait; +/* Subsystem restart: QDSP6 data, functions */ +static struct workqueue_struct *apr_reset_workqueue; +static void apr_reset_deregister(struct work_struct *work); +struct apr_reset_work { + void *handle; + struct work_struct work; +}; + + +int apr_send_pkt(void *handle, uint32_t *buf) +{ + struct apr_svc *svc = handle; + struct apr_client *clnt; + struct apr_hdr *hdr; + uint16_t dest_id; + uint16_t client_id; + uint16_t w_len; + unsigned long flags; + + if (!handle || !buf) { + pr_aud_err("APR: Wrong parameters\n"); + return -EINVAL; + } + if (svc->need_reset) { + pr_aud_err("apr: send_pkt service need reset\n"); + return -ENETRESET; + } + + if ((svc->dest_id == APR_DEST_QDSP6) && + (atomic_read(&dsp_state) == 0)) { + pr_aud_err("apr: Still dsp is not Up\n"); + return -ENETRESET; + } else if ((svc->dest_id == APR_DEST_MODEM) && + (atomic_read(&modem_state) == 0)) { + pr_aud_err("apr: Still Modem is not Up\n"); + return -ENETRESET; + } + + + spin_lock_irqsave(&svc->w_lock, flags); + dest_id = svc->dest_id; + client_id = svc->client_id; + clnt = &client[dest_id][client_id]; + + if (!client[dest_id][client_id].handle) { + pr_aud_err("APR: Still service is not yet opened\n"); + spin_unlock_irqrestore(&svc->w_lock, flags); + return -EINVAL; + } + hdr = (struct apr_hdr *)buf; + + hdr->src_domain = APR_DOMAIN_APPS; + hdr->src_svc = svc->id; + if (dest_id == APR_DEST_MODEM) + hdr->dest_domain = APR_DOMAIN_MODEM; + else if (dest_id == APR_DEST_QDSP6) + hdr->dest_domain = APR_DOMAIN_ADSP; + + hdr->dest_svc = svc->id; + + w_len = apr_tal_write(clnt->handle, buf, hdr->pkt_size); + if (w_len != hdr->pkt_size) + pr_aud_err("Unable to write APR pkt successfully: %d\n", w_len); + spin_unlock_irqrestore(&svc->w_lock, flags); + + return w_len; +} + +static void apr_cb_func(void *buf, int len, void *priv) +{ + struct apr_client_data data; + struct apr_client *apr_client; + struct apr_svc *c_svc; + struct apr_hdr *hdr; + uint16_t hdr_size; + uint16_t msg_type; + uint16_t ver; + uint16_t src; + uint16_t svc; + uint16_t clnt; + int i; + int temp_port = 0; + uint32_t *ptr; + + pr_debug("APR2: len = %d\n", len); + ptr = buf; + pr_debug("\n*****************\n"); + for (i = 0; i < len/4; i++) + pr_debug("%x ", ptr[i]); + pr_debug("\n"); + pr_debug("\n*****************\n"); + + if (!buf || len <= APR_HDR_SIZE) { + pr_aud_err("APR: Improper apr pkt received:%p %d\n", + buf, len); + return; + } + hdr = buf; + + ver = hdr->hdr_field; + ver = (ver & 0x000F); + if (ver > APR_PKT_VER + 1) { + pr_aud_err("APR: Wrong version: %d\n", ver); + return; + } + + hdr_size = hdr->hdr_field; + hdr_size = ((hdr_size & 0x00F0) >> 0x4) * 4; + if (hdr_size < APR_HDR_SIZE) { + pr_aud_err("APR: Wrong hdr size:%d\n", hdr_size); + return; + } + + if (hdr->pkt_size < APR_HDR_SIZE) { + pr_aud_err("APR: Wrong paket size\n"); + return; + } + msg_type = hdr->hdr_field; + msg_type = (msg_type >> 0x08) & 0x0003; + if (msg_type >= APR_MSG_TYPE_MAX && + msg_type != APR_BASIC_RSP_RESULT) { + pr_aud_err("APR: Wrong message type: %d\n", msg_type); + return; + } + + if (hdr->src_domain >= APR_DOMAIN_MAX || + hdr->dest_domain >= APR_DOMAIN_MAX || + hdr->src_svc >= APR_SVC_MAX || + hdr->dest_svc >= APR_SVC_MAX) { + pr_aud_err("APR: Wrong APR header\n"); + return; + } + + svc = hdr->dest_svc; + if (hdr->src_domain == APR_DOMAIN_MODEM) { + src = APR_DEST_MODEM; + if (svc == APR_SVC_MVS || svc == APR_SVC_MVM || + svc == APR_SVC_CVS || svc == APR_SVC_CVP || + svc == APR_SVC_TEST_CLIENT) + clnt = APR_CLIENT_VOICE; + else { + pr_aud_err("APR: Wrong svc :%d\n", svc); + return; + } + } else if (hdr->src_domain == APR_DOMAIN_ADSP) { + src = APR_DEST_QDSP6; + if (svc == APR_SVC_AFE || svc == APR_SVC_ASM || + svc == APR_SVC_VSM || svc == APR_SVC_VPM || + svc == APR_SVC_ADM || svc == APR_SVC_ADSP_CORE || + svc == APR_SVC_TEST_CLIENT || svc == APR_SVC_ADSP_MVM || + svc == APR_SVC_ADSP_CVS || svc == APR_SVC_ADSP_CVP) + clnt = APR_CLIENT_AUDIO; + else { + pr_aud_err("APR: Wrong svc :%d\n", svc); + return; + } + } else { + pr_aud_err("APR: Pkt from wrong source: %d\n", hdr->src_domain); + return; + } + + pr_debug("src =%d clnt = %d\n", src, clnt); + apr_client = &client[src][clnt]; + for (i = 0; i < APR_SVC_MAX; i++) + if (apr_client->svc[i].id == svc) { + pr_debug("%d\n", apr_client->svc[i].id); + c_svc = &apr_client->svc[i]; + break; + } + + if (i == APR_SVC_MAX) { + pr_aud_err("APR: service is not registered\n"); + return; + } + pr_debug("svc_idx = %d\n", i); + pr_debug("%x %x %x %p %p\n", c_svc->id, c_svc->dest_id, + c_svc->client_id, c_svc->fn, c_svc->priv); + data.payload_size = hdr->pkt_size - hdr_size; + data.opcode = hdr->opcode; + data.src = src; + data.src_port = hdr->src_port; + data.dest_port = hdr->dest_port; + data.token = hdr->token; + data.msg_type = msg_type; + if (data.payload_size > 0) + data.payload = (char *)hdr + hdr_size; + + temp_port = ((data.src_port >> 8) * 8) + (data.src_port & 0xFF); + pr_debug("port = %d t_port = %d\n", data.src_port, temp_port); + if (c_svc->port_cnt && c_svc->port_fn[temp_port]) + c_svc->port_fn[temp_port](&data, c_svc->port_priv[temp_port]); + else if (c_svc->fn) + c_svc->fn(&data, c_svc->priv); + else + pr_aud_err("APR: Rxed a packet for NULL callback\n"); +} + +struct apr_svc *apr_register(char *dest, char *svc_name, apr_fn svc_fn, + uint32_t src_port, void *priv) +{ + int client_id = 0; + int svc_idx = 0; + int svc_id = 0; + int dest_id = 0; + int temp_port = 0; + struct apr_svc *svc = NULL; + int rc = 0; + + if (!dest || !svc_name || !svc_fn) + return NULL; + + if (!strcmp(dest, "ADSP")) + dest_id = APR_DEST_QDSP6; + else if (!strcmp(dest, "MODEM")) { + dest_id = APR_DEST_MODEM; + } else { + pr_aud_err("APR: wrong destination\n"); + goto done; + } + + if ((dest_id == APR_DEST_QDSP6) && + (atomic_read(&dsp_state) == 0)) { + rc = wait_event_timeout(dsp_wait, + (atomic_read(&dsp_state) == 1), 5*HZ); + if (rc == 0) { + pr_aud_err("apr: Still dsp is not Up\n"); + return NULL; + } + } else if ((dest_id == APR_DEST_MODEM) && + (atomic_read(&modem_state) == 0)) { + rc = wait_event_timeout(modem_wait, + (atomic_read(&modem_state) == 1), 5*HZ); + if (rc == 0) { + pr_aud_err("apr: Still Modem is not Up\n"); + return NULL; + } + } + + if (!strcmp(svc_name, "AFE")) { + client_id = APR_CLIENT_AUDIO; + svc_idx = 0; + svc_id = APR_SVC_AFE; + } else if (!strcmp(svc_name, "ASM")) { + client_id = APR_CLIENT_AUDIO; + svc_idx = 1; + svc_id = APR_SVC_ASM; + } else if (!strcmp(svc_name, "ADM")) { + client_id = APR_CLIENT_AUDIO; + svc_idx = 2; + svc_id = APR_SVC_ADM; + } else if (!strcmp(svc_name, "CORE")) { + client_id = APR_CLIENT_AUDIO; + svc_idx = 3; + svc_id = APR_SVC_ADSP_CORE; + } else if (!strcmp(svc_name, "TEST")) { + if (dest_id == APR_DEST_QDSP6) { + client_id = APR_CLIENT_AUDIO; + svc_idx = 4; + } else { + client_id = APR_CLIENT_VOICE; + svc_idx = 7; + } + svc_id = APR_SVC_TEST_CLIENT; + } else if (!strcmp(svc_name, "VSM")) { + client_id = APR_CLIENT_VOICE; + svc_idx = 0; + svc_id = APR_SVC_VSM; + } else if (!strcmp(svc_name, "VPM")) { + client_id = APR_CLIENT_VOICE; + svc_idx = 1; + svc_id = APR_SVC_VPM; + } else if (!strcmp(svc_name, "MVS")) { + client_id = APR_CLIENT_VOICE; + svc_idx = 2; + svc_id = APR_SVC_MVS; + } else if (!strcmp(svc_name, "MVM")) { + if (dest_id == APR_DEST_MODEM) { + client_id = APR_CLIENT_VOICE; + svc_idx = 3; + svc_id = APR_SVC_MVM; + } else { + client_id = APR_CLIENT_AUDIO; + svc_idx = 5; + svc_id = APR_SVC_ADSP_MVM; + } + } else if (!strcmp(svc_name, "CVS")) { + if (dest_id == APR_DEST_MODEM) { + client_id = APR_CLIENT_VOICE; + svc_idx = 4; + svc_id = APR_SVC_CVS; + } else { + client_id = APR_CLIENT_AUDIO; + svc_idx = 6; + svc_id = APR_SVC_ADSP_CVS; + } + } else if (!strcmp(svc_name, "CVP")) { + if (dest_id == APR_DEST_MODEM) { + client_id = APR_CLIENT_VOICE; + svc_idx = 5; + svc_id = APR_SVC_CVP; + } else { + client_id = APR_CLIENT_AUDIO; + svc_idx = 7; + svc_id = APR_SVC_ADSP_CVP; + } + } else if (!strcmp(svc_name, "SRD")) { + client_id = APR_CLIENT_VOICE; + svc_idx = 6; + svc_id = APR_SVC_SRD; + } else { + pr_aud_err("APR: Wrong svc name\n"); + goto done; + } + + pr_debug("svc name = %s c_id = %d dest_id = %d\n", + svc_name, client_id, dest_id); + mutex_lock(&q6.lock); + if (q6.state == APR_Q6_NOIMG) { + q6.pil = pil_get("q6"); + if (!q6.pil) { + pr_aud_err("APR: Unable to load q6 image\n"); + mutex_unlock(&q6.lock); + return svc; + } + q6.state = APR_Q6_LOADED; + } + mutex_unlock(&q6.lock); + mutex_lock(&client[dest_id][client_id].m_lock); + if (!client[dest_id][client_id].handle) { + client[dest_id][client_id].handle = apr_tal_open(client_id, + dest_id, APR_DL_SMD, apr_cb_func, NULL); + if (!client[dest_id][client_id].handle) { + svc = NULL; + pr_aud_err("APR: Unable to open handle\n"); + mutex_unlock(&client[dest_id][client_id].m_lock); + goto done; + } + } + mutex_unlock(&client[dest_id][client_id].m_lock); + svc = &client[dest_id][client_id].svc[svc_idx]; + mutex_lock(&svc->m_lock); + client[dest_id][client_id].id = client_id; + if (svc->need_reset) { + mutex_unlock(&svc->m_lock); + pr_aud_err("APR: Service needs reset\n"); + goto done; + } + svc->priv = priv; + svc->id = svc_id; + svc->dest_id = dest_id; + svc->client_id = client_id; + if (src_port != 0xFFFFFFFF) { + temp_port = ((src_port >> 8) * 8) + (src_port & 0xFF); + if (temp_port >= APR_MAX_PORTS) { + mutex_unlock(&svc->m_lock); + pr_aud_err("APR: illegal port ID %d\n", temp_port); + svc = NULL; + goto done; + } + pr_debug("port = %d t_port = %d\n", src_port, temp_port); + if (!svc->port_cnt && !svc->svc_cnt) + client[dest_id][client_id].svc_cnt++; + svc->port_cnt++; + svc->port_fn[temp_port] = svc_fn; + svc->port_priv[temp_port] = priv; + } else { + if (!svc->fn) { + if (!svc->port_cnt && !svc->svc_cnt) + client[dest_id][client_id].svc_cnt++; + svc->fn = svc_fn; + if (svc->port_cnt) + svc->svc_cnt++; + } + } + + mutex_unlock(&svc->m_lock); +done: + return svc; +} + +static void apr_reset_deregister(struct work_struct *work) +{ + struct apr_svc *handle = NULL; + struct apr_reset_work *apr_reset = + container_of(work, struct apr_reset_work, work); + + handle = apr_reset->handle; + pr_debug("%s:handle[%p]\n", __func__, handle); + apr_deregister(handle); + kfree(apr_reset); + msleep(5); +} + +int apr_deregister(void *handle) +{ + struct apr_svc *svc = handle; + struct apr_client *clnt; + uint16_t dest_id; + uint16_t client_id; + + if (!handle) + return -EINVAL; + + mutex_lock(&svc->m_lock); + dest_id = svc->dest_id; + client_id = svc->client_id; + clnt = &client[dest_id][client_id]; + + if (svc->port_cnt > 0 || svc->svc_cnt > 0) { + if (svc->port_cnt) + svc->port_cnt--; + else if (svc->svc_cnt) + svc->svc_cnt--; + if (!svc->port_cnt && !svc->svc_cnt) { + client[dest_id][client_id].svc_cnt--; + svc->need_reset = 0x0; + } + } else if (client[dest_id][client_id].svc_cnt > 0) { + client[dest_id][client_id].svc_cnt--; + if (!client[dest_id][client_id].svc_cnt) { + svc->need_reset = 0x0; + pr_debug("%s: service is reset %p\n", __func__, svc); + } + } + + if (!svc->port_cnt && !svc->svc_cnt) { + svc->priv = NULL; + svc->id = 0; + svc->fn = NULL; + svc->dest_id = 0; + svc->client_id = 0; + svc->need_reset = 0x0; + } + if (client[dest_id][client_id].handle && + !client[dest_id][client_id].svc_cnt) { + apr_tal_close(client[dest_id][client_id].handle); + client[dest_id][client_id].handle = NULL; + } + mutex_unlock(&svc->m_lock); + + return 0; +} + +void apr_reset(void *handle) +{ + struct apr_reset_work *apr_reset_worker = NULL; + + if (!handle) + return; + pr_debug("%s: handle[%p]\n", __func__, handle); + + apr_reset_worker = kzalloc(sizeof(struct apr_reset_work), + GFP_ATOMIC); + if (apr_reset_worker == NULL || apr_reset_workqueue == NULL) { + pr_aud_err("%s: mem failure\n", __func__); + return; + } + apr_reset_worker->handle = handle; + INIT_WORK(&apr_reset_worker->work, apr_reset_deregister); + queue_work(apr_reset_workqueue, &apr_reset_worker->work); +} + +void change_q6_state(int state) +{ + mutex_lock(&q6.lock); + q6.state = state; + mutex_unlock(&q6.lock); +} + +int adsp_state(int state) +{ + pr_aud_info("dsp state = %d\n", state); + return 0; +} + +/* Dispatch the Reset events to Modem and audio clients */ +void dispatch_event(unsigned long code, unsigned short proc) +{ + struct apr_client *apr_client; + struct apr_client_data data; + struct apr_svc *svc; + uint16_t clnt; + int i, j; + + data.opcode = RESET_EVENTS; + data.reset_event = code; + data.reset_proc = proc; + + clnt = APR_CLIENT_AUDIO; + apr_client = &client[proc][clnt]; + for (i = 0; i < APR_SVC_MAX; i++) { + mutex_lock(&apr_client->svc[i].m_lock); + if (apr_client->svc[i].fn) { + apr_client->svc[i].need_reset = 0x1; + apr_client->svc[i].fn(&data, apr_client->svc[i].priv); + } + if (apr_client->svc[i].port_cnt) { + svc = &(apr_client->svc[i]); + svc->need_reset = 0x1; + for (j = 0; j < APR_MAX_PORTS; j++) + if (svc->port_fn[j]) + svc->port_fn[j](&data, + svc->port_priv[j]); + } + mutex_unlock(&apr_client->svc[i].m_lock); + } + + clnt = APR_CLIENT_VOICE; + apr_client = &client[proc][clnt]; + for (i = 0; i < APR_SVC_MAX; i++) { + mutex_lock(&apr_client->svc[i].m_lock); + if (apr_client->svc[i].fn) { + apr_client->svc[i].need_reset = 0x1; + apr_client->svc[i].fn(&data, apr_client->svc[i].priv); + } + if (apr_client->svc[i].port_cnt) { + svc = &(apr_client->svc[i]); + svc->need_reset = 0x1; + for (j = 0; j < APR_MAX_PORTS; j++) + if (svc->port_fn[j]) + svc->port_fn[j](&data, + svc->port_priv[j]); + } + mutex_unlock(&apr_client->svc[i].m_lock); + } +} + +static int modem_notifier_cb(struct notifier_block *this, unsigned long code, + void *_cmd) +{ + switch (code) { + case SUBSYS_BEFORE_SHUTDOWN: + pr_debug("M-Notify: Shutdown started\n"); + atomic_set(&modem_state, 0); + dispatch_event(code, APR_DEST_MODEM); + break; + case SUBSYS_AFTER_SHUTDOWN: + pr_debug("M-Notify: Shutdown Completed\n"); + break; + case SUBSYS_BEFORE_POWERUP: + pr_debug("M-notify: Bootup started\n"); + break; + case SUBSYS_AFTER_POWERUP: + if (atomic_read(&modem_state) == 0) { + atomic_set(&modem_state, 1); + wake_up(&modem_wait); + } + pr_debug("M-Notify: Bootup Completed\n"); + break; + default: + pr_aud_err("M-Notify: General: %lu\n", code); + break; + } + return NOTIFY_DONE; +} + +static struct notifier_block mnb = { + .notifier_call = modem_notifier_cb, +}; + +static int lpass_notifier_cb(struct notifier_block *this, unsigned long code, + void *_cmd) +{ + switch (code) { + case SUBSYS_BEFORE_SHUTDOWN: + pr_debug("L-Notify: Shutdown started\n"); + atomic_set(&dsp_state, 0); + dispatch_event(code, APR_DEST_QDSP6); + break; + case SUBSYS_AFTER_SHUTDOWN: + pr_debug("L-Notify: Shutdown Completed\n"); + break; + case SUBSYS_BEFORE_POWERUP: + pr_debug("L-notify: Bootup started\n"); + break; + case SUBSYS_AFTER_POWERUP: + if (atomic_read(&dsp_state) == 0) { + atomic_set(&dsp_state, 1); + wake_up(&dsp_wait); + } + pr_debug("L-Notify: Bootup Completed\n"); + break; + default: + pr_aud_err("L-Notify: Generel: %lu\n", code); + break; + } + return NOTIFY_DONE; +} + +static struct notifier_block lnb = { + .notifier_call = lpass_notifier_cb, +}; + + +static int __init apr_init(void) +{ + int i, j, k; + + pr_aud_info("apr_probe\n"); + for (i = 0; i < APR_DEST_MAX; i++) + for (j = 0; j < APR_CLIENT_MAX; j++) { + mutex_init(&client[i][j].m_lock); + for (k = 0; k < APR_SVC_MAX; k++) { + mutex_init(&client[i][j].svc[k].m_lock); + spin_lock_init(&client[i][j].svc[k].w_lock); + } + } + mutex_init(&q6.lock); + dsp_debug_register(adsp_state); + apr_reset_workqueue = + create_singlethread_workqueue("apr_driver"); + if (!apr_reset_workqueue) + return -ENOMEM; + return 0; +} +device_initcall(apr_init); + +static int __init apr_late_init(void) +{ + void *ret; + init_waitqueue_head(&dsp_wait); + init_waitqueue_head(&modem_wait); + atomic_set(&dsp_state, 1); + atomic_set(&modem_state, 1); + ret = subsys_notif_register_notifier("modem", &mnb); + pr_debug("subsys_register_notifier: ret1 = %p\n", ret); + ret = subsys_notif_register_notifier("lpass", &lnb); + pr_debug("subsys_register_notifier: ret2 = %p\n", ret); + + return 0; +} +late_initcall(apr_late_init); diff --git a/arch/arm/mach-msm/qdsp6v3/apr_tal.c b/arch/arm/mach-msm/qdsp6v3/apr_tal.c new file mode 100644 index 00000000000..44047bd8756 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v3/apr_tal.c @@ -0,0 +1,288 @@ +/* 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. + * + * 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 "apr_tal.h" +#include "../clock-8x60.h" + +static char *svc_names[APR_DEST_MAX][APR_CLIENT_MAX] = { + { + "apr_audio_svc", + "apr_voice_svc", + }, + { + "apr_audio_svc", + "apr_voice_svc", + }, +}; + +struct apr_svc_ch_dev apr_svc_ch[APR_DL_MAX][APR_DEST_MAX][APR_CLIENT_MAX]; + +int __apr_tal_write(struct apr_svc_ch_dev *apr_ch, void *data, int len) +{ + int w_len; + unsigned long flags; + + + spin_lock_irqsave(&apr_ch->w_lock, flags); + if (smd_write_avail(apr_ch->ch) < len) { + spin_unlock_irqrestore(&apr_ch->w_lock, flags); + return -EAGAIN; + } + + w_len = smd_write(apr_ch->ch, data, len); + spin_unlock_irqrestore(&apr_ch->w_lock, flags); + pr_debug("apr_tal:w_len = %d\n", w_len); + + if (w_len != len) { + pr_aud_err("apr_tal: Error in write\n"); + return -ENETRESET; + } + return w_len; +} + +int apr_tal_write(struct apr_svc_ch_dev *apr_ch, void *data, int len) +{ + int rc = 0, retries = 0; + + if (!apr_ch->ch) + return -EINVAL; + + do { + if (rc == -EAGAIN) + udelay(50); + + rc = __apr_tal_write(apr_ch, data, len); + } while (rc == -EAGAIN && retries++ < 300); + + if (rc == -EAGAIN) + pr_aud_err("apr_tal: TIMEOUT for write\n"); + + return rc; +} + +static void apr_tal_notify(void *priv, unsigned event) +{ + struct apr_svc_ch_dev *apr_ch = priv; + int len, r_len, sz; + int pkt_cnt = 0; + unsigned long flags; + + pr_debug("event = %d\n", event); + switch (event) { + case SMD_EVENT_DATA: + pkt_cnt = 0; + spin_lock_irqsave(&apr_ch->lock, flags); +check_pending: + len = smd_read_avail(apr_ch->ch); + if (len < 0) { + pr_aud_err("apr_tal: Invalid Read Event :%d\n", len); + spin_unlock_irqrestore(&apr_ch->lock, flags); + return; + } + sz = smd_cur_packet_size(apr_ch->ch); + if (sz < 0) { + pr_debug("pkt size is zero\n"); + spin_unlock_irqrestore(&apr_ch->lock, flags); + return; + } + if (!len && !sz && !pkt_cnt) + goto check_write_avail; + if (!len) { + pr_debug("len = %d pkt_cnt = %d\n", len, pkt_cnt); + spin_unlock_irqrestore(&apr_ch->lock, flags); + return; + } + r_len = smd_read_from_cb(apr_ch->ch, apr_ch->data, len); + if (len != r_len) { + pr_aud_err("apr_tal: Invalid Read\n"); + spin_unlock_irqrestore(&apr_ch->lock, flags); + return; + } + pkt_cnt++; + pr_debug("%d %d %d\n", len, sz, pkt_cnt); + if (apr_ch->func) + apr_ch->func(apr_ch->data, r_len, apr_ch->priv); + goto check_pending; +check_write_avail: + if (smd_write_avail(apr_ch->ch)) + wake_up(&apr_ch->wait); + spin_unlock_irqrestore(&apr_ch->lock, flags); + break; + case SMD_EVENT_OPEN: + pr_aud_info("apr_tal: SMD_EVENT_OPEN\n"); + apr_ch->smd_state = 1; + wake_up(&apr_ch->wait); + break; + case SMD_EVENT_CLOSE: + pr_aud_info("apr_tal: SMD_EVENT_CLOSE\n"); + break; + } +} + +struct apr_svc_ch_dev *apr_tal_open(uint32_t svc, uint32_t dest, + uint32_t dl, apr_svc_cb_fn func, void *priv) +{ + int rc; + + if ((svc >= APR_CLIENT_MAX) || (dest >= APR_DEST_MAX) || + (dl >= APR_DL_MAX)) { + pr_aud_err("apr_tal: Invalid params\n"); + return NULL; + } + + if (apr_svc_ch[dl][dest][svc].ch) { + pr_aud_err("apr_tal: This channel alreday openend\n"); + return NULL; + } + + mutex_lock(&apr_svc_ch[dl][dest][svc].m_lock); + if (!apr_svc_ch[dl][dest][svc].dest_state) { + rc = wait_event_timeout(apr_svc_ch[dl][dest][svc].dest, + apr_svc_ch[dl][dest][svc].dest_state, + msecs_to_jiffies(APR_OPEN_TIMEOUT_MS)); + if (rc == 0) { + pr_aud_err("%s: TIMEOUT for dest %d svc %d\n", __func__, dest, svc); + mutex_unlock(&apr_svc_ch[dl][dest][svc].m_lock); + BUG(); + } + pr_debug("apr_tal:Wakeup done\n"); + apr_svc_ch[dl][dest][svc].dest_state = 0; + } + rc = smd_named_open_on_edge(svc_names[dest][svc], dest, + &apr_svc_ch[dl][dest][svc].ch, + &apr_svc_ch[dl][dest][svc], + apr_tal_notify); + if (rc < 0) { + pr_aud_err("apr_tal: smd_open failed %s\n", + svc_names[dest][svc]); + mutex_unlock(&apr_svc_ch[dl][dest][svc].m_lock); + return NULL; + } + rc = wait_event_timeout(apr_svc_ch[dl][dest][svc].wait, + (apr_svc_ch[dl][dest][svc].smd_state == 1), 5 * HZ); + if (rc == 0) { + pr_aud_err("apr_tal:TIMEOUT for OPEN event\n"); + mutex_unlock(&apr_svc_ch[dl][dest][svc].m_lock); + BUG(); + return NULL; + } + if (!apr_svc_ch[dl][dest][svc].dest_state) { + apr_svc_ch[dl][dest][svc].dest_state = 1; + pr_debug("apr_tal:Waiting for apr svc init\n"); + msleep(200); + pr_debug("apr_tal:apr svc init done\n"); + } + apr_svc_ch[dl][dest][svc].smd_state = 0; + + apr_svc_ch[dl][dest][svc].func = func; + apr_svc_ch[dl][dest][svc].priv = priv; + mutex_unlock(&apr_svc_ch[dl][dest][svc].m_lock); + + return &apr_svc_ch[dl][dest][svc]; +} + +int apr_tal_close(struct apr_svc_ch_dev *apr_ch) +{ + int r; + + if (!apr_ch->ch) + return -EINVAL; + + mutex_lock(&apr_ch->m_lock); + r = smd_close(apr_ch->ch); + apr_ch->ch = NULL; + apr_ch->func = NULL; + apr_ch->priv = NULL; + mutex_unlock(&apr_ch->m_lock); + return r; +} + +static int apr_smd_probe(struct platform_device *pdev) +{ + int dest; + int clnt; + + if (pdev->id == APR_DEST_MODEM) { + pr_aud_info("apr_tal:Modem Is Up\n"); + dest = APR_DEST_MODEM; + clnt = APR_CLIENT_VOICE; + apr_svc_ch[APR_DL_SMD][dest][clnt].dest_state = 1; + wake_up(&apr_svc_ch[APR_DL_SMD][dest][clnt].dest); + } else if (pdev->id == APR_DEST_QDSP6) { + pr_aud_info("apr_tal:Q6 Is Up\n"); + /* + local_src_disable(PLL_4); + */ + dest = APR_DEST_QDSP6; + clnt = APR_CLIENT_AUDIO; + apr_svc_ch[APR_DL_SMD][dest][clnt].dest_state = 1; + wake_up(&apr_svc_ch[APR_DL_SMD][dest][clnt].dest); + } else + pr_aud_err("apr_tal:Invalid Dest Id: %d\n", pdev->id); + return 0; +} + +static struct platform_driver apr_q6_driver = { + .probe = apr_smd_probe, + .driver = { + .name = "apr_audio_svc", + .owner = THIS_MODULE, + }, +}; + +static struct platform_driver apr_modem_driver = { + .probe = apr_smd_probe, + .driver = { + .name = "apr_voice_svc", + .owner = THIS_MODULE, + }, +}; + +static int __init apr_tal_init(void) +{ + int i, j, k; + + for (i = 0; i < APR_DL_MAX; i++) + for (j = 0; j < APR_DEST_MAX; j++) + for (k = 0; k < APR_CLIENT_MAX; k++) { + init_waitqueue_head(&apr_svc_ch[i][j][k].wait); + init_waitqueue_head(&apr_svc_ch[i][j][k].dest); + spin_lock_init(&apr_svc_ch[i][j][k].lock); + spin_lock_init(&apr_svc_ch[i][j][k].w_lock); + mutex_init(&apr_svc_ch[i][j][k].m_lock); + } + platform_driver_register(&apr_q6_driver); + platform_driver_register(&apr_modem_driver); + return 0; +} +device_initcall(apr_tal_init); diff --git a/arch/arm/mach-msm/qdsp6v3/apr_tal.h b/arch/arm/mach-msm/qdsp6v3/apr_tal.h new file mode 100644 index 00000000000..88f8ba459ea --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v3/apr_tal.h @@ -0,0 +1,71 @@ +/* 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 __APR_TAL_H_ +#define __APR_TAL_H_ + +#include +#include +#include + +/* APR Client IDs */ +#define APR_CLIENT_AUDIO 0x0 +#define APR_CLIENT_VOICE 0x1 +#define APR_CLIENT_MAX 0x2 + +#define APR_DL_SMD 0 +#define APR_DL_MAX 1 + +#define APR_DEST_MODEM 0 +#define APR_DEST_QDSP6 1 +#define APR_DEST_MAX 2 + +#define APR_MAX_BUF 8192 + +#define APR_OPEN_TIMEOUT_MS 5000 + +typedef void (*apr_svc_cb_fn)(void *buf, int len, void *priv); +struct apr_svc_ch_dev *apr_tal_open(uint32_t svc, uint32_t dest, + uint32_t dl, apr_svc_cb_fn func, void *priv); +int apr_tal_write(struct apr_svc_ch_dev *apr_ch, void *data, int len); +int apr_tal_close(struct apr_svc_ch_dev *apr_ch); +struct apr_svc_ch_dev { + struct smd_channel *ch; + spinlock_t lock; + spinlock_t w_lock; + struct mutex m_lock; + apr_svc_cb_fn func; + char data[APR_MAX_BUF]; + wait_queue_head_t wait; + void *priv; + uint32_t smd_state; + wait_queue_head_t dest; + uint32_t dest_state; +}; + +#endif diff --git a/arch/arm/mach-msm/qdsp6v3/audio_aac.c b/arch/arm/mach-msm/qdsp6v3/audio_aac.c new file mode 100644 index 00000000000..88f8ba459ea --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v3/audio_aac.c @@ -0,0 +1,71 @@ +/* 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 __APR_TAL_H_ +#define __APR_TAL_H_ + +#include +#include +#include + +/* APR Client IDs */ +#define APR_CLIENT_AUDIO 0x0 +#define APR_CLIENT_VOICE 0x1 +#define APR_CLIENT_MAX 0x2 + +#define APR_DL_SMD 0 +#define APR_DL_MAX 1 + +#define APR_DEST_MODEM 0 +#define APR_DEST_QDSP6 1 +#define APR_DEST_MAX 2 + +#define APR_MAX_BUF 8192 + +#define APR_OPEN_TIMEOUT_MS 5000 + +typedef void (*apr_svc_cb_fn)(void *buf, int len, void *priv); +struct apr_svc_ch_dev *apr_tal_open(uint32_t svc, uint32_t dest, + uint32_t dl, apr_svc_cb_fn func, void *priv); +int apr_tal_write(struct apr_svc_ch_dev *apr_ch, void *data, int len); +int apr_tal_close(struct apr_svc_ch_dev *apr_ch); +struct apr_svc_ch_dev { + struct smd_channel *ch; + spinlock_t lock; + spinlock_t w_lock; + struct mutex m_lock; + apr_svc_cb_fn func; + char data[APR_MAX_BUF]; + wait_queue_head_t wait; + void *priv; + uint32_t smd_state; + wait_queue_head_t dest; + uint32_t dest_state; +}; + +#endif diff --git a/arch/arm/mach-msm/qdsp6v3/audio_acdb.c b/arch/arm/mach-msm/qdsp6v3/audio_acdb.c new file mode 100644 index 00000000000..dca0df60769 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v3/audio_acdb.c @@ -0,0 +1,780 @@ +/* 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. + * + * 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 "audio_acdb.h" + + +#define MAX_NETWORKS 9 +#define NUM_ACTIVE_NETWORKS 6 +#define VOCPROC_STREAM_OFFSET NUM_ACTIVE_NETWORKS +#define VOCPROC_VOL_OFFSET (NUM_ACTIVE_NETWORKS * 2) +#define NUM_VOCPROC_CAL_TYPES (NUM_ACTIVE_NETWORKS * 3) +#define NUM_AUDPROC_CAL_TYPES 3 +#define ACDB_BLOCK_SIZE 4096 +#define NUM_VOCPROC_BLOCKS 18 + +enum { + RX_CAL, + TX_CAL, + MAX_AUDPROC_TYPES +}; + +struct acdb_data { + struct mutex acdb_mutex; + + /* ANC Cal */ + struct acdb_cal_block anc_cal; + + /* AudProc Cal */ + struct acdb_cal_block audproc_cal[MAX_AUDPROC_TYPES]; + struct acdb_cal_block audstrm_cal[MAX_AUDPROC_TYPES]; + struct acdb_cal_block audvol_cal[MAX_AUDPROC_TYPES]; + + /* VocProc Cal */ + struct acdb_cal_block vocproc_cal[MAX_NETWORKS]; + struct acdb_cal_block vocstrm_cal[MAX_NETWORKS]; + struct acdb_cal_block vocvol_cal[MAX_NETWORKS]; + uint32_t vocproc_cal_size; + uint32_t vocstrm_cal_size; + uint32_t vocvol_cal_size; + + /* Sidetone Cal */ + struct sidetone_cal sidetone_cal; + + /* PMEM information */ + int pmem_fd; + unsigned long paddr; + unsigned long kvaddr; + unsigned long pmem_len; + struct file *file; + +}; + +static struct acdb_data acdb_data; +static atomic_t usage_count; + +void get_anc_cal(struct acdb_cal_block *cal_block) +{ + pr_debug("%s\n", __func__); + + if (cal_block == NULL) { + pr_aud_err("ACDB=> NULL pointer sent to %s\n", __func__); + goto done; + } + + mutex_lock(&acdb_data.acdb_mutex); + + cal_block->cal_kvaddr = acdb_data.anc_cal.cal_kvaddr; + cal_block->cal_paddr = acdb_data.anc_cal.cal_paddr; + cal_block->cal_size = acdb_data.anc_cal.cal_size; + + mutex_unlock(&acdb_data.acdb_mutex); +done: + return; +} + +void store_anc_cal(struct cal_block *cal_block) +{ + pr_debug("%s,\n", __func__); + + if (cal_block->cal_offset > acdb_data.pmem_len) { + pr_aud_err("%s: offset %d is > pmem_len %ld\n", + __func__, cal_block->cal_offset, + acdb_data.pmem_len); + goto done; + } + + mutex_lock(&acdb_data.acdb_mutex); + + acdb_data.anc_cal.cal_kvaddr = + cal_block->cal_offset + acdb_data.kvaddr; + acdb_data.anc_cal.cal_paddr = + cal_block->cal_offset + acdb_data.paddr; + acdb_data.anc_cal.cal_size = + cal_block->cal_size; + + mutex_unlock(&acdb_data.acdb_mutex); +done: + return; +} + +void get_audproc_buffer_data(struct audproc_buffer_data *cal_buffers) +{ + int i; + pr_debug("%s\n", __func__); + + if (cal_buffers == NULL) { + pr_aud_err("ACDB=> NULL pointer sent to %s\n", __func__); + goto done; + } + + for (i = 0; i < NUM_AUDPROC_BUFFERS; i++) { + cal_buffers->phys_addr[i] = (uint32_t) + (acdb_data.paddr + + (NUM_VOCPROC_BLOCKS + i) * ACDB_BLOCK_SIZE); + cal_buffers->buf_size[i] = ACDB_BLOCK_SIZE; + } +done: + return; +} + +void store_audproc_cal(int32_t path, struct cal_block *cal_block) +{ + pr_debug("%s, path = %d\n", __func__, path); + + mutex_lock(&acdb_data.acdb_mutex); + + if (cal_block->cal_offset > acdb_data.pmem_len) { + pr_aud_err("%s: offset %d is > pmem_len %ld\n", + __func__, cal_block->cal_offset, + acdb_data.pmem_len); + goto done; + } + if (path >= MAX_AUDPROC_TYPES) { + pr_aud_err("ACDB=> Bad path sent to %s, path: %d\n", + __func__, path); + goto done; + } + + acdb_data.audproc_cal[path].cal_kvaddr = + cal_block->cal_offset + acdb_data.kvaddr; + acdb_data.audproc_cal[path].cal_paddr = + cal_block->cal_offset + acdb_data.paddr; + acdb_data.audproc_cal[path].cal_size = + cal_block->cal_size; + +done: + mutex_unlock(&acdb_data.acdb_mutex); + return; +} + +void get_audproc_cal(int32_t path, struct acdb_cal_block *cal_block) +{ + pr_aud_info("%s, path = %d\n", __func__, path); + + if (cal_block == NULL) { + pr_aud_err("ACDB=> NULL pointer sent to %s\n", __func__); + goto done; + } + if (path >= MAX_AUDPROC_TYPES) { + pr_aud_err("ACDB=> Bad path sent to %s, path: %d\n", + __func__, path); + goto done; + } + + mutex_lock(&acdb_data.acdb_mutex); + + cal_block->cal_kvaddr = acdb_data.audproc_cal[path].cal_kvaddr; + cal_block->cal_paddr = acdb_data.audproc_cal[path].cal_paddr; + cal_block->cal_size = acdb_data.audproc_cal[path].cal_size; + + mutex_unlock(&acdb_data.acdb_mutex); +done: + return; +} + +void store_audstrm_cal(int32_t path, struct cal_block *cal_block) +{ + pr_debug("%s, path = %d\n", __func__, path); + + mutex_lock(&acdb_data.acdb_mutex); + + if (cal_block->cal_offset > acdb_data.pmem_len) { + pr_aud_err("%s: offset %d is > pmem_len %ld\n", + __func__, cal_block->cal_offset, + acdb_data.pmem_len); + goto done; + } + if (path >= MAX_AUDPROC_TYPES) { + pr_aud_err("ACDB=> Bad path sent to %s, path: %d\n", + __func__, path); + goto done; + } + + acdb_data.audstrm_cal[path].cal_kvaddr = + cal_block->cal_offset + acdb_data.kvaddr; + acdb_data.audstrm_cal[path].cal_paddr = + cal_block->cal_offset + acdb_data.paddr; + acdb_data.audstrm_cal[path].cal_size = + cal_block->cal_size; + +done: + mutex_unlock(&acdb_data.acdb_mutex); + return; +} + +void get_audstrm_cal(int32_t path, struct acdb_cal_block *cal_block) +{ + pr_debug("%s, path = %d\n", __func__, path); + + if (cal_block == NULL) { + pr_aud_err("ACDB=> NULL pointer sent to %s\n", __func__); + goto done; + } + if (path >= MAX_AUDPROC_TYPES) { + pr_aud_err("ACDB=> Bad path sent to %s, path: %d\n", + __func__, path); + goto done; + } + + mutex_lock(&acdb_data.acdb_mutex); + + cal_block->cal_kvaddr = acdb_data.audstrm_cal[path].cal_kvaddr; + cal_block->cal_paddr = acdb_data.audstrm_cal[path].cal_paddr; + cal_block->cal_size = acdb_data.audstrm_cal[path].cal_size; + + mutex_unlock(&acdb_data.acdb_mutex); +done: + return; +} + +void store_audvol_cal(int32_t path, struct cal_block *cal_block) +{ + pr_debug("%s, path = %d\n", __func__, path); + + mutex_lock(&acdb_data.acdb_mutex); + + if (cal_block->cal_offset > acdb_data.pmem_len) { + pr_aud_err("%s: offset %d is > pmem_len %ld\n", + __func__, cal_block->cal_offset, + acdb_data.pmem_len); + goto done; + } + if (path >= MAX_AUDPROC_TYPES) { + pr_aud_err("ACDB=> Bad path sent to %s, path: %d\n", + __func__, path); + goto done; + } + + acdb_data.audvol_cal[path].cal_kvaddr = + cal_block->cal_offset + acdb_data.kvaddr; + acdb_data.audvol_cal[path].cal_paddr = + cal_block->cal_offset + acdb_data.paddr; + acdb_data.audvol_cal[path].cal_size = + cal_block->cal_size; + +done: + mutex_unlock(&acdb_data.acdb_mutex); + return; +} + +void get_audvol_cal(int32_t path, struct acdb_cal_block *cal_block) +{ + pr_aud_info("%s, path = %d\n", __func__, path); + + if (cal_block == NULL) { + pr_aud_err("ACDB=> NULL pointer sent to %s\n", __func__); + goto done; + } + if (path >= MAX_AUDPROC_TYPES) { + pr_aud_err("ACDB=> Bad path sent to %s, path: %d\n", + __func__, path); + goto done; + } + + mutex_lock(&acdb_data.acdb_mutex); + + cal_block->cal_kvaddr = acdb_data.audvol_cal[path].cal_kvaddr; + cal_block->cal_paddr = acdb_data.audvol_cal[path].cal_paddr; + cal_block->cal_size = acdb_data.audvol_cal[path].cal_size; + + mutex_unlock(&acdb_data.acdb_mutex); +done: + return; +} + + +void store_vocproc_cal(int32_t len, struct cal_block *cal_blocks) +{ + int i; + pr_debug("%s\n", __func__); + + if (len > MAX_NETWORKS) { + pr_aud_err("%s: Calibration sent for %d networks, only %d are " + "supported!\n", __func__, len, MAX_NETWORKS); + goto done; + } + + + mutex_lock(&acdb_data.acdb_mutex); + + for (i = 0; i < len; i++) { + if (cal_blocks[i].cal_offset > acdb_data.pmem_len) { + pr_aud_err("%s: offset %d is > pmem_len %ld\n", + __func__, cal_blocks[i].cal_offset, + acdb_data.pmem_len); + acdb_data.vocproc_cal[i].cal_size = 0; + } else { + acdb_data.vocproc_cal[i].cal_size = + cal_blocks[i].cal_size; + acdb_data.vocproc_cal[i].cal_paddr = + cal_blocks[i].cal_offset + + acdb_data.paddr; + acdb_data.vocproc_cal[i].cal_kvaddr = + cal_blocks[i].cal_offset + + acdb_data.kvaddr; + } + } + acdb_data.vocproc_cal_size = len; + mutex_unlock(&acdb_data.acdb_mutex); +done: + return; +} + +void get_vocproc_cal(struct acdb_cal_data *cal_data) +{ + pr_debug("%s\n", __func__); + + if (cal_data == NULL) { + pr_aud_err("ACDB=> NULL pointer sent to %s\n", __func__); + goto done; + } + + mutex_lock(&acdb_data.acdb_mutex); + + cal_data->num_cal_blocks = acdb_data.vocproc_cal_size; + cal_data->cal_blocks = &acdb_data.vocproc_cal[0]; + + mutex_unlock(&acdb_data.acdb_mutex); +done: + return; +} + +void store_vocstrm_cal(int32_t len, struct cal_block *cal_blocks) +{ + int i; + pr_debug("%s\n", __func__); + + if (len > MAX_NETWORKS) { + pr_aud_err("%s: Calibration sent for %d networks, only %d are " + "supported!\n", __func__, len, MAX_NETWORKS); + goto done; + } + + mutex_lock(&acdb_data.acdb_mutex); + + for (i = 0; i < len; i++) { + if (cal_blocks[i].cal_offset > acdb_data.pmem_len) { + pr_aud_err("%s: offset %d is > pmem_len %ld\n", + __func__, cal_blocks[i].cal_offset, + acdb_data.pmem_len); + acdb_data.vocstrm_cal[i].cal_size = 0; + } else { + acdb_data.vocstrm_cal[i].cal_size = + cal_blocks[i].cal_size; + acdb_data.vocstrm_cal[i].cal_paddr = + cal_blocks[i].cal_offset + + acdb_data.paddr; + acdb_data.vocstrm_cal[i].cal_kvaddr = + cal_blocks[i].cal_offset + + acdb_data.kvaddr; + } + } + acdb_data.vocstrm_cal_size = len; + mutex_unlock(&acdb_data.acdb_mutex); +done: + return; +} + +void get_vocstrm_cal(struct acdb_cal_data *cal_data) +{ + pr_debug("%s\n", __func__); + + if (cal_data == NULL) { + pr_aud_err("ACDB=> NULL pointer sent to %s\n", __func__); + goto done; + } + + mutex_lock(&acdb_data.acdb_mutex); + + cal_data->num_cal_blocks = acdb_data.vocstrm_cal_size; + cal_data->cal_blocks = &acdb_data.vocstrm_cal[0]; + + mutex_unlock(&acdb_data.acdb_mutex); +done: + return; +} + +void store_vocvol_cal(int32_t len, struct cal_block *cal_blocks) +{ + int i; + pr_debug("%s\n", __func__); + + if (len > MAX_NETWORKS) { + pr_aud_err("%s: Calibration sent for %d networks, only %d are " + "supported!\n", __func__, len, MAX_NETWORKS); + goto done; + } + + mutex_lock(&acdb_data.acdb_mutex); + + for (i = 0; i < len; i++) { + if (cal_blocks[i].cal_offset > acdb_data.pmem_len) { + pr_aud_err("%s: offset %d is > pmem_len %ld\n", + __func__, cal_blocks[i].cal_offset, + acdb_data.pmem_len); + acdb_data.vocvol_cal[i].cal_size = 0; + } else { + acdb_data.vocvol_cal[i].cal_size = + cal_blocks[i].cal_size; + acdb_data.vocvol_cal[i].cal_paddr = + cal_blocks[i].cal_offset + + acdb_data.paddr; + acdb_data.vocvol_cal[i].cal_kvaddr = + cal_blocks[i].cal_offset + + acdb_data.kvaddr; + } + } + acdb_data.vocvol_cal_size = len; + mutex_unlock(&acdb_data.acdb_mutex); +done: + return; +} + +void get_vocvol_cal(struct acdb_cal_data *cal_data) +{ + pr_debug("%s\n", __func__); + + if (cal_data == NULL) { + pr_aud_err("ACDB=> NULL pointer sent to %s\n", __func__); + goto done; + } + + mutex_lock(&acdb_data.acdb_mutex); + + cal_data->num_cal_blocks = acdb_data.vocvol_cal_size; + cal_data->cal_blocks = &acdb_data.vocvol_cal[0]; + + mutex_unlock(&acdb_data.acdb_mutex); +done: + return; +} + +void store_sidetone_cal(struct sidetone_cal *cal_data) +{ + pr_debug("%s\n", __func__); + + mutex_lock(&acdb_data.acdb_mutex); + + acdb_data.sidetone_cal.enable = cal_data->enable; + acdb_data.sidetone_cal.gain = cal_data->gain; + + mutex_unlock(&acdb_data.acdb_mutex); +} + + +void get_sidetone_cal(struct sidetone_cal *cal_data) +{ + pr_debug("%s\n", __func__); + + if (cal_data == NULL) { + pr_aud_err("ACDB=> NULL pointer sent to %s\n", __func__); + goto done; + } + + mutex_lock(&acdb_data.acdb_mutex); + + cal_data->enable = acdb_data.sidetone_cal.enable; + cal_data->gain = acdb_data.sidetone_cal.gain; + + mutex_unlock(&acdb_data.acdb_mutex); +done: + return; +} + +static int acdb_open(struct inode *inode, struct file *f) +{ + s32 result = 0; + pr_aud_info("%s\n", __func__); + + mutex_lock(&acdb_data.acdb_mutex); + if (acdb_data.pmem_fd) { + pr_aud_info("%s: ACDB opened but PMEM allocated, using existing PMEM!\n", + __func__); + } + mutex_unlock(&acdb_data.acdb_mutex); + + atomic_inc(&usage_count); + return result; +} + +static int deregister_pmem(void) +{ + int result; + struct audproc_buffer_data buffer; + + get_audproc_buffer_data(&buffer); + + result = adm_memory_unmap_regions(buffer.phys_addr, + buffer.buf_size, NUM_AUDPROC_BUFFERS); + + if (result < 0) + pr_aud_err("Audcal unmap did not work!\n"); + + if (acdb_data.pmem_fd) { + put_pmem_file(acdb_data.file); + acdb_data.pmem_fd = 0; + } + return result; +} + +static int register_pmem(void) +{ + int result; + struct audproc_buffer_data buffer; + + result = get_pmem_file(acdb_data.pmem_fd, &acdb_data.paddr, + &acdb_data.kvaddr, &acdb_data.pmem_len, + &acdb_data.file); + if (result != 0) { + acdb_data.pmem_fd = 0; + pr_aud_err("%s: Could not register PMEM!!!\n", __func__); + goto done; + } + + pr_debug("AUDIO_REGISTER_PMEM done! paddr = 0x%lx, " + "kvaddr = 0x%lx, len = x%lx\n", acdb_data.paddr, + acdb_data.kvaddr, acdb_data.pmem_len); + get_audproc_buffer_data(&buffer); + result = adm_memory_map_regions(buffer.phys_addr, 0, + buffer.buf_size, + NUM_AUDPROC_BUFFERS); + if (result < 0) + pr_aud_err("Audcal mmap did not work!\n"); + goto done; + +done: + return result; +} +static long acdb_ioctl(struct file *f, + unsigned int cmd, unsigned long arg) +{ + s32 result = 0; + s32 audproc_path; + s32 size; + struct cal_block data[MAX_NETWORKS]; + pr_debug("%s\n", __func__); + + switch (cmd) { + case AUDIO_REGISTER_PMEM: + pr_debug("AUDIO_REGISTER_PMEM\n"); + mutex_lock(&acdb_data.acdb_mutex); + if (acdb_data.pmem_fd) { + deregister_pmem(); + pr_aud_info("Remove the existing PMEM\n"); + } + + if (copy_from_user(&acdb_data.pmem_fd, (void *)arg, + sizeof(acdb_data.pmem_fd))) + result = -EFAULT; + else + result = register_pmem(); + mutex_unlock(&acdb_data.acdb_mutex); + goto done; + + case AUDIO_DEREGISTER_PMEM: + pr_debug("AUDIO_DEREGISTER_PMEM\n"); + mutex_lock(&acdb_data.acdb_mutex); + deregister_pmem(); + mutex_unlock(&acdb_data.acdb_mutex); + goto done; + } + + if (copy_from_user(&size, (void *) arg, sizeof(size))) { + + result = -EFAULT; + goto done; + } + + if (size <= 0) { + pr_aud_err("%s: Invalid size sent to driver: %d\n", + __func__, size); + result = -EFAULT; + goto done; + } + + if (copy_from_user(data, (void *)(arg + sizeof(size)), size)) { + + pr_aud_err("%s: fail to copy table size %d\n", __func__, size); + result = -EFAULT; + goto done; + } + + if (data == NULL) { + pr_aud_err("%s: NULL pointer sent to driver!\n", __func__); + result = -EFAULT; + goto done; + } + + switch (cmd) { + case AUDIO_SET_AUDPROC_TX_CAL: + audproc_path = TX_CAL; + if (size > sizeof(struct cal_block)) + pr_aud_err("%s: More Audproc Cal then expected, " + "size received: %d\n", __func__, size); + store_audproc_cal(audproc_path, data); + break; + case AUDIO_SET_AUDPROC_RX_CAL: + audproc_path = RX_CAL; + if (size > sizeof(struct cal_block)) + pr_aud_err("%s: More Audproc Cal then expected, " + "size received: %d\n", __func__, size); + store_audproc_cal(audproc_path, data); + break; + case AUDIO_SET_AUDPROC_TX_STREAM_CAL: + audproc_path = TX_CAL; + if (size > sizeof(struct cal_block)) + pr_aud_err("%s: More Audproc Cal then expected, " + "size received: %d\n", __func__, size); + store_audstrm_cal(audproc_path, data); + break; + case AUDIO_SET_AUDPROC_RX_STREAM_CAL: + audproc_path = RX_CAL; + if (size > sizeof(struct cal_block)) + pr_aud_err("%s: More Audproc Cal then expected, " + "size received: %d\n", __func__, size); + store_audstrm_cal(audproc_path, data); + break; + case AUDIO_SET_AUDPROC_TX_VOL_CAL: + audproc_path = TX_CAL; + if (size > sizeof(struct cal_block)) + pr_aud_err("%s: More Audproc Cal then expected, " + "size received: %d\n", __func__, size); + store_audvol_cal(audproc_path, data); + case AUDIO_SET_AUDPROC_RX_VOL_CAL: + audproc_path = RX_CAL; + if (size > sizeof(struct cal_block)) + pr_aud_err("%s: More Audproc Cal then expected, " + "size received: %d\n", __func__, size); + store_audvol_cal(audproc_path, data); + break; + case AUDIO_SET_VOCPROC_CAL: + store_vocproc_cal(size / sizeof(struct cal_block), data); + break; + case AUDIO_SET_VOCPROC_STREAM_CAL: + store_vocstrm_cal(size / sizeof(struct cal_block), data); + break; + case AUDIO_SET_VOCPROC_VOL_CAL: + store_vocvol_cal(size / sizeof(struct cal_block), data); + break; + case AUDIO_SET_SIDETONE_CAL: + if (size > sizeof(struct sidetone_cal)) + pr_aud_err("%s: More sidetone cal then expected, " + "size received: %d\n", __func__, size); + store_sidetone_cal((struct sidetone_cal *)data); + break; + case AUDIO_SET_ANC_CAL: + store_anc_cal(data); + break; + default: + pr_aud_err("ACDB=> ACDB ioctl not found!\n"); + } + +done: + return result; +} + +static int acdb_mmap(struct file *file, struct vm_area_struct *vma) +{ + int result = 0; + int size = vma->vm_end - vma->vm_start; + + pr_debug("%s\n", __func__); + + mutex_lock(&acdb_data.acdb_mutex); + if (acdb_data.pmem_fd) { + if (size <= acdb_data.pmem_len) { + vma->vm_page_prot = pgprot_noncached( + vma->vm_page_prot); + result = remap_pfn_range(vma, + vma->vm_start, + acdb_data.paddr >> PAGE_SHIFT, + size, + vma->vm_page_prot); + } else { + pr_aud_err("%s: Not enough PMEM memory!\n", __func__); + result = -ENOMEM; + } + } else { + pr_aud_err("%s: PMEM is not allocated, yet!\n", __func__); + result = -ENODEV; + } + mutex_unlock(&acdb_data.acdb_mutex); + + return result; +} + +static int acdb_release(struct inode *inode, struct file *f) +{ + s32 result = 0; + + atomic_dec(&usage_count); + atomic_read(&usage_count); + + pr_aud_info("%s: ref count %d!\n", __func__, + atomic_read(&usage_count)); + + if (atomic_read(&usage_count) >= 1) { + result = -EBUSY; + } else { + mutex_lock(&acdb_data.acdb_mutex); + result = deregister_pmem(); + mutex_unlock(&acdb_data.acdb_mutex); + } + + return result; +} + +static const struct file_operations acdb_fops = { + .owner = THIS_MODULE, + .open = acdb_open, + .release = acdb_release, + .unlocked_ioctl = acdb_ioctl, + .mmap = acdb_mmap, +}; + +struct miscdevice acdb_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_acdb", + .fops = &acdb_fops, +}; + +static int __init acdb_init(void) +{ + pr_aud_info("%s\n", __func__); + memset(&acdb_data, 0, sizeof(acdb_data)); + mutex_init(&acdb_data.acdb_mutex); + atomic_set(&usage_count, 0); + return misc_register(&acdb_misc); +} + +static void __exit acdb_exit(void) +{ +} + +module_init(acdb_init); +module_exit(acdb_exit); + +MODULE_DESCRIPTION("MSM 8x60 Audio ACDB driver"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/qdsp6v3/audio_acdb.h b/arch/arm/mach-msm/qdsp6v3/audio_acdb.h new file mode 100644 index 00000000000..17f9a7c4624 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v3/audio_acdb.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 _AUDIO_ACDB_H +#define _AUDIO_ACDB_H + +#include +#include "q6adm.h" + +#define NUM_AUDPROC_BUFFERS 6 + +struct acdb_cal_block { + uint32_t cal_size; + uint32_t cal_kvaddr; + uint32_t cal_paddr; +}; + +struct acdb_cal_data { + uint32_t num_cal_blocks; + struct acdb_cal_block *cal_blocks; +}; + +struct audproc_buffer_data { + uint32_t buf_size[NUM_AUDPROC_BUFFERS]; + uint32_t phys_addr[NUM_AUDPROC_BUFFERS]; +}; + +void get_audproc_buffer_data(struct audproc_buffer_data *cal_buffers); +void get_audproc_cal(int32_t path, struct acdb_cal_block *cal_block); +void get_audstrm_cal(int32_t path, struct acdb_cal_block *cal_block); +void get_audvol_cal(int32_t path, struct acdb_cal_block *cal_block); +void get_vocproc_cal(struct acdb_cal_data *cal_data); +void get_vocstrm_cal(struct acdb_cal_data *cal_data); +void get_vocvol_cal(struct acdb_cal_data *cal_data); +void get_sidetone_cal(struct sidetone_cal *cal_data); +void get_anc_cal(struct acdb_cal_block *cal_block); + +#endif diff --git a/arch/arm/mach-msm/qdsp6v3/audio_dev_ctl.c b/arch/arm/mach-msm/qdsp6v3/audio_dev_ctl.c new file mode 100644 index 00000000000..f170ae52131 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v3/audio_dev_ctl.c @@ -0,0 +1,1757 @@ +/* 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. + * + * 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 "q6adm.h" +#include "rtac.h" + +#ifndef MAX +#define MAX(x, y) (((x) > (y)) ? (x) : (y)) +#endif + + +static DEFINE_MUTEX(session_lock); + +struct audio_dev_ctrl_state { + struct msm_snddev_info *devs[AUDIO_DEV_CTL_MAX_DEV]; + u32 num_dev; + atomic_t opened; + struct msm_snddev_info *voice_rx_dev; + struct msm_snddev_info *voice_tx_dev; + wait_queue_head_t wait; +}; + +static struct audio_dev_ctrl_state audio_dev_ctrl; +struct event_listner event; + +#define PLAYBACK 0x1 +#define LIVE_RECORDING 0x2 +#define NON_LIVE_RECORDING 0x3 +#define MAX_COPP_DEVICES 4 +static int voc_rx_freq = 0; +static int voc_tx_freq = 0; + +struct session_freq { + int freq; + int evt; +}; + +struct audio_routing_info { + unsigned short mixer_mask[MAX_SESSIONS]; + unsigned short audrec_mixer_mask[MAX_SESSIONS]; + struct session_freq dec_freq[MAX_SESSIONS]; + struct session_freq enc_freq[MAX_SESSIONS]; + unsigned int copp_list[MAX_SESSIONS][AFE_MAX_PORTS]; + int voice_tx_dev_id; + int voice_rx_dev_id; + int voice_tx_sample_rate; + int voice_rx_sample_rate; + signed int voice_tx_vol; + signed int voice_rx_vol; + int tx_mute; + int rx_mute; + int voice_state; + int call_state; + struct mutex copp_list_mutex; + struct mutex adm_mutex; +}; + +static struct audio_routing_info routing_info; + +struct audio_copp_topology { + struct mutex lock; + int session_cnt; + int session_id[MAX_SESSIONS]; + int topolog_id[MAX_SESSIONS]; +}; +static struct audio_copp_topology adm_tx_topology_tbl; + +static struct dev_ctrl_ops default_ctrl_ops; +static struct dev_ctrl_ops *ctrl_ops = &default_ctrl_ops; + +void htc_8x60_register_dev_ctrl_ops(struct dev_ctrl_ops *ops) +{ + ctrl_ops = ops; +} + +int msm_reset_all_device(void) +{ + int rc = 0; + int dev_id = 0; + struct msm_snddev_info *dev_info = NULL; + + for (dev_id = 0; dev_id < audio_dev_ctrl.num_dev; dev_id++) { + dev_info = audio_dev_ctrl_find_dev(dev_id); + if (IS_ERR(dev_info)) { + pr_aud_err("%s:pass invalid dev_id\n", __func__); + rc = PTR_ERR(dev_info); + return rc; + } + if (!dev_info->opened) + continue; + pr_debug("%s:Resetting device %d active on COPP %d" + "with %lld as routing\n", __func__, + dev_id, dev_info->copp_id, dev_info->sessions); + broadcast_event(AUDDEV_EVT_REL_PENDING, + dev_id, + SESSION_IGNORE); + rc = dev_info->dev_ops.close(dev_info); + if (rc < 0) { + pr_aud_err("%s:Snd device failed close!\n", __func__); + return rc; + } else { + dev_info->opened = 0; + broadcast_event(AUDDEV_EVT_DEV_RLS, + dev_id, + SESSION_IGNORE); + + if (dev_info->copp_id == VOICE_PLAYBACK_TX) + voice_start_playback(0); + } + dev_info->sessions = 0; + } + msm_clear_all_session(); + return 0; +} +EXPORT_SYMBOL(msm_reset_all_device); + +int msm_set_copp_id(int session_id, int copp_id) +{ + int rc = 0; + int index; + + if (session_id < 1 || session_id > 8) + return -EINVAL; + if (afe_validate_port(copp_id) < 0) + return -EINVAL; + + index = afe_get_port_index(copp_id); + pr_debug("%s: session[%d] copp_id[%d] index[%d]\n", __func__, + session_id, copp_id, index); + mutex_lock(&routing_info.copp_list_mutex); + if (routing_info.copp_list[session_id][index] == DEVICE_IGNORE) + routing_info.copp_list[session_id][index] = copp_id; + mutex_unlock(&routing_info.copp_list_mutex); + + return rc; +} +EXPORT_SYMBOL(msm_set_copp_id); + +int msm_clear_copp_id(int session_id, int copp_id) +{ + int rc = 0; + int index = afe_get_port_index(copp_id); + + if (session_id < 1 || session_id > 8) { + pr_aud_err("%s: invalid session_id %d\n", __func__, session_id); + return -EINVAL; + } + + if (index < 0 || index >= AFE_MAX_PORTS) { + pr_aud_err("%s: invalid copp_id index %d\n", __func__, index); + return -EINVAL; + } + + pr_debug("%s: session[%d] copp_id[%d] index[%d]\n", __func__, + session_id, copp_id, index); + mutex_lock(&routing_info.copp_list_mutex); + if (routing_info.copp_list[session_id][index] == copp_id) + routing_info.copp_list[session_id][index] = DEVICE_IGNORE; + mutex_unlock(&routing_info.copp_list_mutex); + + return rc; +} +EXPORT_SYMBOL(msm_clear_copp_id); + +int msm_clear_session_id(int session_id) +{ + int rc = 0; + int i = 0; + if (session_id < 1 || session_id > 8) + return -EINVAL; + pr_debug("%s: session[%d]\n", __func__, session_id); + mutex_lock(&routing_info.adm_mutex); + mutex_lock(&routing_info.copp_list_mutex); + for (i = 0; i < AFE_MAX_PORTS; i++) { + if (routing_info.copp_list[session_id][i] != DEVICE_IGNORE) { + rc = adm_close(routing_info.copp_list[session_id][i]); + if (rc < 0) { + pr_aud_err("%s: adm close fail port[%d] rc[%d]\n", + __func__, + routing_info.copp_list[session_id][i], + rc); + continue; + } + routing_info.copp_list[session_id][i] = DEVICE_IGNORE; + rc = 0; + } + } + mutex_unlock(&routing_info.copp_list_mutex); + mutex_unlock(&routing_info.adm_mutex); + + return rc; +} +EXPORT_SYMBOL(msm_clear_session_id); + +int msm_clear_all_session() +{ + int rc = 0; + int i = 0, j = 0; + pr_aud_info("%s:\n", __func__); + mutex_lock(&routing_info.adm_mutex); + mutex_lock(&routing_info.copp_list_mutex); + for (j = 1; j < MAX_SESSIONS; j++) { + for (i = 0; i < AFE_MAX_PORTS; i++) { + if (routing_info.copp_list[j][i] != DEVICE_IGNORE) { + rc = adm_close( + routing_info.copp_list[j][i]); + if (rc < 0) { + pr_aud_err("%s: adm close fail copp[%d]" + "session[%d] rc[%d]\n", + __func__, + routing_info.copp_list[j][i], + j, rc); + continue; + } + routing_info.copp_list[j][i] = DEVICE_IGNORE; + rc = 0; + } + } + } + mutex_unlock(&routing_info.copp_list_mutex); + mutex_unlock(&routing_info.adm_mutex); + return rc; +} +EXPORT_SYMBOL(msm_clear_all_session); + +int msm_get_voice_state(void) +{ + pr_debug("voice state %d\n", routing_info.voice_state); + return routing_info.voice_state; +} +EXPORT_SYMBOL(msm_get_voice_state); + +int msm_get_call_state(void) +{ + pr_debug("call state %d\n", routing_info.call_state); + return routing_info.call_state; +} +EXPORT_SYMBOL(msm_get_call_state); + +int msm_set_voice_mute(int dir, int mute) +{ + pr_debug("dir %x mute %x\n", dir, mute); + if (dir == DIR_TX) { + routing_info.tx_mute = mute; + broadcast_event(AUDDEV_EVT_DEVICE_VOL_MUTE_CHG, + routing_info.voice_tx_dev_id, SESSION_IGNORE); + } else + return -EPERM; + return 0; +} +EXPORT_SYMBOL(msm_set_voice_mute); + +int msm_set_voice_vol(int dir, s32 volume) +{ + if (dir == DIR_TX) { + routing_info.voice_tx_vol = volume; + broadcast_event(AUDDEV_EVT_DEVICE_VOL_MUTE_CHG, + routing_info.voice_tx_dev_id, + SESSION_IGNORE); + } else if (dir == DIR_RX) { + routing_info.voice_rx_vol = volume; + broadcast_event(AUDDEV_EVT_DEVICE_VOL_MUTE_CHG, + routing_info.voice_rx_dev_id, + SESSION_IGNORE); + } else + return -EINVAL; + return 0; +} +EXPORT_SYMBOL(msm_set_voice_vol); + +void msm_snddev_register(struct msm_snddev_info *dev_info) +{ + mutex_lock(&session_lock); + if (audio_dev_ctrl.num_dev < AUDIO_DEV_CTL_MAX_DEV) { + audio_dev_ctrl.devs[audio_dev_ctrl.num_dev] = dev_info; + dev_info->dev_volume = 50; /* 50% */ + dev_info->sessions = 0x0; + dev_info->usage_count = 0; + audio_dev_ctrl.num_dev++; + } else + pr_aud_err("%s: device registry max out\n", __func__); + mutex_unlock(&session_lock); +} +EXPORT_SYMBOL(msm_snddev_register); + +int msm_snddev_devcount(void) +{ + return audio_dev_ctrl.num_dev; +} +EXPORT_SYMBOL(msm_snddev_devcount); + +int msm_snddev_query(int dev_id) +{ + if (dev_id <= audio_dev_ctrl.num_dev) + return 0; + return -ENODEV; +} +EXPORT_SYMBOL(msm_snddev_query); + +int msm_snddev_is_set(int popp_id, int copp_id) +{ + return routing_info.mixer_mask[popp_id] & (0x1 << copp_id); +} +EXPORT_SYMBOL(msm_snddev_is_set); + +unsigned short msm_snddev_route_enc(int enc_id) +{ + if (enc_id >= MAX_SESSIONS) + return -EINVAL; + return routing_info.audrec_mixer_mask[enc_id]; +} +EXPORT_SYMBOL(msm_snddev_route_enc); + +unsigned short msm_snddev_route_dec(int popp_id) +{ + if (popp_id >= MAX_SESSIONS) + return -EINVAL; + return routing_info.mixer_mask[popp_id]; +} +EXPORT_SYMBOL(msm_snddev_route_dec); + +/*To check one->many case*/ +int msm_check_multicopp_per_stream(int session_id, + struct route_payload *payload) +{ + int i = 0; + int flag = 0; + pr_debug("%s: session_id=%d\n", __func__, session_id); + mutex_lock(&routing_info.copp_list_mutex); + for (i = 0; i < AFE_MAX_PORTS; i++) { + if (routing_info.copp_list[session_id][i] == DEVICE_IGNORE) + continue; + else { + pr_debug("Device enabled port_id = %d\n", + routing_info.copp_list[session_id][i]); + payload->copp_ids[flag++] = + routing_info.copp_list[session_id][i]; + } + } + mutex_unlock(&routing_info.copp_list_mutex); + if (flag > 1) { + pr_debug("Multiple copp per stream case num_copps=%d\n", flag); + } else { + pr_debug("Stream routed to single copp\n"); + } + payload->num_copps = flag; + return flag; +} + +int msm_snddev_set_dec(int popp_id, int copp_id, int set, + int rate, int mode) +{ + int rc = 0, i = 0; + struct route_payload payload; + int topology = DEFAULT_COPP_TOPOLOGY; + + if ((popp_id >= MAX_SESSIONS) || (popp_id <= 0)) { + pr_aud_err("%s: Invalid session id %d\n", __func__, popp_id); + return 0; + } + + mutex_lock(&routing_info.adm_mutex); + if (set) { + if (ctrl_ops->support_opendsp) { + if (ctrl_ops->support_opendsp()) + topology = HTC_COPP_TOPOLOGY; + } + pr_aud_info("%s, topology = 0x%x\n", __func__, topology); + rc = adm_open(copp_id, PLAYBACK, rate, mode, + topology); + if (rc < 0) { + pr_aud_err("%s: adm open fail rc[%d]\n", __func__, rc); + rc = -EINVAL; + mutex_unlock(&routing_info.adm_mutex); + return rc; + } + msm_set_copp_id(popp_id, copp_id); + pr_debug("%s:Session id=%d copp_id=%d\n", + __func__, popp_id, copp_id); + memset(payload.copp_ids, DEVICE_IGNORE, + (sizeof(unsigned int) * AFE_MAX_PORTS)); + rc = msm_check_multicopp_per_stream(popp_id, &payload); + /* Multiple streams per copp is handled, one stream at a time */ + rc = adm_matrix_map(popp_id, PLAYBACK, rc, + payload.copp_ids, copp_id); + if (rc < 0) { + pr_aud_err("%s: matrix map failed rc[%d]\n", + __func__, rc); + adm_close(copp_id); + rc = -EINVAL; + mutex_unlock(&routing_info.adm_mutex); + return rc; + } + } else { + for (i = 0; i < AFE_MAX_PORTS; i++) { + if (routing_info.copp_list[popp_id][i] == copp_id) { + rc = adm_close(copp_id); + if (rc < 0) { + pr_aud_err("%s: adm close fail copp[%d]" + "rc[%d]\n", + __func__, copp_id, rc); + rc = -EINVAL; + mutex_unlock(&routing_info.adm_mutex); + return rc; + } + msm_clear_copp_id(popp_id, copp_id); + break; + } + } + } + + if (copp_id == VOICE_PLAYBACK_TX) { + /* Signal uplink playback. */ + rc = voice_start_playback(set); + } + mutex_unlock(&routing_info.adm_mutex); + return rc; +} +EXPORT_SYMBOL(msm_snddev_set_dec); + + +static int check_tx_copp_topology(int session_id) +{ + int cnt; + int ret_val = -ENOENT; + + cnt = adm_tx_topology_tbl.session_cnt; + if (cnt) { + do { + if (adm_tx_topology_tbl.session_id[cnt-1] + == session_id) + ret_val = cnt-1; + } while (--cnt); + } + + return ret_val; +} + +static int add_to_tx_topology_lists(int session_id, int topology) +{ + int idx = 0, tbl_idx; + int ret_val = -ENOSPC; + + mutex_lock(&adm_tx_topology_tbl.lock); + + tbl_idx = check_tx_copp_topology(session_id); + if (tbl_idx == -ENOENT) { + while (adm_tx_topology_tbl.session_id[idx++]) + ; + tbl_idx = idx-1; + } + + if (tbl_idx < MAX_SESSIONS) { + adm_tx_topology_tbl.session_id[tbl_idx] = session_id; + adm_tx_topology_tbl.topolog_id[tbl_idx] = topology; + adm_tx_topology_tbl.session_cnt++; + + ret_val = 0; + } + mutex_unlock(&adm_tx_topology_tbl.lock); + return ret_val; +} + +static void remove_from_tx_topology_lists(int session_id) +{ + int tbl_idx; + + mutex_lock(&adm_tx_topology_tbl.lock); + tbl_idx = check_tx_copp_topology(session_id); + if (tbl_idx != -ENOENT) { + + adm_tx_topology_tbl.session_cnt--; + adm_tx_topology_tbl.session_id[tbl_idx] = 0; + adm_tx_topology_tbl.topolog_id[tbl_idx] = 0; + } + mutex_unlock(&adm_tx_topology_tbl.lock); +} + +int auddev_cfg_tx_copp_topology(int session_id, int cfg) +{ + int ret = 0; + + if (cfg == DEFAULT_COPP_TOPOLOGY) + remove_from_tx_topology_lists(session_id); + else { + switch (cfg) { + case VPM_TX_SM_ECNS_COPP_TOPOLOGY: + case VPM_TX_DM_FLUENCE_COPP_TOPOLOGY: + case HTC_STEREO_RECORD_TOPOLOGY: + ret = add_to_tx_topology_lists(session_id, cfg); + break; + + default: + ret = -ENODEV; + break; + } + } + return ret; +} + +int msm_snddev_set_enc(int popp_id, int copp_id, int set, + int rate, int mode) +{ + int topology; + int tbl_idx; + int rc = 0, i = 0; + mutex_lock(&routing_info.adm_mutex); + if (set) { + mutex_lock(&adm_tx_topology_tbl.lock); + tbl_idx = check_tx_copp_topology(popp_id); + if (tbl_idx == -ENOENT) + topology = DEFAULT_COPP_TOPOLOGY; + else { + topology = adm_tx_topology_tbl.topolog_id[tbl_idx]; + rate = 16000; + } + mutex_unlock(&adm_tx_topology_tbl.lock); + pr_aud_info("%s, topology = 0x%x\n", __func__, topology); + rc = adm_open(copp_id, LIVE_RECORDING, rate, mode, topology); + if (rc < 0) { + pr_aud_err("%s: adm open fail rc[%d]\n", __func__, rc); + rc = -EINVAL; + goto fail_cmd; + } + + rc = adm_matrix_map(popp_id, LIVE_RECORDING, 1, + (unsigned int *)&copp_id, copp_id); + if (rc < 0) { + pr_aud_err("%s: matrix map failed rc[%d]\n", __func__, rc); + adm_close(copp_id); + rc = -EINVAL; + goto fail_cmd; + } + msm_set_copp_id(popp_id, copp_id); + } else { + for (i = 0; i < AFE_MAX_PORTS; i++) { + if (routing_info.copp_list[popp_id][i] == copp_id) { + rc = adm_close(copp_id); + if (rc < 0) { + pr_aud_err("%s: adm close fail copp[%d]" + "rc[%d]\n", + __func__, copp_id, rc); + rc = -EINVAL; + goto fail_cmd; + } + msm_clear_copp_id(popp_id, copp_id); + break; + } + } + } +fail_cmd: + mutex_unlock(&routing_info.adm_mutex); + return rc; +} +EXPORT_SYMBOL(msm_snddev_set_enc); + +int msm_device_is_voice(int dev_id) +{ + if ((dev_id == routing_info.voice_rx_dev_id) + || (dev_id == routing_info.voice_tx_dev_id)) + return 0; + else + return -EINVAL; +} +EXPORT_SYMBOL(msm_device_is_voice); + +int msm_set_voc_route(struct msm_snddev_info *dev_info, + int stream_type, int dev_id) +{ + int rc = 0; + u64 session_mask = 0; + + if (dev_info == NULL) { + pr_aud_err("%s: invalid param\n", __func__); + return -EINVAL; + } + + mutex_lock(&session_lock); + switch (stream_type) { + case AUDIO_ROUTE_STREAM_VOICE_RX: + if (audio_dev_ctrl.voice_rx_dev) + audio_dev_ctrl.voice_rx_dev->sessions &= ~0xFFFF; + + if (!(dev_info->capability & SNDDEV_CAP_RX) | + !(dev_info->capability & SNDDEV_CAP_VOICE)) { + rc = -EINVAL; + break; + } + audio_dev_ctrl.voice_rx_dev = dev_info; + if (audio_dev_ctrl.voice_rx_dev) { + session_mask = + ((u64)0x1) << (MAX_BIT_PER_CLIENT * \ + ((int)AUDDEV_CLNT_VOC-1)); + audio_dev_ctrl.voice_rx_dev->sessions |= + session_mask; + } + routing_info.voice_rx_dev_id = dev_id; + break; + case AUDIO_ROUTE_STREAM_VOICE_TX: + if (audio_dev_ctrl.voice_tx_dev) + audio_dev_ctrl.voice_tx_dev->sessions &= ~0xFFFF; + + if (!(dev_info->capability & SNDDEV_CAP_TX) | + !(dev_info->capability & SNDDEV_CAP_VOICE)) { + rc = -EINVAL; + break; + } + + audio_dev_ctrl.voice_tx_dev = dev_info; + if (audio_dev_ctrl.voice_rx_dev) { + session_mask = + ((u64)0x1) << (MAX_BIT_PER_CLIENT * \ + ((int)AUDDEV_CLNT_VOC-1)); + audio_dev_ctrl.voice_tx_dev->sessions |= + session_mask; + } + routing_info.voice_tx_dev_id = dev_id; + break; + default: + rc = -EINVAL; + } + mutex_unlock(&session_lock); + return rc; +} +EXPORT_SYMBOL(msm_set_voc_route); + +void msm_release_voc_thread(void) +{ + wake_up(&audio_dev_ctrl.wait); +} +EXPORT_SYMBOL(msm_release_voc_thread); + +int msm_snddev_get_enc_freq(session_id) +{ + return routing_info.enc_freq[session_id].freq; +} +EXPORT_SYMBOL(msm_snddev_get_enc_freq); + +int msm_get_voc_freq(int *tx_freq, int *rx_freq) +{ + *tx_freq = (0 == voc_tx_freq ? routing_info.voice_tx_sample_rate + : voc_tx_freq); + *rx_freq = (0 == voc_rx_freq ? routing_info.voice_rx_sample_rate + : voc_rx_freq); + return 0; +} +EXPORT_SYMBOL(msm_get_voc_freq); + +void msm_set_voc_freq(int tx_freq, int rx_freq) +{ + voc_tx_freq = tx_freq; + voc_rx_freq = rx_freq; +} +EXPORT_SYMBOL(msm_set_voc_freq); + + +int msm_get_voc_route(u32 *rx_id, u32 *tx_id) +{ + int rc = 0; + + if (!rx_id || !tx_id) + return -EINVAL; + + mutex_lock(&session_lock); + if (!audio_dev_ctrl.voice_rx_dev || !audio_dev_ctrl.voice_tx_dev) { + rc = -ENODEV; + mutex_unlock(&session_lock); + return rc; + } + + *rx_id = audio_dev_ctrl.voice_rx_dev->acdb_id; + *tx_id = audio_dev_ctrl.voice_tx_dev->acdb_id; + + mutex_unlock(&session_lock); + + return rc; +} +EXPORT_SYMBOL(msm_get_voc_route); + +struct msm_snddev_info *audio_dev_ctrl_find_dev(u32 dev_id) +{ + struct msm_snddev_info *info; + + if ((audio_dev_ctrl.num_dev - 1) < dev_id) { + info = ERR_PTR(-ENODEV); + goto error; + } + + info = audio_dev_ctrl.devs[dev_id]; +error: + return info; + +} +EXPORT_SYMBOL(audio_dev_ctrl_find_dev); + +int snddev_voice_set_volume(int vol, int path) +{ + if (audio_dev_ctrl.voice_rx_dev + && audio_dev_ctrl.voice_tx_dev) { + if (path) + audio_dev_ctrl.voice_tx_dev->dev_volume = vol; + else + audio_dev_ctrl.voice_rx_dev->dev_volume = vol; + } else + return -ENODEV; + return 0; +} +EXPORT_SYMBOL(snddev_voice_set_volume); + +static int audio_dev_ctrl_get_devices(struct audio_dev_ctrl_state *dev_ctrl, + void __user *arg) +{ + int rc = 0; + u32 index; + struct msm_snd_device_list work_list; + struct msm_snd_device_info *work_tbl; + + if (copy_from_user(&work_list, arg, sizeof(work_list))) { + rc = -EFAULT; + goto error; + } + + if (work_list.num_dev > dev_ctrl->num_dev) { + rc = -EINVAL; + goto error; + } + + work_tbl = kmalloc(work_list.num_dev * + sizeof(struct msm_snd_device_info), GFP_KERNEL); + if (!work_tbl) { + rc = -ENOMEM; + goto error; + } + + for (index = 0; index < dev_ctrl->num_dev; index++) { + work_tbl[index].dev_id = index; + work_tbl[index].dev_cap = dev_ctrl->devs[index]->capability; + strlcpy(work_tbl[index].dev_name, dev_ctrl->devs[index]->name, + 64); + } + + if (copy_to_user((void *) (work_list.list), work_tbl, + work_list.num_dev * sizeof(struct msm_snd_device_info))) + rc = -EFAULT; + kfree(work_tbl); +error: + return rc; +} + + +int auddev_register_evt_listner(u32 evt_id, u32 clnt_type, u32 clnt_id, + void (*listner)(u32 evt_id, + union auddev_evt_data *evt_payload, + void *private_data), + void *private_data) +{ + int rc; + struct msm_snd_evt_listner *callback = NULL; + struct msm_snd_evt_listner *new_cb; + + new_cb = kzalloc(sizeof(struct msm_snd_evt_listner), GFP_KERNEL); + if (!new_cb) { + pr_aud_err("No memory to add new listener node\n"); + return -ENOMEM; + } + + mutex_lock(&session_lock); + new_cb->cb_next = NULL; + new_cb->auddev_evt_listener = listner; + new_cb->evt_id = evt_id; + new_cb->clnt_type = clnt_type; + new_cb->clnt_id = clnt_id; + new_cb->private_data = private_data; + if (event.cb == NULL) { + event.cb = new_cb; + new_cb->cb_prev = NULL; + } else { + callback = event.cb; + for (; ;) { + if (callback->cb_next == NULL) + break; + else { + callback = callback->cb_next; + continue; + } + } + callback->cb_next = new_cb; + new_cb->cb_prev = callback; + } + event.num_listner++; + mutex_unlock(&session_lock); + rc = 0; + return rc; +} +EXPORT_SYMBOL(auddev_register_evt_listner); + +int auddev_unregister_evt_listner(u32 clnt_type, u32 clnt_id) +{ + struct msm_snd_evt_listner *callback = event.cb; + struct msm_snddev_info *info; + u64 session_mask = 0; + int i = 0; + + mutex_lock(&session_lock); + while (callback != NULL) { + if ((callback->clnt_type == clnt_type) + && (callback->clnt_id == clnt_id)) + break; + callback = callback->cb_next; + } + if (callback == NULL) { + mutex_unlock(&session_lock); + return -EINVAL; + } + + if ((callback->cb_next == NULL) && (callback->cb_prev == NULL)) + event.cb = NULL; + else if (callback->cb_next == NULL) + callback->cb_prev->cb_next = NULL; + else if (callback->cb_prev == NULL) { + callback->cb_next->cb_prev = NULL; + event.cb = callback->cb_next; + } else { + callback->cb_prev->cb_next = callback->cb_next; + callback->cb_next->cb_prev = callback->cb_prev; + } + kfree(callback); + + session_mask = (((u64)0x1) << clnt_id) << (MAX_BIT_PER_CLIENT * \ + ((int)clnt_type-1)); + for (i = 0; i < audio_dev_ctrl.num_dev; i++) { + info = audio_dev_ctrl.devs[i]; + info->sessions &= ~session_mask; + } + mutex_unlock(&session_lock); + return 0; +} +EXPORT_SYMBOL(auddev_unregister_evt_listner); + +int msm_snddev_withdraw_freq(u32 session_id, u32 capability, u32 clnt_type) +{ + int i = 0; + struct msm_snddev_info *info; + u64 session_mask = 0; + + if ((clnt_type == AUDDEV_CLNT_VOC) && (session_id != 0)) + return -EINVAL; + if ((clnt_type == AUDDEV_CLNT_DEC) + && (session_id >= MAX_SESSIONS)) + return -EINVAL; + if ((clnt_type == AUDDEV_CLNT_ENC) + && (session_id >= MAX_SESSIONS)) + return -EINVAL; + + session_mask = (((u64)0x1) << session_id) << (MAX_BIT_PER_CLIENT * \ + ((int)clnt_type-1)); + + for (i = 0; i < audio_dev_ctrl.num_dev; i++) { + info = audio_dev_ctrl.devs[i]; + if ((info->sessions & session_mask) + && (info->capability & capability)) { + if (!(info->sessions & ~(session_mask))) + info->set_sample_rate = 0; + } + } + if (clnt_type == AUDDEV_CLNT_DEC) + routing_info.dec_freq[session_id].freq + = 0; + else if (clnt_type == AUDDEV_CLNT_ENC) + routing_info.enc_freq[session_id].freq + = 0; + else if (capability == SNDDEV_CAP_TX) + routing_info.voice_tx_sample_rate = 0; + else + routing_info.voice_rx_sample_rate = 48000; + return 0; +} + +int msm_snddev_request_freq(int *freq, u32 session_id, + u32 capability, u32 clnt_type) +{ + int i = 0; + int rc = 0; + struct msm_snddev_info *info; + u32 set_freq; + u64 session_mask = 0; + u64 clnt_type_mask = 0; + + pr_debug(": clnt_type 0x%08x\n", clnt_type); + + if ((clnt_type == AUDDEV_CLNT_VOC) && (session_id != 0)) + return -EINVAL; + if ((clnt_type == AUDDEV_CLNT_DEC) + && (session_id >= MAX_SESSIONS)) + return -EINVAL; + if ((clnt_type == AUDDEV_CLNT_ENC) + && (session_id >= MAX_SESSIONS)) + return -EINVAL; + session_mask = (((u64)0x1) << session_id) << (MAX_BIT_PER_CLIENT * \ + ((int)clnt_type-1)); + clnt_type_mask = (0xFFFF << (MAX_BIT_PER_CLIENT * (clnt_type-1))); + if (!(*freq == 8000) && !(*freq == 11025) && + !(*freq == 12000) && !(*freq == 16000) && + !(*freq == 22050) && !(*freq == 24000) && + !(*freq == 32000) && !(*freq == 44100) && + !(*freq == 48000)) + return -EINVAL; + + for (i = 0; i < audio_dev_ctrl.num_dev; i++) { + info = audio_dev_ctrl.devs[i]; + if ((info->sessions & session_mask) + && (info->capability & capability)) { + rc = 0; + if ((info->sessions & ~clnt_type_mask) + && ((*freq != 8000) && (*freq != 16000) + && (*freq != 48000))) { + if (clnt_type == AUDDEV_CLNT_ENC) { + routing_info.enc_freq[session_id].freq + = 0; + return -EPERM; + } else if (clnt_type == AUDDEV_CLNT_DEC) { + routing_info.dec_freq[session_id].freq + = 0; + return -EPERM; + } + } + if (*freq == info->set_sample_rate) { + rc = info->set_sample_rate; + continue; + } + set_freq = MAX(*freq, info->set_sample_rate); + + + if (clnt_type == AUDDEV_CLNT_DEC) { + routing_info.dec_freq[session_id].evt = 1; + routing_info.dec_freq[session_id].freq + = set_freq; + } else if (clnt_type == AUDDEV_CLNT_ENC) { + routing_info.enc_freq[session_id].evt = 1; + routing_info.enc_freq[session_id].freq + = set_freq; + } else if (capability == SNDDEV_CAP_TX) + routing_info.voice_tx_sample_rate = set_freq; + + rc = set_freq; + info->set_sample_rate = set_freq; + *freq = info->set_sample_rate; + + if (info->opened) { + broadcast_event(AUDDEV_EVT_FREQ_CHG, i, + SESSION_IGNORE); + set_freq = info->dev_ops.set_freq(info, + set_freq); + broadcast_event(AUDDEV_EVT_DEV_RDY, i, + SESSION_IGNORE); + } + } + pr_debug("info->set_sample_rate = %d\n", info->set_sample_rate); + pr_debug("routing_info.enc_freq.freq = %d\n", + routing_info.enc_freq[session_id].freq); + } + return rc; +} +EXPORT_SYMBOL(msm_snddev_request_freq); + +int msm_snddev_enable_sidetone(u32 dev_id, u32 enable, uint16_t gain) +{ + int rc; + struct msm_snddev_info *dev_info; + + pr_debug("dev_id %d enable %d\n", dev_id, enable); + + dev_info = audio_dev_ctrl_find_dev(dev_id); + + if (IS_ERR(dev_info)) { + pr_aud_err("bad dev_id %d\n", dev_id); + rc = -EINVAL; + } else if (!dev_info->dev_ops.enable_sidetone) { + pr_debug("dev %d no sidetone support\n", dev_id); + rc = -EPERM; + } else + rc = dev_info->dev_ops.enable_sidetone(dev_info, enable, gain); + + return rc; +} +EXPORT_SYMBOL(msm_snddev_enable_sidetone); + +int msm_enable_incall_recording(int popp_id, int rec_mode, int rate, + int channel_mode) +{ + int rc = 0; + unsigned int port_id[2]; + port_id[0] = VOICE_RECORD_TX; + port_id[1] = VOICE_RECORD_RX; + + pr_debug("%s: popp_id %d, rec_mode %d, rate %d, channel_mode %d\n", + __func__, popp_id, rec_mode, rate, channel_mode); + + mutex_lock(&routing_info.adm_mutex); + + if (rec_mode == VOC_REC_UPLINK) { + rc = afe_start_pseudo_port(port_id[0]); + if (rc < 0) { + pr_aud_err("%s: Error %d in Tx pseudo port start\n", + __func__, rc); + + goto fail_cmd; + } + + rc = adm_open(port_id[0], LIVE_RECORDING, rate, channel_mode, + DEFAULT_COPP_TOPOLOGY); + if (rc < 0) { + pr_aud_err("%s: Error %d in ADM open %d\n", + __func__, rc, port_id[0]); + + goto fail_cmd; + } + + rc = adm_matrix_map(popp_id, LIVE_RECORDING, 1, + &port_id[0], port_id[0]); + if (rc < 0) { + pr_aud_err("%s: Error %d in ADM matrix map %d\n", + __func__, rc, port_id[0]); + + goto fail_cmd; + } + + msm_set_copp_id(popp_id, port_id[0]); + + } else if (rec_mode == VOC_REC_DOWNLINK) { + rc = afe_start_pseudo_port(port_id[1]); + if (rc < 0) { + pr_aud_err("%s: Error %d in Rx pseudo port start\n", + __func__, rc); + + goto fail_cmd; + } + + rc = adm_open(port_id[1], LIVE_RECORDING, rate, channel_mode, + DEFAULT_COPP_TOPOLOGY); + if (rc < 0) { + pr_aud_err("%s: Error %d in ADM open %d\n", + __func__, rc, port_id[1]); + + goto fail_cmd; + } + + rc = adm_matrix_map(popp_id, LIVE_RECORDING, 1, + &port_id[1], port_id[1]); + if (rc < 0) { + pr_aud_err("%s: Error %d in ADM matrix map %d\n", + __func__, rc, port_id[1]); + + goto fail_cmd; + } + + msm_set_copp_id(popp_id, port_id[1]); + + } else if (rec_mode == VOC_REC_BOTH) { + rc = afe_start_pseudo_port(port_id[0]); + if (rc < 0) { + pr_aud_err("%s: Error %d in Tx pseudo port start\n", + __func__, rc); + + goto fail_cmd; + } + + rc = adm_open(port_id[0], LIVE_RECORDING, rate, channel_mode, + DEFAULT_COPP_TOPOLOGY); + if (rc < 0) { + pr_aud_err("%s: Error %d in ADM open %d\n", + __func__, rc, port_id[0]); + + goto fail_cmd; + } + + msm_set_copp_id(popp_id, port_id[0]); + + rc = afe_start_pseudo_port(port_id[1]); + if (rc < 0) { + pr_aud_err("%s: Error %d in Rx pseudo port start\n", + __func__, rc); + + goto fail_cmd; + } + + rc = adm_open(port_id[1], LIVE_RECORDING, rate, channel_mode, + DEFAULT_COPP_TOPOLOGY); + if (rc < 0) { + pr_aud_err("%s: Error %d in ADM open %d\n", + __func__, rc, port_id[0]); + + goto fail_cmd; + } + + rc = adm_matrix_map(popp_id, LIVE_RECORDING, 2, + &port_id[0], port_id[1]); + if (rc < 0) { + pr_aud_err("%s: Error %d in ADM matrix map\n", + __func__, rc); + + goto fail_cmd; + } + + msm_set_copp_id(popp_id, port_id[1]); + } else { + pr_aud_err("%s Unknown rec_mode %d\n", __func__, rec_mode); + + goto fail_cmd; + } + + rc = voice_start_record(rec_mode, 1); + +fail_cmd: + mutex_unlock(&routing_info.adm_mutex); + return rc; +} + +int msm_disable_incall_recording(uint32_t popp_id, uint32_t rec_mode) +{ + int rc = 0; + uint32_t port_id[2]; + port_id[0] = VOICE_RECORD_TX; + port_id[1] = VOICE_RECORD_RX; + + pr_debug("%s: popp_id %d, rec_mode %d\n", __func__, popp_id, rec_mode); + + mutex_lock(&routing_info.adm_mutex); + + rc = voice_start_record(rec_mode, 0); + if (rc < 0) { + pr_aud_err("%s: Error %d stopping record\n", __func__, rc); + + goto fail_cmd; + } + + if (rec_mode == VOC_REC_UPLINK) { + rc = adm_close(port_id[0]); + if (rc < 0) { + pr_aud_err("%s: Error %d in ADM close %d\n", + __func__, rc, port_id[0]); + + goto fail_cmd; + } + + msm_clear_copp_id(popp_id, port_id[0]); + + rc = afe_stop_pseudo_port(port_id[0]); + if (rc < 0) { + pr_aud_err("%s: Error %d in Tx pseudo port stop\n", + __func__, rc); + goto fail_cmd; + } + + } else if (rec_mode == VOC_REC_DOWNLINK) { + rc = adm_close(port_id[1]); + if (rc < 0) { + pr_aud_err("%s: Error %d in ADM close %d\n", + __func__, rc, port_id[1]); + + goto fail_cmd; + } + + msm_clear_copp_id(popp_id, port_id[1]); + + rc = afe_stop_pseudo_port(port_id[1]); + if (rc < 0) { + pr_aud_err("%s: Error %d in Rx pseudo port stop\n", + __func__, rc); + goto fail_cmd; + } + } else if (rec_mode == VOC_REC_BOTH) { + rc = adm_close(port_id[0]); + if (rc < 0) { + pr_aud_err("%s: Error %d in ADM close %d\n", + __func__, rc, port_id[0]); + + goto fail_cmd; + } + + msm_clear_copp_id(popp_id, port_id[0]); + + rc = afe_stop_pseudo_port(port_id[0]); + if (rc < 0) { + pr_aud_err("%s: Error %d in Tx pseudo port stop\n", + __func__, rc); + goto fail_cmd; + } + + rc = adm_close(port_id[1]); + if (rc < 0) { + pr_aud_err("%s: Error %d in ADM close %d\n", + __func__, rc, port_id[1]); + + goto fail_cmd; + } + + msm_clear_copp_id(popp_id, port_id[1]); + + rc = afe_stop_pseudo_port(port_id[1]); + if (rc < 0) { + pr_aud_err("%s: Error %d in Rx pseudo port stop\n", + __func__, rc); + goto fail_cmd; + } + } else { + pr_aud_err("%s Unknown rec_mode %d\n", __func__, rec_mode); + + goto fail_cmd; + } + +fail_cmd: + mutex_unlock(&routing_info.adm_mutex); + return rc; +} + +static long audio_dev_ctrl_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + int rc = 0; + struct audio_dev_ctrl_state *dev_ctrl = file->private_data; + + mutex_lock(&session_lock); + switch (cmd) { + case AUDIO_GET_NUM_SND_DEVICE: + rc = put_user(dev_ctrl->num_dev, (uint32_t __user *) arg); + break; + case AUDIO_GET_SND_DEVICES: + rc = audio_dev_ctrl_get_devices(dev_ctrl, (void __user *) arg); + break; + case AUDIO_ENABLE_SND_DEVICE: { + struct msm_snddev_info *dev_info; + u32 dev_id; + + if (get_user(dev_id, (u32 __user *) arg)) { + rc = -EFAULT; + break; + } + dev_info = audio_dev_ctrl_find_dev(dev_id); + if (IS_ERR(dev_info)) + rc = PTR_ERR(dev_info); + else { + rc = dev_info->dev_ops.open(dev_info); + if (!rc) + dev_info->opened = 1; + wake_up(&audio_dev_ctrl.wait); + } + break; + + } + + case AUDIO_DISABLE_SND_DEVICE: { + struct msm_snddev_info *dev_info; + u32 dev_id; + + if (get_user(dev_id, (u32 __user *) arg)) { + rc = -EFAULT; + break; + } + dev_info = audio_dev_ctrl_find_dev(dev_id); + if (IS_ERR(dev_info)) + rc = PTR_ERR(dev_info); + else { + rc = dev_info->dev_ops.close(dev_info); + dev_info->opened = 0; + } + break; + } + + case AUDIO_ROUTE_STREAM: { + struct msm_audio_route_config route_cfg; + struct msm_snddev_info *dev_info; + + if (copy_from_user(&route_cfg, (void __user *) arg, + sizeof(struct msm_audio_route_config))) { + rc = -EFAULT; + break; + } + pr_debug("%s: route cfg %d %d type\n", __func__, + route_cfg.dev_id, route_cfg.stream_type); + dev_info = audio_dev_ctrl_find_dev(route_cfg.dev_id); + if (IS_ERR(dev_info)) { + pr_aud_err("%s: pass invalid dev_id\n", __func__); + rc = PTR_ERR(dev_info); + break; + } + + switch (route_cfg.stream_type) { + + case AUDIO_ROUTE_STREAM_VOICE_RX: + if (!(dev_info->capability & SNDDEV_CAP_RX) | + !(dev_info->capability & SNDDEV_CAP_VOICE)) { + rc = -EINVAL; + break; + } + dev_ctrl->voice_rx_dev = dev_info; + break; + case AUDIO_ROUTE_STREAM_VOICE_TX: + if (!(dev_info->capability & SNDDEV_CAP_TX) | + !(dev_info->capability & SNDDEV_CAP_VOICE)) { + rc = -EINVAL; + break; + } + dev_ctrl->voice_tx_dev = dev_info; + break; + } + break; + } + + default: + rc = -EINVAL; + } + mutex_unlock(&session_lock); + return rc; +} + + +static int audio_dev_ctrl_open(struct inode *inode, struct file *file) +{ + pr_debug("open audio_dev_ctrl\n"); + atomic_inc(&audio_dev_ctrl.opened); + file->private_data = &audio_dev_ctrl; + return 0; +} + +static int audio_dev_ctrl_release(struct inode *inode, struct file *file) +{ + pr_debug("release audio_dev_ctrl\n"); + atomic_dec(&audio_dev_ctrl.opened); + return 0; +} + +static const struct file_operations audio_dev_ctrl_fops = { + .owner = THIS_MODULE, + .open = audio_dev_ctrl_open, + .release = audio_dev_ctrl_release, + .unlocked_ioctl = audio_dev_ctrl_ioctl, +}; + + +struct miscdevice audio_dev_ctrl_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_audio_dev_ctrl", + .fops = &audio_dev_ctrl_fops, +}; + +/* session id is 64 bit routing mask per device + * 0-15 for voice clients + * 16-31 for Decoder clients + * 32-47 for Encoder clients + * 48-63 Do not care + */ +void broadcast_event(u32 evt_id, u32 dev_id, u64 session_id) +{ + int clnt_id = 0, i; + union auddev_evt_data *evt_payload = NULL; + struct msm_snd_evt_listner *callback; + struct msm_snddev_info *dev_info = NULL; + u64 session_mask = 0; + static int pending_sent; + + pr_debug(": evt_id = %d\n", evt_id); + + if ((evt_id != AUDDEV_EVT_START_VOICE) + && (evt_id != AUDDEV_EVT_END_VOICE) + && (evt_id != AUDDEV_EVT_STREAM_VOL_CHG) + && (evt_id != AUDDEV_EVT_VOICE_STATE_CHG)) + dev_info = audio_dev_ctrl_find_dev(dev_id); + +#ifdef CONFIG_MSM8X60_RTAC + update_rtac(evt_id, dev_id, dev_info); +#endif + + if (event.cb != NULL) + callback = event.cb; + else + return; + mutex_lock(&session_lock); + + if (evt_id == AUDDEV_EVT_VOICE_STATE_CHG) + routing_info.voice_state = dev_id; + + evt_payload = kzalloc(sizeof(union auddev_evt_data), + GFP_KERNEL); + + if (evt_payload == NULL) { + pr_aud_err("%s: fail to allocate evt_payload", __func__); + return; + } + + for (; ;) { + if (!(evt_id & callback->evt_id)) { + if (callback->cb_next == NULL) + break; + else { + callback = callback->cb_next; + continue; + } + } + clnt_id = callback->clnt_id; + memset(evt_payload, 0, sizeof(union auddev_evt_data)); + + if (evt_id == AUDDEV_EVT_START_VOICE) + routing_info.call_state = 1; + if (evt_id == AUDDEV_EVT_END_VOICE) + routing_info.call_state = 0; + + if ((evt_id == AUDDEV_EVT_START_VOICE) + || (evt_id == AUDDEV_EVT_END_VOICE)) + goto skip_check; + if (callback->clnt_type == AUDDEV_CLNT_AUDIOCAL) + goto aud_cal; + + session_mask = (((u64)0x1) << clnt_id) + << (MAX_BIT_PER_CLIENT * \ + ((int)callback->clnt_type-1)); + + if ((evt_id == AUDDEV_EVT_STREAM_VOL_CHG) || \ + (evt_id == AUDDEV_EVT_VOICE_STATE_CHG)) { + pr_debug("AUDDEV_EVT_STREAM_VOL_CHG or\ + AUDDEV_EVT_VOICE_STATE_CHG\n"); + goto volume_strm; + } + if (dev_info) + pr_debug("dev_info->sessions = %llu\n", dev_info->sessions); + else { + pr_aud_err("dev_info is NULL\n"); + break; + } + if ((!session_id && !(dev_info->sessions & session_mask)) || + (session_id && ((dev_info->sessions & session_mask) != + session_id))) { + if (callback->cb_next == NULL) + break; + else { + callback = callback->cb_next; + continue; + } + } + if (evt_id == AUDDEV_EVT_DEV_CHG_VOICE) + goto voc_events; + +volume_strm: + if (callback->clnt_type == AUDDEV_CLNT_DEC) { + pr_debug("AUDDEV_CLNT_DEC\n"); + if (evt_id == AUDDEV_EVT_STREAM_VOL_CHG) { + pr_debug("clnt_id = %d, session_id = %llu\n", + clnt_id, session_id); + if (session_mask != session_id) + goto sent_dec; + else + evt_payload->session_vol = + msm_vol_ctl.volume; + } else if (evt_id == AUDDEV_EVT_FREQ_CHG) { + if (routing_info.dec_freq[clnt_id].evt) { + routing_info.dec_freq[clnt_id].evt + = 0; + goto sent_dec; + } else if (routing_info.dec_freq[clnt_id].freq + == dev_info->set_sample_rate) + goto sent_dec; + else { + evt_payload->freq_info.sample_rate + = dev_info->set_sample_rate; + evt_payload->freq_info.dev_type + = dev_info->capability; + evt_payload->freq_info.acdb_dev_id + = dev_info->acdb_id; + } + } else if (evt_id == AUDDEV_EVT_VOICE_STATE_CHG) + evt_payload->voice_state = + routing_info.voice_state; + else + evt_payload->routing_id = dev_info->copp_id; + callback->auddev_evt_listener( + evt_id, + evt_payload, + callback->private_data); +sent_dec: + if ((evt_id != AUDDEV_EVT_STREAM_VOL_CHG) && + (evt_id != AUDDEV_EVT_VOICE_STATE_CHG)) + routing_info.dec_freq[clnt_id].freq + = dev_info->set_sample_rate; + + if (callback->cb_next == NULL) + break; + else { + callback = callback->cb_next; + continue; + } + } + if (callback->clnt_type == AUDDEV_CLNT_ENC) { + pr_debug("AUDDEV_CLNT_ENC\n"); + if (evt_id == AUDDEV_EVT_FREQ_CHG) { + if (routing_info.enc_freq[clnt_id].evt) { + routing_info.enc_freq[clnt_id].evt + = 0; + goto sent_enc; + } else { + evt_payload->freq_info.sample_rate + = dev_info->set_sample_rate; + evt_payload->freq_info.dev_type + = dev_info->capability; + evt_payload->freq_info.acdb_dev_id + = dev_info->acdb_id; + } + } else if (evt_id == AUDDEV_EVT_VOICE_STATE_CHG) + evt_payload->voice_state = + routing_info.voice_state; + else { + if (dev_info) + evt_payload->routing_id = dev_info->copp_id; + else + pr_aud_info("dev_info == NULL\n"); + } + callback->auddev_evt_listener( + evt_id, + evt_payload, + callback->private_data); +sent_enc: + if (callback->cb_next == NULL) + break; + else { + callback = callback->cb_next; + continue; + } + } +aud_cal: + if (callback->clnt_type == AUDDEV_CLNT_AUDIOCAL) { + pr_debug("AUDDEV_CLNT_AUDIOCAL\n"); + if (evt_id == AUDDEV_EVT_VOICE_STATE_CHG) + evt_payload->voice_state = + routing_info.voice_state; + else if (!dev_info->sessions) + goto sent_aud_cal; + else { + evt_payload->audcal_info.dev_id = + dev_info->copp_id; + evt_payload->audcal_info.acdb_id = + dev_info->acdb_id; + evt_payload->audcal_info.dev_type = + (dev_info->capability & SNDDEV_CAP_TX) ? + SNDDEV_CAP_TX : SNDDEV_CAP_RX; + evt_payload->audcal_info.sample_rate = + dev_info->set_sample_rate ? + dev_info->set_sample_rate : + dev_info->sample_rate; + } + callback->auddev_evt_listener( + evt_id, + evt_payload, + callback->private_data); + +sent_aud_cal: + if (callback->cb_next == NULL) + break; + else { + callback = callback->cb_next; + continue; + } + } +skip_check: +voc_events: + if (callback->clnt_type == AUDDEV_CLNT_VOC) { + pr_debug("AUDDEV_CLNT_VOC\n"); + if (evt_id == AUDDEV_EVT_DEV_RLS) { + if (!pending_sent) + goto sent_voc; + else + pending_sent = 0; + } + if (evt_id == AUDDEV_EVT_REL_PENDING) + pending_sent = 1; + + if (evt_id == AUDDEV_EVT_DEVICE_VOL_MUTE_CHG) { + if (dev_info->capability & SNDDEV_CAP_TX) { + evt_payload->voc_vm_info.dev_type = + SNDDEV_CAP_TX; + evt_payload->voc_vm_info.acdb_dev_id = + dev_info->acdb_id; + evt_payload-> + voc_vm_info.dev_vm_val.mute = + routing_info.tx_mute; + } else { + evt_payload->voc_vm_info.dev_type = + SNDDEV_CAP_RX; + evt_payload->voc_vm_info.acdb_dev_id = + dev_info->acdb_id; + evt_payload-> + voc_vm_info.dev_vm_val.vol = + routing_info.voice_rx_vol; + } + } else if ((evt_id == AUDDEV_EVT_START_VOICE) + || (evt_id == AUDDEV_EVT_END_VOICE)) + memset(evt_payload, 0, + sizeof(union auddev_evt_data)); + else if (evt_id == AUDDEV_EVT_FREQ_CHG) { + if (routing_info.voice_tx_sample_rate + != dev_info->set_sample_rate) { + routing_info.voice_tx_sample_rate + = dev_info->set_sample_rate; + evt_payload->freq_info.sample_rate + = dev_info->set_sample_rate; + evt_payload->freq_info.dev_type + = dev_info->capability; + evt_payload->freq_info.acdb_dev_id + = dev_info->acdb_id; + } else + goto sent_voc; + } else if (evt_id == AUDDEV_EVT_VOICE_STATE_CHG) + evt_payload->voice_state = + routing_info.voice_state; + else { + evt_payload->voc_devinfo.dev_type = + (dev_info->capability & SNDDEV_CAP_TX) ? + SNDDEV_CAP_TX : SNDDEV_CAP_RX; + evt_payload->voc_devinfo.acdb_dev_id = + dev_info->acdb_id; + evt_payload->voc_devinfo.dev_port_id = + dev_info->copp_id; + evt_payload->voc_devinfo.dev_sample = + dev_info->set_sample_rate ? + dev_info->set_sample_rate : + dev_info->sample_rate; + evt_payload->voc_devinfo.dev_id = dev_id; + if (dev_info->capability & SNDDEV_CAP_RX) { + for (i = 0; i < VOC_RX_VOL_ARRAY_NUM; + i++) { + evt_payload-> + voc_devinfo.max_rx_vol[i] = + dev_info->max_voc_rx_vol[i]; + evt_payload + ->voc_devinfo.min_rx_vol[i] = + dev_info->min_voc_rx_vol[i]; + } + } + } + callback->auddev_evt_listener( + evt_id, + evt_payload, + callback->private_data); + if (evt_id == AUDDEV_EVT_DEV_RLS) + dev_info->sessions &= ~(0xFFFF); +sent_voc: + if (callback->cb_next == NULL) + break; + else { + callback = callback->cb_next; + continue; + } + } + } + kfree(evt_payload); + mutex_unlock(&session_lock); +} +EXPORT_SYMBOL(broadcast_event); + + +void mixer_post_event(u32 evt_id, u32 id) +{ + + pr_debug("evt_id = %d\n", evt_id); + switch (evt_id) { + case AUDDEV_EVT_DEV_CHG_VOICE: /* Called from Voice_route */ + broadcast_event(AUDDEV_EVT_DEV_CHG_VOICE, id, SESSION_IGNORE); + break; + case AUDDEV_EVT_DEV_RDY: + broadcast_event(AUDDEV_EVT_DEV_RDY, id, SESSION_IGNORE); + break; + case AUDDEV_EVT_DEV_RLS: + broadcast_event(AUDDEV_EVT_DEV_RLS, id, SESSION_IGNORE); + break; + case AUDDEV_EVT_REL_PENDING: + broadcast_event(AUDDEV_EVT_REL_PENDING, id, SESSION_IGNORE); + break; + case AUDDEV_EVT_DEVICE_VOL_MUTE_CHG: + broadcast_event(AUDDEV_EVT_DEVICE_VOL_MUTE_CHG, id, + SESSION_IGNORE); + break; + case AUDDEV_EVT_STREAM_VOL_CHG: + broadcast_event(AUDDEV_EVT_STREAM_VOL_CHG, id, + SESSION_IGNORE); + break; + case AUDDEV_EVT_START_VOICE: + broadcast_event(AUDDEV_EVT_START_VOICE, + id, SESSION_IGNORE); + break; + case AUDDEV_EVT_END_VOICE: + broadcast_event(AUDDEV_EVT_END_VOICE, + id, SESSION_IGNORE); + break; + case AUDDEV_EVT_FREQ_CHG: + broadcast_event(AUDDEV_EVT_FREQ_CHG, id, SESSION_IGNORE); + break; + default: + break; + } +} +EXPORT_SYMBOL(mixer_post_event); + +static int __init audio_dev_ctrl_init(void) +{ + init_waitqueue_head(&audio_dev_ctrl.wait); + + event.cb = NULL; + + atomic_set(&audio_dev_ctrl.opened, 0); + audio_dev_ctrl.num_dev = 0; + audio_dev_ctrl.voice_tx_dev = NULL; + audio_dev_ctrl.voice_rx_dev = NULL; + routing_info.voice_state = VOICE_STATE_INVALID; + routing_info.call_state = 0; + mutex_init(&adm_tx_topology_tbl.lock); + mutex_init(&routing_info.copp_list_mutex); + mutex_init(&routing_info.adm_mutex); + + memset(routing_info.copp_list, DEVICE_IGNORE, + (sizeof(unsigned int) * MAX_SESSIONS * AFE_MAX_PORTS)); + return misc_register(&audio_dev_ctrl_misc); +} + +static void __exit audio_dev_ctrl_exit(void) +{ +} +module_init(audio_dev_ctrl_init); +module_exit(audio_dev_ctrl_exit); + +MODULE_DESCRIPTION("MSM 8K Audio Device Control driver"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/qdsp6v3/audio_lpa.c b/arch/arm/mach-msm/qdsp6v3/audio_lpa.c new file mode 100644 index 00000000000..7845a35b3fc --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v3/audio_lpa.c @@ -0,0 +1,1435 @@ +/* low power audio output device + * + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * 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 + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT 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 "audio_lpa.h" + +#include +#include +#include + +#include +#include + +#define MAX_BUF 4 +#define BUFSZ (655360) + +#define AUDDEC_DEC_PCM 0 + +#define AUDLPA_EVENT_NUM 10 /* Default number of pre-allocated event packets */ + +#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 - 1; \ + int res = (IN_RANGE(__r1, __v) || IN_RANGE(__r1, __e)); \ + res; \ +}) + +struct audlpa_event { + struct list_head list; + int event_type; + union msm_audio_event_payload payload; +}; + +struct audlpa_pmem_region { + struct list_head list; + struct file *file; + int fd; + void *vaddr; + unsigned long paddr; + unsigned long kvaddr; + unsigned long len; + unsigned ref_cnt; +}; + +struct audlpa_buffer_node { + struct list_head list; + struct msm_audio_aio_buf buf; + unsigned long paddr; +}; + +struct audlpa_dec { + char *name; + int dec_attrb; + long (*ioctl)(struct file *, unsigned int, unsigned long); + int (*set_params)(void *); +}; + +static void audlpa_post_event(struct audio *audio, int type, + union msm_audio_event_payload payload); +static unsigned long audlpa_pmem_fixup(struct audio *audio, void *addr, + unsigned long len, int ref_up); +static void audlpa_async_send_data(struct audio *audio, unsigned needed, + uint32_t token); +static int audlpa_pause(struct audio *audio); +static void audlpa_unmap_pmem_region(struct audio *audio); +static long pcm_ioctl(struct file *file, unsigned int cmd, unsigned long arg); +static int audlpa_set_pcm_params(void *data); + +struct audlpa_dec audlpa_decs[] = { + {"msm_pcm_lp_dec", AUDDEC_DEC_PCM, &pcm_ioctl, + &audlpa_set_pcm_params}, +}; + +static void lpa_listner(u32 evt_id, union auddev_evt_data *evt_payload, + void *private_data) +{ + struct audio *audio = (struct audio *) private_data; + int rc = 0; + + switch (evt_id) { + case AUDDEV_EVT_STREAM_VOL_CHG: + audio->volume = evt_payload->session_vol; + pr_debug("%s: AUDDEV_EVT_STREAM_VOL_CHG, stream vol %d, " + "enabled = %d\n", __func__, audio->volume, + audio->out_enabled); + if (audio->out_enabled == 1) { + if (audio->ac) { + rc = q6asm_set_volume(audio->ac, audio->volume); + if (rc < 0) { + pr_aud_err("%s: Send Volume command failed" + " rc=%d\n", __func__, rc); + } + } + } + break; + default: + pr_aud_err("%s:ERROR:wrong event\n", __func__); + break; + } +} + +static void audlpa_prevent_sleep(struct audio *audio) +{ + pr_debug("%s:\n", __func__); + wake_lock(&audio->wakelock); +} + +static void audlpa_allow_sleep(struct audio *audio) +{ + pr_debug("%s:\n", __func__); + wake_unlock(&audio->wakelock); +} + +/* must be called with audio->lock held */ +static int audio_enable(struct audio *audio) +{ + pr_aud_info("%s\n", __func__); + + return q6asm_run(audio->ac, 0, 0, 0); + +} + +static int audlpa_async_flush(struct audio *audio) +{ + struct audlpa_buffer_node *buf_node; + struct list_head *ptr, *next; + union msm_audio_event_payload payload; + int rc = 0; + + pr_aud_info("%s:out_enabled = %d, drv_status = 0x%x\n", __func__, + audio->out_enabled, audio->drv_status); + if (audio->out_enabled) { + list_for_each_safe(ptr, next, &audio->out_queue) { + buf_node = list_entry(ptr, struct audlpa_buffer_node, + list); + list_del(&buf_node->list); + payload.aio_buf = buf_node->buf; + audlpa_post_event(audio, AUDIO_EVENT_WRITE_DONE, + payload); + kfree(buf_node); + } + /* Implicitly issue a pause to the decoder before flushing if + it is not in pause state */ + if (!(audio->drv_status & ADRV_STATUS_PAUSE)) { + rc = audlpa_pause(audio); + if (rc < 0) + pr_aud_err("%s: pause cmd failed rc=%d\n", __func__, + rc); + } + + rc = q6asm_cmd(audio->ac, CMD_FLUSH); + if (rc < 0) + pr_aud_err("%s: flush cmd failed rc=%d\n", __func__, rc); + + audio->drv_status &= ~ADRV_STATUS_OBUF_GIVEN; + audio->out_needed = 0; + + if (audio->stopped == 0) { + rc = audio_enable(audio); + if (rc < 0) + pr_aud_err("%s: audio enable failed\n", __func__); + else { + audio->out_enabled = 1; + audio->out_needed = 1; + if (audio->drv_status & ADRV_STATUS_PAUSE) + audio->drv_status &= ~ADRV_STATUS_PAUSE; + } + } + wake_up(&audio->write_wait); + } + return rc; +} + +/* must be called with audio->lock held */ +static int audio_disable(struct audio *audio) +{ + int rc = 0; + + pr_aud_info("%s:%d %d\n", __func__, audio->opened, audio->out_enabled); + + if (audio->opened) { + audio->out_enabled = 0; + audio->opened = 0; + rc = q6asm_cmd(audio->ac, CMD_CLOSE); + if (rc < 0) + pr_aud_err("%s: CLOSE cmd failed\n", __func__); + else + pr_debug("%s: rxed CLOSE resp\n", __func__); + audio->drv_status &= ~ADRV_STATUS_OBUF_GIVEN; + wake_up(&audio->write_wait); + audio->out_needed = 0; + } + return rc; +} +static int audlpa_pause(struct audio *audio) +{ + int rc = 0; + + pr_aud_info("%s, enabled = %d\n", __func__, + audio->out_enabled); + if (audio->out_enabled) { + rc = q6asm_cmd(audio->ac, CMD_PAUSE); + if (rc < 0) + pr_aud_err("%s: pause cmd failed rc=%d\n", __func__, rc); + + } else + pr_aud_err("%s: Driver not enabled\n", __func__); + return rc; +} + +/* ------------------- dsp --------------------- */ +static void audlpa_async_send_data(struct audio *audio, unsigned needed, + uint32_t token) +{ + unsigned long flags; + struct audio_client *ac; + int rc = 0; + + pr_debug("%s:\n", __func__); + spin_lock_irqsave(&audio->dsp_lock, flags); + + pr_debug("%s: needed = %d, out_needed = %d, token = 0x%x\n", + __func__, needed, audio->out_needed, token); + if (needed && !audio->wflush) { + audio->out_needed = 1; + if (audio->drv_status & ADRV_STATUS_OBUF_GIVEN) { + /* pop one node out of queue */ + union msm_audio_event_payload evt_payload; + struct audlpa_buffer_node *used_buf; + + used_buf = list_first_entry(&audio->out_queue, + struct audlpa_buffer_node, list); + if (token == used_buf->paddr) { + pr_debug("%s, Release: addr: %lx," + " token = 0x%x\n", __func__, + used_buf->paddr, token); + list_del(&used_buf->list); + evt_payload.aio_buf = used_buf->buf; + audlpa_post_event(audio, AUDIO_EVENT_WRITE_DONE, + evt_payload); + kfree(used_buf); + audio->drv_status &= ~ADRV_STATUS_OBUF_GIVEN; + } + } + } + pr_debug("%s: out_needed = %d, stopped = %d, drv_status = 0x%x\n", + __func__, audio->out_needed, audio->stopped, + audio->drv_status); + if (audio->out_needed && (audio->stopped == 0)) { + struct audlpa_buffer_node *next_buf; + struct audio_aio_write_param param; + if (!list_empty(&audio->out_queue)) { + pr_debug("%s: list not empty\n", __func__); + next_buf = list_first_entry(&audio->out_queue, + struct audlpa_buffer_node, list); + if (next_buf) { + pr_debug("%s: Send: addr: %lx\n", __func__, + next_buf->paddr); + ac = audio->ac; + param.paddr = next_buf->paddr; + param.len = next_buf->buf.data_len; + param.msw_ts = 0; + param.lsw_ts = 0; + /* No time stamp valid */ + param.flags = NO_TIMESTAMP; + param.uid = next_buf->paddr; + rc = q6asm_async_write(ac, ¶m); + if (rc < 0) + pr_aud_err("%s:q6asm_async_write failed\n", + __func__); + audio->out_needed = 0; + audio->drv_status |= ADRV_STATUS_OBUF_GIVEN; + } + } else if (list_empty(&audio->out_queue) && + (audio->drv_status & ADRV_STATUS_FSYNC)) { + pr_debug("%s: list is empty, reached EOS\n", __func__); + wake_up(&audio->write_wait); + } + } + + spin_unlock_irqrestore(&audio->dsp_lock, flags); +} + +static int audlpa_events_pending(struct audio *audio) +{ + int empty; + + spin_lock(&audio->event_queue_lock); + empty = !list_empty(&audio->event_queue); + spin_unlock(&audio->event_queue_lock); + return empty || audio->event_abort; +} + +static void audlpa_reset_event_queue(struct audio *audio) +{ + struct audlpa_event *drv_evt; + struct list_head *ptr, *next; + + spin_lock(&audio->event_queue_lock); + list_for_each_safe(ptr, next, &audio->event_queue) { + drv_evt = list_first_entry(&audio->event_queue, + struct audlpa_event, list); + list_del(&drv_evt->list); + kfree(drv_evt); + } + list_for_each_safe(ptr, next, &audio->free_event_queue) { + drv_evt = list_first_entry(&audio->free_event_queue, + struct audlpa_event, list); + list_del(&drv_evt->list); + kfree(drv_evt); + } + spin_unlock(&audio->event_queue_lock); + + return; +} + +static long audlpa_process_event_req(struct audio *audio, void __user *arg) +{ + long rc; + struct msm_audio_event usr_evt; + struct audlpa_event *drv_evt = NULL; + int timeout; + + if (copy_from_user(&usr_evt, arg, sizeof(struct msm_audio_event))) + return -EFAULT; + + timeout = (int) usr_evt.timeout_ms; + + if (timeout > 0) { + rc = wait_event_interruptible_timeout( + audio->event_wait, audlpa_events_pending(audio), + msecs_to_jiffies(timeout)); + if (rc == 0) + return -ETIMEDOUT; + } else { + rc = wait_event_interruptible( + audio->event_wait, audlpa_events_pending(audio)); + } + + if (rc < 0) + return rc; + + if (audio->event_abort) { + audio->event_abort = 0; + return -ENODEV; + } + + rc = 0; + + spin_lock(&audio->event_queue_lock); + if (!list_empty(&audio->event_queue)) { + drv_evt = list_first_entry(&audio->event_queue, + struct audlpa_event, list); + list_del(&drv_evt->list); + } + if (drv_evt) { + usr_evt.event_type = drv_evt->event_type; + usr_evt.event_payload = drv_evt->payload; + list_add_tail(&drv_evt->list, &audio->free_event_queue); + } else { + rc = -1; + spin_unlock(&audio->event_queue_lock); + return rc; + } + spin_unlock(&audio->event_queue_lock); + + if (drv_evt->event_type == AUDIO_EVENT_WRITE_DONE || + drv_evt->event_type == AUDIO_EVENT_READ_DONE) { + pr_debug("%s: AUDIO_EVENT_WRITE_DONE completing\n", __func__); + mutex_lock(&audio->lock); + audlpa_pmem_fixup(audio, drv_evt->payload.aio_buf.buf_addr, + drv_evt->payload.aio_buf.buf_len, 0); + mutex_unlock(&audio->lock); + } + if (!rc && copy_to_user(arg, &usr_evt, sizeof(usr_evt))) + rc = -EFAULT; + + return rc; +} + +static int audlpa_pmem_check(struct audio *audio, + void *vaddr, unsigned long len) +{ + struct audlpa_pmem_region *region_elt; + struct audlpa_pmem_region t = { .vaddr = vaddr, .len = len }; + + list_for_each_entry(region_elt, &audio->pmem_region_queue, list) { + if (CONTAINS(region_elt, &t) || CONTAINS(&t, region_elt) || + OVERLAPS(region_elt, &t)) { + pr_aud_err("%s: region (vaddr %p len %ld)" + " clashes with registered region" + " (vaddr %p paddr %p len %ld)\n", + __func__, vaddr, len, + region_elt->vaddr, + (void *)region_elt->paddr, + region_elt->len); + return -EINVAL; + } + } + + return 0; +} + +static int audlpa_pmem_add(struct audio *audio, + struct msm_audio_pmem_info *info) +{ + unsigned long paddr, kvaddr, len; + struct file *file; + struct audlpa_pmem_region *region; + int rc = -EINVAL; + + pr_aud_info("%s:\n", __func__); + region = kmalloc(sizeof(*region), GFP_KERNEL); + + if (!region) { + rc = -ENOMEM; + goto end; + } + + if (get_pmem_file(info->fd, &paddr, &kvaddr, &len, &file)) { + kfree(region); + goto end; + } + + rc = audlpa_pmem_check(audio, info->vaddr, len); + if (rc < 0) { + put_pmem_file(file); + kfree(region); + goto end; + } + + region->vaddr = info->vaddr; + region->fd = info->fd; + region->paddr = paddr; + region->kvaddr = kvaddr; + region->len = len; + region->file = file; + region->ref_cnt = 0; + pr_debug("%s: add region paddr %lx vaddr %p, len %lu\n", __func__, + region->paddr, region->vaddr, + region->len); + list_add_tail(®ion->list, &audio->pmem_region_queue); + rc = q6asm_memory_map(audio->ac, (uint32_t)paddr, IN, (uint32_t)len, 1); + if (rc < 0) + pr_aud_err("%s: memory map failed\n", __func__); +end: + return rc; +} + +static int audlpa_pmem_remove(struct audio *audio, + struct msm_audio_pmem_info *info) +{ + struct audlpa_pmem_region *region; + struct list_head *ptr, *next; + int rc = -EINVAL; + + list_for_each_safe(ptr, next, &audio->pmem_region_queue) { + region = list_entry(ptr, struct audlpa_pmem_region, list); + + if ((region != NULL) && (region->fd == info->fd) && + (region->vaddr == info->vaddr)) { + if (region->ref_cnt) { + pr_debug("%s: region %p in use ref_cnt %d\n", + __func__, region, region->ref_cnt); + break; + } + rc = q6asm_memory_unmap(audio->ac, + (uint32_t)region->paddr, + IN); + if (rc < 0) + pr_aud_err("%s: memory unmap failed\n", __func__); + + list_del(®ion->list); + put_pmem_file(region->file); + kfree(region); + rc = 0; + break; + } + } + + return rc; +} + +static int audlpa_pmem_lookup_vaddr(struct audio *audio, void *addr, + unsigned long len, struct audlpa_pmem_region **region) +{ + struct audlpa_pmem_region *region_elt; + + int match_count = 0; + + *region = NULL; + + /* returns physical address or zero */ + list_for_each_entry(region_elt, &audio->pmem_region_queue, + list) { + if (addr >= region_elt->vaddr && + addr < region_elt->vaddr + region_elt->len && + addr + 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) { + pr_aud_err("%s: multiple hits for vaddr %p, len %ld\n", __func__, + addr, len); + list_for_each_entry(region_elt, + &audio->pmem_region_queue, list) { + if (addr >= region_elt->vaddr && + addr < region_elt->vaddr + region_elt->len && + addr + len <= region_elt->vaddr + region_elt->len) + pr_aud_err("%s: \t%p, %ld --> %p\n", __func__, + region_elt->vaddr, region_elt->len, + (void *)region_elt->paddr); + } + } + + return *region ? 0 : -1; +} + +unsigned long audlpa_pmem_fixup(struct audio *audio, void *addr, + unsigned long len, int ref_up) +{ + struct audlpa_pmem_region *region; + unsigned long paddr; + int ret; + + ret = audlpa_pmem_lookup_vaddr(audio, addr, len, ®ion); + if (ret) { + pr_aud_err("%s: lookup (%p, %ld) failed\n", __func__, addr, len); + return 0; + } + if (ref_up) + region->ref_cnt++; + else + region->ref_cnt--; + paddr = region->paddr + (addr - region->vaddr); + return paddr; +} + +/* audio -> lock must be held at this point */ +static int audlpa_aio_buf_add(struct audio *audio, unsigned dir, + void __user *arg) +{ + struct audlpa_buffer_node *buf_node; + + buf_node = kmalloc(sizeof(*buf_node), GFP_KERNEL); + + if (!buf_node) + return -ENOMEM; + + if (copy_from_user(&buf_node->buf, arg, sizeof(buf_node->buf))) { + kfree(buf_node); + return -EFAULT; + } + + buf_node->paddr = audlpa_pmem_fixup( + audio, buf_node->buf.buf_addr, + buf_node->buf.buf_len, 1); + if (dir) { + /* write */ + if (!buf_node->paddr || + (buf_node->paddr & 0x1) || + (buf_node->buf.data_len & 0x1)) { + kfree(buf_node); + return -EINVAL; + } + list_add_tail(&buf_node->list, &audio->out_queue); + pr_debug("%s, Added to list: addr: %lx, length = %d\n", + __func__, buf_node->paddr, buf_node->buf.data_len); + audlpa_async_send_data(audio, 0, 0); + } else { + /* read */ + } + return 0; +} + +static int config(struct audio *audio) +{ + int rc = 0; + if (!audio->out_prefill) { + if (audio->codec_ops.set_params != NULL) { + rc = audio->codec_ops.set_params(audio); + audio->out_prefill = 1; + } + } + return rc; +} + +void q6_audlpa_out_cb(uint32_t opcode, uint32_t token, + uint32_t *payload, void *priv) +{ + struct audio *audio = (struct audio *) priv; + + switch (opcode) { + case ASM_DATA_EVENT_WRITE_DONE: + pr_debug("%s: ASM_DATA_EVENT_WRITE_DONE, token = 0x%x\n", + __func__, token); + audlpa_async_send_data(audio, 1, token); + break; + case ASM_DATA_EVENT_EOS: + case ASM_DATA_CMDRSP_EOS: + pr_debug("%s: ASM_DATA_CMDRSP_EOS, teos = %d\n", __func__, + audio->teos); + if (audio->teos == 0) { + audio->teos = 1; + wake_up(&audio->write_wait); + } + break; + case ASM_SESSION_CMDRSP_GET_SESSION_TIME: + break; + default: + break; + } +} + +static long pcm_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + pr_debug("%s: cmd = %d\n", __func__, cmd); + return -EINVAL; +} + +static int audlpa_set_pcm_params(void *data) +{ + struct audio *audio = (struct audio *)data; + int rc; + + rc = q6asm_media_format_block_pcm(audio->ac, audio->out_sample_rate, + audio->out_channel_mode); + if (rc < 0) + pr_aud_err("%s: Format block pcm failed\n", __func__); + return rc; +} + +static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct audio *audio = file->private_data; + int rc = -EINVAL; + uint64_t timestamp = 0; + uint64_t temp; + + pr_debug("%s: audio_ioctl() cmd = %d\n", __func__, cmd); + + if (cmd == AUDIO_GET_STATS) { + struct msm_audio_stats stats; + + pr_aud_info("%s: audio_get_stats command\n", __func__); + memset(&stats, 0, sizeof(stats)); + timestamp = q6asm_get_session_time(audio->ac); + if (timestamp < 0) { + pr_aud_err("%s: Get Session Time return value =%lld\n", + __func__, timestamp); + return -EAGAIN; + } + temp = (timestamp * 2 * audio->out_channel_mode); + temp = temp * (audio->out_sample_rate/1000); + temp = div_u64(temp, 1000); + audio->bytes_consumed = (uint32_t)(temp & 0xFFFFFFFF); + stats.byte_count = audio->bytes_consumed; + stats.unused[0] = (uint32_t)((temp >> 32) & 0xFFFFFFFF); + pr_debug("%s: bytes_consumed:lsb = %d, msb = %d," + "timestamp = %lld\n", __func__, + audio->bytes_consumed, stats.unused[0], timestamp); + if (copy_to_user((void *) arg, &stats, sizeof(stats))) + return -EFAULT; + return 0; + } + + switch (cmd) { + case AUDIO_ENABLE_AUDPP: + break; + + case AUDIO_SET_VOLUME: + break; + + case AUDIO_SET_PAN: + break; + + case AUDIO_SET_EQ: + break; + } + + if (cmd == AUDIO_GET_EVENT) { + pr_debug("%s: AUDIO_GET_EVENT\n", __func__); + if (mutex_trylock(&audio->get_event_lock)) { + rc = audlpa_process_event_req(audio, + (void __user *) arg); + mutex_unlock(&audio->get_event_lock); + } else + rc = -EBUSY; + return rc; + } + + if (cmd == AUDIO_ABORT_GET_EVENT) { + audio->event_abort = 1; + wake_up(&audio->event_wait); + return 0; + } + + mutex_lock(&audio->lock); + switch (cmd) { + case AUDIO_START: + pr_aud_info("%s: AUDIO_START: Session %d\n", __func__, + audio->ac->session); + if (!audio->opened) { + pr_aud_err("%s: Driver not opened\n", __func__); + rc = -EFAULT; + goto fail; + } + rc = config(audio); + if (rc) { + pr_aud_err("%s: Out Configuration failed\n", __func__); + rc = -EFAULT; + goto fail; + } + + rc = audio_enable(audio); + if (rc) { + pr_aud_err("%s: audio enable failed\n", __func__); + rc = -EFAULT; + goto fail; + } else { + struct asm_softpause_params param = { + .enable = SOFT_PAUSE_ENABLE, + .period = SOFT_PAUSE_PERIOD, + .step = SOFT_PAUSE_STEP, + .rampingcurve = SOFT_PAUSE_CURVE_LINEAR, + }; + audio->out_enabled = 1; + audio->out_needed = 1; + rc = q6asm_set_volume(audio->ac, audio->volume); + if (rc < 0) + pr_aud_err("%s: Send Volume command failed rc=%d\n", + __func__, rc); + rc = q6asm_set_softpause(audio->ac, ¶m); + if (rc < 0) + pr_aud_err("%s: Send SoftPause Param failed rc=%d\n", + __func__, rc); + rc = q6asm_set_lrgain(audio->ac, 0x2000, 0x2000); + if (rc < 0) + pr_aud_err("%s: Send channel gain failed rc=%d\n", + __func__, rc); + /* disable mute by default */ + rc = q6asm_set_mute(audio->ac, 0); + if (rc < 0) + pr_aud_err("%s: Send mute command failed rc=%d\n", + __func__, rc); + if (!list_empty(&audio->out_queue)) + pr_aud_err("%s: write_list is not empty!!!\n", + __func__); + if (audio->stopped == 1) + audio->stopped = 0; + audlpa_prevent_sleep(audio); + } + break; + + case AUDIO_STOP: + pr_aud_info("%s: AUDIO_STOP: session_id:%d\n", __func__, + audio->ac->session); + audio->stopped = 1; + audlpa_async_flush(audio); + audio->out_enabled = 0; + audio->out_needed = 0; + audio->drv_status &= ~ADRV_STATUS_PAUSE; + audlpa_allow_sleep(audio); + break; + + case AUDIO_FLUSH: + pr_aud_info("%s: AUDIO_FLUSH: session_id:%d\n", __func__, + audio->ac->session); + audio->wflush = 1; + if (audio->out_enabled) + rc = audlpa_async_flush(audio); + else + audio->wflush = 0; + audio->wflush = 0; + break; + + case AUDIO_SET_CONFIG:{ + struct msm_audio_config config; + pr_aud_info("%s: AUDIO_SET_CONFIG\n", __func__); + if (copy_from_user(&config, (void *) arg, sizeof(config))) { + rc = -EFAULT; + pr_aud_err("%s: ERROR: copy from user\n", __func__); + break; + } + if (!((config.channel_count == 1) || + (config.channel_count == 2))) { + rc = -EINVAL; + pr_aud_err("%s: ERROR: config.channel_count == %d\n", + __func__, config.channel_count); + break; + } + + if (!((config.bits == 8) || (config.bits == 16) || + (config.bits == 24))) { + rc = -EINVAL; + pr_aud_err("%s: ERROR: config.bits = %d\n", __func__, + config.bits); + break; + } + audio->out_sample_rate = config.sample_rate; + audio->out_channel_mode = config.channel_count; + audio->out_bits = config.bits; + audio->buffer_count = config.buffer_count; + audio->buffer_size = config.buffer_size; + rc = 0; + break; + } + + case AUDIO_GET_CONFIG:{ + struct msm_audio_config config; + config.buffer_count = audio->buffer_count; + config.buffer_size = audio->buffer_size; + config.sample_rate = audio->out_sample_rate; + config.channel_count = audio->out_channel_mode; + config.bits = audio->out_bits; + + config.meta_field = 0; + config.unused[0] = 0; + config.unused[1] = 0; + config.unused[2] = 0; + if (copy_to_user((void *) arg, &config, sizeof(config))) + rc = -EFAULT; + else + rc = 0; + break; + } + + case AUDIO_PAUSE: + pr_aud_info("%s: AUDIO_PAUSE %ld\n", __func__, arg); + if (arg == 1) { + rc = audlpa_pause(audio); + if (rc < 0) + pr_aud_err("%s: pause FAILED rc=%d\n", __func__, + rc); + audio->drv_status |= ADRV_STATUS_PAUSE; + } else if (arg == 0) { + if (audio->drv_status & ADRV_STATUS_PAUSE) { + rc = audio_enable(audio); + if (rc) + pr_aud_err("%s: audio enable failed\n", + __func__); + else { + audio->drv_status &= ~ADRV_STATUS_PAUSE; + audio->out_enabled = 1; + } + } + } + break; + + case AUDIO_REGISTER_PMEM: { + struct msm_audio_pmem_info info; + pr_aud_info("%s: AUDIO_REGISTER_PMEM\n", __func__); + if (copy_from_user(&info, (void *) arg, sizeof(info))) + rc = -EFAULT; + else + rc = audlpa_pmem_add(audio, &info); + break; + } + + case AUDIO_DEREGISTER_PMEM: { + struct msm_audio_pmem_info info; + pr_aud_info("%s: AUDIO_DEREGISTER_PMEM\n", __func__); + if (copy_from_user(&info, (void *) arg, sizeof(info))) + rc = -EFAULT; + else + rc = audlpa_pmem_remove(audio, &info); + break; + } + case AUDIO_ASYNC_WRITE: + pr_debug("%s: AUDIO_ASYNC_WRITE\n", __func__); + if (audio->drv_status & ADRV_STATUS_FSYNC) + rc = -EBUSY; + else + rc = audlpa_aio_buf_add(audio, 1, (void __user *) arg); + break; + + case AUDIO_GET_SESSION_ID: + if (copy_to_user((void *) arg, &audio->ac->session, + sizeof(unsigned short))) + return -EFAULT; + rc = 0; + break; + + default: + rc = audio->codec_ops.ioctl(file, cmd, arg); + } +fail: + mutex_unlock(&audio->lock); + return rc; +} + +/* Only useful in tunnel-mode */ +int audlpa_async_fsync(struct audio *audio) +{ + int rc = 0; + + pr_aud_info("%s:Session %d\n", __func__, audio->ac->session); + + /* Blocking client sends more data */ + mutex_lock(&audio->lock); + audio->drv_status |= ADRV_STATUS_FSYNC; + mutex_unlock(&audio->lock); + + mutex_lock(&audio->write_lock); + audio->teos = 0; + + rc = wait_event_interruptible(audio->write_wait, + ((list_empty(&audio->out_queue)) || + audio->wflush || audio->stopped)); + + if (audio->wflush || audio->stopped) + goto flush_event; + + if (rc < 0) { + pr_aud_err("%s: wait event for list_empty failed, rc = %d\n", + __func__, rc); + goto done; + } + + rc = q6asm_cmd(audio->ac, CMD_EOS); + + if (rc < 0) { + pr_aud_err("%s: q6asm_cmd failed, rc = %d", __func__, rc); + goto done; + } + rc = wait_event_interruptible_timeout(audio->write_wait, + (audio->teos || audio->wflush || + audio->stopped), 5*HZ); + + if (rc < 0) { + pr_aud_err("%s: wait event for teos failed, rc = %d\n", __func__, + rc); + goto done; + } + + if (audio->teos == 1) { + rc = audio_enable(audio); + if (rc) + pr_aud_err("%s: audio enable failed\n", __func__); + else { + audio->drv_status &= ~ADRV_STATUS_PAUSE; + audio->out_enabled = 1; + audio->out_needed = 1; + } + } + +flush_event: + if (audio->stopped || audio->wflush) + rc = -EBUSY; + +done: + mutex_unlock(&audio->write_lock); + mutex_lock(&audio->lock); + audio->drv_status &= ~ADRV_STATUS_FSYNC; + mutex_unlock(&audio->lock); + + return rc; +} + +int audlpa_fsync(struct file *file, int datasync) +{ + struct audio *audio = file->private_data; + + return audlpa_async_fsync(audio); +} + +static void audlpa_reset_pmem_region(struct audio *audio) +{ + struct audlpa_pmem_region *region; + struct list_head *ptr, *next; + + list_for_each_safe(ptr, next, &audio->pmem_region_queue) { + region = list_entry(ptr, struct audlpa_pmem_region, list); + list_del(®ion->list); + put_pmem_file(region->file); + kfree(region); + } + + return; +} + +static void audlpa_unmap_pmem_region(struct audio *audio) +{ + struct audlpa_pmem_region *region; + struct list_head *ptr, *next; + int rc = -EINVAL; + + pr_aud_info("%s:\n", __func__); + list_for_each_safe(ptr, next, &audio->pmem_region_queue) { + region = list_entry(ptr, struct audlpa_pmem_region, list); + if (region != NULL) { + pr_aud_info("%s: phy_address = 0x%lx\n", __func__, + region->paddr); + rc = q6asm_memory_unmap(audio->ac, + (uint32_t)region->paddr, IN); + if (rc < 0) + pr_aud_err("%s: memory unmap failed\n", __func__); + } + } +} + +static int audio_release(struct inode *inode, struct file *file) +{ + struct audio *audio = file->private_data; + + pr_aud_info("%s: audio instance 0x%08x freeing, session %d\n", __func__, + (int)audio, audio->ac->session); + + mutex_lock(&audio->lock); + audio->wflush = 1; + if (audio->out_enabled) + audlpa_async_flush(audio); + audio->wflush = 0; + audlpa_unmap_pmem_region(audio); + audio_disable(audio); + msm_clear_session_id(audio->ac->session); + auddev_unregister_evt_listner(AUDDEV_CLNT_DEC, audio->ac->session); + q6asm_audio_client_free(audio->ac); + audlpa_reset_pmem_region(audio); +#ifdef CONFIG_HAS_EARLYSUSPEND + unregister_early_suspend(&audio->suspend_ctl.node); +#endif + audio->opened = 0; + audio->out_enabled = 0; + audio->out_prefill = 0; + audio->event_abort = 1; + wake_up(&audio->event_wait); + audlpa_reset_event_queue(audio); + iounmap(audio->data); + pmem_kfree(audio->phys); + if (audio->stopped == 0) + audlpa_allow_sleep(audio); + wake_lock_destroy(&audio->wakelock); + + mutex_unlock(&audio->lock); +#ifdef CONFIG_DEBUG_FS + if (audio->dentry) + debugfs_remove(audio->dentry); +#endif + kfree(audio); + return 0; +} + +static void audlpa_post_event(struct audio *audio, int type, + union msm_audio_event_payload payload) +{ + struct audlpa_event *e_node = NULL; + + spin_lock(&audio->event_queue_lock); + + pr_debug("%s:\n", __func__); + if (!list_empty(&audio->free_event_queue)) { + e_node = list_first_entry(&audio->free_event_queue, + struct audlpa_event, list); + list_del(&e_node->list); + } else { + e_node = kmalloc(sizeof(struct audlpa_event), GFP_ATOMIC); + if (!e_node) { + pr_aud_err("%s: No mem to post event %d\n", __func__, type); + return; + } + } + + e_node->event_type = type; + e_node->payload = payload; + + list_add_tail(&e_node->list, &audio->event_queue); + spin_unlock(&audio->event_queue_lock); + wake_up(&audio->event_wait); +} + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void audlpa_suspend(struct early_suspend *h) +{ + struct audlpa_suspend_ctl *ctl = + container_of(h, struct audlpa_suspend_ctl, node); + union msm_audio_event_payload payload; + + pr_aud_info("%s:\n", __func__); + audlpa_post_event(ctl->audio, AUDIO_EVENT_SUSPEND, payload); +} + +static void audlpa_resume(struct early_suspend *h) +{ + struct audlpa_suspend_ctl *ctl = + container_of(h, struct audlpa_suspend_ctl, node); + union msm_audio_event_payload payload; + + pr_aud_info("%s:\n", __func__); + audlpa_post_event(ctl->audio, AUDIO_EVENT_RESUME, payload); +} +#endif + +#ifdef CONFIG_DEBUG_FS +static ssize_t audlpa_debug_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static ssize_t audlpa_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 audio *audio = file->private_data; + + mutex_lock(&audio->lock); + n = scnprintf(buffer, debug_bufmax, "opened %d\n", audio->opened); + n += scnprintf(buffer + n, debug_bufmax - n, + "out_enabled %d\n", audio->out_enabled); + n += scnprintf(buffer + n, debug_bufmax - n, + "stopped %d\n", audio->stopped); + n += scnprintf(buffer + n, debug_bufmax - n, + "volume %x\n", audio->volume); + n += scnprintf(buffer + n, debug_bufmax - n, + "sample rate %d\n", + audio->out_sample_rate); + n += scnprintf(buffer + n, debug_bufmax - n, + "channel mode %d\n", + audio->out_channel_mode); + mutex_unlock(&audio->lock); + /* Following variables are only useful for debugging when + * when playback halts unexpectedly. Thus, no mutual exclusion + * enforced + */ + n += scnprintf(buffer + n, debug_bufmax - n, + "wflush %d\n", audio->wflush); + n += scnprintf(buffer + n, debug_bufmax - n, + "running %d\n", audio->running); + n += scnprintf(buffer + n, debug_bufmax - n, + "out_needed %d\n", audio->out_needed); + buffer[n] = 0; + return simple_read_from_buffer(buf, count, ppos, buffer, n); +} + +static const struct file_operations audlpa_debug_fops = { + .read = audlpa_debug_read, + .open = audlpa_debug_open, +}; +#endif + +static int audio_open(struct inode *inode, struct file *file) +{ + struct audio *audio = NULL; + int rc, i, dec_attrb = 0; + struct audlpa_event *e_node = NULL; +#ifdef CONFIG_DEBUG_FS + /* 4 bytes represents decoder number, 1 byte for terminate string */ + char name[sizeof "msm_lpa_" + 5]; +#endif + char wake_lock_name[24]; + + /* Allocate audio instance, set to zero */ + audio = kzalloc(sizeof(struct audio), GFP_KERNEL); + if (!audio) { + pr_aud_err("%s: no memory to allocate audio instance\n", __func__); + rc = -ENOMEM; + goto done; + } + + if ((file->f_mode & FMODE_WRITE) && !(file->f_mode & FMODE_READ)) { + pr_aud_info("%s: Tunnel Mode playback\n", __func__); + } else { + kfree(audio); + rc = -EACCES; + goto done; + } + + /* Allocate the decoder based on inode minor number*/ + audio->minor_no = iminor(inode); + + if (audio->minor_no >= ARRAY_SIZE(audlpa_decs)) { + pr_aud_err("%s: incorrect inode %d\n", __func__, audio->minor_no); + kfree(audio); + rc = -EINVAL; + goto done; + } + + dec_attrb |= audlpa_decs[audio->minor_no].dec_attrb; + audio->codec_ops.ioctl = audlpa_decs[audio->minor_no].ioctl; + audio->codec_ops.set_params = audlpa_decs[audio->minor_no].set_params; + audio->buffer_size = BUFSZ; + audio->buffer_count = MAX_BUF; + + audio->ac = q6asm_audio_client_alloc((app_cb)q6_audlpa_out_cb, + (void *)audio); + if (!audio->ac) { + pr_aud_err("%s: Could not allocate memory for lpa client\n", + __func__); + rc = -ENOMEM; + goto err; + } + rc = q6asm_open_write(audio->ac, FORMAT_LINEAR_PCM); + if (rc < 0) { + pr_aud_info("%s: lpa out open failed\n", __func__); + goto err; + } + + pr_debug("%s: Set mode to AIO session[%d]\n", + __func__, + audio->ac->session); + rc = q6asm_set_io_mode(audio->ac, ASYNC_IO_MODE); + if (rc < 0) + pr_aud_err("%s: Set IO mode failed\n", __func__); + + + /* Initialize all locks of audio instance */ + mutex_init(&audio->lock); + mutex_init(&audio->write_lock); + mutex_init(&audio->get_event_lock); + spin_lock_init(&audio->dsp_lock); + init_waitqueue_head(&audio->write_wait); + INIT_LIST_HEAD(&audio->out_queue); + INIT_LIST_HEAD(&audio->pmem_region_queue); + INIT_LIST_HEAD(&audio->free_event_queue); + INIT_LIST_HEAD(&audio->event_queue); + init_waitqueue_head(&audio->wait); + init_waitqueue_head(&audio->event_wait); + spin_lock_init(&audio->event_queue_lock); + snprintf(wake_lock_name, sizeof wake_lock_name, "audio_lpa_%x", + audio->ac->session); + wake_lock_init(&audio->wakelock, WAKE_LOCK_SUSPEND, wake_lock_name); + + audio->out_sample_rate = 44100; + audio->out_channel_mode = 2; + audio->out_bits = 16; + audio->volume = 0x2000; + + file->private_data = audio; + audio->opened = 1; + audio->out_enabled = 0; + audio->out_prefill = 0; + audio->bytes_consumed = 0; + + audio->device_events = AUDDEV_EVT_STREAM_VOL_CHG; + audio->drv_status &= ~ADRV_STATUS_PAUSE; + + rc = auddev_register_evt_listner(audio->device_events, + AUDDEV_CLNT_DEC, + audio->ac->session, + lpa_listner, + (void *)audio); + if (rc) { + pr_aud_err("%s: failed to register listner\n", __func__); + goto err; + } + +#ifdef CONFIG_DEBUG_FS + snprintf(name, sizeof name, "msm_lpa_%04x", audio->ac->session); + audio->dentry = debugfs_create_file(name, S_IFREG | S_IRUGO, + NULL, (void *) audio, &audlpa_debug_fops); + + if (IS_ERR(audio->dentry)) + pr_aud_info("%s: debugfs_create_file failed\n", __func__); +#endif +#ifdef CONFIG_HAS_EARLYSUSPEND + audio->suspend_ctl.node.level = EARLY_SUSPEND_LEVEL_DISABLE_FB; + audio->suspend_ctl.node.resume = audlpa_resume; + audio->suspend_ctl.node.suspend = audlpa_suspend; + audio->suspend_ctl.audio = audio; + register_early_suspend(&audio->suspend_ctl.node); +#endif + for (i = 0; i < AUDLPA_EVENT_NUM; i++) { + e_node = kmalloc(sizeof(struct audlpa_event), GFP_KERNEL); + if (e_node) + list_add_tail(&e_node->list, &audio->free_event_queue); + else { + pr_aud_err("%s: event pkt alloc failed\n", __func__); + break; + } + } + pr_aud_info("%s: audio instance 0x%08x created session[%d]\n", __func__, + (int)audio, + audio->ac->session); +done: + return rc; +err: + q6asm_audio_client_free(audio->ac); + iounmap(audio->data); + pmem_kfree(audio->phys); + kfree(audio); + return rc; +} + +static const struct file_operations audio_lpa_fops = { + .owner = THIS_MODULE, + .open = audio_open, + .release = audio_release, + .unlocked_ioctl = audio_ioctl, + .fsync = audlpa_fsync, +}; + +static dev_t audlpa_devno; +static struct class *audlpa_class; +struct audlpa_device { + const char *name; + struct device *device; + struct cdev cdev; +}; + +static struct audlpa_device *audlpa_devices; + +static void audlpa_create(struct audlpa_device *adev, const char *name, + struct device *parent, dev_t devt) +{ + struct device *dev; + int rc; + + dev = device_create(audlpa_class, parent, devt, "%s", name); + if (IS_ERR(dev)) + return; + + cdev_init(&adev->cdev, &audio_lpa_fops); + adev->cdev.owner = THIS_MODULE; + + rc = cdev_add(&adev->cdev, devt, 1); + if (rc < 0) { + device_destroy(audlpa_class, devt); + } else { + adev->device = dev; + adev->name = name; + } +} + +static int __init audio_init(void) +{ + int rc; + int n = ARRAY_SIZE(audlpa_decs); + + audlpa_devices = kzalloc(sizeof(struct audlpa_device) * n, GFP_KERNEL); + if (!audlpa_devices) + return -ENOMEM; + + audlpa_class = class_create(THIS_MODULE, "audlpa"); + if (IS_ERR(audlpa_class)) + goto fail_create_class; + + rc = alloc_chrdev_region(&audlpa_devno, 0, n, "msm_audio_lpa"); + if (rc < 0) + goto fail_alloc_region; + + for (n = 0; n < ARRAY_SIZE(audlpa_decs); n++) { + audlpa_create(audlpa_devices + n, + audlpa_decs[n].name, NULL, + MKDEV(MAJOR(audlpa_devno), n)); + } + + return 0; + +fail_alloc_region: + class_unregister(audlpa_class); + return rc; +fail_create_class: + kfree(audlpa_devices); + return -ENOMEM; +} + +static void __exit audio_exit(void) +{ + class_unregister(audlpa_class); + kfree(audlpa_devices); +} + +module_init(audio_init); +module_exit(audio_exit); + +MODULE_DESCRIPTION("MSM LPA driver"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/qdsp6v3/audio_lpa.h b/arch/arm/mach-msm/qdsp6v3/audio_lpa.h new file mode 100644 index 00000000000..8e7a22cd9c8 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v3/audio_lpa.h @@ -0,0 +1,129 @@ +/* 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 AUDIO_LPA_H +#define AUDIO_LPA_H + +#include +#include + +#define ADRV_STATUS_OBUF_GIVEN 0x00000001 +#define ADRV_STATUS_IBUF_GIVEN 0x00000002 +#define ADRV_STATUS_FSYNC 0x00000004 +#define ADRV_STATUS_PAUSE 0x00000008 + +#define SOFT_PAUSE_PERIOD 30 /* ramp up/down for 30ms */ +#define SOFT_PAUSE_STEP 2000 /* Step value 2ms or 2000us */ +enum { + SOFT_PAUSE_CURVE_LINEAR = 0, + SOFT_PAUSE_CURVE_EXP, + SOFT_PAUSE_CURVE_LOG, +}; + +struct buffer { + void *data; + unsigned size; + unsigned used; /* Input usage actual DSP produced PCM size */ + unsigned addr; +}; + +#ifdef CONFIG_HAS_EARLYSUSPEND +struct audlpa_suspend_ctl { + struct early_suspend node; + struct audio *audio; +}; +#endif + +struct codec_operations { + long (*ioctl)(struct file *, unsigned int, unsigned long); + int (*set_params)(void *); +}; + +struct audio { + spinlock_t dsp_lock; + + uint8_t out_needed; /* number of buffers the dsp is waiting for */ + struct list_head out_queue; /* queue to retain output buffers */ + + struct mutex lock; + struct mutex write_lock; + wait_queue_head_t write_wait; + + struct audio_client *ac; + + /* configuration to use on next enable */ + uint32_t out_sample_rate; + uint32_t out_channel_mode; + uint32_t out_bits; /* bits per sample (used by PCM decoder) */ + + /* data allocated for various buffers */ + char *data; + int32_t phys; /* physical address of write buffer */ + + uint32_t drv_status; + int wflush; /* Write flush */ + int opened; + int out_enabled; + int out_prefill; + int running; + int stopped; /* set when stopped, cleared on flush */ + int buf_refresh; + int teos; /* valid only if tunnel mode & no data left for decoder */ + +#ifdef CONFIG_HAS_EARLYSUSPEND + struct audlpa_suspend_ctl suspend_ctl; +#endif + + struct wake_lock wakelock; +#ifdef CONFIG_DEBUG_FS + struct dentry *dentry; +#endif + + wait_queue_head_t wait; + struct list_head free_event_queue; + struct list_head event_queue; + wait_queue_head_t event_wait; + spinlock_t event_queue_lock; + struct mutex get_event_lock; + int event_abort; + + uint32_t device_events; + + struct list_head pmem_region_queue; /* protected by lock */ + + int eq_enable; + int eq_needs_commit; + uint32_t volume; + + unsigned int minor_no; + struct codec_operations codec_ops; + uint32_t buffer_size; + uint32_t buffer_count; + uint32_t bytes_consumed; +}; + +#endif /* !AUDIO_LPA_H */ diff --git a/arch/arm/mach-msm/qdsp6v3/audio_mvs.c b/arch/arm/mach-msm/qdsp6v3/audio_mvs.c new file mode 100644 index 00000000000..529b5840b06 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v3/audio_mvs.c @@ -0,0 +1,998 @@ +/* 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. + * + * 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 + +/* Each buffer is 20 ms, queue holds 200 ms of data. */ +#define MVS_MAX_Q_LEN 10 + +/* Length of the DSP frame info header added to the voc packet. */ +#define DSP_FRAME_HDR_LEN 1 + +enum audio_mvs_state_type { + AUDIO_MVS_CLOSED, + AUDIO_MVS_STARTED, + AUDIO_MVS_STOPPED +}; + +struct audio_mvs_buf_node { + struct list_head list; + struct msm_audio_mvs_frame frame; +}; + +struct audio_mvs_info_type { + enum audio_mvs_state_type state; + + uint32_t mvs_mode; + uint32_t rate_type; + uint32_t dtx_mode; + + struct list_head in_queue; + struct list_head free_in_queue; + + struct list_head out_queue; + struct list_head free_out_queue; + + wait_queue_head_t out_wait; + + struct mutex lock; + struct mutex in_lock; + struct mutex out_lock; + + spinlock_t dsp_lock; + + struct wake_lock suspend_lock; + struct wake_lock idle_lock; + + void *memory_chunk; +}; + +static struct audio_mvs_info_type audio_mvs_info; + +static uint32_t audio_mvs_get_rate(uint32_t mvs_mode, uint32_t rate_type) +{ + uint32_t cvs_rate; + + if (mvs_mode == MVS_MODE_AMR_WB) + cvs_rate = rate_type - MVS_AMR_MODE_0660; + else + cvs_rate = rate_type; + + pr_debug("%s: CVS rate is %d for MVS mode %d\n", + __func__, cvs_rate, mvs_mode); + + return cvs_rate; +} + +static void audio_mvs_process_ul_pkt(uint8_t *voc_pkt, + uint32_t pkt_len, + void *private_data) +{ + struct audio_mvs_buf_node *buf_node = NULL; + struct audio_mvs_info_type *audio = private_data; + unsigned long dsp_flags; + + /* Copy up-link packet into out_queue. */ + spin_lock_irqsave(&audio->dsp_lock, dsp_flags); + + if (!list_empty(&audio->free_out_queue)) { + buf_node = list_first_entry(&audio->free_out_queue, + struct audio_mvs_buf_node, + list); + list_del(&buf_node->list); + + switch (audio->mvs_mode) { + case MVS_MODE_AMR: + case MVS_MODE_AMR_WB: { + /* Remove the DSP frame info header. Header format: + * Bits 0-3: Frame rate + * Bits 4-7: Frame type + */ + buf_node->frame.frame_type = ((*voc_pkt) & 0xF0) >> 4; + buf_node->frame.frame_rate = ((*voc_pkt) & 0x0F); + voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN; + + buf_node->frame.len = pkt_len - DSP_FRAME_HDR_LEN; + + memcpy(&buf_node->frame.voc_pkt[0], + voc_pkt, + buf_node->frame.len); + + list_add_tail(&buf_node->list, &audio->out_queue); + break; + } + + case MVS_MODE_IS127: { + buf_node->frame.frame_type = 0; + voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN; + + buf_node->frame.len = pkt_len - DSP_FRAME_HDR_LEN; + + memcpy(&buf_node->frame.voc_pkt[0], + voc_pkt, + buf_node->frame.len); + + list_add_tail(&buf_node->list, &audio->out_queue); + break; + } + + case MVS_MODE_G729A: { + /* G729 frames are 10ms each, but the DSP works with + * 20ms frames and sends two 10ms frames per buffer. + * Extract the two frames and put them in separate + * buffers. + */ + /* Remove the first DSP frame info header. + * Header format: + * Bits 0-1: Frame type + */ + buf_node->frame.frame_type = (*voc_pkt) & 0x03; + voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN; + + /* There are two frames in the buffer. Length of the + * first frame: + */ + buf_node->frame.len = (pkt_len - + 2 * DSP_FRAME_HDR_LEN) / 2; + + memcpy(&buf_node->frame.voc_pkt[0], + voc_pkt, + buf_node->frame.len); + voc_pkt = voc_pkt + buf_node->frame.len; + + list_add_tail(&buf_node->list, &audio->out_queue); + + /* Get another buffer from the free Q and fill in the + * second frame. + */ + if (!list_empty(&audio->free_out_queue)) { + buf_node = + list_first_entry(&audio->free_out_queue, + struct audio_mvs_buf_node, + list); + list_del(&buf_node->list); + + /* Remove the second DSP frame info header. + * Header format: + * Bits 0-1: Frame type + */ + buf_node->frame.frame_type = (*voc_pkt) & 0x03; + voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN; + + /* There are two frames in the buffer. Length + * of the first frame: + */ + buf_node->frame.len = (pkt_len - + 2 * DSP_FRAME_HDR_LEN) / 2; + + memcpy(&buf_node->frame.voc_pkt[0], + voc_pkt, + buf_node->frame.len); + + list_add_tail(&buf_node->list, + &audio->out_queue); + + } else { + /* Drop the second frame. */ + pr_aud_err("%s: UL data dropped, read is slow\n", + __func__); + } + + break; + } + + case MVS_MODE_G711A: { + /* G711 frames are 10ms each, but the DSP works with + * 20ms frames and sends two 10ms frames per buffer. + * Extract the two frames and put them in separate + * buffers. + */ + /* Remove the first DSP frame info header. + * Header format: + * Bits 0-1: Frame type + * Bits 2-3: Frame rate + */ + buf_node->frame.frame_type = (*voc_pkt) & 0x03; + voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN; + + /* There are two frames in the buffer. Length of the + * first frame: + */ + buf_node->frame.len = (pkt_len - + 2 * DSP_FRAME_HDR_LEN) / 2; + + memcpy(&buf_node->frame.voc_pkt[0], + voc_pkt, + buf_node->frame.len); + voc_pkt = voc_pkt + buf_node->frame.len; + + list_add_tail(&buf_node->list, &audio->out_queue); + + /* Get another buffer from the free Q and fill in the + * second frame. + */ + if (!list_empty(&audio->free_out_queue)) { + buf_node = + list_first_entry(&audio->free_out_queue, + struct audio_mvs_buf_node, + list); + list_del(&buf_node->list); + + /* Remove the second DSP frame info header. + * Header format: + * Bits 0-1: Frame type + * Bits 2-3: Frame rate + */ + buf_node->frame.frame_type = (*voc_pkt) & 0x03; + voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN; + + /* There are two frames in the buffer. Length + * of the second frame: + */ + buf_node->frame.len = (pkt_len - + 2 * DSP_FRAME_HDR_LEN) / 2; + + memcpy(&buf_node->frame.voc_pkt[0], + voc_pkt, + buf_node->frame.len); + + list_add_tail(&buf_node->list, + &audio->out_queue); + } else { + /* Drop the second frame. */ + pr_aud_err("%s: UL data dropped, read is slow\n", + __func__); + } + break; + } + + default: { + buf_node->frame.frame_type = 0; + + buf_node->frame.len = pkt_len; + + memcpy(&buf_node->frame.voc_pkt[0], + voc_pkt, + buf_node->frame.len); + + list_add_tail(&buf_node->list, &audio->out_queue); + } + } + } else { + pr_aud_err("%s: UL data dropped, read is slow\n", __func__); + } + + spin_unlock_irqrestore(&audio->dsp_lock, dsp_flags); + + wake_up(&audio->out_wait); +} + +static void audio_mvs_process_dl_pkt(uint8_t *voc_pkt, + uint32_t *pkt_len, + void *private_data) +{ + struct audio_mvs_buf_node *buf_node = NULL; + struct audio_mvs_info_type *audio = private_data; + unsigned long dsp_flags; + + spin_lock_irqsave(&audio->dsp_lock, dsp_flags); + + if (!list_empty(&audio->in_queue)) { + uint32_t rate_type = audio_mvs_get_rate(audio->mvs_mode, + audio->rate_type); + + buf_node = list_first_entry(&audio->in_queue, + struct audio_mvs_buf_node, + list); + list_del(&buf_node->list); + + switch (audio->mvs_mode) { + case MVS_MODE_AMR: + case MVS_MODE_AMR_WB: { + /* Add the DSP frame info header. Header format: + * Bits 0-3: Frame rate + * Bits 4-7: Frame type + */ + *voc_pkt = ((buf_node->frame.frame_type & 0x0F) << 4) | + (rate_type & 0x0F); + voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN; + + *pkt_len = buf_node->frame.len + DSP_FRAME_HDR_LEN; + + memcpy(voc_pkt, + &buf_node->frame.voc_pkt[0], + buf_node->frame.len); + + list_add_tail(&buf_node->list, &audio->free_in_queue); + break; + } + + case MVS_MODE_IS127: { + /* Add the DSP frame info header. Header format: + * Bits 0-3: Frame rate + * Bits 4-7: Frame type + */ + *voc_pkt = rate_type & 0x0F; + voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN; + + *pkt_len = buf_node->frame.len + DSP_FRAME_HDR_LEN; + + memcpy(voc_pkt, + &buf_node->frame.voc_pkt[0], + buf_node->frame.len); + + list_add_tail(&buf_node->list, &audio->free_in_queue); + break; + } + + case MVS_MODE_G729A: { + /* G729 frames are 10ms each but the DSP expects 20ms + * worth of data, so send two 10ms frames per buffer. + */ + /* Add the first DSP frame info header. Header format: + * Bits 0-1: Frame type + */ + *voc_pkt = buf_node->frame.frame_type & 0x03; + voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN; + + *pkt_len = buf_node->frame.len + DSP_FRAME_HDR_LEN; + + memcpy(voc_pkt, + &buf_node->frame.voc_pkt[0], + buf_node->frame.len); + voc_pkt = voc_pkt + buf_node->frame.len; + + list_add_tail(&buf_node->list, &audio->free_in_queue); + + if (!list_empty(&audio->in_queue)) { + /* Get the second buffer. */ + buf_node = list_first_entry(&audio->in_queue, + struct audio_mvs_buf_node, + list); + list_del(&buf_node->list); + + /* Add the second DSP frame info header. + * Header format: + * Bits 0-1: Frame type + */ + *voc_pkt = buf_node->frame.frame_type & 0x03; + voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN; + + *pkt_len = *pkt_len + + buf_node->frame.len + DSP_FRAME_HDR_LEN; + + memcpy(voc_pkt, + &buf_node->frame.voc_pkt[0], + buf_node->frame.len); + + list_add_tail(&buf_node->list, + &audio->free_in_queue); + } else { + /* Only 10ms worth of data is available, signal + * erasure frame. + */ + *voc_pkt = MVS_G729A_ERASURE & 0x03; + + *pkt_len = *pkt_len + DSP_FRAME_HDR_LEN; + } + + break; + } + + case MVS_MODE_G711A: { + /* G711 frames are 10ms each but the DSP expects 20ms + * worth of data, so send two 10ms frames per buffer. + */ + /* Add the first DSP frame info header. Header format: + * Bits 0-1: Frame type + * Bits 2-3: Frame rate + */ + *voc_pkt = ((rate_type & 0x0F) << 2) | + (buf_node->frame.frame_type & 0x03); + voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN; + + *pkt_len = buf_node->frame.len + DSP_FRAME_HDR_LEN; + + memcpy(voc_pkt, + &buf_node->frame.voc_pkt[0], + buf_node->frame.len); + voc_pkt = voc_pkt + buf_node->frame.len; + + list_add_tail(&buf_node->list, &audio->free_in_queue); + + if (!list_empty(&audio->in_queue)) { + /* Get the second buffer. */ + buf_node = list_first_entry(&audio->in_queue, + struct audio_mvs_buf_node, + list); + list_del(&buf_node->list); + + /* Add the second DSP frame info header. + * Header format: + * Bits 0-1: Frame type + * Bits 2-3: Frame rate + */ + *voc_pkt = ((rate_type & 0x0F) << 2) | + (buf_node->frame.frame_type & 0x03); + voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN; + + *pkt_len = *pkt_len + + buf_node->frame.len + DSP_FRAME_HDR_LEN; + + memcpy(voc_pkt, + &buf_node->frame.voc_pkt[0], + buf_node->frame.len); + + list_add_tail(&buf_node->list, + &audio->free_in_queue); + } else { + /* Only 10ms worth of data is available, signal + * erasure frame. + */ + *voc_pkt = ((rate_type & 0x0F) << 2) | + (MVS_G711A_ERASURE & 0x03); + + *pkt_len = *pkt_len + DSP_FRAME_HDR_LEN; + } + break; + } + + default: { + *pkt_len = buf_node->frame.len; + + memcpy(voc_pkt, + &buf_node->frame.voc_pkt[0], + buf_node->frame.len); + + list_add_tail(&buf_node->list, &audio->free_in_queue); + } + } + } else { + *pkt_len = 0; + + pr_aud_info("%s: No DL data available to send to MVS\n", __func__); + } + + spin_unlock_irqrestore(&audio->dsp_lock, dsp_flags); +} + +static uint32_t audio_mvs_get_media_type(uint32_t mvs_mode, uint32_t rate_type) +{ + uint32_t media_type; + + switch (mvs_mode) { + case MVS_MODE_IS127: + media_type = VSS_MEDIA_ID_EVRC_MODEM; + break; + + case MVS_MODE_AMR: + media_type = VSS_MEDIA_ID_AMR_NB_MODEM; + break; + + case MVS_MODE_LINEAR_PCM: + media_type = VSS_MEDIA_ID_PCM_NB; + break; + + case MVS_MODE_PCM: + media_type = VSS_MEDIA_ID_PCM_NB; + break; + + case MVS_MODE_AMR_WB: + media_type = VSS_MEDIA_ID_AMR_WB_MODEM; + break; + + case MVS_MODE_G729A: + media_type = VSS_MEDIA_ID_G729; + break; + + case MVS_MODE_G711A: + if (rate_type == MVS_G711A_MODE_MULAW) + media_type = VSS_MEDIA_ID_G711_MULAW; + else + media_type = VSS_MEDIA_ID_G711_ALAW; + break; + + default: + media_type = VSS_MEDIA_ID_PCM_NB; + } + + pr_debug("%s: media_type is 0x%x\n", __func__, media_type); + + return media_type; +} + +static uint32_t audio_mvs_get_network_type(uint32_t mvs_mode) +{ + uint32_t network_type; + + switch (mvs_mode) { + case MVS_MODE_IS127: + case MVS_MODE_AMR: + case MVS_MODE_LINEAR_PCM: + case MVS_MODE_PCM: + case MVS_MODE_G729A: + case MVS_MODE_G711A: + network_type = VSS_NETWORK_ID_VOIP_NB; + break; + + case MVS_MODE_AMR_WB: + network_type = VSS_NETWORK_ID_VOIP_WB; + break; + + default: + network_type = VSS_NETWORK_ID_DEFAULT; + } + + pr_debug("%s: network_type is 0x%x\n", __func__, network_type); + + return network_type; +} + +static int audio_mvs_start(struct audio_mvs_info_type *audio) +{ + int rc = 0; + + pr_aud_info("%s\n", __func__); + + /* Prevent sleep. */ + wake_lock(&audio->suspend_lock); + wake_lock(&audio->idle_lock); + + rc = voice_set_voc_path_full(1); + + if (rc == 0) { + voice_register_mvs_cb(audio_mvs_process_ul_pkt, + audio_mvs_process_dl_pkt, + audio); + + voice_config_vocoder( + audio_mvs_get_media_type(audio->mvs_mode, audio->rate_type), + audio_mvs_get_rate(audio->mvs_mode, audio->rate_type), + audio_mvs_get_network_type(audio->mvs_mode), + audio->dtx_mode); + + audio->state = AUDIO_MVS_STARTED; + } else { + pr_aud_err("%s: Error %d setting voc path to full\n", __func__, rc); + } + + return rc; +} + +static int audio_mvs_stop(struct audio_mvs_info_type *audio) +{ + int rc = 0; + + pr_aud_info("%s\n", __func__); + + voice_set_voc_path_full(0); + + audio->state = AUDIO_MVS_STOPPED; + + /* Allow sleep. */ + wake_unlock(&audio->suspend_lock); + wake_unlock(&audio->idle_lock); + + return rc; +} + +static int audio_mvs_open(struct inode *inode, struct file *file) +{ + int rc = 0; + int i; + int offset = 0; + struct audio_mvs_buf_node *buf_node = NULL; + + pr_aud_info("%s\n", __func__); + + mutex_lock(&audio_mvs_info.lock); + + /* Allocate input and output buffers. */ + audio_mvs_info.memory_chunk = kmalloc(2 * MVS_MAX_Q_LEN * + sizeof(struct audio_mvs_buf_node), + GFP_KERNEL); + + if (audio_mvs_info.memory_chunk != NULL) { + for (i = 0; i < MVS_MAX_Q_LEN; i++) { + buf_node = audio_mvs_info.memory_chunk + offset; + + list_add_tail(&buf_node->list, + &audio_mvs_info.free_in_queue); + + offset = offset + sizeof(struct audio_mvs_buf_node); + } + + for (i = 0; i < MVS_MAX_Q_LEN; i++) { + buf_node = audio_mvs_info.memory_chunk + offset; + + list_add_tail(&buf_node->list, + &audio_mvs_info.free_out_queue); + + offset = offset + sizeof(struct audio_mvs_buf_node); + } + + audio_mvs_info.state = AUDIO_MVS_STOPPED; + + file->private_data = &audio_mvs_info; + + } else { + pr_aud_err("%s: No memory for IO buffers\n", __func__); + + rc = -ENOMEM; + } + + mutex_unlock(&audio_mvs_info.lock); + + return rc; +} + +static int audio_mvs_release(struct inode *inode, struct file *file) +{ + struct list_head *ptr = NULL; + struct list_head *next = NULL; + struct audio_mvs_buf_node *buf_node = NULL; + struct audio_mvs_info_type *audio = file->private_data; + + pr_aud_info("%s\n", __func__); + + mutex_lock(&audio->lock); + + if (audio->state == AUDIO_MVS_STARTED) + audio_mvs_stop(audio); + + /* Free input and output memory. */ + mutex_lock(&audio->in_lock); + + list_for_each_safe(ptr, next, &audio->in_queue) { + buf_node = list_entry(ptr, struct audio_mvs_buf_node, list); + list_del(&buf_node->list); + } + + list_for_each_safe(ptr, next, &audio->free_in_queue) { + buf_node = list_entry(ptr, struct audio_mvs_buf_node, list); + list_del(&buf_node->list); + } + + mutex_unlock(&audio->in_lock); + + + mutex_lock(&audio->out_lock); + + list_for_each_safe(ptr, next, &audio->out_queue) { + buf_node = list_entry(ptr, struct audio_mvs_buf_node, list); + list_del(&buf_node->list); + } + + list_for_each_safe(ptr, next, &audio->free_out_queue) { + buf_node = list_entry(ptr, struct audio_mvs_buf_node, list); + list_del(&buf_node->list); + } + + mutex_unlock(&audio->out_lock); + + kfree(audio->memory_chunk); + audio->memory_chunk = NULL; + + audio->state = AUDIO_MVS_CLOSED; + + mutex_unlock(&audio->lock); + + return 0; +} + +static ssize_t audio_mvs_read(struct file *file, + char __user *buf, + size_t count, + loff_t *pos) +{ + int rc = 0; + struct audio_mvs_buf_node *buf_node = NULL; + struct audio_mvs_info_type *audio = file->private_data; + + pr_debug("%s:\n", __func__); + + rc = wait_event_interruptible_timeout(audio->out_wait, + (!list_empty(&audio->out_queue) || + audio->state == AUDIO_MVS_STOPPED), + 1 * HZ); + + if (rc > 0) { + mutex_lock(&audio->out_lock); + + if ((audio->state == AUDIO_MVS_STARTED) && + (!list_empty(&audio->out_queue))) { + + if (count >= sizeof(struct msm_audio_mvs_frame)) { + buf_node = list_first_entry(&audio->out_queue, + struct audio_mvs_buf_node, + list); + list_del(&buf_node->list); + + rc = copy_to_user(buf, + &buf_node->frame, + sizeof(struct msm_audio_mvs_frame)); + + if (rc == 0) { + rc = buf_node->frame.len + + sizeof(buf_node->frame.frame_type) + + sizeof(buf_node->frame.len); + } else { + pr_aud_err("%s: Copy to user retuned %d", + __func__, rc); + + rc = -EFAULT; + } + + list_add_tail(&buf_node->list, + &audio->free_out_queue); + } else { + pr_aud_err("%s: Read count %d < sizeof(frame) %d", + __func__, count, + sizeof(struct msm_audio_mvs_frame)); + + rc = -ENOMEM; + } + } else { + pr_aud_err("%s: Read performed in state %d\n", + __func__, audio->state); + + rc = -EPERM; + } + + mutex_unlock(&audio->out_lock); + + } else if (rc == 0) { + pr_aud_err("%s: No UL data available\n", __func__); + + rc = -ETIMEDOUT; + } else { + pr_aud_err("%s: Read was interrupted\n", __func__); + + rc = -ERESTARTSYS; + } + + return rc; +} + +static ssize_t audio_mvs_write(struct file *file, + const char __user *buf, + size_t count, + loff_t *pos) +{ + int rc = 0; + struct audio_mvs_buf_node *buf_node = NULL; + struct audio_mvs_info_type *audio = file->private_data; + + pr_debug("%s:\n", __func__); + + mutex_lock(&audio->in_lock); + + if (audio->state == AUDIO_MVS_STARTED) { + if (count <= sizeof(struct msm_audio_mvs_frame)) { + if (!list_empty(&audio->free_in_queue)) { + buf_node = + list_first_entry(&audio->free_in_queue, + struct audio_mvs_buf_node, + list); + list_del(&buf_node->list); + + rc = copy_from_user(&buf_node->frame, + buf, + count); + + list_add_tail(&buf_node->list, + &audio->in_queue); + } else { + pr_aud_err("%s: No free DL buffs\n", __func__); + } + } else { + pr_aud_err("%s: Write count %d < sizeof(frame) %d", + __func__, count, + sizeof(struct msm_audio_mvs_frame)); + + rc = -ENOMEM; + } + } else { + pr_aud_err("%s: Write performed in invalid state %d\n", + __func__, audio->state); + + rc = -EPERM; + } + + mutex_unlock(&audio->in_lock); + + return rc; +} + +static long audio_mvs_ioctl(struct file *file, + unsigned int cmd, + unsigned long arg) +{ + int rc = 0; + struct audio_mvs_info_type *audio = file->private_data; + + pr_aud_info("%s:\n", __func__); + + switch (cmd) { + case AUDIO_GET_MVS_CONFIG: { + struct msm_audio_mvs_config config; + + pr_aud_info("%s: IOCTL GET_MVS_CONFIG\n", __func__); + + mutex_lock(&audio->lock); + + config.mvs_mode = audio->mvs_mode; + config.rate_type = audio->rate_type; + config.dtx_mode = audio->dtx_mode; + + mutex_unlock(&audio->lock); + + rc = copy_to_user((void *)arg, &config, sizeof(config)); + if (rc == 0) + rc = sizeof(config); + else + pr_aud_err("%s: Config copy failed %d\n", __func__, rc); + + break; + } + + case AUDIO_SET_MVS_CONFIG: { + struct msm_audio_mvs_config config; + + pr_aud_info("%s: IOCTL SET_MVS_CONFIG\n", __func__); + + rc = copy_from_user(&config, (void *)arg, sizeof(config)); + if (rc == 0) { + mutex_lock(&audio->lock); + + if (audio->state == AUDIO_MVS_STOPPED) { + audio->mvs_mode = config.mvs_mode; + audio->rate_type = config.rate_type; + audio->dtx_mode = config.dtx_mode; + } else { + pr_aud_err("%s: Set confg called in state %d\n", + __func__, audio->state); + + rc = -EPERM; + } + + mutex_unlock(&audio->lock); + } else { + pr_aud_err("%s: Config copy failed %d\n", __func__, rc); + } + + break; + } + + case AUDIO_START: { + pr_aud_info("%s: IOCTL START\n", __func__); + + mutex_lock(&audio->lock); + + if (audio->state == AUDIO_MVS_STOPPED) { + rc = audio_mvs_start(audio); + + if (rc != 0) + audio_mvs_stop(audio); + } else { + pr_aud_err("%s: Start called in invalid state %d\n", + __func__, audio->state); + + rc = -EPERM; + } + + mutex_unlock(&audio->lock); + + break; + } + + case AUDIO_STOP: { + pr_aud_info("%s: IOCTL STOP\n", __func__); + + mutex_lock(&audio->lock); + + if (audio->state == AUDIO_MVS_STARTED) { + rc = audio_mvs_stop(audio); + } else { + pr_aud_err("%s: Stop called in invalid state %d\n", + __func__, audio->state); + + rc = -EPERM; + } + + mutex_unlock(&audio->lock); + + break; + } + + default: { + pr_aud_err("%s: Unknown IOCTL %d\n", __func__, cmd); + } + } + + return rc; +} + +static const struct file_operations audio_mvs_fops = { + .owner = THIS_MODULE, + .open = audio_mvs_open, + .release = audio_mvs_release, + .read = audio_mvs_read, + .write = audio_mvs_write, + .unlocked_ioctl = audio_mvs_ioctl +}; + +struct miscdevice audio_mvs_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_mvs", + .fops = &audio_mvs_fops +}; + +static int __init audio_mvs_init(void) +{ + int rc = 0; + + memset(&audio_mvs_info, 0, sizeof(audio_mvs_info)); + + init_waitqueue_head(&audio_mvs_info.out_wait); + + mutex_init(&audio_mvs_info.lock); + mutex_init(&audio_mvs_info.in_lock); + mutex_init(&audio_mvs_info.out_lock); + + spin_lock_init(&audio_mvs_info.dsp_lock); + + INIT_LIST_HEAD(&audio_mvs_info.in_queue); + INIT_LIST_HEAD(&audio_mvs_info.free_in_queue); + INIT_LIST_HEAD(&audio_mvs_info.out_queue); + INIT_LIST_HEAD(&audio_mvs_info.free_out_queue); + + wake_lock_init(&audio_mvs_info.suspend_lock, + WAKE_LOCK_SUSPEND, + "audio_mvs_suspend"); + wake_lock_init(&audio_mvs_info.idle_lock, + WAKE_LOCK_IDLE, + "audio_mvs_idle"); + + rc = misc_register(&audio_mvs_misc); + + return rc; +} + +static void __exit audio_mvs_exit(void){ + pr_aud_info("%s:\n", __func__); + + misc_deregister(&audio_mvs_misc); +} + +module_init(audio_mvs_init); +module_exit(audio_mvs_exit); + +MODULE_DESCRIPTION("MSM MVS driver"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/qdsp6v3/audio_utils.c b/arch/arm/mach-msm/qdsp6v3/audio_utils.c new file mode 100644 index 00000000000..aa641af7ee8 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v3/audio_utils.c @@ -0,0 +1,644 @@ +/* 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. + * + * 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 "audio_utils.h" + +static int audio_in_pause(struct q6audio_in *audio) +{ + int rc; + + rc = q6asm_cmd(audio->ac, CMD_PAUSE); + if (rc < 0) + pr_aud_err("%s:session id %d: pause cmd failed rc=%d\n", __func__, + audio->ac->session, rc); + + return rc; +} + +static int audio_in_flush(struct q6audio_in *audio) +{ + int rc; + + pr_debug("%s:session id %d: flush\n", __func__, audio->ac->session); + /* Implicitly issue a pause to the decoder before flushing */ + rc = audio_in_pause(audio); + if (rc < 0) { + pr_aud_err("%s:session id %d: pause cmd failed rc=%d\n", __func__, + audio->ac->session, rc); + return rc; + } + + rc = q6asm_cmd(audio->ac, CMD_FLUSH); + if (rc < 0) { + pr_aud_err("%s:session id %d: flush cmd failed rc=%d\n", __func__, + audio->ac->session, rc); + return rc; + } + audio->rflush = 1; + audio->wflush = 1; + memset(audio->out_frame_info, 0, sizeof(audio->out_frame_info)); + wake_up(&audio->read_wait); + /* get read_lock to ensure no more waiting read thread */ + mutex_lock(&audio->read_lock); + audio->rflush = 0; + mutex_unlock(&audio->read_lock); + wake_up(&audio->write_wait); + /* get write_lock to ensure no more waiting write thread */ + mutex_lock(&audio->write_lock); + audio->wflush = 0; + mutex_unlock(&audio->write_lock); + pr_debug("%s:session id %d: in_bytes %d\n", __func__, + audio->ac->session, atomic_read(&audio->in_bytes)); + pr_debug("%s:session id %d: in_samples %d\n", __func__, + audio->ac->session, atomic_read(&audio->in_samples)); + atomic_set(&audio->in_bytes, 0); + atomic_set(&audio->in_samples, 0); + return 0; +} + +void audio_in_get_dsp_frames(struct q6audio_in *audio, + uint32_t token, uint32_t *payload) +{ + uint32_t index; + + index = token; + pr_debug("%s:session id %d: index=%d nr frames=%d offset[%d]\n", + __func__, audio->ac->session, token, payload[7], + payload[3]); + pr_debug("%s:session id %d: timemsw=%d lsw=%d\n", __func__, + audio->ac->session, payload[4], payload[5]); + pr_debug("%s:session id %d: uflags=0x%8x uid=0x%8x\n", __func__, + audio->ac->session, payload[6], payload[8]); + pr_debug("%s:session id %d: enc frame size=0x%8x\n", __func__, + audio->ac->session, payload[2]); + + audio->out_frame_info[index][0] = payload[7]; + audio->out_frame_info[index][1] = payload[3]; + + /* statistics of read */ + atomic_add(payload[2], &audio->in_bytes); + atomic_add(payload[7], &audio->in_samples); + + if (atomic_read(&audio->out_count) <= audio->str_cfg.buffer_count) { + atomic_inc(&audio->out_count); + wake_up(&audio->read_wait); + } +} + +/* must be called with audio->lock held */ +int audio_in_enable(struct q6audio_in *audio) +{ + if (audio->enabled) + return 0; + + /* 2nd arg: 0 -> run immediately + 3rd arg: 0 -> msw_ts, 4th arg: 0 ->lsw_ts */ + return q6asm_run(audio->ac, 0x00, 0x00, 0x00); +} + +/* must be called with audio->lock held */ +int audio_in_disable(struct q6audio_in *audio) +{ + int rc = 0; + if (audio->opened) { + audio->enabled = 0; + audio->opened = 0; + pr_debug("%s:session id %d: inbytes[%d] insamples[%d]\n", + __func__, audio->ac->session, + atomic_read(&audio->in_bytes), + atomic_read(&audio->in_samples)); + + rc = q6asm_cmd(audio->ac, CMD_CLOSE); + if (rc < 0) + pr_aud_err("%s:session id %d: Failed to close the\ + session rc=%d\n", __func__, audio->ac->session, + rc); + audio->stopped = 1; + memset(audio->out_frame_info, 0, + sizeof(audio->out_frame_info)); + wake_up(&audio->read_wait); + wake_up(&audio->write_wait); + } + pr_debug("%s:session id %d: enabled[%d]\n", __func__, + audio->ac->session, audio->enabled); + return rc; +} + +int audio_in_buf_alloc(struct q6audio_in *audio) +{ + int rc = 0; + + switch (audio->buf_alloc) { + case NO_BUF_ALLOC: + if (audio->feedback == NON_TUNNEL_MODE) { + rc = q6asm_audio_client_buf_alloc(IN, + audio->ac, + ALIGN_BUF_SIZE(audio->pcm_cfg.buffer_size), + audio->pcm_cfg.buffer_count); + if (rc < 0) { + pr_aud_err("%s:session id %d: Buffer Alloc\ + failed\n", __func__, + audio->ac->session); + rc = -ENOMEM; + break; + } + audio->buf_alloc |= BUF_ALLOC_IN; + } + rc = q6asm_audio_client_buf_alloc(OUT, audio->ac, + ALIGN_BUF_SIZE(audio->str_cfg.buffer_size), + audio->str_cfg.buffer_count); + if (rc < 0) { + pr_aud_err("%s:session id %d: Buffer Alloc failed rc=%d\n", + __func__, audio->ac->session, rc); + rc = -ENOMEM; + break; + } + audio->buf_alloc |= BUF_ALLOC_OUT; + break; + case BUF_ALLOC_IN: + rc = q6asm_audio_client_buf_alloc(OUT, audio->ac, + ALIGN_BUF_SIZE(audio->str_cfg.buffer_size), + audio->str_cfg.buffer_count); + if (rc < 0) { + pr_aud_err("%s:session id %d: Buffer Alloc failed rc=%d\n", + __func__, audio->ac->session, rc); + rc = -ENOMEM; + break; + } + audio->buf_alloc |= BUF_ALLOC_OUT; + break; + case BUF_ALLOC_OUT: + if (audio->feedback == NON_TUNNEL_MODE) { + rc = q6asm_audio_client_buf_alloc(IN, audio->ac, + ALIGN_BUF_SIZE(audio->pcm_cfg.buffer_size), + audio->pcm_cfg.buffer_count); + if (rc < 0) { + pr_aud_err("%s:session id %d: Buffer Alloc\ + failed\n", __func__, + audio->ac->session); + rc = -ENOMEM; + break; + } + audio->buf_alloc |= BUF_ALLOC_IN; + } + break; + default: + pr_debug("%s:session id %d: buf[%d]\n", __func__, + audio->ac->session, audio->buf_alloc); + } + + return rc; +} +/* ------------------- device --------------------- */ +long audio_in_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct q6audio_in *audio = file->private_data; + int rc = 0; + + if (cmd == AUDIO_GET_STATS) { + struct msm_audio_stats stats; + stats.byte_count = atomic_read(&audio->in_bytes); + stats.sample_count = atomic_read(&audio->in_samples); + if (copy_to_user((void *) arg, &stats, sizeof(stats))) + return -EFAULT; + return rc; + } + + mutex_lock(&audio->lock); + switch (cmd) { + case AUDIO_FLUSH: { + /* 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. + */ + rc = audio_in_flush(audio); + if (rc < 0) + pr_aud_err("%s:session id %d: Flush Fail rc=%d\n", + __func__, audio->ac->session, rc); + break; + } + case AUDIO_PAUSE: { + pr_debug("%s:session id %d: AUDIO_PAUSE\n", __func__, + audio->ac->session); + if (audio->enabled) + audio_in_pause(audio); + break; + } + case AUDIO_GET_STREAM_CONFIG: { + struct msm_audio_stream_config cfg; + memset(&cfg, 0, sizeof(cfg)); + cfg.buffer_size = audio->str_cfg.buffer_size; + cfg.buffer_count = audio->str_cfg.buffer_count; + if (copy_to_user((void *)arg, &cfg, sizeof(cfg))) + rc = -EFAULT; + pr_debug("%s:session id %d: AUDIO_GET_STREAM_CONFIG %d %d\n", + __func__, audio->ac->session, cfg.buffer_size, + cfg.buffer_count); + break; + } + case AUDIO_SET_STREAM_CONFIG: { + struct msm_audio_stream_config cfg; + if (copy_from_user(&cfg, (void *)arg, sizeof(cfg))) { + rc = -EFAULT; + break; + } + /* Minimum single frame size, + but with in maximum frames number */ + if ((cfg.buffer_size < (audio->min_frame_size+ \ + sizeof(struct meta_out_dsp))) || + (cfg.buffer_count < FRAME_NUM)) { + rc = -EINVAL; + break; + } + audio->str_cfg.buffer_size = cfg.buffer_size; + audio->str_cfg.buffer_count = cfg.buffer_count; + rc = q6asm_audio_client_buf_alloc(OUT, audio->ac, + ALIGN_BUF_SIZE(audio->str_cfg.buffer_size), + audio->str_cfg.buffer_count); + if (rc < 0) { + pr_aud_err("%s: session id %d: Buffer Alloc failed rc=%d\n", + __func__, audio->ac->session, rc); + rc = -ENOMEM; + break; + } + audio->buf_alloc |= BUF_ALLOC_OUT; + rc = 0; + pr_debug("%s:session id %d: AUDIO_SET_STREAM_CONFIG %d %d\n", + __func__, audio->ac->session, + audio->str_cfg.buffer_size, + audio->str_cfg.buffer_count); + break; + } + case AUDIO_GET_SESSION_ID: { + if (copy_to_user((void *) arg, &audio->ac->session, + sizeof(unsigned short))) { + rc = -EFAULT; + } + break; + } + case AUDIO_SET_BUF_CFG: { + struct msm_audio_buf_cfg cfg; + if (copy_from_user(&cfg, (void *)arg, sizeof(cfg))) { + rc = -EFAULT; + break; + } + if ((audio->feedback == NON_TUNNEL_MODE) && + !cfg.meta_info_enable) { + rc = -EFAULT; + break; + } + + /* Restrict the num of frames per buf to coincide with + * default buf size */ + if (cfg.frames_per_buf > audio->max_frames_per_buf) { + rc = -EFAULT; + break; + } + audio->buf_cfg.meta_info_enable = cfg.meta_info_enable; + audio->buf_cfg.frames_per_buf = cfg.frames_per_buf; + pr_debug("%s:session id %d: Set-buf-cfg: meta[%d]\ + framesperbuf[%d]\n", __func__, + audio->ac->session, cfg.meta_info_enable, + cfg.frames_per_buf); + break; + } + case AUDIO_GET_BUF_CFG: { + pr_debug("%s:session id %d: Get-buf-cfg: meta[%d]\ + framesperbuf[%d]\n", __func__, + audio->ac->session, audio->buf_cfg.meta_info_enable, + audio->buf_cfg.frames_per_buf); + + if (copy_to_user((void *)arg, &audio->buf_cfg, + sizeof(struct msm_audio_buf_cfg))) + rc = -EFAULT; + break; + } + case AUDIO_GET_CONFIG: { + if (copy_to_user((void *)arg, &audio->pcm_cfg, + sizeof(struct msm_audio_config))) + rc = -EFAULT; + break; + + } + case AUDIO_SET_CONFIG: { + struct msm_audio_config cfg; + if (copy_from_user(&cfg, (void *)arg, sizeof(cfg))) { + rc = -EFAULT; + break; + } + if (audio->feedback != NON_TUNNEL_MODE) { + pr_aud_err("%s:session id %d: Not sufficient permission to" + "change the record mode\n", __func__, + audio->ac->session); + rc = -EACCES; + break; + } + if ((cfg.buffer_count > PCM_BUF_COUNT) || + (cfg.buffer_count == 1)) + cfg.buffer_count = PCM_BUF_COUNT; + + audio->pcm_cfg.buffer_count = cfg.buffer_count; + audio->pcm_cfg.buffer_size = cfg.buffer_size; + audio->pcm_cfg.channel_count = cfg.channel_count; + audio->pcm_cfg.sample_rate = cfg.sample_rate; + rc = q6asm_audio_client_buf_alloc(IN, audio->ac, + ALIGN_BUF_SIZE(audio->pcm_cfg.buffer_size), + audio->pcm_cfg.buffer_count); + if (rc < 0) { + pr_aud_err("%s:session id %d: Buffer Alloc failed\n", + __func__, audio->ac->session); + rc = -ENOMEM; + break; + } + audio->buf_alloc |= BUF_ALLOC_IN; + rc = 0; + pr_debug("%s:session id %d: AUDIO_SET_CONFIG %d %d\n", __func__, + audio->ac->session, audio->pcm_cfg.buffer_count, + audio->pcm_cfg.buffer_size); + break; + } + default: + /* call codec specific ioctl */ + rc = audio->enc_ioctl(file, cmd, arg); + } + mutex_unlock(&audio->lock); + return rc; +} + +ssize_t audio_in_read(struct file *file, + char __user *buf, + size_t count, loff_t *pos) +{ + struct q6audio_in *audio = file->private_data; + const char __user *start = buf; + unsigned char *data; + uint32_t offset = 0; + uint32_t size = 0; + int rc = 0; + uint32_t idx; + struct meta_out_dsp meta; + uint32_t bytes_to_copy = 0; + uint32_t mfield_size = (audio->buf_cfg.meta_info_enable == 0) ? 0 : + (sizeof(unsigned char) + + (sizeof(struct meta_out_dsp)*(audio->buf_cfg.frames_per_buf))); + + pr_debug("%s:session id %d: read - %d\n", __func__, audio->ac->session, + count); + if (!audio->enabled) + return -EFAULT; + mutex_lock(&audio->read_lock); + while (count > 0) { + rc = wait_event_interruptible( + audio->read_wait, + ((atomic_read(&audio->out_count) > 0) || + (audio->stopped) || + audio->rflush || audio->eos_rsp)); + + if (rc < 0) + break; + + if ((audio->stopped && !(atomic_read(&audio->out_count))) || + audio->rflush) { + pr_debug("%s:session id %d: driver in stop state or\ + flush,No more buf to read", __func__, + audio->ac->session); + rc = 0;/* End of File */ + break; + } + if (!(atomic_read(&audio->out_count)) && + (audio->eos_rsp == 1) && + (count >= (sizeof(unsigned char) + + sizeof(struct meta_out_dsp)))) { + unsigned char num_of_frames; + pr_aud_info("%s:session id %d: eos %d at output\n", + __func__, audio->ac->session, audio->eos_rsp); + if (buf != start) + break; + num_of_frames = 0xFF; + if (copy_to_user(buf, &num_of_frames, + sizeof(unsigned char))) { + rc = -EFAULT; + break; + } + buf += sizeof(unsigned char); + meta.frame_size = 0xFFFF; + meta.encoded_pcm_samples = 0xFFFF; + meta.msw_ts = 0x00; + meta.lsw_ts = 0x00; + meta.nflags = AUD_EOS_SET; + audio->eos_rsp = 0; + if (copy_to_user(buf, &meta, sizeof(meta))) { + rc = -EFAULT; + break; + } + buf += sizeof(meta); + break; + } + data = (unsigned char *)q6asm_is_cpu_buf_avail(OUT, audio->ac, + &size, &idx); + if ((count >= (size + mfield_size)) && data) { + if (audio->buf_cfg.meta_info_enable) { + if (copy_to_user(buf, + &audio->out_frame_info[idx][0], + sizeof(unsigned char))) { + rc = -EFAULT; + break; + } + bytes_to_copy = + (size + audio->out_frame_info[idx][1]); + /* Number of frames information copied */ + buf += sizeof(unsigned char); + count -= sizeof(unsigned char); + } else { + offset = audio->out_frame_info[idx][1]; + bytes_to_copy = size; + } + + pr_debug("%s:session id %d: offset=%d nr of frames= %d\n", + __func__, audio->ac->session, + audio->out_frame_info[idx][1], + audio->out_frame_info[idx][0]); + + if (copy_to_user(buf, &data[offset], bytes_to_copy)) { + rc = -EFAULT; + break; + } + count -= bytes_to_copy; + buf += bytes_to_copy; + } else { + pr_aud_err("%s:session id %d: short read data[%p]\ + bytesavail[%d]bytesrequest[%d]\n", __func__, + audio->ac->session, + data, size, count); + } + atomic_dec(&audio->out_count); + q6asm_read(audio->ac); + break; + } + mutex_unlock(&audio->read_lock); + + pr_debug("%s:session id %d: read: %d bytes\n", __func__, + audio->ac->session, (buf-start)); + if (buf > start) + return buf - start; + return rc; +} + +static int extract_meta_info(char *buf, unsigned long *msw_ts, + unsigned long *lsw_ts, unsigned int *flags) +{ + struct meta_in *meta = (struct meta_in *)buf; + *msw_ts = meta->ntimestamp.highpart; + *lsw_ts = meta->ntimestamp.lowpart; + *flags = meta->nflags; + return 0; +} + +ssize_t audio_in_write(struct file *file, + const char __user *buf, + size_t count, loff_t *pos) +{ + struct q6audio_in *audio = file->private_data; + const char __user *start = buf; + size_t xfer = 0; + char *cpy_ptr; + int rc = 0; + unsigned char *data; + uint32_t size = 0; + uint32_t idx = 0; + uint32_t nflags = 0; + unsigned long msw_ts = 0; + unsigned long lsw_ts = 0; + uint32_t mfield_size = (audio->buf_cfg.meta_info_enable == 0) ? 0 : + sizeof(struct meta_in); + + pr_debug("%s:session id %d: to write[%d]\n", __func__, + audio->ac->session, count); + if (!audio->enabled) + return -EFAULT; + mutex_lock(&audio->write_lock); + + while (count > 0) { + rc = wait_event_interruptible(audio->write_wait, + ((atomic_read(&audio->in_count) > 0) || + (audio->stopped) || + (audio->wflush))); + if (rc < 0) + break; + if (audio->stopped || audio->wflush) { + pr_debug("%s: session id %d: stop or flush\n", __func__, + audio->ac->session); + rc = -EBUSY; + break; + } + data = (unsigned char *)q6asm_is_cpu_buf_avail(IN, audio->ac, + &size, &idx); + if (!data) { + pr_debug("%s:session id %d: No buf available\n", + __func__, audio->ac->session); + continue; + } + cpy_ptr = data; + if (audio->buf_cfg.meta_info_enable) { + if (buf == start) { + /* Processing beginning of user buffer */ + if (copy_from_user(cpy_ptr, buf, mfield_size)) { + rc = -EFAULT; + break; + } + /* Check if EOS flag is set and buffer has + * contains just meta field + */ + extract_meta_info(cpy_ptr, &msw_ts, &lsw_ts, + &nflags); + buf += mfield_size; + if (count == mfield_size) { + /* send the EOS and return */ + pr_debug("%s:session id %d: send EOS\ + 0x%8x\n", __func__, + audio->ac->session, nflags); + break; + } + count -= mfield_size; + } else { + pr_debug("%s:session id %d: continuous\ + buffer\n", __func__, audio->ac->session); + } + } + xfer = (count > (audio->pcm_cfg.buffer_size)) ? + (audio->pcm_cfg.buffer_size) : count; + + if (copy_from_user(cpy_ptr, buf, xfer)) { + rc = -EFAULT; + break; + } + rc = q6asm_write(audio->ac, xfer, msw_ts, lsw_ts, 0x00); + if (rc < 0) { + rc = -EFAULT; + break; + } + atomic_dec(&audio->in_count); + count -= xfer; + buf += xfer; + } + mutex_unlock(&audio->write_lock); + pr_debug("%s:session id %d: eos_condition 0x%8x buf[0x%x]\ + start[0x%x]\n", __func__, audio->ac->session, + nflags, (int) buf, (int) start); + if (nflags & AUD_EOS_SET) { + rc = q6asm_cmd(audio->ac, CMD_EOS); + pr_aud_info("%s:session id %d: eos %d at input\n", __func__, + audio->ac->session, audio->eos_rsp); + } + pr_debug("%s:session id %d: Written %d Avail Buf[%d]", __func__, + audio->ac->session, (buf - start - mfield_size), + atomic_read(&audio->in_count)); + if (!rc) { + if (buf > start) + return buf - start; + } + return rc; +} + +int audio_in_release(struct inode *inode, struct file *file) +{ + struct q6audio_in *audio = file->private_data; + pr_aud_info("%s: session id %d\n", __func__, audio->ac->session); + mutex_lock(&audio->lock); + audio_in_disable(audio); + q6asm_audio_client_free(audio->ac); + mutex_unlock(&audio->lock); + kfree(audio->enc_cfg); + kfree(audio); + return 0; +} + diff --git a/arch/arm/mach-msm/qdsp6v3/audio_utils.h b/arch/arm/mach-msm/qdsp6v3/audio_utils.h new file mode 100644 index 00000000000..da4a520da4a --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v3/audio_utils.h @@ -0,0 +1,109 @@ +/* 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 + +#define FRAME_NUM (8) + +#define PCM_BUF_COUNT (2) + +#define AUD_EOS_SET 0x01 +#define TUNNEL_MODE 0x0000 +#define NON_TUNNEL_MODE 0x0001 + +#define NO_BUF_ALLOC 0x00 +#define BUF_ALLOC_IN 0x01 +#define BUF_ALLOC_OUT 0x02 +#define BUF_ALLOC_INOUT 0x03 +#define ALIGN_BUF_SIZE(size) ((size + 4095) & (~4095)) + +struct timestamp{ + unsigned long lowpart; + unsigned long highpart; +} __attribute__ ((packed)); + +struct meta_in{ + unsigned short offset; + struct timestamp ntimestamp; + unsigned int nflags; +} __attribute__ ((packed)); + +struct meta_out_dsp{ + u32 offset_to_frame; + u32 frame_size; + u32 encoded_pcm_samples; + u32 msw_ts; + u32 lsw_ts; + u32 nflags; +} __attribute__ ((packed)); + +struct meta_out{ + unsigned char num_of_frames; + struct meta_out_dsp meta_out_dsp[]; +} __attribute__ ((packed)); + +struct q6audio_in{ + spinlock_t dsp_lock; + atomic_t in_bytes; + atomic_t in_samples; + + struct mutex lock; + struct mutex read_lock; + struct mutex write_lock; + wait_queue_head_t read_wait; + wait_queue_head_t write_wait; + + struct audio_client *ac; + struct msm_audio_stream_config str_cfg; + void *enc_cfg; + struct msm_audio_buf_cfg buf_cfg; + struct msm_audio_config pcm_cfg; + void *codec_cfg; + + /* number of buffers available to read/write */ + atomic_t in_count; + atomic_t out_count; + + /* first idx: num of frames per buf, second idx: offset to frame */ + uint32_t out_frame_info[FRAME_NUM][2]; + int eos_rsp; + int opened; + int enabled; + int stopped; + int feedback; /* Flag indicates whether used + in Non Tunnel mode */ + int rflush; + int wflush; + int buf_alloc; + uint16_t min_frame_size; + uint16_t max_frames_per_buf; + long (*enc_ioctl)(struct file *, unsigned int, unsigned long); +}; + +void audio_in_get_dsp_frames(struct q6audio_in *audio, + uint32_t token, uint32_t *payload); +int audio_in_enable(struct q6audio_in *audio); +int audio_in_disable(struct q6audio_in *audio); +int audio_in_buf_alloc(struct q6audio_in *audio); +long audio_in_ioctl(struct file *file, + unsigned int cmd, unsigned long arg); +ssize_t audio_in_read(struct file *file, char __user *buf, + size_t count, loff_t *pos); +ssize_t audio_in_write(struct file *file, const char __user *buf, + size_t count, loff_t *pos); +int audio_in_release(struct inode *inode, struct file *file); + diff --git a/arch/arm/mach-msm/qdsp6v3/audio_wma.c b/arch/arm/mach-msm/qdsp6v3/audio_wma.c new file mode 100644 index 00000000000..f2deb4f5f46 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v3/audio_wma.c @@ -0,0 +1,1585 @@ +/* wma audio output device + * + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * 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 + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT 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 + +#define ADRV_STATUS_AIO_INTF 0x00000001 /* AIO interface */ +#define ADRV_STATUS_FSYNC 0x00000008 +#define ADRV_STATUS_PAUSE 0x00000010 + +#define TUNNEL_MODE 0x0000 +#define NON_TUNNEL_MODE 0x0001 +#define AUDWMA_EOS_SET 0x00000001 + +/* Default number of pre-allocated event packets */ +#define AUDWMA_EVENT_NUM 10 + +#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 - 1; \ + int res = (IN_RANGE(__r1, __v) || IN_RANGE(__r1, __e)); \ + res; \ +}) + +struct timestamp { + unsigned long lowpart; + unsigned long highpart; +} __attribute__ ((packed)); + +struct meta_in { + unsigned char reserved[18]; + unsigned short offset; + struct timestamp ntimestamp; + unsigned int nflags; +} __attribute__ ((packed)); + +struct meta_out_dsp{ + u32 offset_to_frame; + u32 frame_size; + u32 encoded_pcm_samples; + u32 msw_ts; + u32 lsw_ts; + u32 nflags; +} __attribute__ ((packed)); + +struct dec_meta_out{ + unsigned int reserved[7]; + unsigned int num_of_frames; + struct meta_out_dsp meta_out_dsp[]; +} __attribute__ ((packed)); + +/* General meta field to store meta info +locally */ +union meta_data { + struct dec_meta_out meta_out; + struct meta_in meta_in; +} __attribute__ ((packed)); + +struct audwma_event { + struct list_head list; + int event_type; + union msm_audio_event_payload payload; +}; + +struct audwma_pmem_region { + struct list_head list; + struct file *file; + int fd; + void *vaddr; + unsigned long paddr; + unsigned long kvaddr; + unsigned long len; + unsigned ref_cnt; +}; + +struct audwma_buffer_node { + struct list_head list; + struct msm_audio_aio_buf buf; + unsigned long paddr; + unsigned long token; + void *kvaddr; + union meta_data meta_info; +}; + +struct q6audio; + +struct audwma_drv_operations { + void (*out_flush) (struct q6audio *); + void (*in_flush) (struct q6audio *); + int (*fsync)(struct q6audio *); +}; + +#define PCM_BUF_COUNT (2) +/* Buffer with meta */ +#define PCM_BUFSZ_MIN ((4*1024) + sizeof(struct dec_meta_out)) + +/* FRAME_NUM must be a power of two */ +#define FRAME_NUM (2) +#define FRAME_SIZE ((4*1024) + sizeof(struct meta_in)) + +struct q6audio { + atomic_t in_bytes; + atomic_t in_samples; + + struct msm_audio_stream_config str_cfg; + struct msm_audio_buf_cfg buf_cfg; + struct msm_audio_config pcm_cfg; + struct msm_audio_wma_config_v2 wma_config; + + struct audio_client *ac; + + struct mutex lock; + struct mutex read_lock; + struct mutex write_lock; + struct mutex get_event_lock; + wait_queue_head_t cmd_wait; + wait_queue_head_t write_wait; + wait_queue_head_t event_wait; + spinlock_t dsp_lock; + spinlock_t event_queue_lock; + +#ifdef CONFIG_DEBUG_FS + struct dentry *dentry; +#endif + struct list_head out_queue; /* queue to retain output buffers */ + struct list_head in_queue; /* queue to retain input buffers */ + struct list_head free_event_queue; + struct list_head event_queue; + struct list_head pmem_region_queue; /* protected by lock */ + struct audwma_drv_operations drv_ops; + union msm_audio_event_payload eos_write_payload; + + uint32_t drv_status; + int event_abort; + int eos_rsp; + int eos_flag; + int opened; + int enabled; + int stopped; + int feedback; + int rflush; /* Read flush */ + int wflush; /* Write flush */ +}; + +static int insert_eos_buf(struct q6audio *audio, + struct audwma_buffer_node *buf_node) { + struct dec_meta_out *eos_buf = buf_node->kvaddr; + eos_buf->num_of_frames = 0xFFFFFFFF; + eos_buf->meta_out_dsp[0].offset_to_frame = 0x0; + eos_buf->meta_out_dsp[0].nflags = AUDWMA_EOS_SET; + return sizeof(struct dec_meta_out) + + sizeof(eos_buf->meta_out_dsp[0]); +} + +/* Routine which updates read buffers of driver/dsp, + for flush operation as DSP output might not have proper + value set */ +static int insert_meta_data(struct q6audio *audio, + struct audwma_buffer_node *buf_node) { + struct dec_meta_out *meta_data = buf_node->kvaddr; + meta_data->num_of_frames = 0x0; + meta_data->meta_out_dsp[0].offset_to_frame = 0x0; + meta_data->meta_out_dsp[0].nflags = 0x0; + return sizeof(struct dec_meta_out) + + sizeof(meta_data->meta_out_dsp[0]); +} + +static void extract_meta_info(struct q6audio *audio, + struct audwma_buffer_node *buf_node, int dir) +{ + if (dir) { /* Read */ + if (audio->buf_cfg.meta_info_enable) + memcpy(&buf_node->meta_info.meta_in, + (char *)buf_node->kvaddr, sizeof(struct meta_in)); + else + memset(&buf_node->meta_info.meta_in, + 0, sizeof(struct meta_in)); + pr_debug("i/p: msw_ts 0x%lx lsw_ts 0x%lx nflags 0x%8x\n", + buf_node->meta_info.meta_in.ntimestamp.highpart, + buf_node->meta_info.meta_in.ntimestamp.lowpart, + buf_node->meta_info.meta_in.nflags); + } else { /* Write */ + memcpy((char *)buf_node->kvaddr, + &buf_node->meta_info.meta_out, + sizeof(struct dec_meta_out)); + pr_debug("o/p: msw_ts 0x%8x lsw_ts 0x%8x nflags 0x%8x\n", + ((struct dec_meta_out *)buf_node->kvaddr)->\ + meta_out_dsp[0].msw_ts, + ((struct dec_meta_out *)buf_node->kvaddr)->\ + meta_out_dsp[0].lsw_ts, + ((struct dec_meta_out *)buf_node->kvaddr)->\ + meta_out_dsp[0].nflags); + } +} + +static int audwma_pmem_lookup_vaddr(struct q6audio *audio, void *addr, + unsigned long len, struct audwma_pmem_region **region) +{ + struct audwma_pmem_region *region_elt; + + int match_count = 0; + + *region = NULL; + + /* returns physical address or zero */ + list_for_each_entry(region_elt, &audio->pmem_region_queue, list) { + if (addr >= region_elt->vaddr && + addr < region_elt->vaddr + region_elt->len && + addr + 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) { + pr_aud_err("multiple hits for vaddr %p, len %ld\n", addr, len); + list_for_each_entry(region_elt, &audio->pmem_region_queue, + list) { + if (addr >= region_elt->vaddr && + addr < region_elt->vaddr + region_elt->len && + addr + len <= region_elt->vaddr + region_elt->len) + pr_aud_err("\t%p, %ld --> %p\n", region_elt->vaddr, + region_elt->len, + (void *)region_elt->paddr); + } + } + + return *region ? 0 : -1; +} + +static unsigned long audwma_pmem_fixup(struct q6audio *audio, void *addr, + unsigned long len, int ref_up, void **kvaddr) +{ + struct audwma_pmem_region *region; + unsigned long paddr; + int ret; + + ret = audwma_pmem_lookup_vaddr(audio, addr, len, ®ion); + if (ret) { + pr_aud_err("lookup (%p, %ld) failed\n", addr, len); + return 0; + } + if (ref_up) + region->ref_cnt++; + else + region->ref_cnt--; + pr_debug("found region %p ref_cnt %d\n", region, region->ref_cnt); + paddr = region->paddr + (addr - region->vaddr); + /* provide kernel virtual address for accessing meta information */ + if (kvaddr) + *kvaddr = (void *) (region->kvaddr + (addr - region->vaddr)); + return paddr; +} + +static void audwma_post_event(struct q6audio *audio, int type, + union msm_audio_event_payload payload) +{ + struct audwma_event *e_node = NULL; + unsigned long flags; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + + if (!list_empty(&audio->free_event_queue)) { + e_node = list_first_entry(&audio->free_event_queue, + struct audwma_event, list); + list_del(&e_node->list); + } else { + e_node = kmalloc(sizeof(struct audwma_event), GFP_ATOMIC); + if (!e_node) { + pr_aud_err("No mem to post event %d\n", type); + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + return; + } + } + + e_node->event_type = type; + e_node->payload = payload; + + list_add_tail(&e_node->list, &audio->event_queue); + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + wake_up(&audio->event_wait); +} + +static int audwma_enable(struct q6audio *audio) +{ + /* 2nd arg: 0 -> run immediately + 3rd arg: 0 -> msw_ts, 4th arg: 0 ->lsw_ts */ + return q6asm_run(audio->ac, 0x00, 0x00, 0x00); +} + +static int audwma_disable(struct q6audio *audio) +{ + int rc = 0; + if (audio->opened) { + audio->enabled = 0; + audio->opened = 0; + pr_debug("%s: inbytes[%d] insamples[%d]\n", __func__, + atomic_read(&audio->in_bytes), + atomic_read(&audio->in_samples)); + /* Close the session */ + rc = q6asm_cmd(audio->ac, CMD_CLOSE); + if (rc < 0) + pr_aud_err("Failed to close the session rc=%d\n", rc); + audio->stopped = 1; + wake_up(&audio->write_wait); + wake_up(&audio->cmd_wait); + } + pr_debug("enabled[%d]\n", audio->enabled); + return rc; +} + +static int audwma_pause(struct q6audio *audio) +{ + int rc = 0; + + pr_aud_info("%s, enabled = %d\n", __func__, + audio->enabled); + if (audio->enabled) { + rc = q6asm_cmd(audio->ac, CMD_PAUSE); + if (rc < 0) + pr_aud_err("%s: pause cmd failed rc=%d\n", __func__, rc); + + } else + pr_aud_err("%s: Driver not enabled\n", __func__); + return rc; +} + +static int audwma_flush(struct q6audio *audio) +{ + int rc; + + if (audio->enabled) { + /* Implicitly issue a pause to the decoder before flushing if + it is not in pause state */ + if (!(audio->drv_status & ADRV_STATUS_PAUSE)) { + rc = audwma_pause(audio); + if (rc < 0) + pr_aud_err("%s: pause cmd failed rc=%d\n", __func__, + rc); + else + audio->drv_status |= ADRV_STATUS_PAUSE; + } + rc = q6asm_cmd(audio->ac, CMD_FLUSH); + if (rc < 0) + pr_aud_err("%s: flush cmd failed rc=%d\n", __func__, rc); + /* Not in stop state, reenable the stream */ + if (audio->stopped == 0) { + rc = audwma_enable(audio); + if (rc) + pr_aud_err("%s:audio re-enable failed\n", __func__); + else { + audio->enabled = 1; + if (audio->drv_status & ADRV_STATUS_PAUSE) + audio->drv_status &= ~ADRV_STATUS_PAUSE; + } + } + } + pr_debug("in_bytes %d\n", atomic_read(&audio->in_bytes)); + pr_debug("in_samples %d\n", atomic_read(&audio->in_samples)); + atomic_set(&audio->in_bytes, 0); + atomic_set(&audio->in_samples, 0); + return 0; +} + +static void audwma_async_read(struct q6audio *audio, + struct audwma_buffer_node *buf_node) +{ + struct audio_client *ac; + struct audio_aio_read_param param; + int rc; + + pr_debug("%s: Send read buff %p phy %lx len %d\n", __func__, buf_node, + buf_node->paddr, buf_node->buf.buf_len); + ac = audio->ac; + /* Provide address so driver can append nr frames information */ + param.paddr = buf_node->paddr + + sizeof(struct dec_meta_out); + param.len = buf_node->buf.buf_len - + sizeof(struct dec_meta_out); + param.uid = param.paddr; + /* Write command will populate paddr as token */ + buf_node->token = param.paddr; + rc = q6asm_async_read(ac, ¶m); + if (rc < 0) + pr_aud_err("%s:failed\n", __func__); +} + +static void audwma_async_write(struct q6audio *audio, + struct audwma_buffer_node *buf_node) +{ + int rc; + struct audio_client *ac; + struct audio_aio_write_param param; + + pr_debug("%s: Send write buff %p phy %lx len %d\n", __func__, buf_node, + buf_node->paddr, buf_node->buf.data_len); + + ac = audio->ac; + /* Offset with appropriate meta */ + param.paddr = buf_node->paddr + sizeof(struct meta_in); + param.len = buf_node->buf.data_len - sizeof(struct meta_in); + param.msw_ts = buf_node->meta_info.meta_in.ntimestamp.highpart; + param.lsw_ts = buf_node->meta_info.meta_in.ntimestamp.lowpart; + /* If no meta_info enaled, indicate no time stamp valid */ + if (audio->buf_cfg.meta_info_enable) + param.flags = 0; + else + param.flags = 0xFF00; + param.uid = param.paddr; + /* Read command will populate paddr as token */ + buf_node->token = param.paddr; + rc = q6asm_async_write(ac, ¶m); + if (rc < 0) + pr_aud_err("%s:failed\n", __func__); +} + +/* Write buffer to DSP / Handle Ack from DSP */ +static void audwma_async_write_ack(struct q6audio *audio, uint32_t token, + uint32_t *payload) +{ + unsigned long flags; + union msm_audio_event_payload event_payload; + struct audwma_buffer_node *used_buf; + + /* No active flush in progress */ + if (audio->wflush) + return; + + spin_lock_irqsave(&audio->dsp_lock, flags); + BUG_ON(list_empty(&audio->out_queue)); + used_buf = list_first_entry(&audio->out_queue, + struct audwma_buffer_node, list); + if (token == used_buf->token) { + list_del(&used_buf->list); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + pr_debug("consumed buffer\n"); + event_payload.aio_buf = used_buf->buf; + audwma_post_event(audio, AUDIO_EVENT_WRITE_DONE, + event_payload); + kfree(used_buf); + if (list_empty(&audio->out_queue) && + (audio->drv_status & ADRV_STATUS_FSYNC)) { + pr_debug("%s: list is empty, reached EOS in\ + Tunnel\n", __func__); + wake_up(&audio->write_wait); + } + } else { + pr_aud_err("expected=%lx ret=%x\n", used_buf->token, token); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + } +} + +/* Read buffer from DSP / Handle Ack from DSP */ +static void audwma_async_read_ack(struct q6audio *audio, uint32_t token, + uint32_t *payload) +{ + unsigned long flags; + union msm_audio_event_payload event_payload; + struct audwma_buffer_node *filled_buf; + + /* No active flush in progress */ + if (audio->rflush) + return; + + /* Statistics of read */ + atomic_add(payload[2], &audio->in_bytes); + atomic_add(payload[7], &audio->in_samples); + + spin_lock_irqsave(&audio->dsp_lock, flags); + BUG_ON(list_empty(&audio->in_queue)); + filled_buf = list_first_entry(&audio->in_queue, + struct audwma_buffer_node, list); + if (token == (filled_buf->token)) { + list_del(&filled_buf->list); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + event_payload.aio_buf = filled_buf->buf; + /* Read done Buffer due to flush/normal condition + after EOS event, so append EOS buffer */ + if (audio->eos_rsp == 0x1) { + event_payload.aio_buf.data_len = + insert_eos_buf(audio, filled_buf); + /* Reset flag back to indicate eos intimated */ + audio->eos_rsp = 0; + } else { + filled_buf->meta_info.meta_out.num_of_frames = + payload[7]; + pr_debug("nr of frames 0x%8x\n", + filled_buf->meta_info.meta_out.num_of_frames); + event_payload.aio_buf.data_len = payload[2] + \ + payload[3] + \ + sizeof(struct dec_meta_out); + extract_meta_info(audio, filled_buf, 0); + audio->eos_rsp = 0; + } + audwma_post_event(audio, AUDIO_EVENT_READ_DONE, event_payload); + kfree(filled_buf); + } else { + pr_aud_err("expected=%lx ret=%x\n", filled_buf->token, token); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + } +} + +static void q6_audwma_cb(uint32_t opcode, uint32_t token, + uint32_t *payload, void *priv) +{ + struct q6audio *audio = (struct q6audio *)priv; + + switch (opcode) { + case ASM_DATA_EVENT_WRITE_DONE: + pr_debug("%s:ASM_DATA_EVENT_WRITE_DONE token = 0x%x\n", + __func__, token); + audwma_async_write_ack(audio, token, payload); + break; + case ASM_DATA_EVENT_READ_DONE: + pr_debug("%s:ASM_DATA_EVENT_READ_DONE token = 0x%x\n", + __func__, token); + audwma_async_read_ack(audio, token, payload); + break; + case ASM_DATA_CMDRSP_EOS: + /* EOS Handle */ + pr_debug("%s:ASM_DATA_CMDRSP_EOS\n", __func__); + if (audio->feedback) { /* Non-Tunnel mode */ + audio->eos_rsp = 1; + /* propagate input EOS i/p buffer, + after receiving DSP acknowledgement */ + if (audio->eos_flag && + (audio->eos_write_payload.aio_buf.buf_addr)) { + audwma_post_event(audio, AUDIO_EVENT_WRITE_DONE, + audio->eos_write_payload); + memset(&audio->eos_write_payload , 0, + sizeof(union msm_audio_event_payload)); + audio->eos_flag = 0; + } + } else { /* Tunnel mode */ + audio->eos_rsp = 1; + wake_up(&audio->write_wait); + wake_up(&audio->cmd_wait); + } + break; + default: + pr_debug("%s:Unhandled event = 0x%8x\n", __func__, opcode); + break; + } +} + +/* ------------------- device --------------------- */ +static void audwma_async_out_flush(struct q6audio *audio) +{ + struct audwma_buffer_node *buf_node; + struct list_head *ptr, *next; + union msm_audio_event_payload payload; + unsigned long flags; + + pr_debug("%s\n", __func__); + /* EOS followed by flush, EOS response not guranteed, free EOS i/p + buffer */ + spin_lock_irqsave(&audio->dsp_lock, flags); + if (audio->eos_flag && (audio->eos_write_payload.aio_buf.buf_addr)) { + pr_debug("%s: EOS followed by flush received,acknowledge eos"\ + " i/p buffer immediately\n", __func__); + audwma_post_event(audio, AUDIO_EVENT_WRITE_DONE, + audio->eos_write_payload); + memset(&audio->eos_write_payload , 0, + sizeof(union msm_audio_event_payload)); + } + spin_unlock_irqrestore(&audio->dsp_lock, flags); + list_for_each_safe(ptr, next, &audio->out_queue) { + buf_node = list_entry(ptr, struct audwma_buffer_node, list); + list_del(&buf_node->list); + payload.aio_buf = buf_node->buf; + audwma_post_event(audio, AUDIO_EVENT_WRITE_DONE, payload); + kfree(buf_node); + pr_debug("%s: Propagate WRITE_DONE during flush\n", __func__); + } +} + +static void audwma_async_in_flush(struct q6audio *audio) +{ + struct audwma_buffer_node *buf_node; + struct list_head *ptr, *next; + union msm_audio_event_payload payload; + + pr_debug("%s\n", __func__); + list_for_each_safe(ptr, next, &audio->in_queue) { + buf_node = list_entry(ptr, struct audwma_buffer_node, list); + list_del(&buf_node->list); + /* Forcefull send o/p eos buffer after flush, if no eos response + * received by dsp even after sending eos command */ + if ((audio->eos_rsp != 1) && audio->eos_flag) { + pr_debug("%s: send eos on o/p buffer during flush\n",\ + __func__); + payload.aio_buf = buf_node->buf; + payload.aio_buf.data_len = + insert_eos_buf(audio, buf_node); + audio->eos_flag = 0; + } else { + payload.aio_buf = buf_node->buf; + payload.aio_buf.data_len = + insert_meta_data(audio, buf_node); + } + audwma_post_event(audio, AUDIO_EVENT_READ_DONE, payload); + kfree(buf_node); + pr_debug("%s: Propagate READ_DONE during flush\n", __func__); + } +} + +static void audwma_ioport_reset(struct q6audio *audio) +{ + if (audio->drv_status & ADRV_STATUS_AIO_INTF) { + /* If fsync is in progress, make sure + * return value of fsync indicates + * abort due to flush + */ + if (audio->drv_status & ADRV_STATUS_FSYNC) { + pr_debug("fsync in progress\n"); + audio->drv_ops.out_flush(audio); + } else + audio->drv_ops.out_flush(audio); + audio->drv_ops.in_flush(audio); + } +} + +static int audwma_events_pending(struct q6audio *audio) +{ + unsigned long flags; + int empty; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + empty = !list_empty(&audio->event_queue); + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + return empty || audio->event_abort; +} + +static void audwma_reset_event_queue(struct q6audio *audio) +{ + unsigned long flags; + struct audwma_event *drv_evt; + struct list_head *ptr, *next; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + list_for_each_safe(ptr, next, &audio->event_queue) { + drv_evt = list_first_entry(&audio->event_queue, + struct audwma_event, list); + list_del(&drv_evt->list); + kfree(drv_evt); + } + list_for_each_safe(ptr, next, &audio->free_event_queue) { + drv_evt = list_first_entry(&audio->free_event_queue, + struct audwma_event, list); + list_del(&drv_evt->list); + kfree(drv_evt); + } + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + + return; +} + +static long audwma_process_event_req(struct q6audio *audio, void __user * arg) +{ + long rc; + struct msm_audio_event usr_evt; + struct audwma_event *drv_evt = NULL; + int timeout; + unsigned long flags; + + if (copy_from_user(&usr_evt, arg, sizeof(struct msm_audio_event))) + return -EFAULT; + + timeout = (int)usr_evt.timeout_ms; + + if (timeout > 0) { + rc = wait_event_interruptible_timeout(audio->event_wait, + audwma_events_pending + (audio), + msecs_to_jiffies + (timeout)); + if (rc == 0) + return -ETIMEDOUT; + } else { + rc = wait_event_interruptible(audio->event_wait, + audwma_events_pending(audio)); + } + + if (rc < 0) + return rc; + + if (audio->event_abort) { + audio->event_abort = 0; + return -ENODEV; + } + + rc = 0; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + if (!list_empty(&audio->event_queue)) { + drv_evt = list_first_entry(&audio->event_queue, + struct audwma_event, list); + list_del(&drv_evt->list); + } + if (drv_evt) { + usr_evt.event_type = drv_evt->event_type; + usr_evt.event_payload = drv_evt->payload; + list_add_tail(&drv_evt->list, &audio->free_event_queue); + } else { + pr_aud_err("Unexpected path\n"); + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + return -EPERM; + } + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + + if (drv_evt->event_type == AUDIO_EVENT_WRITE_DONE) { + pr_debug("posted AUDIO_EVENT_WRITE_DONE to user\n"); + mutex_lock(&audio->write_lock); + audwma_pmem_fixup(audio, drv_evt->payload.aio_buf.buf_addr, + drv_evt->payload.aio_buf.buf_len, 0, 0); + mutex_unlock(&audio->write_lock); + } else if (drv_evt->event_type == AUDIO_EVENT_READ_DONE) { + pr_debug("posted AUDIO_EVENT_READ_DONE to user\n"); + mutex_lock(&audio->read_lock); + audwma_pmem_fixup(audio, drv_evt->payload.aio_buf.buf_addr, + drv_evt->payload.aio_buf.buf_len, 0, 0); + mutex_unlock(&audio->read_lock); + } + + /* Some read buffer might be held up in DSP,release all + Once EOS indicated*/ + if (audio->eos_rsp && !list_empty(&audio->in_queue)) { + pr_debug("Send flush command to release read buffers"\ + "held up in DSP\n"); + audwma_flush(audio); + } + + if (copy_to_user(arg, &usr_evt, sizeof(usr_evt))) + rc = -EFAULT; + + return rc; +} + +static int audwma_pmem_check(struct q6audio *audio, + void *vaddr, unsigned long len) +{ + struct audwma_pmem_region *region_elt; + struct audwma_pmem_region t = {.vaddr = vaddr, .len = len }; + + list_for_each_entry(region_elt, &audio->pmem_region_queue, list) { + if (CONTAINS(region_elt, &t) || CONTAINS(&t, region_elt) || + OVERLAPS(region_elt, &t)) { + pr_aud_err("region (vaddr %p len %ld)" + " clashes with registered region" + " (vaddr %p paddr %p len %ld)\n", + vaddr, len, + region_elt->vaddr, + (void *)region_elt->paddr, region_elt->len); + return -EINVAL; + } + } + + return 0; +} + +static int audwma_pmem_add(struct q6audio *audio, + struct msm_audio_pmem_info *info) +{ + unsigned long paddr, kvaddr, len; + struct file *file; + struct audwma_pmem_region *region; + int rc = -EINVAL; + + pr_debug("%s:\n", __func__); + region = kmalloc(sizeof(*region), GFP_KERNEL); + + if (!region) { + rc = -ENOMEM; + goto end; + } + + if (get_pmem_file(info->fd, &paddr, &kvaddr, &len, &file)) { + kfree(region); + goto end; + } + + rc = audwma_pmem_check(audio, info->vaddr, len); + if (rc < 0) { + put_pmem_file(file); + kfree(region); + goto end; + } + + region->vaddr = info->vaddr; + region->fd = info->fd; + region->paddr = paddr; + region->kvaddr = kvaddr; + region->len = len; + region->file = file; + region->ref_cnt = 0; + pr_debug("add region paddr %lx vaddr %p, len %lu kvaddr %lx\n", + region->paddr, region->vaddr, region->len, region->kvaddr); + list_add_tail(®ion->list, &audio->pmem_region_queue); + + rc = q6asm_memory_map(audio->ac, (uint32_t) paddr, IN, (uint32_t) len, + 1); + if (rc < 0) + pr_aud_err("%s: memory map failed\n", __func__); +end: + return rc; +} + +static int audwma_pmem_remove(struct q6audio *audio, + struct msm_audio_pmem_info *info) +{ + struct audwma_pmem_region *region; + struct list_head *ptr, *next; + int rc = -EINVAL; + + pr_debug("info fd %d vaddr %p\n", info->fd, info->vaddr); + + list_for_each_safe(ptr, next, &audio->pmem_region_queue) { + region = list_entry(ptr, struct audwma_pmem_region, list); + + if ((region->fd == info->fd) && + (region->vaddr == info->vaddr)) { + if (region->ref_cnt) { + pr_debug("region %p in use ref_cnt %d\n", + region, region->ref_cnt); + break; + } + pr_debug("remove region fd %d vaddr %p\n", + info->fd, info->vaddr); + rc = q6asm_memory_unmap(audio->ac, + (uint32_t) region->paddr, IN); + if (rc < 0) + pr_aud_err("%s: memory unmap failed\n", __func__); + + list_del(®ion->list); + put_pmem_file(region->file); + kfree(region); + rc = 0; + break; + } + } + + return rc; +} + +/* audio -> lock must be held at this point */ +static int audwma_aio_buf_add(struct q6audio *audio, unsigned dir, + void __user *arg) +{ + unsigned long flags; + struct audwma_buffer_node *buf_node; + + buf_node = kzalloc(sizeof(*buf_node), GFP_KERNEL); + + if (!buf_node) + return -ENOMEM; + + if (copy_from_user(&buf_node->buf, arg, sizeof(buf_node->buf))) { + kfree(buf_node); + return -EFAULT; + } + + pr_debug("node %p dir %x buf_addr %p buf_len %d data_len \ + %d\n", buf_node, dir, buf_node->buf.buf_addr, + buf_node->buf.buf_len, buf_node->buf.data_len); + + buf_node->paddr = audwma_pmem_fixup(audio, buf_node->buf.buf_addr, + buf_node->buf.buf_len, 1, + &buf_node->kvaddr); + if (dir) { + /* write */ + if (!buf_node->paddr || + (buf_node->paddr & 0x1) || + (!audio->feedback && !buf_node->buf.data_len)) { + kfree(buf_node); + return -EINVAL; + } + extract_meta_info(audio, buf_node, 1); + /* Not a EOS buffer */ + if (!(buf_node->meta_info.meta_in.nflags & AUDWMA_EOS_SET)) { + spin_lock_irqsave(&audio->dsp_lock, flags); + audwma_async_write(audio, buf_node); + /* EOS buffer handled in driver */ + list_add_tail(&buf_node->list, &audio->out_queue); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + } + if (buf_node->meta_info.meta_in.nflags & AUDWMA_EOS_SET) { + if (!audio->wflush) { + pr_debug("%s:Send EOS cmd at i/p\n", __func__); + /* Driver will forcefully post writedone event + once eos ack recived from DSP*/ + audio->eos_write_payload.aio_buf =\ + buf_node->buf; + audio->eos_flag = 1; + audio->eos_rsp = 0; + q6asm_cmd(audio->ac, CMD_EOS); + kfree(buf_node); + } else { /* Flush in progress, send back i/p EOS buffer + as is */ + union msm_audio_event_payload event_payload; + event_payload.aio_buf = buf_node->buf; + audwma_post_event(audio, AUDIO_EVENT_WRITE_DONE, + event_payload); + kfree(buf_node); + } + } + } else { + /* read */ + if (!buf_node->paddr || + (buf_node->paddr & 0x1) || + (buf_node->buf.buf_len < PCM_BUFSZ_MIN)) { + kfree(buf_node); + return -EINVAL; + } + /* No EOS reached */ + if (!audio->eos_rsp) { + spin_lock_irqsave(&audio->dsp_lock, flags); + audwma_async_read(audio, buf_node); + /* EOS buffer handled in driver */ + list_add_tail(&buf_node->list, &audio->in_queue); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + } + /* EOS reached at input side fake all upcoming read buffer to + indicate the same */ + else { + union msm_audio_event_payload event_payload; + event_payload.aio_buf = buf_node->buf; + event_payload.aio_buf.data_len = + insert_eos_buf(audio, buf_node); + pr_debug("%s: propagate READ_DONE as EOS done\n",\ + __func__); + audwma_post_event(audio, AUDIO_EVENT_READ_DONE, + event_payload); + kfree(buf_node); + } + } + return 0; +} + +/* TBD: Only useful in tunnel-mode */ +int audwma_async_fsync(struct q6audio *audio) +{ + int rc = 0; + + /* Blocking client sends more data */ + mutex_lock(&audio->lock); + audio->drv_status |= ADRV_STATUS_FSYNC; + mutex_unlock(&audio->lock); + + pr_aud_info("%s:\n", __func__); + + mutex_lock(&audio->write_lock); + audio->eos_rsp = 0; + + rc = wait_event_interruptible(audio->write_wait, + (list_empty(&audio->out_queue)) || + audio->wflush || audio->stopped); + + if (rc < 0) { + pr_aud_err("%s: wait event for list_empty failed, rc = %d\n", + __func__, rc); + goto done; + } + + rc = q6asm_cmd(audio->ac, CMD_EOS); + + if (rc < 0) + pr_aud_err("%s: q6asm_cmd failed, rc = %d", __func__, rc); + + rc = wait_event_interruptible(audio->write_wait, + (audio->eos_rsp || audio->wflush || + audio->stopped)); + + if (rc < 0) { + pr_aud_err("%s: wait event for eos_rsp failed, rc = %d\n", __func__, + rc); + goto done; + } + + if (audio->eos_rsp == 1) { + rc = audwma_enable(audio); + if (rc) + pr_aud_err("%s: audio enable failed\n", __func__); + else { + audio->drv_status &= ~ADRV_STATUS_PAUSE; + audio->enabled = 1; + } + } + + if (audio->stopped || audio->wflush) + rc = -EBUSY; + +done: + mutex_unlock(&audio->write_lock); + mutex_lock(&audio->lock); + audio->drv_status &= ~ADRV_STATUS_FSYNC; + mutex_unlock(&audio->lock); + + return rc; +} + +int audwma_fsync(struct file *file, int datasync) +{ + struct q6audio *audio = file->private_data; + + if (!audio->enabled || audio->feedback) + return -EINVAL; + + return audio->drv_ops.fsync(audio); +} + +static void audwma_reset_pmem_region(struct q6audio *audio) +{ + struct audwma_pmem_region *region; + struct list_head *ptr, *next; + + list_for_each_safe(ptr, next, &audio->pmem_region_queue) { + region = list_entry(ptr, struct audwma_pmem_region, list); + list_del(®ion->list); + put_pmem_file(region->file); + kfree(region); + } + + return; +} + +#ifdef CONFIG_DEBUG_FS +static ssize_t audwma_debug_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static ssize_t audwma_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 q6audio *audio = file->private_data; + + mutex_lock(&audio->lock); + n = scnprintf(buffer, debug_bufmax, "opened %d\n", audio->opened); + n += scnprintf(buffer + n, debug_bufmax - n, + "enabled %d\n", audio->enabled); + n += scnprintf(buffer + n, debug_bufmax - n, + "stopped %d\n", audio->stopped); + n += scnprintf(buffer + n, debug_bufmax - n, + "feedback %d\n", audio->feedback); + mutex_unlock(&audio->lock); + /* Following variables are only useful for debugging when + * when playback halts unexpectedly. Thus, no mutual exclusion + * enforced + */ + n += scnprintf(buffer + n, debug_bufmax - n, + "wflush %d\n", audio->wflush); + n += scnprintf(buffer + n, debug_bufmax - n, + "rflush %d\n", audio->rflush); + n += scnprintf(buffer + n, debug_bufmax - n, + "inqueue empty %d\n", list_empty(&audio->in_queue)); + n += scnprintf(buffer + n, debug_bufmax - n, + "outqueue empty %d\n", list_empty(&audio->out_queue)); + buffer[n] = 0; + return simple_read_from_buffer(buf, count, ppos, buffer, n); +} + +static const struct file_operations audwma_debug_fops = { + .read = audwma_debug_read, + .open = audwma_debug_open, +}; +#endif + +static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct q6audio *audio = file->private_data; + int rc = 0; + + if (cmd == AUDIO_GET_STATS) { + struct msm_audio_stats stats; + stats.byte_count = atomic_read(&audio->in_bytes); + stats.sample_count = atomic_read(&audio->in_samples); + if (copy_to_user((void *)arg, &stats, sizeof(stats))) + return -EFAULT; + return rc; + } + + if (cmd == AUDIO_GET_EVENT) { + pr_debug("AUDIO_GET_EVENT\n"); + if (mutex_trylock(&audio->get_event_lock)) { + rc = audwma_process_event_req(audio, + (void __user *)arg); + mutex_unlock(&audio->get_event_lock); + } else + rc = -EBUSY; + return rc; + } + + if (cmd == AUDIO_ASYNC_WRITE) { + mutex_lock(&audio->write_lock); + if (audio->drv_status & ADRV_STATUS_FSYNC) + rc = -EBUSY; + else { + if (audio->enabled) + rc = audwma_aio_buf_add(audio, 1, + (void __user *)arg); + else + rc = -EPERM; + } + mutex_unlock(&audio->write_lock); + return rc; + } + + if (cmd == AUDIO_ASYNC_READ) { + mutex_lock(&audio->read_lock); + if ((audio->feedback) && (audio->enabled)) + rc = audwma_aio_buf_add(audio, 0, + (void __user *)arg); + else + rc = -EPERM; + mutex_unlock(&audio->read_lock); + return rc; + } + + if (cmd == AUDIO_ABORT_GET_EVENT) { + audio->event_abort = 1; + wake_up(&audio->event_wait); + return 0; + } + + mutex_lock(&audio->lock); + switch (cmd) { + case AUDIO_START: { + struct asm_wma_cfg wma_cfg; + if (audio->feedback == NON_TUNNEL_MODE) { + /* Configure PCM output block */ + rc = q6asm_enc_cfg_blk_pcm(audio->ac, + audio->pcm_cfg.sample_rate, + audio->pcm_cfg.channel_count); + if (rc < 0) { + pr_aud_err("pcm output block config failed\n"); + break; + } + } + wma_cfg.format_tag = audio->wma_config.format_tag; + wma_cfg.ch_cfg = audio->wma_config.numchannels; + wma_cfg.sample_rate = audio->wma_config.samplingrate; + wma_cfg.avg_bytes_per_sec = audio->wma_config.avgbytespersecond; + wma_cfg.block_align = audio->wma_config.block_align; + wma_cfg.valid_bits_per_sample = + audio->wma_config.validbitspersample; + wma_cfg.ch_mask = audio->wma_config.channelmask; + wma_cfg.encode_opt = audio->wma_config.encodeopt; + /* Configure Media format block */ + rc = q6asm_media_format_block_wma(audio->ac, &wma_cfg); + if (rc < 0) { + pr_aud_err("cmd media format block failed\n"); + break; + } + rc = audwma_enable(audio); + audio->eos_rsp = 0; + audio->eos_flag = 0; + if (!rc) { + audio->enabled = 1; + } else { + audio->enabled = 0; + pr_aud_err("Audio Start procedure failed rc=%d\n", rc); + break; + } + pr_debug("AUDIO_START success enable[%d]\n", audio->enabled); + if (audio->stopped == 1) + audio->stopped = 0; + break; + } + case AUDIO_STOP: { + pr_debug("AUDIO_STOP\n"); + audio->stopped = 1; + audwma_flush(audio); + audio->enabled = 0; + audio->drv_status &= ~ADRV_STATUS_PAUSE; + if (rc < 0) { + pr_aud_err("Audio Stop procedure failed rc=%d\n", rc); + break; + } + break; + } + case AUDIO_PAUSE: { + pr_debug("AUDIO_PAUSE %ld\n", arg); + if (arg == 1) { + rc = audwma_pause(audio); + if (rc < 0) + pr_aud_err("%s: pause FAILED rc=%d\n", __func__, + rc); + audio->drv_status |= ADRV_STATUS_PAUSE; + } else if (arg == 0) { + if (audio->drv_status & ADRV_STATUS_PAUSE) { + rc = audwma_enable(audio); + if (rc) + pr_aud_err("%s: audio enable failed\n", + __func__); + else { + audio->drv_status &= ~ADRV_STATUS_PAUSE; + audio->enabled = 1; + } + } + } + break; + } + case AUDIO_FLUSH: { + pr_debug("AUDIO_FLUSH\n"); + audio->rflush = 1; + audio->wflush = 1; + /* Flush DSP */ + rc = audwma_flush(audio); + /* Flush input / Output buffer in software*/ + audwma_ioport_reset(audio); + if (rc < 0) { + pr_aud_err("AUDIO_FLUSH interrupted\n"); + rc = -EINTR; + } else { + audio->rflush = 0; + audio->wflush = 0; + } + audio->eos_flag = 0; + audio->eos_rsp = 0; + break; + } + case AUDIO_REGISTER_PMEM: { + struct msm_audio_pmem_info info; + pr_debug("AUDIO_REGISTER_PMEM\n"); + if (copy_from_user(&info, (void *)arg, sizeof(info))) + rc = -EFAULT; + else + rc = audwma_pmem_add(audio, &info); + break; + } + case AUDIO_DEREGISTER_PMEM: { + struct msm_audio_pmem_info info; + pr_debug("AUDIO_DEREGISTER_PMEM\n"); + if (copy_from_user(&info, (void *)arg, sizeof(info))) + rc = -EFAULT; + else + rc = audwma_pmem_remove(audio, &info); + break; + } + case AUDIO_GET_WMA_CONFIG_V2: { + if (copy_to_user((void *)arg, &audio->wma_config, + sizeof(struct msm_audio_wma_config_v2))) { + rc = -EFAULT; + break; + } + break; + } + case AUDIO_SET_WMA_CONFIG_V2: { + if (copy_from_user(&audio->wma_config, (void *)arg, + sizeof(struct msm_audio_wma_config_v2))) { + rc = -EFAULT; + break; + } + break; + } + case AUDIO_GET_STREAM_CONFIG: { + struct msm_audio_stream_config cfg; + memset(&cfg, 0, sizeof(cfg)); + cfg.buffer_size = audio->str_cfg.buffer_size; + cfg.buffer_count = audio->str_cfg.buffer_count; + pr_debug("GET STREAM CFG %d %d\n", cfg.buffer_size, + cfg.buffer_count); + if (copy_to_user((void *)arg, &cfg, sizeof(cfg))) + rc = -EFAULT; + break; + } + case AUDIO_SET_STREAM_CONFIG: { + struct msm_audio_stream_config cfg; + pr_debug("SET STREAM CONFIG\n"); + if (copy_from_user(&cfg, (void *)arg, sizeof(cfg))) { + rc = -EFAULT; + break; + } + audio->str_cfg.buffer_size = FRAME_SIZE; + audio->str_cfg.buffer_count = FRAME_NUM; + rc = 0; + break; + } + case AUDIO_GET_CONFIG: { + struct msm_audio_config cfg; + if (copy_to_user((void *)arg, &audio->pcm_cfg, sizeof(cfg))) + rc = -EFAULT; + break; + } + case AUDIO_SET_CONFIG: { + struct msm_audio_config config; + if (copy_from_user(&config, (void *)arg, sizeof(config))) { + rc = -EFAULT; + break; + } + if (audio->feedback != NON_TUNNEL_MODE) { + pr_aud_err("Not sufficient permission to" + "change the playback mode\n"); + rc = -EACCES; + break; + } + if ((config.buffer_count > PCM_BUF_COUNT) || + (config.buffer_count == 1)) + config.buffer_count = PCM_BUF_COUNT; + + if (config.buffer_size < PCM_BUFSZ_MIN) + config.buffer_size = PCM_BUFSZ_MIN; + + audio->pcm_cfg.buffer_count = config.buffer_count; + audio->pcm_cfg.buffer_size = config.buffer_size; + audio->pcm_cfg.channel_count = config.channel_count; + audio->pcm_cfg.sample_rate = config.sample_rate; + rc = 0; + break; + } + case AUDIO_SET_BUF_CFG: { + struct msm_audio_buf_cfg cfg; + if (copy_from_user(&cfg, (void *)arg, sizeof(cfg))) { + rc = -EFAULT; + break; + } + if ((audio->feedback == NON_TUNNEL_MODE) && + !cfg.meta_info_enable) { + rc = -EFAULT; + break; + } + + audio->buf_cfg.meta_info_enable = cfg.meta_info_enable; + pr_debug("%s:session id %d: Set-buf-cfg: meta[%d]", __func__, + audio->ac->session, cfg.meta_info_enable); + break; + } + case AUDIO_GET_BUF_CFG: { + pr_debug("%s:session id %d: Get-buf-cfg: meta[%d]\ + framesperbuf[%d]\n", __func__, + audio->ac->session, audio->buf_cfg.meta_info_enable, + audio->buf_cfg.frames_per_buf); + + if (copy_to_user((void *)arg, &audio->buf_cfg, + sizeof(struct msm_audio_buf_cfg))) + rc = -EFAULT; + break; + } + case AUDIO_GET_SESSION_ID: { + if (copy_to_user((void *)arg, &audio->ac->session, + sizeof(unsigned short))) { + rc = -EFAULT; + } + break; + } + default: + rc = -EINVAL; + } + mutex_unlock(&audio->lock); + return rc; +} + +static int audio_release(struct inode *inode, struct file *file) +{ + struct q6audio *audio = file->private_data; + mutex_lock(&audio->lock); + audwma_disable(audio); + audio->drv_ops.out_flush(audio); + audio->drv_ops.in_flush(audio); + audwma_reset_pmem_region(audio); + audio->event_abort = 1; + wake_up(&audio->event_wait); + audwma_reset_event_queue(audio); + q6asm_audio_client_free(audio->ac); + mutex_unlock(&audio->lock); + mutex_destroy(&audio->lock); + mutex_destroy(&audio->read_lock); + mutex_destroy(&audio->write_lock); + mutex_destroy(&audio->get_event_lock); +#ifdef CONFIG_DEBUG_FS + if (audio->dentry) + debugfs_remove(audio->dentry); +#endif + kfree(audio); + pr_aud_info("%s: wma_decoder success\n", __func__); + return 0; +} + +static int audio_open(struct inode *inode, struct file *file) +{ + struct q6audio *audio = NULL; + int rc = 0; + int i; + struct audwma_event *e_node = NULL; + +#ifdef CONFIG_DEBUG_FS + /* 4 bytes represents decoder number, 1 byte for terminate string */ + char name[sizeof "msm_wma_" + 5]; +#endif + audio = kzalloc(sizeof(struct q6audio), GFP_KERNEL); + + if (audio == NULL) { + pr_aud_err("Could not allocate memory for wma decode driver\n"); + return -ENOMEM; + } + + /* Settings will be re-config at AUDIO_SET_CONFIG, + * but at least we need to have initial config + */ + audio->str_cfg.buffer_size = FRAME_SIZE; + audio->str_cfg.buffer_count = FRAME_NUM; + audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN; + audio->pcm_cfg.buffer_count = PCM_BUF_COUNT; + audio->pcm_cfg.sample_rate = 48000; + audio->pcm_cfg.channel_count = 2; + + audio->ac = q6asm_audio_client_alloc((app_cb) q6_audwma_cb, + (void *)audio); + + if (!audio->ac) { + pr_aud_err("Could not allocate memory for audio client\n"); + kfree(audio); + return -ENOMEM; + } + /* Only AIO interface */ + if (file->f_flags & O_NONBLOCK) { + pr_debug("set to aio interface\n"); + audio->drv_status |= ADRV_STATUS_AIO_INTF; + audio->drv_ops.out_flush = audwma_async_out_flush; + audio->drv_ops.in_flush = audwma_async_in_flush; + audio->drv_ops.fsync = audwma_async_fsync; + q6asm_set_io_mode(audio->ac, ASYNC_IO_MODE); + } else { + pr_aud_err("SIO interface not supported\n"); + rc = -EACCES; + goto fail; + } + + /* open in T/NT mode */ + if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) { + rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM, + FORMAT_WMA_V9); + if (rc < 0) { + pr_aud_err("NT mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = NON_TUNNEL_MODE; + /* open WMA decoder, expected frames is always 1*/ + audio->buf_cfg.frames_per_buf = 0x01; + audio->buf_cfg.meta_info_enable = 0x01; + } else if ((file->f_mode & FMODE_WRITE) && + !(file->f_mode & FMODE_READ)) { + rc = q6asm_open_write(audio->ac, FORMAT_WMA_V9); + if (rc < 0) { + pr_aud_err("T mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = TUNNEL_MODE; + audio->buf_cfg.meta_info_enable = 0x00; + } else { + pr_aud_err("Not supported mode\n"); + rc = -EACCES; + goto fail; + } + /* Initialize all locks of audio instance */ + mutex_init(&audio->lock); + mutex_init(&audio->read_lock); + mutex_init(&audio->write_lock); + mutex_init(&audio->get_event_lock); + spin_lock_init(&audio->dsp_lock); + spin_lock_init(&audio->event_queue_lock); + init_waitqueue_head(&audio->cmd_wait); + init_waitqueue_head(&audio->write_wait); + init_waitqueue_head(&audio->event_wait); + INIT_LIST_HEAD(&audio->out_queue); + INIT_LIST_HEAD(&audio->in_queue); + INIT_LIST_HEAD(&audio->pmem_region_queue); + INIT_LIST_HEAD(&audio->free_event_queue); + INIT_LIST_HEAD(&audio->event_queue); + + audio->drv_ops.out_flush(audio); + audio->opened = 1; + file->private_data = audio; + +#ifdef CONFIG_DEBUG_FS + snprintf(name, sizeof name, "msm_wma_%04x", audio->ac->session); + audio->dentry = debugfs_create_file(name, S_IFREG | S_IRUGO, + NULL, (void *)audio, + &audwma_debug_fops); + + if (IS_ERR(audio->dentry)) + pr_debug("debugfs_create_file failed\n"); +#endif + for (i = 0; i < AUDWMA_EVENT_NUM; i++) { + e_node = kmalloc(sizeof(struct audwma_event), GFP_KERNEL); + if (e_node) + list_add_tail(&e_node->list, &audio->free_event_queue); + else { + pr_aud_err("event pkt alloc failed\n"); + break; + } + } + pr_aud_info("%s:wma decoder open success, session_id = %d\n", __func__, + audio->ac->session); + return 0; +fail: + q6asm_audio_client_free(audio->ac); + kfree(audio); + return rc; +} + +static const struct file_operations audio_wma_fops = { + .owner = THIS_MODULE, + .open = audio_open, + .release = audio_release, + .unlocked_ioctl = audio_ioctl, + .fsync = audwma_fsync, +}; + +struct miscdevice audwma_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_wma", + .fops = &audio_wma_fops, +}; + +static int __init audio_wma_init(void) +{ + return misc_register(&audwma_misc); +} + +device_initcall(audio_wma_init); diff --git a/arch/arm/mach-msm/qdsp6v3/audio_wmapro.c b/arch/arm/mach-msm/qdsp6v3/audio_wmapro.c new file mode 100644 index 00000000000..d0e67af8eab --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v3/audio_wmapro.c @@ -0,0 +1,1644 @@ +/* wmapro audio output device + * + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * 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 + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT 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 + +#define ADRV_STATUS_AIO_INTF 0x00000001 /* AIO interface */ +#define ADRV_STATUS_FSYNC 0x00000008 +#define ADRV_STATUS_PAUSE 0x00000010 + +#define TUNNEL_MODE 0x0000 +#define NON_TUNNEL_MODE 0x0001 +#define AUDWMAPRO_EOS_SET 0x00000001 + +/* Default number of pre-allocated event packets */ +#define AUDWMAPRO_EVENT_NUM 10 + +#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 - 1; \ + int res = (IN_RANGE(__r1, __v) || IN_RANGE(__r1, __e)); \ + res; \ +}) + +struct timestamp { + unsigned long lowpart; + unsigned long highpart; +} __attribute__ ((packed)); + +struct meta_in { + unsigned char reserved[18]; + unsigned short offset; + struct timestamp ntimestamp; + unsigned int nflags; +} __attribute__ ((packed)); + +struct meta_out_dsp{ + u32 offset_to_frame; + u32 frame_size; + u32 encoded_pcm_samples; + u32 msw_ts; + u32 lsw_ts; + u32 nflags; +} __attribute__ ((packed)); + +struct dec_meta_out{ + unsigned int reserved[7]; + unsigned int num_of_frames; + struct meta_out_dsp meta_out_dsp[]; +} __attribute__ ((packed)); + +/* General meta field to store meta info +locally */ +union meta_data { + struct dec_meta_out meta_out; + struct meta_in meta_in; +} __attribute__ ((packed)); + +struct audwmapro_event { + struct list_head list; + int event_type; + union msm_audio_event_payload payload; +}; + +struct audwmapro_pmem_region { + struct list_head list; + struct file *file; + int fd; + void *vaddr; + unsigned long paddr; + unsigned long kvaddr; + unsigned long len; + unsigned ref_cnt; +}; + +struct audwmapro_buffer_node { + struct list_head list; + struct msm_audio_aio_buf buf; + unsigned long paddr; + unsigned long token; + void *kvaddr; + union meta_data meta_info; +}; + +struct q6audio; + +struct audwmapro_drv_operations { + void (*out_flush) (struct q6audio *); + void (*in_flush) (struct q6audio *); + int (*fsync)(struct q6audio *); +}; + +#define PCM_BUF_COUNT (2) +/* Buffer with meta */ +#define PCM_BUFSZ_MIN ((4*1024) + sizeof(struct dec_meta_out)) + +/* FRAME_NUM must be a power of two */ +#define FRAME_NUM (2) +#define FRAME_SIZE ((4*1024) + sizeof(struct meta_in)) + +struct q6audio { + atomic_t in_bytes; + atomic_t in_samples; + + struct msm_audio_stream_config str_cfg; + struct msm_audio_buf_cfg buf_cfg; + struct msm_audio_config pcm_cfg; + struct msm_audio_wmapro_config wmapro_config; + + struct audio_client *ac; + + struct mutex lock; + struct mutex read_lock; + struct mutex write_lock; + struct mutex get_event_lock; + wait_queue_head_t cmd_wait; + wait_queue_head_t write_wait; + wait_queue_head_t event_wait; + spinlock_t dsp_lock; + spinlock_t event_queue_lock; + +#ifdef CONFIG_DEBUG_FS + struct dentry *dentry; +#endif + struct list_head out_queue; /* queue to retain output buffers */ + struct list_head in_queue; /* queue to retain input buffers */ + struct list_head free_event_queue; + struct list_head event_queue; + struct list_head pmem_region_queue; /* protected by lock */ + struct audwmapro_drv_operations drv_ops; + union msm_audio_event_payload eos_write_payload; + + uint32_t drv_status; + int event_abort; + int eos_rsp; + int eos_flag; + int opened; + int enabled; + int stopped; + int feedback; + int rflush; /* Read flush */ + int wflush; /* Write flush */ +}; + +static int insert_eos_buf(struct q6audio *audio, + struct audwmapro_buffer_node *buf_node) { + struct dec_meta_out *eos_buf = buf_node->kvaddr; + eos_buf->num_of_frames = 0xFFFFFFFF; + eos_buf->meta_out_dsp[0].offset_to_frame = 0x0; + eos_buf->meta_out_dsp[0].nflags = AUDWMAPRO_EOS_SET; + return sizeof(struct dec_meta_out) + + sizeof(eos_buf->meta_out_dsp[0]); +} + +/* Routine which updates read buffers of driver/dsp, + for flush operation as DSP output might not have proper + value set */ +static int insert_meta_data(struct q6audio *audio, + struct audwmapro_buffer_node *buf_node) { + struct dec_meta_out *meta_data = buf_node->kvaddr; + meta_data->num_of_frames = 0x0; + meta_data->meta_out_dsp[0].offset_to_frame = 0x0; + meta_data->meta_out_dsp[0].nflags = 0x0; + return sizeof(struct dec_meta_out) + + sizeof(meta_data->meta_out_dsp[0]); +} + +static void extract_meta_info(struct q6audio *audio, + struct audwmapro_buffer_node *buf_node, int dir) +{ + if (dir) { /* Read */ + if (audio->buf_cfg.meta_info_enable) + memcpy(&buf_node->meta_info.meta_in, + (char *)buf_node->kvaddr, sizeof(struct meta_in)); + else + memset(&buf_node->meta_info.meta_in, + 0, sizeof(struct meta_in)); + pr_debug("i/p: msw_ts 0x%lx lsw_ts 0x%lx nflags 0x%8x\n", + buf_node->meta_info.meta_in.ntimestamp.highpart, + buf_node->meta_info.meta_in.ntimestamp.lowpart, + buf_node->meta_info.meta_in.nflags); + } else { /* Write */ + memcpy((char *)buf_node->kvaddr, + &buf_node->meta_info.meta_out, + sizeof(struct dec_meta_out)); + pr_debug("o/p: msw_ts 0x%8x lsw_ts 0x%8x nflags 0x%8x\n", + ((struct dec_meta_out *)buf_node->kvaddr)->\ + meta_out_dsp[0].msw_ts, + ((struct dec_meta_out *)buf_node->kvaddr)->\ + meta_out_dsp[0].lsw_ts, + ((struct dec_meta_out *)buf_node->kvaddr)->\ + meta_out_dsp[0].nflags); + } +} + +static int audwmapro_pmem_lookup_vaddr(struct q6audio *audio, void *addr, + unsigned long len, struct audwmapro_pmem_region **region) +{ + struct audwmapro_pmem_region *region_elt; + + int match_count = 0; + + *region = NULL; + + /* returns physical address or zero */ + list_for_each_entry(region_elt, &audio->pmem_region_queue, list) { + if (addr >= region_elt->vaddr && + addr < region_elt->vaddr + region_elt->len && + addr + 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) { + pr_aud_err("multiple hits for vaddr %p, len %ld\n", addr, len); + list_for_each_entry(region_elt, &audio->pmem_region_queue, + list) { + if (addr >= region_elt->vaddr && + addr < region_elt->vaddr + region_elt->len && + addr + len <= region_elt->vaddr + region_elt->len) + pr_aud_err("\t%p, %ld --> %p\n", region_elt->vaddr, + region_elt->len, + (void *)region_elt->paddr); + } + } + + return *region ? 0 : -1; +} + +static unsigned long audwmapro_pmem_fixup(struct q6audio *audio, void *addr, + unsigned long len, int ref_up, void **kvaddr) +{ + struct audwmapro_pmem_region *region; + unsigned long paddr; + int ret; + + ret = audwmapro_pmem_lookup_vaddr(audio, addr, len, ®ion); + if (ret) { + pr_aud_err("lookup (%p, %ld) failed\n", addr, len); + return 0; + } + if (ref_up) + region->ref_cnt++; + else + region->ref_cnt--; + pr_debug("found region %p ref_cnt %d\n", region, region->ref_cnt); + paddr = region->paddr + (addr - region->vaddr); + /* provide kernel virtual address for accessing meta information */ + if (kvaddr) + *kvaddr = (void *) (region->kvaddr + (addr - region->vaddr)); + return paddr; +} + +static void audwmapro_post_event(struct q6audio *audio, int type, + union msm_audio_event_payload payload) +{ + struct audwmapro_event *e_node = NULL; + unsigned long flags; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + + if (!list_empty(&audio->free_event_queue)) { + e_node = list_first_entry(&audio->free_event_queue, + struct audwmapro_event, list); + list_del(&e_node->list); + } else { + e_node = kmalloc(sizeof(struct audwmapro_event), GFP_ATOMIC); + if (!e_node) { + pr_aud_err("No mem to post event %d\n", type); + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + return; + } + } + + e_node->event_type = type; + e_node->payload = payload; + + list_add_tail(&e_node->list, &audio->event_queue); + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + wake_up(&audio->event_wait); +} + +static int audwmapro_enable(struct q6audio *audio) +{ + /* 2nd arg: 0 -> run immediately + 3rd arg: 0 -> msw_ts, 4th arg: 0 ->lsw_ts */ + return q6asm_run(audio->ac, 0x00, 0x00, 0x00); +} + +static int audwmapro_disable(struct q6audio *audio) +{ + int rc = 0; + if (audio->opened) { + audio->enabled = 0; + audio->opened = 0; + pr_debug("%s: inbytes[%d] insamples[%d]\n", __func__, + atomic_read(&audio->in_bytes), + atomic_read(&audio->in_samples)); + /* Close the session */ + rc = q6asm_cmd(audio->ac, CMD_CLOSE); + if (rc < 0) + pr_aud_err("Failed to close the session rc=%d\n", rc); + audio->stopped = 1; + wake_up(&audio->write_wait); + wake_up(&audio->cmd_wait); + } + pr_debug("enabled[%d]\n", audio->enabled); + return rc; +} + +static int audwmapro_pause(struct q6audio *audio) +{ + int rc = 0; + + pr_aud_info("%s, enabled = %d\n", __func__, + audio->enabled); + if (audio->enabled) { + rc = q6asm_cmd(audio->ac, CMD_PAUSE); + if (rc < 0) + pr_aud_err("%s: pause cmd failed rc=%d\n", __func__, rc); + + } else + pr_aud_err("%s: Driver not enabled\n", __func__); + return rc; +} + +static int audwmapro_flush(struct q6audio *audio) +{ + int rc; + + if (audio->enabled) { + /* Implicitly issue a pause to the decoder before flushing if + it is not in pause state */ + if (!(audio->drv_status & ADRV_STATUS_PAUSE)) { + rc = audwmapro_pause(audio); + if (rc < 0) + pr_aud_err("%s: pause cmd failed rc=%d\n", __func__, + rc); + else + audio->drv_status |= ADRV_STATUS_PAUSE; + } + rc = q6asm_cmd(audio->ac, CMD_FLUSH); + if (rc < 0) + pr_aud_err("%s: flush cmd failed rc=%d\n", __func__, rc); + /* Not in stop state, reenable the stream */ + if (audio->stopped == 0) { + rc = audwmapro_enable(audio); + if (rc) + pr_aud_err("%s:audio re-enable failed\n", __func__); + else { + audio->enabled = 1; + if (audio->drv_status & ADRV_STATUS_PAUSE) + audio->drv_status &= ~ADRV_STATUS_PAUSE; + } + } + } + pr_debug("in_bytes %d\n", atomic_read(&audio->in_bytes)); + pr_debug("in_samples %d\n", atomic_read(&audio->in_samples)); + atomic_set(&audio->in_bytes, 0); + atomic_set(&audio->in_samples, 0); + return 0; +} + +static void audwmapro_async_read(struct q6audio *audio, + struct audwmapro_buffer_node *buf_node) +{ + struct audio_client *ac; + struct audio_aio_read_param param; + int rc; + + pr_debug("%s: Send read buff %p phy %lx len %d\n", __func__, buf_node, + buf_node->paddr, buf_node->buf.buf_len); + ac = audio->ac; + /* Provide address so driver can append nr frames information */ + param.paddr = buf_node->paddr + + sizeof(struct dec_meta_out); + param.len = buf_node->buf.buf_len - + sizeof(struct dec_meta_out); + param.uid = param.paddr; + /* Write command will populate paddr as token */ + buf_node->token = param.paddr; + rc = q6asm_async_read(ac, ¶m); + if (rc < 0) + pr_aud_err("%s:failed\n", __func__); +} + +static void audwmapro_async_write(struct q6audio *audio, + struct audwmapro_buffer_node *buf_node) +{ + int rc; + struct audio_client *ac; + struct audio_aio_write_param param; + + pr_debug("%s: Send write buff %p phy %lx len %d\n", __func__, buf_node, + buf_node->paddr, buf_node->buf.data_len); + + ac = audio->ac; + /* Offset with appropriate meta */ + param.paddr = buf_node->paddr + sizeof(struct meta_in); + param.len = buf_node->buf.data_len - sizeof(struct meta_in); + param.msw_ts = buf_node->meta_info.meta_in.ntimestamp.highpart; + param.lsw_ts = buf_node->meta_info.meta_in.ntimestamp.lowpart; + /* If no meta_info enaled, indicate no time stamp valid */ + if (audio->buf_cfg.meta_info_enable) + param.flags = 0; + else + param.flags = 0xFF00; + param.uid = param.paddr; + /* Read command will populate paddr as token */ + buf_node->token = param.paddr; + rc = q6asm_async_write(ac, ¶m); + if (rc < 0) + pr_aud_err("%s:failed\n", __func__); +} + +/* Write buffer to DSP / Handle Ack from DSP */ +static void audwmapro_async_write_ack(struct q6audio *audio, uint32_t token, + uint32_t *payload) +{ + unsigned long flags; + union msm_audio_event_payload event_payload; + struct audwmapro_buffer_node *used_buf; + + /* No active flush in progress */ + if (audio->wflush) + return; + + spin_lock_irqsave(&audio->dsp_lock, flags); + BUG_ON(list_empty(&audio->out_queue)); + used_buf = list_first_entry(&audio->out_queue, + struct audwmapro_buffer_node, list); + if (token == used_buf->token) { + list_del(&used_buf->list); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + pr_debug("consumed buffer\n"); + event_payload.aio_buf = used_buf->buf; + audwmapro_post_event(audio, AUDIO_EVENT_WRITE_DONE, + event_payload); + kfree(used_buf); + if (list_empty(&audio->out_queue) && + (audio->drv_status & ADRV_STATUS_FSYNC)) { + pr_debug("%s: list is empty, reached EOS in\ + Tunnel\n", __func__); + wake_up(&audio->write_wait); + } + } else { + pr_aud_err("expected=%lx ret=%x\n", used_buf->token, token); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + } +} + +/* Read buffer from DSP / Handle Ack from DSP */ +static void audwmapro_async_read_ack(struct q6audio *audio, uint32_t token, + uint32_t *payload) +{ + unsigned long flags; + union msm_audio_event_payload event_payload; + struct audwmapro_buffer_node *filled_buf; + + /* No active flush in progress */ + if (audio->rflush) + return; + + /* Statistics of read */ + atomic_add(payload[2], &audio->in_bytes); + atomic_add(payload[7], &audio->in_samples); + + spin_lock_irqsave(&audio->dsp_lock, flags); + BUG_ON(list_empty(&audio->in_queue)); + filled_buf = list_first_entry(&audio->in_queue, + struct audwmapro_buffer_node, list); + if (token == (filled_buf->token)) { + list_del(&filled_buf->list); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + event_payload.aio_buf = filled_buf->buf; + /* Read done Buffer due to flush/normal condition + after EOS event, so append EOS buffer */ + if (audio->eos_rsp == 0x1) { + event_payload.aio_buf.data_len = + insert_eos_buf(audio, filled_buf); + /* Reset flag back to indicate eos intimated */ + audio->eos_rsp = 0; + } else { + filled_buf->meta_info.meta_out.num_of_frames = + payload[7]; + pr_debug("nr of frames 0x%8x\n", + filled_buf->meta_info.meta_out.num_of_frames); + event_payload.aio_buf.data_len = payload[2] + \ + payload[3] + \ + sizeof(struct dec_meta_out); + extract_meta_info(audio, filled_buf, 0); + audio->eos_rsp = 0; + } + audwmapro_post_event(audio, AUDIO_EVENT_READ_DONE, + event_payload); + kfree(filled_buf); + } else { + pr_aud_err("expected=%lx ret=%x\n", filled_buf->token, token); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + } +} + +static void q6_audwmapro_cb(uint32_t opcode, uint32_t token, + uint32_t *payload, void *priv) +{ + struct q6audio *audio = (struct q6audio *)priv; + + switch (opcode) { + case ASM_DATA_EVENT_WRITE_DONE: + pr_debug("%s:ASM_DATA_EVENT_WRITE_DONE token = 0x%x\n", + __func__, token); + audwmapro_async_write_ack(audio, token, payload); + break; + case ASM_DATA_EVENT_READ_DONE: + pr_debug("%s:ASM_DATA_EVENT_READ_DONE token = 0x%x\n", + __func__, token); + audwmapro_async_read_ack(audio, token, payload); + break; + case ASM_DATA_CMDRSP_EOS: + /* EOS Handle */ + pr_debug("%s:ASM_DATA_CMDRSP_EOS\n", __func__); + if (audio->feedback) { /* Non-Tunnel mode */ + audio->eos_rsp = 1; + /* propagate input EOS i/p buffer, + after receiving DSP acknowledgement */ + if (audio->eos_flag && + (audio->eos_write_payload.aio_buf.buf_addr)) { + audwmapro_post_event(audio, + AUDIO_EVENT_WRITE_DONE, + audio->eos_write_payload); + memset(&audio->eos_write_payload , 0, + sizeof(union msm_audio_event_payload)); + audio->eos_flag = 0; + } + } else { /* Tunnel mode */ + audio->eos_rsp = 1; + wake_up(&audio->write_wait); + wake_up(&audio->cmd_wait); + } + break; + default: + pr_debug("%s:Unhandled event = 0x%8x\n", __func__, opcode); + break; + } +} + +/* ------------------- device --------------------- */ +static void audwmapro_async_out_flush(struct q6audio *audio) +{ + struct audwmapro_buffer_node *buf_node; + struct list_head *ptr, *next; + union msm_audio_event_payload payload; + unsigned long flags; + + pr_debug("%s\n", __func__); + /* EOS followed by flush, EOS response not guranteed, free EOS i/p + buffer */ + spin_lock_irqsave(&audio->dsp_lock, flags); + if (audio->eos_flag && (audio->eos_write_payload.aio_buf.buf_addr)) { + pr_debug("%s: EOS followed by flush received,acknowledge eos"\ + " i/p buffer immediately\n", __func__); + audwmapro_post_event(audio, AUDIO_EVENT_WRITE_DONE, + audio->eos_write_payload); + memset(&audio->eos_write_payload , 0, + sizeof(union msm_audio_event_payload)); + } + spin_unlock_irqrestore(&audio->dsp_lock, flags); + list_for_each_safe(ptr, next, &audio->out_queue) { + buf_node = list_entry(ptr, struct audwmapro_buffer_node, list); + list_del(&buf_node->list); + payload.aio_buf = buf_node->buf; + audwmapro_post_event(audio, AUDIO_EVENT_WRITE_DONE, payload); + kfree(buf_node); + pr_debug("%s: Propagate WRITE_DONE during flush\n", __func__); + } +} + +static void audwmapro_async_in_flush(struct q6audio *audio) +{ + struct audwmapro_buffer_node *buf_node; + struct list_head *ptr, *next; + union msm_audio_event_payload payload; + + pr_debug("%s\n", __func__); + list_for_each_safe(ptr, next, &audio->in_queue) { + buf_node = list_entry(ptr, struct audwmapro_buffer_node, list); + list_del(&buf_node->list); + /* Forcefull send o/p eos buffer after flush, if no eos response + * received by dsp even after sending eos command */ + if ((audio->eos_rsp != 1) && audio->eos_flag) { + pr_debug("%s: send eos on o/p buffer during flush\n",\ + __func__); + payload.aio_buf = buf_node->buf; + payload.aio_buf.data_len = + insert_eos_buf(audio, buf_node); + audio->eos_flag = 0; + } else { + payload.aio_buf = buf_node->buf; + payload.aio_buf.data_len = + insert_meta_data(audio, buf_node); + } + audwmapro_post_event(audio, AUDIO_EVENT_READ_DONE, payload); + kfree(buf_node); + pr_debug("%s: Propagate READ_DONE during flush\n", __func__); + } +} + +static void audwmapro_ioport_reset(struct q6audio *audio) +{ + if (audio->drv_status & ADRV_STATUS_AIO_INTF) { + /* If fsync is in progress, make sure + * return value of fsync indicates + * abort due to flush + */ + if (audio->drv_status & ADRV_STATUS_FSYNC) { + pr_debug("fsync in progress\n"); + audio->drv_ops.out_flush(audio); + } else + audio->drv_ops.out_flush(audio); + audio->drv_ops.in_flush(audio); + } +} + +static int audwmapro_events_pending(struct q6audio *audio) +{ + unsigned long flags; + int empty; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + empty = !list_empty(&audio->event_queue); + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + return empty || audio->event_abort; +} + +static void audwmapro_reset_event_queue(struct q6audio *audio) +{ + unsigned long flags; + struct audwmapro_event *drv_evt; + struct list_head *ptr, *next; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + list_for_each_safe(ptr, next, &audio->event_queue) { + drv_evt = list_first_entry(&audio->event_queue, + struct audwmapro_event, list); + list_del(&drv_evt->list); + kfree(drv_evt); + } + list_for_each_safe(ptr, next, &audio->free_event_queue) { + drv_evt = list_first_entry(&audio->free_event_queue, + struct audwmapro_event, list); + list_del(&drv_evt->list); + kfree(drv_evt); + } + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + + return; +} + +static long audwmapro_process_event_req(struct q6audio *audio, + void __user *arg) +{ + long rc; + struct msm_audio_event usr_evt; + struct audwmapro_event *drv_evt = NULL; + int timeout; + unsigned long flags; + + if (copy_from_user(&usr_evt, arg, sizeof(struct msm_audio_event))) + return -EFAULT; + + timeout = (int)usr_evt.timeout_ms; + + if (timeout > 0) { + rc = wait_event_interruptible_timeout(audio->event_wait, + audwmapro_events_pending + (audio), + msecs_to_jiffies + (timeout)); + if (rc == 0) + return -ETIMEDOUT; + } else { + rc = wait_event_interruptible(audio->event_wait, + audwmapro_events_pending(audio)); + } + + if (rc < 0) + return rc; + + if (audio->event_abort) { + audio->event_abort = 0; + return -ENODEV; + } + + rc = 0; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + if (!list_empty(&audio->event_queue)) { + drv_evt = list_first_entry(&audio->event_queue, + struct audwmapro_event, list); + list_del(&drv_evt->list); + } + if (drv_evt) { + usr_evt.event_type = drv_evt->event_type; + usr_evt.event_payload = drv_evt->payload; + list_add_tail(&drv_evt->list, &audio->free_event_queue); + } else { + pr_aud_err("Unexpected path\n"); + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + return -EPERM; + } + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + + if (drv_evt->event_type == AUDIO_EVENT_WRITE_DONE) { + pr_debug("posted AUDIO_EVENT_WRITE_DONE to user\n"); + mutex_lock(&audio->write_lock); + audwmapro_pmem_fixup(audio, drv_evt->payload.aio_buf.buf_addr, + drv_evt->payload.aio_buf.buf_len, 0, 0); + mutex_unlock(&audio->write_lock); + } else if (drv_evt->event_type == AUDIO_EVENT_READ_DONE) { + pr_debug("posted AUDIO_EVENT_READ_DONE to user\n"); + mutex_lock(&audio->read_lock); + audwmapro_pmem_fixup(audio, drv_evt->payload.aio_buf.buf_addr, + drv_evt->payload.aio_buf.buf_len, 0, 0); + mutex_unlock(&audio->read_lock); + } + + /* Some read buffer might be held up in DSP,release all + Once EOS indicated*/ + if (audio->eos_rsp && !list_empty(&audio->in_queue)) { + pr_debug("Send flush command to release read buffers"\ + " held up in DSP\n"); + audwmapro_flush(audio); + } + + if (copy_to_user(arg, &usr_evt, sizeof(usr_evt))) + rc = -EFAULT; + + return rc; +} + +static int audwmapro_pmem_check(struct q6audio *audio, + void *vaddr, unsigned long len) +{ + struct audwmapro_pmem_region *region_elt; + struct audwmapro_pmem_region t = {.vaddr = vaddr, .len = len }; + + list_for_each_entry(region_elt, &audio->pmem_region_queue, list) { + if (CONTAINS(region_elt, &t) || CONTAINS(&t, region_elt) || + OVERLAPS(region_elt, &t)) { + pr_aud_err("region (vaddr %p len %ld)" + " clashes with registered region" + " (vaddr %p paddr %p len %ld)\n", + vaddr, len, + region_elt->vaddr, + (void *)region_elt->paddr, region_elt->len); + return -EINVAL; + } + } + + return 0; +} + +static int audwmapro_pmem_add(struct q6audio *audio, + struct msm_audio_pmem_info *info) +{ + unsigned long paddr, kvaddr, len; + struct file *file; + struct audwmapro_pmem_region *region; + int rc = -EINVAL; + + pr_debug("%s\n", __func__); + region = kmalloc(sizeof(*region), GFP_KERNEL); + + if (!region) { + rc = -ENOMEM; + goto end; + } + + if (get_pmem_file(info->fd, &paddr, &kvaddr, &len, &file)) { + kfree(region); + goto end; + } + + rc = audwmapro_pmem_check(audio, info->vaddr, len); + if (rc < 0) { + put_pmem_file(file); + kfree(region); + goto end; + } + + region->vaddr = info->vaddr; + region->fd = info->fd; + region->paddr = paddr; + region->kvaddr = kvaddr; + region->len = len; + region->file = file; + region->ref_cnt = 0; + pr_debug("add region paddr %lx vaddr %p, len %lu kvaddr %lx\n", + region->paddr, region->vaddr, region->len, region->kvaddr); + list_add_tail(®ion->list, &audio->pmem_region_queue); + + rc = q6asm_memory_map(audio->ac, (uint32_t) paddr, IN, (uint32_t) len, + 1); + if (rc < 0) + pr_aud_err("%s: memory map failed\n", __func__); +end: + return rc; +} + +static int audwmapro_pmem_remove(struct q6audio *audio, + struct msm_audio_pmem_info *info) +{ + struct audwmapro_pmem_region *region; + struct list_head *ptr, *next; + int rc = -EINVAL; + + pr_debug("info fd %d vaddr %p\n", info->fd, info->vaddr); + + list_for_each_safe(ptr, next, &audio->pmem_region_queue) { + region = list_entry(ptr, struct audwmapro_pmem_region, list); + + if ((region->fd == info->fd) && + (region->vaddr == info->vaddr)) { + if (region->ref_cnt) { + pr_debug("region %p in use ref_cnt %d\n", + region, region->ref_cnt); + break; + } + pr_debug("remove region fd %d vaddr %p\n", + info->fd, info->vaddr); + rc = q6asm_memory_unmap(audio->ac, + (uint32_t) region->paddr, IN); + if (rc < 0) + pr_aud_err("%s: memory unmap failed\n", __func__); + + list_del(®ion->list); + put_pmem_file(region->file); + kfree(region); + rc = 0; + break; + } + } + + return rc; +} + +/* audio -> lock must be held at this point */ +static int audwmapro_aio_buf_add(struct q6audio *audio, unsigned dir, + void __user *arg) +{ + unsigned long flags; + struct audwmapro_buffer_node *buf_node; + + buf_node = kzalloc(sizeof(*buf_node), GFP_KERNEL); + + if (!buf_node) + return -ENOMEM; + + if (copy_from_user(&buf_node->buf, arg, sizeof(buf_node->buf))) { + kfree(buf_node); + return -EFAULT; + } + + pr_debug("node %p dir %x buf_addr %p buf_len %d data_len \ + %d\n", buf_node, dir, buf_node->buf.buf_addr, + buf_node->buf.buf_len, buf_node->buf.data_len); + + buf_node->paddr = audwmapro_pmem_fixup(audio, buf_node->buf.buf_addr, + buf_node->buf.buf_len, 1, + &buf_node->kvaddr); + if (dir) { + /* write */ + if (!buf_node->paddr || + (buf_node->paddr & 0x1) || + (!audio->feedback && !buf_node->buf.data_len)) { + kfree(buf_node); + return -EINVAL; + } + extract_meta_info(audio, buf_node, 1); + /* Not a EOS buffer */ + if (!(buf_node->meta_info.meta_in.nflags & AUDWMAPRO_EOS_SET)) { + spin_lock_irqsave(&audio->dsp_lock, flags); + audwmapro_async_write(audio, buf_node); + /* EOS buffer handled in driver */ + list_add_tail(&buf_node->list, &audio->out_queue); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + } + if (buf_node->meta_info.meta_in.nflags & AUDWMAPRO_EOS_SET) { + if (!audio->wflush) { + pr_debug("%s:Send EOS cmd at i/p\n", __func__); + /* Driver will forcefully post writedone event + once eos ack recived from DSP*/ + audio->eos_write_payload.aio_buf =\ + buf_node->buf; + audio->eos_flag = 1; + audio->eos_rsp = 0; + q6asm_cmd(audio->ac, CMD_EOS); + kfree(buf_node); + } else { /* Flush in progress, send back i/p EOS buffer + as is */ + union msm_audio_event_payload event_payload; + event_payload.aio_buf = buf_node->buf; + audwmapro_post_event(audio, + AUDIO_EVENT_WRITE_DONE, + event_payload); + kfree(buf_node); + } + } + } else { + /* read */ + if (!buf_node->paddr || + (buf_node->paddr & 0x1) || + (buf_node->buf.buf_len < PCM_BUFSZ_MIN)) { + kfree(buf_node); + return -EINVAL; + } + /* No EOS reached */ + if (!audio->eos_rsp) { + spin_lock_irqsave(&audio->dsp_lock, flags); + audwmapro_async_read(audio, buf_node); + /* EOS buffer handled in driver */ + list_add_tail(&buf_node->list, &audio->in_queue); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + } + /* EOS reached at input side fake all upcoming read buffer to + indicate the same */ + else { + union msm_audio_event_payload event_payload; + event_payload.aio_buf = buf_node->buf; + event_payload.aio_buf.data_len = + insert_eos_buf(audio, buf_node); + pr_debug("%s: propagate READ_DONE as EOS done\n",\ + __func__); + audwmapro_post_event(audio, AUDIO_EVENT_READ_DONE, + event_payload); + kfree(buf_node); + } + } + return 0; +} + +/* TBD: Only useful in tunnel-mode */ +int audwmapro_async_fsync(struct q6audio *audio) +{ + int rc = 0; + + /* Blocking client sends more data */ + mutex_lock(&audio->lock); + audio->drv_status |= ADRV_STATUS_FSYNC; + mutex_unlock(&audio->lock); + + pr_aud_info("%s:\n", __func__); + + mutex_lock(&audio->write_lock); + audio->eos_rsp = 0; + + rc = wait_event_interruptible(audio->write_wait, + (list_empty(&audio->out_queue)) || + audio->wflush || audio->stopped); + + if (rc < 0) { + pr_aud_err("%s: wait event for list_empty failed, rc = %d\n", + __func__, rc); + goto done; + } + + rc = q6asm_cmd(audio->ac, CMD_EOS); + + if (rc < 0) + pr_aud_err("%s: q6asm_cmd failed, rc = %d", __func__, rc); + + rc = wait_event_interruptible(audio->write_wait, + (audio->eos_rsp || audio->wflush || + audio->stopped)); + + if (rc < 0) { + pr_aud_err("%s: wait event for eos_rsp failed, rc = %d\n", __func__, + rc); + goto done; + } + + if (audio->eos_rsp == 1) { + rc = audwmapro_enable(audio); + if (rc) + pr_aud_err("%s: audio enable failed\n", __func__); + else { + audio->drv_status &= ~ADRV_STATUS_PAUSE; + audio->enabled = 1; + } + } + + if (audio->stopped || audio->wflush) + rc = -EBUSY; + +done: + mutex_unlock(&audio->write_lock); + mutex_lock(&audio->lock); + audio->drv_status &= ~ADRV_STATUS_FSYNC; + mutex_unlock(&audio->lock); + + return rc; +} + +int audwmapro_fsync(struct file *file, int datasync) +{ + struct q6audio *audio = file->private_data; + + if (!audio->enabled || audio->feedback) + return -EINVAL; + + return audio->drv_ops.fsync(audio); +} + +static void audwmapro_reset_pmem_region(struct q6audio *audio) +{ + struct audwmapro_pmem_region *region; + struct list_head *ptr, *next; + + list_for_each_safe(ptr, next, &audio->pmem_region_queue) { + region = list_entry(ptr, struct audwmapro_pmem_region, list); + list_del(®ion->list); + put_pmem_file(region->file); + kfree(region); + } + + return; +} + +#ifdef CONFIG_DEBUG_FS +static ssize_t audwmapro_debug_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static ssize_t audwmapro_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 q6audio *audio = file->private_data; + + mutex_lock(&audio->lock); + n = scnprintf(buffer, debug_bufmax, "opened %d\n", audio->opened); + n += scnprintf(buffer + n, debug_bufmax - n, + "enabled %d\n", audio->enabled); + n += scnprintf(buffer + n, debug_bufmax - n, + "stopped %d\n", audio->stopped); + n += scnprintf(buffer + n, debug_bufmax - n, + "feedback %d\n", audio->feedback); + mutex_unlock(&audio->lock); + /* Following variables are only useful for debugging when + * when playback halts unexpectedly. Thus, no mutual exclusion + * enforced + */ + n += scnprintf(buffer + n, debug_bufmax - n, + "wflush %d\n", audio->wflush); + n += scnprintf(buffer + n, debug_bufmax - n, + "rflush %d\n", audio->rflush); + n += scnprintf(buffer + n, debug_bufmax - n, + "inqueue empty %d\n", list_empty(&audio->in_queue)); + n += scnprintf(buffer + n, debug_bufmax - n, + "outqueue empty %d\n", list_empty(&audio->out_queue)); + buffer[n] = 0; + return simple_read_from_buffer(buf, count, ppos, buffer, n); +} + +static const struct file_operations audwmapro_debug_fops = { + .read = audwmapro_debug_read, + .open = audwmapro_debug_open, +}; +#endif + +static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct q6audio *audio = file->private_data; + int rc = 0; + + if (cmd == AUDIO_GET_STATS) { + struct msm_audio_stats stats; + stats.byte_count = atomic_read(&audio->in_bytes); + stats.sample_count = atomic_read(&audio->in_samples); + if (copy_to_user((void *)arg, &stats, sizeof(stats))) + return -EFAULT; + return rc; + } + + if (cmd == AUDIO_GET_EVENT) { + pr_debug("AUDIO_GET_EVENT\n"); + if (mutex_trylock(&audio->get_event_lock)) { + rc = audwmapro_process_event_req(audio, + (void __user *)arg); + mutex_unlock(&audio->get_event_lock); + } else + rc = -EBUSY; + return rc; + } + + if (cmd == AUDIO_ASYNC_WRITE) { + mutex_lock(&audio->write_lock); + if (audio->drv_status & ADRV_STATUS_FSYNC) + rc = -EBUSY; + else { + if (audio->enabled) + rc = audwmapro_aio_buf_add(audio, 1, + (void __user *)arg); + else + rc = -EPERM; + } + mutex_unlock(&audio->write_lock); + return rc; + } + + if (cmd == AUDIO_ASYNC_READ) { + mutex_lock(&audio->read_lock); + if ((audio->feedback) && (audio->enabled)) + rc = audwmapro_aio_buf_add(audio, 0, + (void __user *)arg); + else + rc = -EPERM; + mutex_unlock(&audio->read_lock); + return rc; + } + + if (cmd == AUDIO_ABORT_GET_EVENT) { + audio->event_abort = 1; + wake_up(&audio->event_wait); + return 0; + } + + mutex_lock(&audio->lock); + switch (cmd) { + case AUDIO_START: { + struct asm_wmapro_cfg wmapro_cfg; + if (audio->feedback == NON_TUNNEL_MODE) { + /* Configure PCM output block */ + rc = q6asm_enc_cfg_blk_pcm(audio->ac, + audio->pcm_cfg.sample_rate, + audio->pcm_cfg.channel_count); + if (rc < 0) { + pr_aud_err("pcm output block config failed\n"); + break; + } + } + if ((audio->wmapro_config.formattag == 0x162) || + (audio->wmapro_config.formattag == 0x166)) { + wmapro_cfg.format_tag = audio->wmapro_config.formattag; + } else { + pr_aud_err("%s:AUDIO_START failed: formattag = %d\n", + __func__, audio->wmapro_config.formattag); + rc = -EINVAL; + break; + } + if ((audio->wmapro_config.numchannels == 1) || + (audio->wmapro_config.numchannels == 2)) { + wmapro_cfg.ch_cfg = audio->wmapro_config.numchannels; + } else { + pr_aud_err("%s:AUDIO_START failed: channels = %d\n", + __func__, audio->wmapro_config.numchannels); + rc = -EINVAL; + break; + } + if ((audio->wmapro_config.samplingrate <= 48000) || + (audio->wmapro_config.samplingrate > 0)) { + wmapro_cfg.sample_rate = + audio->wmapro_config.samplingrate; + } else { + pr_aud_err("%s:AUDIO_START failed: sample_rate = %d\n", + __func__, audio->wmapro_config.samplingrate); + rc = -EINVAL; + break; + } + wmapro_cfg.avg_bytes_per_sec = + audio->wmapro_config.avgbytespersecond; + if ((audio->wmapro_config.asfpacketlength <= 13376) || + (audio->wmapro_config.asfpacketlength > 0)) { + wmapro_cfg.block_align = + audio->wmapro_config.asfpacketlength; + } else { + pr_aud_err("%s:AUDIO_START failed: block_align = %d\n", + __func__, audio->wmapro_config.asfpacketlength); + rc = -EINVAL; + break; + } + if (audio->wmapro_config.validbitspersample == 16) { + wmapro_cfg.valid_bits_per_sample = + audio->wmapro_config.validbitspersample; + } else { + pr_aud_err("%s:AUDIO_START failed: bitspersample = %d\n", + __func__, + audio->wmapro_config.validbitspersample); + rc = -EINVAL; + break; + } + if ((audio->wmapro_config.channelmask == 4) || + (audio->wmapro_config.channelmask == 3)) { + wmapro_cfg.ch_mask = audio->wmapro_config.channelmask; + } else { + pr_aud_err("%s:AUDIO_START failed: channel_mask = %d\n", + __func__, audio->wmapro_config.channelmask); + rc = -EINVAL; + break; + } + wmapro_cfg.encode_opt = audio->wmapro_config.encodeopt; + wmapro_cfg.adv_encode_opt = + audio->wmapro_config.advancedencodeopt; + wmapro_cfg.adv_encode_opt2 = + audio->wmapro_config.advancedencodeopt2; + /* Configure Media format block */ + rc = q6asm_media_format_block_wmapro(audio->ac, &wmapro_cfg); + if (rc < 0) { + pr_aud_err("cmd media format block failed\n"); + break; + } + rc = audwmapro_enable(audio); + audio->eos_rsp = 0; + audio->eos_flag = 0; + if (!rc) { + audio->enabled = 1; + } else { + audio->enabled = 0; + pr_aud_err("Audio Start procedure failed rc=%d\n", rc); + break; + } + pr_debug("AUDIO_START success enable[%d]\n", audio->enabled); + if (audio->stopped == 1) + audio->stopped = 0; + break; + } + case AUDIO_STOP: { + pr_debug("AUDIO_STOP\n"); + audio->stopped = 1; + audwmapro_flush(audio); + audio->enabled = 0; + audio->drv_status &= ~ADRV_STATUS_PAUSE; + if (rc < 0) { + pr_aud_err("Audio Stop procedure failed rc=%d\n", rc); + break; + } + break; + } + case AUDIO_PAUSE: { + pr_debug("AUDIO_PAUSE %ld\n", arg); + if (arg == 1) { + rc = audwmapro_pause(audio); + if (rc < 0) + pr_aud_err("%s: pause FAILED rc=%d\n", __func__, + rc); + audio->drv_status |= ADRV_STATUS_PAUSE; + } else if (arg == 0) { + if (audio->drv_status & ADRV_STATUS_PAUSE) { + rc = audwmapro_enable(audio); + if (rc) + pr_aud_err("%s: audio enable failed\n", + __func__); + else { + audio->drv_status &= ~ADRV_STATUS_PAUSE; + audio->enabled = 1; + } + } + } + break; + } + case AUDIO_FLUSH: { + pr_debug("AUDIO_FLUSH\n"); + audio->rflush = 1; + audio->wflush = 1; + /* Flush DSP */ + rc = audwmapro_flush(audio); + /* Flush input / Output buffer in software*/ + audwmapro_ioport_reset(audio); + if (rc < 0) { + pr_aud_err("AUDIO_FLUSH interrupted\n"); + rc = -EINTR; + } else { + audio->rflush = 0; + audio->wflush = 0; + } + audio->eos_flag = 0; + audio->eos_rsp = 0; + break; + } + case AUDIO_REGISTER_PMEM: { + struct msm_audio_pmem_info info; + pr_debug("AUDIO_REGISTER_PMEM\n"); + if (copy_from_user(&info, (void *)arg, sizeof(info))) + rc = -EFAULT; + else + rc = audwmapro_pmem_add(audio, &info); + break; + } + case AUDIO_DEREGISTER_PMEM: { + struct msm_audio_pmem_info info; + pr_debug("AUDIO_DEREGISTER_PMEM\n"); + if (copy_from_user(&info, (void *)arg, sizeof(info))) + rc = -EFAULT; + else + rc = audwmapro_pmem_remove(audio, &info); + break; + } + case AUDIO_GET_WMAPRO_CONFIG: { + if (copy_to_user((void *)arg, &audio->wmapro_config, + sizeof(struct msm_audio_wmapro_config))) { + rc = -EFAULT; + break; + } + break; + } + case AUDIO_SET_WMAPRO_CONFIG: { + if (copy_from_user(&audio->wmapro_config, (void *)arg, + sizeof(struct msm_audio_wmapro_config))) { + rc = -EFAULT; + break; + } + break; + } + case AUDIO_GET_STREAM_CONFIG: { + struct msm_audio_stream_config cfg; + memset(&cfg, 0, sizeof(cfg)); + cfg.buffer_size = audio->str_cfg.buffer_size; + cfg.buffer_count = audio->str_cfg.buffer_count; + pr_debug("GET STREAM CFG %d %d\n", cfg.buffer_size, + cfg.buffer_count); + if (copy_to_user((void *)arg, &cfg, sizeof(cfg))) + rc = -EFAULT; + break; + } + case AUDIO_SET_STREAM_CONFIG: { + struct msm_audio_stream_config cfg; + pr_debug("SET STREAM CONFIG\n"); + if (copy_from_user(&cfg, (void *)arg, sizeof(cfg))) { + rc = -EFAULT; + break; + } + audio->str_cfg.buffer_size = FRAME_SIZE; + audio->str_cfg.buffer_count = FRAME_NUM; + rc = 0; + break; + } + case AUDIO_GET_CONFIG: { + struct msm_audio_config cfg; + if (copy_to_user((void *)arg, &audio->pcm_cfg, sizeof(cfg))) + rc = -EFAULT; + break; + } + case AUDIO_SET_CONFIG: { + struct msm_audio_config config; + if (copy_from_user(&config, (void *)arg, sizeof(config))) { + rc = -EFAULT; + break; + } + if (audio->feedback != NON_TUNNEL_MODE) { + pr_aud_err("Not sufficient permission to" + "change the playback mode\n"); + rc = -EACCES; + break; + } + if ((config.buffer_count > PCM_BUF_COUNT) || + (config.buffer_count == 1)) + config.buffer_count = PCM_BUF_COUNT; + + if (config.buffer_size < PCM_BUFSZ_MIN) + config.buffer_size = PCM_BUFSZ_MIN; + + audio->pcm_cfg.buffer_count = config.buffer_count; + audio->pcm_cfg.buffer_size = config.buffer_size; + audio->pcm_cfg.channel_count = config.channel_count; + audio->pcm_cfg.sample_rate = config.sample_rate; + rc = 0; + break; + } + case AUDIO_SET_BUF_CFG: { + struct msm_audio_buf_cfg cfg; + if (copy_from_user(&cfg, (void *)arg, sizeof(cfg))) { + rc = -EFAULT; + break; + } + if ((audio->feedback == NON_TUNNEL_MODE) && + !cfg.meta_info_enable) { + rc = -EFAULT; + break; + } + + audio->buf_cfg.meta_info_enable = cfg.meta_info_enable; + pr_debug("%s:session id %d: Set-buf-cfg: meta[%d]", __func__, + audio->ac->session, cfg.meta_info_enable); + break; + } + case AUDIO_GET_BUF_CFG: { + pr_debug("%s:session id %d: Get-buf-cfg: meta[%d]\ + framesperbuf[%d]\n", __func__, + audio->ac->session, audio->buf_cfg.meta_info_enable, + audio->buf_cfg.frames_per_buf); + + if (copy_to_user((void *)arg, &audio->buf_cfg, + sizeof(struct msm_audio_buf_cfg))) + rc = -EFAULT; + break; + } + case AUDIO_GET_SESSION_ID: { + if (copy_to_user((void *)arg, &audio->ac->session, + sizeof(unsigned short))) { + rc = -EFAULT; + } + break; + } + default: + rc = -EINVAL; + } + mutex_unlock(&audio->lock); + return rc; +} + +static int audio_release(struct inode *inode, struct file *file) +{ + struct q6audio *audio = file->private_data; + mutex_lock(&audio->lock); + audwmapro_disable(audio); + audio->drv_ops.out_flush(audio); + audio->drv_ops.in_flush(audio); + audwmapro_reset_pmem_region(audio); + audio->event_abort = 1; + wake_up(&audio->event_wait); + audwmapro_reset_event_queue(audio); + q6asm_audio_client_free(audio->ac); + mutex_unlock(&audio->lock); + mutex_destroy(&audio->lock); + mutex_destroy(&audio->read_lock); + mutex_destroy(&audio->write_lock); + mutex_destroy(&audio->get_event_lock); +#ifdef CONFIG_DEBUG_FS + if (audio->dentry) + debugfs_remove(audio->dentry); +#endif + kfree(audio); + pr_aud_info("%s: wmapro decoder success\n", __func__); + return 0; +} + +static int audio_open(struct inode *inode, struct file *file) +{ + struct q6audio *audio = NULL; + int rc = 0; + int i; + struct audwmapro_event *e_node = NULL; + +#ifdef CONFIG_DEBUG_FS + /* 4 bytes represents decoder number, 1 byte for terminate string */ + char name[sizeof "msm_wmapro_" + 5]; +#endif + audio = kzalloc(sizeof(struct q6audio), GFP_KERNEL); + + if (audio == NULL) { + pr_aud_err("Could not allocate memory for wma decode driver\n"); + return -ENOMEM; + } + + /* Settings will be re-config at AUDIO_SET_CONFIG, + * but at least we need to have initial config + */ + audio->str_cfg.buffer_size = FRAME_SIZE; + audio->str_cfg.buffer_count = FRAME_NUM; + audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN; + audio->pcm_cfg.buffer_count = PCM_BUF_COUNT; + audio->pcm_cfg.sample_rate = 48000; + audio->pcm_cfg.channel_count = 2; + + audio->ac = q6asm_audio_client_alloc((app_cb) q6_audwmapro_cb, + (void *)audio); + + if (!audio->ac) { + pr_aud_err("Could not allocate memory for audio client\n"); + kfree(audio); + return -ENOMEM; + } + /* Only AIO interface */ + if (file->f_flags & O_NONBLOCK) { + pr_debug("set to aio interface\n"); + audio->drv_status |= ADRV_STATUS_AIO_INTF; + audio->drv_ops.out_flush = audwmapro_async_out_flush; + audio->drv_ops.in_flush = audwmapro_async_in_flush; + audio->drv_ops.fsync = audwmapro_async_fsync; + q6asm_set_io_mode(audio->ac, ASYNC_IO_MODE); + } else { + pr_aud_err("SIO interface not supported\n"); + rc = -EACCES; + goto fail; + } + + /* open in T/NT mode */ + if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) { + rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM, + FORMAT_WMA_V10PRO); + if (rc < 0) { + pr_aud_err("NT mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = NON_TUNNEL_MODE; + /* open WMA decoder, expected frames is always 1*/ + audio->buf_cfg.frames_per_buf = 0x01; + audio->buf_cfg.meta_info_enable = 0x01; + } else if ((file->f_mode & FMODE_WRITE) && + !(file->f_mode & FMODE_READ)) { + rc = q6asm_open_write(audio->ac, FORMAT_WMA_V10PRO); + if (rc < 0) { + pr_aud_err("T mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = TUNNEL_MODE; + audio->buf_cfg.meta_info_enable = 0x00; + } else { + pr_aud_err("Not supported mode\n"); + rc = -EACCES; + goto fail; + } + /* Initialize all locks of audio instance */ + mutex_init(&audio->lock); + mutex_init(&audio->read_lock); + mutex_init(&audio->write_lock); + mutex_init(&audio->get_event_lock); + spin_lock_init(&audio->dsp_lock); + spin_lock_init(&audio->event_queue_lock); + init_waitqueue_head(&audio->cmd_wait); + init_waitqueue_head(&audio->write_wait); + init_waitqueue_head(&audio->event_wait); + INIT_LIST_HEAD(&audio->out_queue); + INIT_LIST_HEAD(&audio->in_queue); + INIT_LIST_HEAD(&audio->pmem_region_queue); + INIT_LIST_HEAD(&audio->free_event_queue); + INIT_LIST_HEAD(&audio->event_queue); + + audio->drv_ops.out_flush(audio); + audio->opened = 1; + file->private_data = audio; + +#ifdef CONFIG_DEBUG_FS + snprintf(name, sizeof name, "msm_wmapro_%04x", audio->ac->session); + audio->dentry = debugfs_create_file(name, S_IFREG | S_IRUGO, + NULL, (void *)audio, + &audwmapro_debug_fops); + + if (IS_ERR(audio->dentry)) + pr_debug("debugfs_create_file failed\n"); +#endif + for (i = 0; i < AUDWMAPRO_EVENT_NUM; i++) { + e_node = kmalloc(sizeof(struct audwmapro_event), GFP_KERNEL); + if (e_node) + list_add_tail(&e_node->list, &audio->free_event_queue); + else { + pr_aud_err("event pkt alloc failed\n"); + break; + } + } + pr_aud_info("%s:wmapro decoder open success, session_id = %d\n", __func__, + audio->ac->session); + return 0; +fail: + q6asm_audio_client_free(audio->ac); + kfree(audio); + return rc; +} + +static const struct file_operations audio_wmapro_fops = { + .owner = THIS_MODULE, + .open = audio_open, + .release = audio_release, + .unlocked_ioctl = audio_ioctl, + .fsync = audwmapro_fsync, +}; + +struct miscdevice audwmapro_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_wmapro", + .fops = &audio_wmapro_fops, +}; + +static int __init audio_wmapro_init(void) +{ + return misc_register(&audwmapro_misc); +} + +device_initcall(audio_wmapro_init); diff --git a/arch/arm/mach-msm/qdsp6v3/board-msm8x60-audio.c b/arch/arm/mach-msm/qdsp6v3/board-msm8x60-audio.c new file mode 100644 index 00000000000..00ba57e5aa0 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v3/board-msm8x60-audio.c @@ -0,0 +1,1901 @@ +/* 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 +#include +#include "timpani_profile_8x60.h" + +#ifdef CONFIG_MACH_VIGOR +#include "timpani_profile_8x60_vigor.h" +#else +#include "timpani_profile_8x60_lead.h" +#endif + +#include +#include "snddev_mi2s.h" +#include + +#include "snddev_virtual.h" + +#ifdef CONFIG_DEBUG_FS +static struct dentry *debugfs_hsed_config; +static void snddev_hsed_config_modify_setting(int type); +static void snddev_hsed_config_restore_setting(void); +#endif + +/* define the value for BT_SCO */ + + +#define SNDDEV_GPIO_MIC2_ANCR_SEL 294 + +static struct q6v2audio_analog_ops default_audio_ops; +static struct q6v2audio_analog_ops *audio_ops = &default_audio_ops; + +void speaker_enable(int en) +{ + if (audio_ops->speaker_enable) + audio_ops->speaker_enable(en); +} + +void headset_enable(int en) +{ + if (audio_ops->headset_enable) + audio_ops->headset_enable(en); +} + +void handset_enable(int en) +{ + if (audio_ops->handset_enable) + audio_ops->handset_enable(en); +} + +void headset_speaker_enable(int en) +{ + if (audio_ops->headset_speaker_enable) + audio_ops->headset_speaker_enable(en); +} + +void int_mic_enable(int en) +{ + if (audio_ops->int_mic_enable) + audio_ops->int_mic_enable(en); +} + +void back_mic_enable(int en) +{ + if (audio_ops->back_mic_enable) + audio_ops->back_mic_enable(en); +} + +void ext_mic_enable(int en) +{ + if (audio_ops->ext_mic_enable) + audio_ops->ext_mic_enable(en); +} + +void stereo_mic_enable(int en) +{ + if (audio_ops->stereo_mic_enable) + audio_ops->stereo_mic_enable(en); +} + +void usb_headset_enable(int en) +{ + if (audio_ops->usb_headset_enable) + audio_ops->usb_headset_enable(en); +} + +void fm_headset_enable(int en) +{ + if (audio_ops->fm_headset_enable) + audio_ops->fm_headset_enable(en); +} + +void fm_speaker_enable(int en) +{ + if (audio_ops->fm_speaker_enable) + audio_ops->fm_speaker_enable(en); +} + +void voltage_on(int on) +{ + if (audio_ops->voltage_on) + audio_ops->voltage_on(on); +} + +static struct regulator *s3; +static struct regulator *mvs; + +static void msm_snddev_enable_dmic_power(void) +{ + int ret; + + pr_aud_err("%s", __func__); + s3 = regulator_get(NULL, "8058_s3"); + if (IS_ERR(s3)) + return; + + ret = regulator_set_voltage(s3, 1800000, 1800000); + if (ret) { + pr_aud_err("%s: error setting voltage\n", __func__); + goto fail_s3; + } + + ret = regulator_enable(s3); + if (ret) { + pr_aud_err("%s: error enabling regulator\n", __func__); + goto fail_s3; + } + + mvs = regulator_get(NULL, "8901_mvs0"); + if (IS_ERR(mvs)) + goto fail_mvs0_get; + + ret = regulator_enable(mvs); + if (ret) { + pr_aud_err("%s: error setting regulator\n", __func__); + goto fail_mvs0_enable; + } + + stereo_mic_enable(1); + return; + +fail_mvs0_enable: + regulator_put(mvs); + mvs = NULL; +fail_mvs0_get: + regulator_disable(s3); +fail_s3: + regulator_put(s3); + s3 = NULL; +} + +static void msm_snddev_disable_dmic_power(void) +{ + int ret; + pr_aud_err("%s", __func__); + + if (mvs) { + ret = regulator_disable(mvs); + if (ret < 0) + pr_aud_err("%s: error disabling vreg mvs\n", __func__); + regulator_put(mvs); + mvs = NULL; + } + + if (s3) { + ret = regulator_disable(s3); + if (ret < 0) + pr_aud_err("%s: error disabling regulator s3\n", __func__); + regulator_put(s3); + s3 = NULL; + } + stereo_mic_enable(0); + +} + +static void msm_snddev_dmic_power(int en) +{ + pr_aud_err("%s", __func__); + if (en) + msm_snddev_enable_dmic_power(); + else + msm_snddev_disable_dmic_power(); +} + +/* GPIO_CLASS_D0_EN */ +#define SNDDEV_GPIO_CLASS_D0_EN 227 + +/* GPIO_CLASS_D1_EN */ +#define SNDDEV_GPIO_CLASS_D1_EN 229 + +#define PM8901_MPP_3 (2) /* PM8901 MPP starts from 0 */ +static void config_class_d1_gpio(int enable) +{ + int rc; + + if (enable) { + rc = gpio_request(SNDDEV_GPIO_CLASS_D1_EN, "CLASSD1_EN"); + if (rc) { + pr_aud_err("%s: spkr pamp gpio %d request" + "failed\n", __func__, SNDDEV_GPIO_CLASS_D1_EN); + return; + } + gpio_direction_output(SNDDEV_GPIO_CLASS_D1_EN, 1); + gpio_set_value_cansleep(SNDDEV_GPIO_CLASS_D1_EN, 1); + } else { + gpio_set_value_cansleep(SNDDEV_GPIO_CLASS_D1_EN, 0); + gpio_free(SNDDEV_GPIO_CLASS_D1_EN); + } +} + +static void config_class_d0_gpio(int enable) +{ + int rc; + + if (enable) { + rc = pm8901_mpp_config_digital_out(PM8901_MPP_3, + PM8901_MPP_DIG_LEVEL_MSMIO, 1); + + if (rc) { + pr_aud_err("%s: CLASS_D0_EN failed\n", __func__); + return; + } + + rc = gpio_request(SNDDEV_GPIO_CLASS_D0_EN, "CLASSD0_EN"); + + if (rc) { + pr_aud_err("%s: spkr pamp gpio pm8901 mpp3 request" + "failed\n", __func__); + pm8901_mpp_config_digital_out(PM8901_MPP_3, + PM8901_MPP_DIG_LEVEL_MSMIO, 0); + return; + } + + gpio_direction_output(SNDDEV_GPIO_CLASS_D0_EN, 1); + gpio_set_value(SNDDEV_GPIO_CLASS_D0_EN, 1); + + } else { + pm8901_mpp_config_digital_out(PM8901_MPP_3, + PM8901_MPP_DIG_LEVEL_MSMIO, 0); + gpio_set_value(SNDDEV_GPIO_CLASS_D0_EN, 0); + gpio_free(SNDDEV_GPIO_CLASS_D0_EN); + } +} + +void msm_snddev_poweramp_on(void) +{ + + pr_debug("%s: enable stereo spkr amp\n", __func__); + config_class_d0_gpio(1); + config_class_d1_gpio(1); +} + +void msm_snddev_poweramp_off(void) +{ + + pr_debug("%s: disable stereo spkr amp\n", __func__); + config_class_d0_gpio(0); + config_class_d1_gpio(0); + msleep(30); +} + + +static void msm_snddev_enable_dmic_sec_power(void) +{ + pr_aud_err("%s", __func__); + msm_snddev_enable_dmic_power(); + +#ifdef CONFIG_PMIC8058_OTHC + pm8058_micbias_enable(OTHC_MICBIAS_2, OTHC_SIGNAL_ALWAYS_ON); +#endif +} + +static void msm_snddev_disable_dmic_sec_power(void) +{ + pr_aud_err("%s", __func__); + msm_snddev_disable_dmic_power(); + +#ifdef CONFIG_PMIC8058_OTHC + pm8058_micbias_enable(OTHC_MICBIAS_2, OTHC_SIGNAL_OFF); +#endif +} + +static void msm_snddev_dmic_sec_power(int en) +{ + pr_aud_err("%s", __func__); + if (en) + msm_snddev_enable_dmic_sec_power(); + else + msm_snddev_disable_dmic_sec_power(); +} + +static struct adie_codec_action_unit iearpiece_48KHz_osr256_actions[] = + EAR_PRI_MONO_48000_OSR_256; + +static struct adie_codec_hwsetting_entry iearpiece_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = iearpiece_48KHz_osr256_actions, + .action_sz = ARRAY_SIZE(iearpiece_48KHz_osr256_actions), + } +}; + +static struct adie_codec_dev_profile iearpiece_profile = { + .path_type = ADIE_CODEC_RX, + .settings = iearpiece_settings, + .setting_sz = ARRAY_SIZE(iearpiece_settings), +}; + +static struct snddev_icodec_data snddev_iearpiece_data = { + .capability = (SNDDEV_CAP_RX | SNDDEV_CAP_VOICE), + .name = "handset_rx", + .copp_id = 0, + .profile = &iearpiece_profile, + .channel_mode = 1, + .default_sample_rate = 48000, + .pamp_on = handset_enable, + .voltage_on = voltage_on, + .aic3254_id = PLAYBACK_RECEIVER, + .aic3254_voc_id = CALL_DOWNLINK_IMIC_RECEIVER, + .default_aic3254_id = PLAYBACK_RECEIVER, +}; + +static struct platform_device msm_iearpiece_device = { + .name = "snddev_icodec", + .id = 0, + .dev = { .platform_data = &snddev_iearpiece_data }, +}; + +static struct adie_codec_action_unit iearpiece_hac_48KHz_osr256_actions[] = + EAR_PRI_MONO_48000_OSR_256; + +static struct adie_codec_hwsetting_entry iearpiece_hac_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = iearpiece_hac_48KHz_osr256_actions, + .action_sz = ARRAY_SIZE(iearpiece_hac_48KHz_osr256_actions), + } +}; + +static struct adie_codec_dev_profile iearpiece_hac_profile = { + .path_type = ADIE_CODEC_RX, + .settings = iearpiece_hac_settings, + .setting_sz = ARRAY_SIZE(iearpiece_hac_settings), +}; + +static struct snddev_icodec_data snddev_ihac_data = { + .capability = (SNDDEV_CAP_RX | SNDDEV_CAP_VOICE), + .name = "hac_rx", + .copp_id = 0, + .profile = &iearpiece_hac_profile, + .channel_mode = 1, + .default_sample_rate = 48000, + .pamp_on = handset_enable, + .voltage_on = voltage_on, + .aic3254_id = PLAYBACK_RECEIVER, + .aic3254_voc_id = HAC, + .default_aic3254_id = PLAYBACK_RECEIVER, +}; + +static struct platform_device msm_ihac_device = { + .name = "snddev_icodec", + .id = 38, + .dev = { .platform_data = &snddev_ihac_data }, +}; + +static struct adie_codec_action_unit imic_48KHz_osr256_actions[] = + AMIC_PRI_MONO_48000_OSR_256; + +static struct adie_codec_hwsetting_entry imic_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = imic_48KHz_osr256_actions, + .action_sz = ARRAY_SIZE(imic_48KHz_osr256_actions), + } +}; + +static struct adie_codec_dev_profile imic_profile = { + .path_type = ADIE_CODEC_TX, + .settings = imic_settings, + .setting_sz = ARRAY_SIZE(imic_settings), +}; + +static struct snddev_icodec_data snddev_imic_data = { + .capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE), + .name = "handset_tx", + .copp_id = 1, + .profile = &imic_profile, + .channel_mode = 1, + .default_sample_rate = 48000, + .pamp_on = int_mic_enable, + .aic3254_id = VOICERECOGNITION_IMIC, + .aic3254_voc_id = CALL_UPLINK_IMIC_RECEIVER, + .default_aic3254_id = VOICERECOGNITION_IMIC, +}; + +static struct platform_device msm_imic_device = { + .name = "snddev_icodec", + .id = 1, + .dev = { .platform_data = &snddev_imic_data }, +}; + +static struct snddev_icodec_data snddev_nomic_headset_data = { + .capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE), + .name = "nomic_headset_tx", + .copp_id = 1, + .profile = &imic_profile, + .channel_mode = 1, + .default_sample_rate = 48000, + .pamp_on = int_mic_enable, + .aic3254_id = VOICERECORD_IMIC, + .aic3254_voc_id = CALL_UPLINK_IMIC_HEADSET, + .default_aic3254_id = VOICERECORD_IMIC, +}; + +static struct platform_device msm_nomic_headset_tx_device = { + .name = "snddev_icodec", + .id = 40, + .dev = { .platform_data = &snddev_nomic_headset_data }, +}; + +static struct adie_codec_action_unit headset_ab_cpls_48KHz_osr256_actions[] = + HEADSET_AB_CPLS_48000_OSR_256; + +static struct adie_codec_hwsetting_entry headset_ab_cpls_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = headset_ab_cpls_48KHz_osr256_actions, + .action_sz = ARRAY_SIZE(headset_ab_cpls_48KHz_osr256_actions), + } +}; + +static struct adie_codec_dev_profile headset_ab_cpls_profile = { + .path_type = ADIE_CODEC_RX, + .settings = headset_ab_cpls_settings, + .setting_sz = ARRAY_SIZE(headset_ab_cpls_settings), +}; + +static struct snddev_icodec_data snddev_ihs_stereo_rx_data = { + .capability = (SNDDEV_CAP_RX | SNDDEV_CAP_VOICE), + .name = "headset_stereo_rx", + .copp_id = 0, + .profile = &headset_ab_cpls_profile, + .channel_mode = 2, + .default_sample_rate = 48000, + .pamp_on = headset_enable, + .voltage_on = voltage_on, + .aic3254_id = PLAYBACK_HEADSET, + .aic3254_voc_id = CALL_DOWNLINK_EMIC_HEADSET, + .default_aic3254_id = PLAYBACK_HEADSET, +}; + +static struct platform_device msm_headset_stereo_device = { + .name = "snddev_icodec", + .id = 34, + .dev = { .platform_data = &snddev_ihs_stereo_rx_data }, +}; + +static struct snddev_icodec_data snddev_nomic_ihs_stereo_rx_data = { + .capability = (SNDDEV_CAP_RX | SNDDEV_CAP_VOICE), + .name = "nomic_headset_stereo_rx", + .copp_id = 0, + .profile = &headset_ab_cpls_profile, + .channel_mode = 2, + .default_sample_rate = 48000, + .pamp_on = headset_enable, + .voltage_on = voltage_on, + .aic3254_id = PLAYBACK_HEADSET, + .aic3254_voc_id = CALL_DOWNLINK_IMIC_HEADSET, + .default_aic3254_id = PLAYBACK_HEADSET, +}; + +static struct platform_device msm_nomic_headset_stereo_device = { + .name = "snddev_icodec", + .id = 39, + .dev = { .platform_data = &snddev_nomic_ihs_stereo_rx_data }, +}; + +static struct adie_codec_action_unit headset_anc_48KHz_osr256_actions[] = + ANC_HEADSET_CPLS_AMIC1_AUXL_RX1_48000_OSR_256; + +static struct adie_codec_hwsetting_entry headset_anc_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = headset_anc_48KHz_osr256_actions, + .action_sz = ARRAY_SIZE(headset_anc_48KHz_osr256_actions), + } +}; + +static struct adie_codec_dev_profile headset_anc_profile = { + .path_type = ADIE_CODEC_RX, + .settings = headset_anc_settings, + .setting_sz = ARRAY_SIZE(headset_anc_settings), +}; + +static struct snddev_icodec_data snddev_anc_headset_data = { + .capability = (SNDDEV_CAP_RX | SNDDEV_CAP_VOICE | SNDDEV_CAP_ANC), + .name = "anc_headset_stereo_rx", + .copp_id = PRIMARY_I2S_RX, + .profile = &headset_anc_profile, + .channel_mode = 2, + .default_sample_rate = 48000, + .pamp_on = int_mic_enable, +}; + +static struct platform_device msm_anc_headset_device = { + .name = "snddev_icodec", + .id = 51, + .dev = { .platform_data = &snddev_anc_headset_data }, +}; + +static struct adie_codec_action_unit ispkr_stereo_48KHz_osr256_actions[] = + SPEAKER_PRI_48000_OSR_256; + +static struct adie_codec_hwsetting_entry ispkr_stereo_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = ispkr_stereo_48KHz_osr256_actions, + .action_sz = ARRAY_SIZE(ispkr_stereo_48KHz_osr256_actions), + } +}; + +static struct adie_codec_dev_profile ispkr_stereo_profile = { + .path_type = ADIE_CODEC_RX, + .settings = ispkr_stereo_settings, + .setting_sz = ARRAY_SIZE(ispkr_stereo_settings), +}; + +static struct snddev_icodec_data snddev_ispkr_stereo_data = { + .capability = (SNDDEV_CAP_RX | SNDDEV_CAP_VOICE), + .name = "speaker_stereo_rx", + .copp_id = 0, + .profile = &ispkr_stereo_profile, + .channel_mode = 2, + .default_sample_rate = 48000, + .pamp_on = speaker_enable, + .voltage_on = voltage_on, + .aic3254_id = PLAYBACK_SPEAKER, + .aic3254_voc_id = CALL_DOWNLINK_IMIC_SPEAKER, + .default_aic3254_id = PLAYBACK_SPEAKER, +}; + +static struct platform_device msm_ispkr_stereo_device = { + .name = "snddev_icodec", + .id = 2, + .dev = { .platform_data = &snddev_ispkr_stereo_data }, +}; + +static struct adie_codec_action_unit idmic_mono_48KHz_osr256_actions[] = + AMIC_PRI_MONO_48000_OSR_256; + +static struct adie_codec_hwsetting_entry idmic_mono_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = idmic_mono_48KHz_osr256_actions, + .action_sz = ARRAY_SIZE(idmic_mono_48KHz_osr256_actions), + } +}; + +static struct adie_codec_dev_profile idmic_mono_profile = { + .path_type = ADIE_CODEC_TX, + .settings = idmic_mono_settings, + .setting_sz = ARRAY_SIZE(idmic_mono_settings), +}; + +static struct snddev_icodec_data snddev_ispkr_mic_data = { + .capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE), + .name = "speaker_mono_tx", + .copp_id = PRIMARY_I2S_TX, + .profile = &idmic_mono_profile, + .channel_mode = 1, + .default_sample_rate = 48000, + .pamp_on = int_mic_enable, + .aic3254_id = VOICERECORD_IMIC, + .aic3254_voc_id = CALL_UPLINK_IMIC_SPEAKER, + .default_aic3254_id = VOICERECORD_IMIC, +}; + +static struct platform_device msm_ispkr_mic_device = { + .name = "snddev_icodec", + .id = 3, + .dev = { .platform_data = &snddev_ispkr_mic_data }, +}; + +static struct adie_codec_action_unit handset_dual_mic_endfire_8KHz_osr256_actions[] = + DMIC1_PRI_STEREO_8000_OSR_256; + +static struct adie_codec_action_unit spk_dual_mic_endfire_8KHz_osr256_actions[] = + DMIC1_PRI_STEREO_8000_OSR_256; + +static struct adie_codec_hwsetting_entry handset_dual_mic_endfire_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = handset_dual_mic_endfire_8KHz_osr256_actions, + .action_sz = ARRAY_SIZE(handset_dual_mic_endfire_8KHz_osr256_actions), + } +}; + +static struct adie_codec_hwsetting_entry spk_dual_mic_endfire_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = spk_dual_mic_endfire_8KHz_osr256_actions, + .action_sz = ARRAY_SIZE(spk_dual_mic_endfire_8KHz_osr256_actions), + } +}; + +static struct adie_codec_dev_profile handset_dual_mic_endfire_profile = { + .path_type = ADIE_CODEC_TX, + .settings = handset_dual_mic_endfire_settings, + .setting_sz = ARRAY_SIZE(handset_dual_mic_endfire_settings), +}; + +static struct adie_codec_dev_profile spk_dual_mic_endfire_profile = { + .path_type = ADIE_CODEC_TX, + .settings = spk_dual_mic_endfire_settings, + .setting_sz = ARRAY_SIZE(spk_dual_mic_endfire_settings), +}; + +static struct snddev_icodec_data snddev_dual_mic_endfire_data = { + .capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE), + .name = "handset_dual_mic_endfire_tx", + .copp_id = PRIMARY_I2S_TX, + .profile = &handset_dual_mic_endfire_profile, + .channel_mode = 2, + .default_sample_rate = 48000, + .pamp_on = msm_snddev_dmic_power, + .aic3254_id = VOICERECORD_IMIC, + .aic3254_voc_id = CALL_UPLINK_IMIC_RECEIVER, + .default_aic3254_id = VOICERECORD_IMIC, +}; + +static struct platform_device msm_hs_dual_mic_endfire_device = { + .name = "snddev_icodec", + .id = 14, + .dev = { .platform_data = &snddev_dual_mic_endfire_data }, +}; + +static struct snddev_icodec_data snddev_dual_mic_spkr_endfire_data = { + .capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE), + .name = "speaker_dual_mic_endfire_tx", + .copp_id = PRIMARY_I2S_TX, + .profile = &spk_dual_mic_endfire_profile, + .channel_mode = 2, + .default_sample_rate = 48000, + .pamp_on = msm_snddev_dmic_power, + .aic3254_id = VOICERECORD_IMIC, + .aic3254_voc_id = CALL_UPLINK_IMIC_SPEAKER, + .default_aic3254_id = VOICERECORD_IMIC, +}; + +static struct platform_device msm_spkr_dual_mic_endfire_device = { + .name = "snddev_icodec", + .id = 15, + .dev = { .platform_data = &snddev_dual_mic_spkr_endfire_data }, +}; + +static struct adie_codec_action_unit dual_mic_broadside_8osr256_actions[] = + HS_DMIC2_STEREO_8000_OSR_256; + +static struct adie_codec_hwsetting_entry dual_mic_broadside_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = dual_mic_broadside_8osr256_actions, + .action_sz = ARRAY_SIZE(dual_mic_broadside_8osr256_actions), + } +}; + +static struct adie_codec_dev_profile dual_mic_broadside_profile = { + .path_type = ADIE_CODEC_TX, + .settings = dual_mic_broadside_settings, + .setting_sz = ARRAY_SIZE(dual_mic_broadside_settings), +}; + +static struct snddev_icodec_data snddev_hs_dual_mic_broadside_data = { + .capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE), + .name = "handset_dual_mic_broadside_tx", + .copp_id = PRIMARY_I2S_TX, + .profile = &dual_mic_broadside_profile, + .channel_mode = 2, + .default_sample_rate = 48000, + .pamp_on = msm_snddev_dmic_sec_power, +}; + +static struct platform_device msm_hs_dual_mic_broadside_device = { + .name = "snddev_icodec", + .id = 21, + .dev = { .platform_data = &snddev_hs_dual_mic_broadside_data }, +}; + +static struct snddev_icodec_data snddev_spkr_dual_mic_broadside_data = { + .capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE), + .name = "speaker_dual_mic_broadside_tx", + .copp_id = PRIMARY_I2S_TX, + .profile = &dual_mic_broadside_profile, + .channel_mode = 2, + .default_sample_rate = 48000, + .pamp_on = msm_snddev_dmic_sec_power, +}; + +static struct platform_device msm_spkr_dual_mic_broadside_device = { + .name = "snddev_icodec", + .id = 18, + .dev = { .platform_data = &snddev_spkr_dual_mic_broadside_data }, +}; + +static struct snddev_hdmi_data snddev_hdmi_stereo_rx_data = { + .capability = SNDDEV_CAP_RX , + .name = "hdmi_stereo_rx", + .copp_id = HDMI_RX, + .channel_mode = 0, + .default_sample_rate = 48000, +}; + +static struct platform_device msm_snddev_hdmi_stereo_rx_device = { + .name = "snddev_hdmi", + .id = 0, + .dev = { .platform_data = &snddev_hdmi_stereo_rx_data }, +}; + +static struct snddev_mi2s_data snddev_mi2s_fm_tx_data = { + .capability = SNDDEV_CAP_TX , + .name = "fmradio_stereo_tx", + .copp_id = MI2S_TX, + .channel_mode = 2, /* stereo */ + .sd_lines = MI2S_SD3, /* sd3 */ + .sample_rate = 48000, +}; + +static struct platform_device msm_mi2s_fm_tx_device = { + .name = "snddev_mi2s", + .id = 0, + .dev = { .platform_data = &snddev_mi2s_fm_tx_data }, +}; + +static struct adie_codec_action_unit ifmradio_speaker_osr256_actions[] = + AUXPGA_SPEAKER_RX; + +static struct adie_codec_hwsetting_entry ifmradio_speaker_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = ifmradio_speaker_osr256_actions, + .action_sz = ARRAY_SIZE(ifmradio_speaker_osr256_actions), + } +}; + +static struct adie_codec_dev_profile ifmradio_speaker_profile = { + .path_type = ADIE_CODEC_RX, + .settings = ifmradio_speaker_settings, + .setting_sz = ARRAY_SIZE(ifmradio_speaker_settings), +}; + +static struct snddev_mi2s_data snddev_mi2s_fm_rx_data = { + .capability = SNDDEV_CAP_RX , + .name = "fmradio_stereo_rx", + .copp_id = MI2S_RX, + .channel_mode = 2, /* stereo */ + .sd_lines = MI2S_SD3, /* sd3 */ + .sample_rate = 48000, +}; + +static struct platform_device msm_mi2s_fm_rx_device = { + .name = "snddev_mi2s", + .id = 1, + .dev = { .platform_data = &snddev_mi2s_fm_rx_data }, +}; + +static struct snddev_icodec_data snddev_ifmradio_speaker_data = { + .capability = (SNDDEV_CAP_RX | SNDDEV_CAP_FM), + .name = "fmradio_speaker_rx", + .copp_id = 0, + .profile = &ifmradio_speaker_profile, + .channel_mode = 1, + .default_sample_rate = 48000, + .pamp_on = fm_speaker_enable, + .voltage_on = voltage_on, + .aic3254_id = FM_OUT_SPEAKER, + .aic3254_voc_id = FM_OUT_SPEAKER, + .default_aic3254_id = FM_OUT_SPEAKER, +}; + +static struct platform_device msm_ifmradio_speaker_device = { + .name = "snddev_icodec", + .id = 9, + .dev = { .platform_data = &snddev_ifmradio_speaker_data }, +}; + +static struct adie_codec_action_unit ifmradio_headset_osr256_actions[] = + AUXPGA_HEADSET_AB_CPLS_RX_48000; + +static struct adie_codec_hwsetting_entry ifmradio_headset_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = ifmradio_headset_osr256_actions, + .action_sz = ARRAY_SIZE(ifmradio_headset_osr256_actions), + } +}; + +static struct adie_codec_dev_profile ifmradio_headset_profile = { + .path_type = ADIE_CODEC_RX, + .settings = ifmradio_headset_settings, + .setting_sz = ARRAY_SIZE(ifmradio_headset_settings), +}; + +static struct snddev_icodec_data snddev_ifmradio_headset_data = { + .capability = (SNDDEV_CAP_RX | SNDDEV_CAP_FM), + .name = "fmradio_headset_rx", + .copp_id = 0, + .profile = &ifmradio_headset_profile, + .channel_mode = 2, + .default_sample_rate = 48000, + .pamp_on = fm_headset_enable, + .voltage_on = voltage_on, + .aic3254_id = FM_OUT_HEADSET, + .aic3254_voc_id = FM_OUT_HEADSET, + .default_aic3254_id = FM_OUT_HEADSET, +}; + +static struct platform_device msm_ifmradio_headset_device = { + .name = "snddev_icodec", + .id = 10, + .dev = { .platform_data = &snddev_ifmradio_headset_data }, +}; + +static struct adie_codec_action_unit iheadset_mic_tx_osr256_actions[] = + HS_AMIC2_MONO_48000_OSR_256; + +static struct adie_codec_hwsetting_entry iheadset_mic_tx_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = iheadset_mic_tx_osr256_actions, + .action_sz = ARRAY_SIZE(iheadset_mic_tx_osr256_actions), + } +}; + +static struct adie_codec_dev_profile iheadset_mic_profile = { + .path_type = ADIE_CODEC_TX, + .settings = iheadset_mic_tx_settings, + .setting_sz = ARRAY_SIZE(iheadset_mic_tx_settings), +}; + +static struct snddev_icodec_data snddev_headset_mic_data = { + .capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE), + .name = "headset_mono_tx", + .copp_id = PRIMARY_I2S_TX, + .profile = &iheadset_mic_profile, + .channel_mode = 1, + .default_sample_rate = 48000, + .pamp_on = ext_mic_enable, + .aic3254_id = VOICERECOGNITION_EMIC, + .aic3254_voc_id = CALL_UPLINK_EMIC_HEADSET, + .default_aic3254_id = VOICERECORD_EMIC, +}; + +static struct platform_device msm_headset_mic_device = { + .name = "snddev_icodec", + .id = 33, + .dev = { .platform_data = &snddev_headset_mic_data }, +}; + +static struct adie_codec_action_unit + ihs_stereo_speaker_stereo_rx_48KHz_osr256_actions[] = + SPEAKER_HPH_AB_CPL_PRI_STEREO_48000_OSR_256; + +static struct adie_codec_hwsetting_entry + ihs_stereo_speaker_stereo_rx_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = ihs_stereo_speaker_stereo_rx_48KHz_osr256_actions, + .action_sz = + ARRAY_SIZE(ihs_stereo_speaker_stereo_rx_48KHz_osr256_actions), + } +}; + +static struct adie_codec_dev_profile ihs_stereo_speaker_stereo_rx_profile = { + .path_type = ADIE_CODEC_RX, + .settings = ihs_stereo_speaker_stereo_rx_settings, + .setting_sz = ARRAY_SIZE(ihs_stereo_speaker_stereo_rx_settings), +}; + +static struct snddev_icodec_data snddev_ihs_stereo_speaker_stereo_rx_data = { + .capability = (SNDDEV_CAP_RX | SNDDEV_CAP_VOICE), + .name = "headset_stereo_speaker_stereo_rx", + .copp_id = 0, + .profile = &ihs_stereo_speaker_stereo_rx_profile, + .channel_mode = 2, + .default_sample_rate = 48000, + .pamp_on = headset_speaker_enable, + .voltage_on = voltage_on, + .aic3254_id = RING_HEADSET_SPEAKER, + .aic3254_voc_id = RING_HEADSET_SPEAKER, + .default_aic3254_id = RING_HEADSET_SPEAKER, +}; + +static struct platform_device msm_ihs_stereo_speaker_stereo_rx_device = { + .name = "snddev_icodec", + .id = 22, + .dev = { .platform_data = &snddev_ihs_stereo_speaker_stereo_rx_data }, +}; + +static struct snddev_ecodec_data snddev_bt_sco_earpiece_data = { + .capability = (SNDDEV_CAP_RX | SNDDEV_CAP_VOICE), + .name = "bt_sco_rx", + .copp_id = PCM_RX, + .channel_mode = 1, +}; + +static struct snddev_ecodec_data snddev_bt_sco_mic_data = { + .capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE), + .name = "bt_sco_tx", + .copp_id = PCM_TX, + .channel_mode = 1, +}; + +struct platform_device msm_bt_sco_earpiece_device = { + .name = "msm_snddev_ecodec", + .id = 0, + .dev = { .platform_data = &snddev_bt_sco_earpiece_data }, +}; + +struct platform_device msm_bt_sco_mic_device = { + .name = "msm_snddev_ecodec", + .id = 1, + .dev = { .platform_data = &snddev_bt_sco_mic_data }, +}; + +static struct adie_codec_action_unit itty_mono_tx_actions[] = + TTY_HEADSET_MONO_TX_48000_OSR_256; + +static struct adie_codec_hwsetting_entry itty_mono_tx_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = itty_mono_tx_actions, + .action_sz = ARRAY_SIZE(itty_mono_tx_actions), + }, +}; + +static struct adie_codec_dev_profile itty_mono_tx_profile = { + .path_type = ADIE_CODEC_TX, + .settings = itty_mono_tx_settings, + .setting_sz = ARRAY_SIZE(itty_mono_tx_settings), +}; + +static struct snddev_icodec_data snddev_itty_mono_tx_data = { + .capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE | SNDDEV_CAP_TTY), + .name = "tty_headset_mono_tx", + .copp_id = PRIMARY_I2S_TX, + .profile = &itty_mono_tx_profile, + .channel_mode = 1, + .default_sample_rate = 48000, + .pamp_on = ext_mic_enable, + .aic3254_id = TTY_IN_FULL, + .aic3254_voc_id = TTY_IN_FULL, + .default_aic3254_id = TTY_IN_FULL, +}; + +static struct platform_device msm_itty_mono_tx_device = { + .name = "snddev_icodec", + .id = 16, + .dev = { .platform_data = &snddev_itty_mono_tx_data }, +}; + +static struct adie_codec_action_unit itty_mono_rx_actions[] = + TTY_HEADSET_MONO_RX_48000_OSR_256; + +static struct adie_codec_hwsetting_entry itty_mono_rx_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = itty_mono_rx_actions, + .action_sz = ARRAY_SIZE(itty_mono_rx_actions), + }, +}; + +static struct adie_codec_dev_profile itty_mono_rx_profile = { + .path_type = ADIE_CODEC_RX, + .settings = itty_mono_rx_settings, + .setting_sz = ARRAY_SIZE(itty_mono_rx_settings), +}; + +static struct snddev_icodec_data snddev_itty_mono_rx_data = { + .capability = (SNDDEV_CAP_RX | SNDDEV_CAP_VOICE | SNDDEV_CAP_TTY), + .name = "tty_headset_mono_rx", + .copp_id = PRIMARY_I2S_RX, + .profile = &itty_mono_rx_profile, + .channel_mode = 1, + .default_sample_rate = 48000, + .pamp_on = headset_enable, + .voltage_on = voltage_on, + .aic3254_id = TTY_OUT_FULL, + .aic3254_voc_id = TTY_OUT_FULL, + .default_aic3254_id = TTY_OUT_FULL, +}; + +static struct platform_device msm_itty_mono_rx_device = { + .name = "snddev_icodec", + .id = 17, + .dev = { .platform_data = &snddev_itty_mono_rx_data }, +}; + +#if 1 //HTC created device +static struct adie_codec_action_unit bmic_tx_48KHz_osr256_actions[] = + AMIC_SEC_MONO_48000_OSR_256; + +static struct adie_codec_hwsetting_entry bmic_tx_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = bmic_tx_48KHz_osr256_actions, + .action_sz = ARRAY_SIZE(bmic_tx_48KHz_osr256_actions), + } +}; + +static struct adie_codec_dev_profile bmic_tx_profile = { + .path_type = ADIE_CODEC_TX, + .settings = bmic_tx_settings, + .setting_sz = ARRAY_SIZE(bmic_tx_settings), +}; + +static struct snddev_icodec_data snddev_bmic_tx_data = { + .capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE), + .name = "back_mic_tx", + .copp_id = 1, + .profile = &bmic_tx_profile, + .channel_mode = 1, + .default_sample_rate = 48000, + .pamp_on = back_mic_enable, + .aic3254_id = VOICERECORD_EMIC, + .aic3254_voc_id = VOICERECORD_EMIC, + .default_aic3254_id = VOICERECORD_EMIC, +}; + +static struct platform_device msm_bmic_tx_device = { + .name = "snddev_icodec", + .id = 50, /* FIX ME */ + .dev = { .platform_data = &snddev_bmic_tx_data }, +}; + +static struct adie_codec_action_unit headset_mono_ab_cpls_48KHz_osr256_actions[] = + HEADSET_AB_CPLS_48000_OSR_256; /* FIX ME */ + +static struct adie_codec_hwsetting_entry headset_mono_ab_cpls_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = headset_mono_ab_cpls_48KHz_osr256_actions, + .action_sz = ARRAY_SIZE(headset_mono_ab_cpls_48KHz_osr256_actions), + } +}; + +static struct adie_codec_dev_profile headset_mono_ab_cpls_profile = { + .path_type = ADIE_CODEC_RX, + .settings = headset_mono_ab_cpls_settings, + .setting_sz = ARRAY_SIZE(headset_mono_ab_cpls_settings), +}; + +static struct snddev_icodec_data snddev_ihs_mono_rx_data = { + .capability = (SNDDEV_CAP_RX | SNDDEV_CAP_VOICE), + .name = "headset_mono_rx", + .copp_id = 0, + .profile = &headset_mono_ab_cpls_profile, + .channel_mode = 1, + .default_sample_rate = 48000, + .pamp_on = headset_enable, + .voltage_on = voltage_on, + .aic3254_id = PLAYBACK_HEADSET, + .aic3254_voc_id = CALL_DOWNLINK_EMIC_HEADSET, + .default_aic3254_id = PLAYBACK_HEADSET, +}; + +static struct platform_device msm_headset_mono_ab_cpls_device = { + .name = "snddev_icodec", + .id = 35, /* FIX ME */ + .dev = { .platform_data = &snddev_ihs_mono_rx_data }, +}; + +static struct adie_codec_action_unit ihs_ispk_stereo_rx_48KHz_osr256_actions[] = + SPEAKER_HPH_AB_CPL_PRI_48000_OSR_256; + +static struct adie_codec_hwsetting_entry ihs_ispk_stereo_rx_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = ihs_ispk_stereo_rx_48KHz_osr256_actions, + .action_sz = ARRAY_SIZE(ihs_ispk_stereo_rx_48KHz_osr256_actions), + } +}; + +static struct adie_codec_dev_profile ihs_ispk_stereo_rx_profile = { + .path_type = ADIE_CODEC_RX, + .settings = ihs_ispk_stereo_rx_settings, + .setting_sz = ARRAY_SIZE(ihs_ispk_stereo_rx_settings), +}; + +static struct snddev_icodec_data snddev_ihs_ispk_stereo_rx_data = { + .capability = (SNDDEV_CAP_RX | SNDDEV_CAP_VOICE), + .name = "headset_speaker_stereo_rx", + .copp_id = 0, + .profile = &ihs_ispk_stereo_rx_profile, + .channel_mode = 2, + .default_sample_rate = 48000, + .pamp_on = headset_speaker_enable, + .voltage_on = voltage_on, + .aic3254_id = RING_HEADSET_SPEAKER, + .aic3254_voc_id = RING_HEADSET_SPEAKER, + .default_aic3254_id = RING_HEADSET_SPEAKER, +}; + +static struct platform_device msm_iheadset_ispeaker_rx_device = { + .name = "snddev_icodec", + .id = 36, /* FIX ME */ + .dev = { .platform_data = &snddev_ihs_ispk_stereo_rx_data }, +}; + +static struct adie_codec_action_unit idual_mic_48KHz_osr256_actions[] = + DUAL_MIC_STEREO_TX_48000_OSR_256; + +static struct adie_codec_hwsetting_entry idual_mic_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = idual_mic_48KHz_osr256_actions, + .action_sz = ARRAY_SIZE(idual_mic_48KHz_osr256_actions), + } +}; + +static struct adie_codec_dev_profile idual_mic_profile = { + .path_type = ADIE_CODEC_TX, + .settings = idual_mic_settings, + .setting_sz = ARRAY_SIZE(idual_mic_settings), +}; + +static struct snddev_icodec_data snddev_idual_mic_endfire_real_stereo_data = { + .capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE), + .name = "dual_mic_stereo_tx", + .copp_id = PRIMARY_I2S_TX, + .profile = &idual_mic_profile, + .channel_mode = 2, + .default_sample_rate = 48000, + .pamp_on = stereo_mic_enable, + .aic3254_id = VIDEORECORD_IMIC, + .aic3254_voc_id = VOICERECORD_EMIC, /* FIX ME */ + .default_aic3254_id = VIDEORECORD_IMIC, +}; + +static struct platform_device msm_real_stereo_tx_device = { + .name = "snddev_icodec", + .id = 26, /* FIX ME */ + .dev = { .platform_data = &snddev_idual_mic_endfire_real_stereo_data }, +}; + + + +static struct adie_codec_action_unit iusb_headset_stereo_rx_48KHz_osr256_actions[] = + SPEAKER_HPH_AB_CPL_PRI_48000_OSR_256; + +static struct adie_codec_hwsetting_entry iusb_headset_stereo_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = iusb_headset_stereo_rx_48KHz_osr256_actions, + .action_sz = ARRAY_SIZE(iusb_headset_stereo_rx_48KHz_osr256_actions), + } +}; + +static struct adie_codec_dev_profile iusb_headset_stereo_profile = { + .path_type = ADIE_CODEC_RX, + .settings = iusb_headset_stereo_settings, + .setting_sz = ARRAY_SIZE(iusb_headset_stereo_settings), +}; + +static struct snddev_icodec_data snddev_iusb_headset_stereo_rx_data = { + .capability = (SNDDEV_CAP_RX | SNDDEV_CAP_VOICE), + .name = "usb_headset_stereo_rx", + .copp_id = 0, + .profile = &iusb_headset_stereo_profile, + .channel_mode = 2, + .default_sample_rate = 48000, + .pamp_on = usb_headset_enable, + .voltage_on = voltage_on, + .aic3254_id = USB_AUDIO, + .aic3254_voc_id = USB_AUDIO, + .default_aic3254_id = USB_AUDIO, +}; + +static struct platform_device msm_iusb_headset_rx_device = { + .name = "snddev_icodec", + .id = 27, /* FIX ME */ + .dev = { .platform_data = &snddev_iusb_headset_stereo_rx_data }, +}; + +static struct adie_codec_action_unit ispkr_mono_48KHz_osr256_actions[] = + SPEAKER_PRI_48000_OSR_256; + +static struct adie_codec_hwsetting_entry ispkr_mono_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = ispkr_mono_48KHz_osr256_actions, + .action_sz = ARRAY_SIZE(ispkr_mono_48KHz_osr256_actions), + } +}; + +static struct adie_codec_dev_profile ispkr_mono_profile = { + .path_type = ADIE_CODEC_RX, + .settings = ispkr_mono_settings, + .setting_sz = ARRAY_SIZE(ispkr_mono_settings), +}; + +static struct snddev_icodec_data snddev_ispkr_mono_data = { + .capability = (SNDDEV_CAP_RX | SNDDEV_CAP_VOICE), + .name = "speaker_mono_rx", + .copp_id = 0, + .profile = &ispkr_mono_profile, + .channel_mode = 1, + .default_sample_rate = 48000, + .pamp_on = speaker_enable, + .voltage_on = voltage_on, + .aic3254_id = PLAYBACK_SPEAKER, + .aic3254_voc_id = CALL_DOWNLINK_IMIC_SPEAKER, + .default_aic3254_id = PLAYBACK_SPEAKER, +}; + +static struct platform_device msm_ispkr_mono_device = { + .name = "snddev_icodec", + .id = 28, + .dev = { .platform_data = &snddev_ispkr_mono_data }, +}; + +static struct adie_codec_action_unit camcorder_imic_48KHz_osr256_actions[] = + AMIC_PRI_MONO_48000_OSR_256; + +static struct adie_codec_hwsetting_entry camcorder_imic_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = camcorder_imic_48KHz_osr256_actions, + .action_sz = ARRAY_SIZE(camcorder_imic_48KHz_osr256_actions), + } +}; + +static struct adie_codec_dev_profile camcorder_imic_profile = { + .path_type = ADIE_CODEC_TX, + .settings = camcorder_imic_settings, + .setting_sz = ARRAY_SIZE(camcorder_imic_settings), +}; + +static struct snddev_icodec_data snddev_camcorder_imic_data = { + .capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE), + .name = "camcorder_mono_tx", + .copp_id = 1, + .profile = &camcorder_imic_profile, + .channel_mode = 1, + .default_sample_rate = 48000, + .pamp_on = int_mic_enable, + .aic3254_id = VOICERECOGNITION_IMIC, + .aic3254_voc_id = CALL_UPLINK_IMIC_RECEIVER, + .default_aic3254_id = VOICERECOGNITION_IMIC, +}; + +static struct platform_device msm_camcorder_imic_device = { + .name = "snddev_icodec", + .id = 53, + .dev = { .platform_data = &snddev_camcorder_imic_data }, +}; + +static struct adie_codec_action_unit camcorder_idual_mic_48KHz_osr256_actions[] = + DUAL_MIC_STEREO_TX_48000_OSR_256; + +static struct adie_codec_hwsetting_entry camcorder_idual_mic_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = camcorder_idual_mic_48KHz_osr256_actions, + .action_sz = ARRAY_SIZE(camcorder_idual_mic_48KHz_osr256_actions), + } +}; + +static struct adie_codec_dev_profile camcorder_idual_mic_profile = { + .path_type = ADIE_CODEC_TX, + .settings = camcorder_idual_mic_settings, + .setting_sz = ARRAY_SIZE(camcorder_idual_mic_settings), +}; + +static struct snddev_icodec_data snddev_camcorder_imic_stereo_data = { + .capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE), + .name = "camcorder_stereo_tx", + .copp_id = PRIMARY_I2S_TX, + .profile = &camcorder_idual_mic_profile, + .channel_mode = 2, + .default_sample_rate = 48000, + .pamp_on = stereo_mic_enable, + .aic3254_id = VIDEORECORD_IMIC, + .aic3254_voc_id = VOICERECORD_EMIC, /* FIX ME */ + .default_aic3254_id = VIDEORECORD_IMIC, +}; + +static struct platform_device msm_camcorder_imic_stereo_device = { + .name = "snddev_icodec", + .id = 54, /* FIX ME */ + .dev = { .platform_data = &snddev_camcorder_imic_stereo_data }, +}; + +static struct adie_codec_action_unit camcorder_idual_mic_rev_48KHz_osr256_actions[] = + DUAL_MIC_STEREO_TX_48000_OSR_256; + +static struct adie_codec_hwsetting_entry camcorder_idual_mic_rev_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = camcorder_idual_mic_rev_48KHz_osr256_actions, + .action_sz = ARRAY_SIZE(camcorder_idual_mic_rev_48KHz_osr256_actions), + } +}; + +static struct adie_codec_dev_profile camcorder_idual_mic_rev_profile = { + .path_type = ADIE_CODEC_TX, + .settings = camcorder_idual_mic_rev_settings, + .setting_sz = ARRAY_SIZE(camcorder_idual_mic_rev_settings), +}; + +static struct snddev_icodec_data snddev_camcorder_imic_stereo_rev_data = { + .capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE), + .name = "camcorder_stereo_rev_tx", + .copp_id = PRIMARY_I2S_TX, + .profile = &camcorder_idual_mic_rev_profile, + .channel_mode = 2, + .default_sample_rate = 48000, + .pamp_on = stereo_mic_enable, + .aic3254_id = VIDEORECORD_IMIC, + .aic3254_voc_id = VOICERECORD_EMIC, /* FIX ME */ + .default_aic3254_id = VIDEORECORD_IMIC, +}; + +static struct platform_device msm_camcorder_imic_stereo_rev_device = { + .name = "snddev_icodec", + .id = 55, + .dev = { .platform_data = &snddev_camcorder_imic_stereo_rev_data }, +}; + +static struct adie_codec_action_unit camcorder_iheadset_mic_tx_osr256_actions[] = + HS_AMIC2_MONO_48000_OSR_256; + +static struct adie_codec_hwsetting_entry camcorder_iheadset_mic_tx_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = camcorder_iheadset_mic_tx_osr256_actions, + .action_sz = ARRAY_SIZE(camcorder_iheadset_mic_tx_osr256_actions), + } +}; + +static struct adie_codec_dev_profile camcorder_iheadset_mic_profile = { + .path_type = ADIE_CODEC_TX, + .settings = camcorder_iheadset_mic_tx_settings, + .setting_sz = ARRAY_SIZE(camcorder_iheadset_mic_tx_settings), +}; + +static struct snddev_icodec_data snddev_camcorder_headset_mic_data = { + .capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE), + .name = "camcorder_headset_tx", + .copp_id = PRIMARY_I2S_TX, + .profile = &camcorder_iheadset_mic_profile, + .channel_mode = 1, + .default_sample_rate = 48000, + .pamp_on = ext_mic_enable, + .aic3254_id = VOICERECOGNITION_EMIC, + .aic3254_voc_id = CALL_UPLINK_EMIC_HEADSET, + .default_aic3254_id = VOICERECORD_EMIC, +}; + +static struct platform_device msm_camcorder_headset_mic_device = { + .name = "snddev_icodec", + .id = 56, + .dev = { .platform_data = &snddev_camcorder_headset_mic_data }, +}; + +static struct adie_codec_action_unit vr_iearpiece_48KHz_osr256_actions[] = + EAR_PRI_MONO_48000_OSR_256; + +static struct adie_codec_hwsetting_entry vr_iearpiece_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = vr_iearpiece_48KHz_osr256_actions, + .action_sz = ARRAY_SIZE(vr_iearpiece_48KHz_osr256_actions), + } +}; + +static struct adie_codec_dev_profile vr_iearpiece_profile = { + .path_type = ADIE_CODEC_RX, + .settings = vr_iearpiece_settings, + .setting_sz = ARRAY_SIZE(vr_iearpiece_settings), +}; + +static struct snddev_icodec_data snddev_vr_iearpiece_data = { + .capability = (SNDDEV_CAP_RX | SNDDEV_CAP_VOICE), + .name = "vr_handset_mono_tx", + .copp_id = 0, + .profile = &vr_iearpiece_profile, + .channel_mode = 1, + .default_sample_rate = 48000, + .pamp_on = handset_enable, + .voltage_on = voltage_on, + .aic3254_id = PLAYBACK_RECEIVER, + .aic3254_voc_id = CALL_DOWNLINK_IMIC_RECEIVER, + .default_aic3254_id = PLAYBACK_RECEIVER, +}; + +static struct platform_device msm_vr_iearpiece_device = { + .name = "snddev_icodec", + .id = 57, + .dev = { .platform_data = &snddev_vr_iearpiece_data }, +}; + +static struct adie_codec_action_unit vr_iheadset_mic_tx_osr256_actions[] = + HS_AMIC2_MONO_48000_OSR_256; + +static struct adie_codec_hwsetting_entry vr_iheadset_mic_tx_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = vr_iheadset_mic_tx_osr256_actions, + .action_sz = ARRAY_SIZE(vr_iheadset_mic_tx_osr256_actions), + } +}; + +static struct adie_codec_dev_profile vr_iheadset_mic_profile = { + .path_type = ADIE_CODEC_TX, + .settings = vr_iheadset_mic_tx_settings, + .setting_sz = ARRAY_SIZE(vr_iheadset_mic_tx_settings), +}; + +static struct snddev_icodec_data snddev_vr_headset_mic_data = { + .capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE), + .name = "vr_headset_mono_tx", + .copp_id = PRIMARY_I2S_TX, + .profile = &vr_iheadset_mic_profile, + .channel_mode = 1, + .default_sample_rate = 48000, + .pamp_on = ext_mic_enable, + .aic3254_id = VOICERECOGNITION_EMIC, + .aic3254_voc_id = CALL_UPLINK_EMIC_HEADSET, + .default_aic3254_id = VOICERECORD_EMIC, +}; + +static struct platform_device msm_vr_headset_mic_device = { + .name = "snddev_icodec", + .id = 58, + .dev = { .platform_data = &snddev_vr_headset_mic_data }, +}; + +static struct adie_codec_action_unit ispkr_mono_alt_48KHz_osr256_actions[] = + SPEAKER_PRI_48000_OSR_256; + +static struct adie_codec_hwsetting_entry ispkr_mono_alt_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = ispkr_mono_alt_48KHz_osr256_actions, + .action_sz = ARRAY_SIZE(ispkr_mono_alt_48KHz_osr256_actions), + } +}; + +static struct adie_codec_dev_profile ispkr_mono_alt_profile = { + .path_type = ADIE_CODEC_RX, + .settings = ispkr_mono_alt_settings, + .setting_sz = ARRAY_SIZE(ispkr_mono_alt_settings), +}; + +static struct snddev_icodec_data snddev_ispkr_mono_alt_data = { + .capability = (SNDDEV_CAP_RX | SNDDEV_CAP_VOICE), + .name = "speaker_mono_alt_rx", + .copp_id = 0, + .profile = &ispkr_mono_alt_profile, + .channel_mode = 1, + .default_sample_rate = 48000, + .pamp_on = speaker_enable, + .voltage_on = voltage_on, + .aic3254_id = PLAYBACK_SPEAKER, + .aic3254_voc_id = CALL_DOWNLINK_IMIC_SPEAKER, + .default_aic3254_id = PLAYBACK_SPEAKER, +}; + +static struct platform_device msm_ispkr_mono_alt_device = { + .name = "snddev_icodec", + .id = 59, + .dev = { .platform_data = &snddev_ispkr_mono_alt_data }, +}; + + +static struct adie_codec_action_unit imic_note_48KHz_osr256_actions[] = + AMIC_PRI_MONO_48000_OSR_256; + +static struct adie_codec_hwsetting_entry imic_note_settings[] = { + { + .freq_plan = 16000, + .osr = 256, + .actions = imic_note_48KHz_osr256_actions, + .action_sz = ARRAY_SIZE(imic_note_48KHz_osr256_actions), + } +}; + +static struct adie_codec_dev_profile imic_note_profile = { + .path_type = ADIE_CODEC_TX, + .settings = imic_note_settings, + .setting_sz = ARRAY_SIZE(imic_note_settings), +}; + +static struct snddev_icodec_data snddev_imic_note_data = { + .capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE), + .name = "imic_note_tx", + .copp_id = 1, + .profile = &imic_note_profile, + .channel_mode = 2, + .default_sample_rate = 16000, + .pamp_on = stereo_mic_enable, + .aic3254_id = VOICERECORD_IMIC, + .aic3254_voc_id = CALL_UPLINK_IMIC_RECEIVER, + .default_aic3254_id = VOICERECORD_IMIC, +}; + +static struct platform_device msm_imic_note_device = { + .name = "snddev_icodec", + .id = 60, + .dev = { .platform_data = &snddev_imic_note_data }, +}; + +static struct adie_codec_action_unit ispkr_note_48KHz_osr256_actions[] = + SPEAKER_PRI_48000_OSR_256; + +static struct adie_codec_hwsetting_entry ispkr_note_settings[] = { + { + .freq_plan = 16000, + .osr = 256, + .actions = ispkr_note_48KHz_osr256_actions, + .action_sz = ARRAY_SIZE(ispkr_note_48KHz_osr256_actions), + } +}; + +static struct adie_codec_dev_profile ispkr_note_profile = { + .path_type = ADIE_CODEC_RX, + .settings = ispkr_note_settings, + .setting_sz = ARRAY_SIZE(ispkr_note_settings), +}; + +static struct snddev_icodec_data snddev_ispkr_note_data = { + .capability = (SNDDEV_CAP_RX | SNDDEV_CAP_VOICE), + .name = "ispkr_note_rx", + .copp_id = 0, + .profile = &ispkr_note_profile, + .channel_mode = 2, + .default_sample_rate = 16000, + .pamp_on = speaker_enable, + .voltage_on = voltage_on, + .aic3254_id = PLAYBACK_SPEAKER, + .aic3254_voc_id = CALL_DOWNLINK_IMIC_SPEAKER, + .default_aic3254_id = PLAYBACK_SPEAKER, +}; + +static struct platform_device msm_ispkr_note_device = { + .name = "snddev_icodec", + .id = 61, + .dev = { .platform_data = &snddev_ispkr_note_data }, +}; + +static struct adie_codec_action_unit emic_note_16KHz_osr256_actions[] = + AMIC_PRI_MONO_48000_OSR_256; + +static struct adie_codec_hwsetting_entry emic_note_settings[] = { + { + .freq_plan = 16000, + .osr = 256, + .actions = emic_note_16KHz_osr256_actions, + .action_sz = ARRAY_SIZE(emic_note_16KHz_osr256_actions), + } +}; + +static struct adie_codec_dev_profile emic_note_profile = { + .path_type = ADIE_CODEC_TX, + .settings = emic_note_settings, + .setting_sz = ARRAY_SIZE(emic_note_settings), +}; + +static struct snddev_icodec_data snddev_emic_note_data = { + .capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE), + .name = "emic_note_tx", + .copp_id = 1, + .profile = &emic_note_profile, + .channel_mode = 1, + .default_sample_rate = 16000, + .pamp_on = ext_mic_enable, + .aic3254_id = VOICERECORD_EMIC, + .aic3254_voc_id = VOICERECORD_EMIC, + .default_aic3254_id = VOICERECORD_EMIC, +}; + +static struct platform_device msm_emic_note_device = { + .name = "snddev_icodec", + .id = 62, + .dev = { .platform_data = &snddev_emic_note_data }, +}; + +static struct adie_codec_action_unit headset_note_48KHz_osr256_actions[] = + HEADSET_AB_CPLS_48000_OSR_256; + +static struct adie_codec_hwsetting_entry headset_note_settings[] = { + { + .freq_plan = 16000, + .osr = 256, + .actions = headset_note_48KHz_osr256_actions, + .action_sz = ARRAY_SIZE(headset_note_48KHz_osr256_actions), + } +}; + +static struct adie_codec_dev_profile headset_note_profile = { + .path_type = ADIE_CODEC_RX, + .settings = headset_note_settings, + .setting_sz = ARRAY_SIZE(headset_note_settings), +}; + +static struct snddev_icodec_data snddev_ihs_note_data = { + .capability = (SNDDEV_CAP_RX | SNDDEV_CAP_VOICE), + .name = "headset_note_rx", + .copp_id = 0, + .profile = &headset_note_profile, + .channel_mode = 2, + .default_sample_rate = 16000, + .pamp_on = headset_enable, + .voltage_on = voltage_on, + .aic3254_id = PLAYBACK_HEADSET, + .aic3254_voc_id = CALL_DOWNLINK_EMIC_HEADSET, + .default_aic3254_id = PLAYBACK_HEADSET, +}; + +static struct platform_device msm_headset_note_device = { + .name = "snddev_icodec", + .id = 63, + .dev = { .platform_data = &snddev_ihs_note_data }, +}; + + + +#endif + +#ifdef CONFIG_DEBUG_FS +static struct adie_codec_action_unit + ihs_stereo_rx_class_d_legacy_48KHz_osr256_actions[] = + HPH_PRI_D_LEG_STEREO; + +static struct adie_codec_hwsetting_entry + ihs_stereo_rx_class_d_legacy_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = + ihs_stereo_rx_class_d_legacy_48KHz_osr256_actions, + .action_sz = ARRAY_SIZE + (ihs_stereo_rx_class_d_legacy_48KHz_osr256_actions), + } +}; + +static struct adie_codec_action_unit + ihs_stereo_rx_class_ab_legacy_48KHz_osr256_actions[] = + HPH_PRI_AB_LEG_STEREO; + +static struct adie_codec_hwsetting_entry + ihs_stereo_rx_class_ab_legacy_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = + ihs_stereo_rx_class_ab_legacy_48KHz_osr256_actions, + .action_sz = ARRAY_SIZE + (ihs_stereo_rx_class_ab_legacy_48KHz_osr256_actions), + } +}; + +static void snddev_hsed_config_modify_setting(int type) +{ + struct platform_device *device; + struct snddev_icodec_data *icodec_data; + + device = &msm_headset_stereo_device; + icodec_data = (struct snddev_icodec_data *)device->dev.platform_data; + + if (icodec_data) { + if (type == 1) { + icodec_data->voltage_on = NULL; + icodec_data->profile->settings = + ihs_stereo_rx_class_d_legacy_settings; + icodec_data->profile->setting_sz = + ARRAY_SIZE(ihs_stereo_rx_class_d_legacy_settings); + } else if (type == 2) { + icodec_data->voltage_on = NULL; + icodec_data->profile->settings = + ihs_stereo_rx_class_ab_legacy_settings; + icodec_data->profile->setting_sz = + ARRAY_SIZE(ihs_stereo_rx_class_ab_legacy_settings); + } + } +} + +static void snddev_hsed_config_restore_setting(void) +{ + struct platform_device *device; + struct snddev_icodec_data *icodec_data; + + device = &msm_headset_stereo_device; + icodec_data = (struct snddev_icodec_data *)device->dev.platform_data; + + if (icodec_data) { + icodec_data->voltage_on = voltage_on; + icodec_data->profile->settings = headset_ab_cpls_settings; + icodec_data->profile->setting_sz = + ARRAY_SIZE(headset_ab_cpls_settings); + } +} + +static ssize_t snddev_hsed_config_debug_write(struct file *filp, + const char __user *ubuf, size_t cnt, loff_t *ppos) +{ + char *lb_str = filp->private_data; + char cmd; + + if (get_user(cmd, ubuf)) + return -EFAULT; + + if (!strcmp(lb_str, "msm_hsed_config")) { + switch (cmd) { + case '0': + snddev_hsed_config_restore_setting(); + break; + + case '1': + snddev_hsed_config_modify_setting(1); + break; + + case '2': + snddev_hsed_config_modify_setting(2); + break; + + default: + break; + } + } + return cnt; +} + +static int snddev_hsed_config_debug_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static const struct file_operations snddev_hsed_config_debug_fops = { + .open = snddev_hsed_config_debug_open, + .write = snddev_hsed_config_debug_write +}; +#endif + +static struct snddev_virtual_data snddev_uplink_rx_data = { + .capability = SNDDEV_CAP_RX, + .name = "uplink_rx", + .copp_id = VOICE_PLAYBACK_TX, +}; + +static struct platform_device msm_uplink_rx_device = { + .name = "snddev_virtual", + .dev = { .platform_data = &snddev_uplink_rx_data }, +}; + +static struct platform_device *snd_devices_common[] __initdata = { + &msm_uplink_rx_device, +}; + +static struct platform_device *snd_devices_surf[] __initdata = { + &msm_iearpiece_device, + &msm_imic_device, + &msm_ispkr_stereo_device, + &msm_snddev_hdmi_stereo_rx_device, + &msm_headset_mic_device, + &msm_ispkr_mic_device, + &msm_bt_sco_earpiece_device, + &msm_bt_sco_mic_device, + &msm_headset_stereo_device, + &msm_itty_mono_tx_device, + &msm_itty_mono_rx_device, + &msm_mi2s_fm_tx_device, + &msm_mi2s_fm_rx_device, + &msm_ifmradio_speaker_device, + &msm_ifmradio_headset_device, + &msm_hs_dual_mic_endfire_device, + &msm_spkr_dual_mic_endfire_device, + &msm_hs_dual_mic_broadside_device, + &msm_spkr_dual_mic_broadside_device, + &msm_ihs_stereo_speaker_stereo_rx_device, + &msm_headset_mono_ab_cpls_device, + &msm_iheadset_ispeaker_rx_device, + &msm_bmic_tx_device, + &msm_anc_headset_device, + &msm_real_stereo_tx_device, + &msm_ihac_device, + &msm_nomic_headset_tx_device, + &msm_nomic_headset_stereo_device, + &msm_iusb_headset_rx_device, + &msm_ispkr_mono_device, + &msm_camcorder_imic_device, + &msm_camcorder_imic_stereo_device, + &msm_camcorder_imic_stereo_rev_device, + &msm_camcorder_headset_mic_device, + &msm_vr_iearpiece_device, + &msm_vr_headset_mic_device, + &msm_ispkr_mono_alt_device, + &msm_imic_note_device, + &msm_ispkr_note_device, + &msm_emic_note_device, + &msm_headset_note_device, +}; + +void htc_8x60_register_analog_ops(struct q6v2audio_analog_ops *ops) +{ + audio_ops = ops; +} + +void __init msm_snddev_init(void) +{ + int rc, i; + int dev_id; + + platform_add_devices(snd_devices_surf, ARRAY_SIZE(snd_devices_surf)); +#ifdef CONFIG_DEBUG_FS + debugfs_hsed_config = debugfs_create_file("msm_hsed_config", + S_IFREG | S_IRUGO, NULL, + (void *) "msm_hsed_config", &snddev_hsed_config_debug_fops); +#endif + + for (i = 0, dev_id = 0; i < ARRAY_SIZE(snd_devices_common); i++) + snd_devices_common[i]->id = dev_id++; + + platform_add_devices(snd_devices_common, + ARRAY_SIZE(snd_devices_common)); + + rc = gpio_request(SNDDEV_GPIO_CLASS_D1_EN, "CLASSD1_EN"); + if (rc) { + pr_aud_err("%s: spkr pamp gpio %d request" + "failed\n", __func__, SNDDEV_GPIO_CLASS_D1_EN); + } else { + gpio_direction_output(SNDDEV_GPIO_CLASS_D1_EN, 0); + gpio_free(SNDDEV_GPIO_CLASS_D1_EN); + } +} diff --git a/arch/arm/mach-msm/qdsp6v3/dsp_debug.c b/arch/arm/mach-msm/qdsp6v3/dsp_debug.c new file mode 100644 index 00000000000..8bf7fee3830 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v3/dsp_debug.c @@ -0,0 +1,202 @@ +/* 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 + +#include "../proc_comm.h" +#include +#include "dsp_debug.h" + +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); +dsp_state_cb cb_ptr; + +void q6audio_dsp_not_responding(void) +{ + if (cb_ptr) + cb_ptr(DSP_STATE_CRASHED); + if (atomic_add_return(1, &dsp_crash_count) != 1) { + pr_aud_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_aud_err("q6audio_dsp_not_responding() - no waiter?\n"); + } + if (cb_ptr) + cb_ptr(DSP_STATE_CRASH_DUMP_DONE); + + BUG(); +} + +static int dsp_open(struct inode *inode, struct file *file) +{ + return 0; +} + +#define DSP_NMI_ADDR 0x28800010 + +static ssize_t dsp_write(struct file *file, const char __user *buf, + size_t count, loff_t *pos) +{ + char cmd[32]; + void __iomem *ptr; + + 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); + if (res < 0) { + dsp_wait_count--; + return res; + } + } + /* assert DSP NMI */ + ptr = ioremap(DSP_NMI_ADDR, 0x16); + if (!ptr) { + pr_aud_err("Unable to map DSP NMI\n"); + return -EFAULT; + } + writel(0x1, (void *)ptr); + iounmap(ptr); + } 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_aud_err("[%s:%s] unknown dsp_debug command: %s\n", __MM_FILE__, + __func__, cmd); + } + + return count; +} + +#define DSP_RAM_BASE 0x46700000 +#define DSP_RAM_SIZE 0x2000000 + +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_aud_err("[%s:%s] map error @ %x\n", __MM_FILE__, + __func__, addr); + return -EFAULT; + } + if (copy_to_user(buf, ptr, PAGE_SIZE)) { + iounmap(ptr); + pr_aud_err("[%s:%s] copy error @ %p\n", __MM_FILE__, + __func__, 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; +} + +int dsp_debug_register(dsp_state_cb ptr) +{ + if (ptr == NULL) + return -EINVAL; + cb_ptr = ptr; + + 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); diff --git a/arch/arm/mach-msm/qdsp6v3/dsp_debug.h b/arch/arm/mach-msm/qdsp6v3/dsp_debug.h new file mode 100644 index 00000000000..1a73dd42395 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v3/dsp_debug.h @@ -0,0 +1,38 @@ +/* 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 __DSP_DEBUG_H_ +#define __DSP_DEBUG_H_ + +typedef int (*dsp_state_cb)(int state); +int dsp_debug_register(dsp_state_cb ptr); + +#define DSP_STATE_CRASHED 0x0 +#define DSP_STATE_CRASH_DUMP_DONE 0x1 + +#endif diff --git a/arch/arm/mach-msm/qdsp6v3/evrc_in.c b/arch/arm/mach-msm/qdsp6v3/evrc_in.c new file mode 100644 index 00000000000..5d4463583ae --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v3/evrc_in.c @@ -0,0 +1,337 @@ +/* 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 "audio_utils.h" + +/* Buffer with meta*/ +#define PCM_BUF_SIZE (4096 + sizeof(struct meta_in)) + +/* Maximum 10 frames in buffer with meta */ +#define FRAME_SIZE (1 + ((23+sizeof(struct meta_out_dsp)) * 10)) + +void q6asm_evrc_in_cb(uint32_t opcode, uint32_t token, + uint32_t *payload, void *priv) +{ + struct q6audio_in * audio = (struct q6audio_in *)priv; + unsigned long flags; + + pr_debug("%s:session id %d: opcode - %d\n", __func__, + audio->ac->session, opcode); + + spin_lock_irqsave(&audio->dsp_lock, flags); + switch (opcode) { + case ASM_DATA_EVENT_READ_DONE: + audio_in_get_dsp_frames(audio, token, payload); + break; + case ASM_DATA_EVENT_WRITE_DONE: + atomic_inc(&audio->in_count); + wake_up(&audio->write_wait); + break; + case ASM_DATA_CMDRSP_EOS: + audio->eos_rsp = 1; + wake_up(&audio->read_wait); + break; + case ASM_STREAM_CMDRSP_GET_ENCDEC_PARAM: + break; + case ASM_STREAM_CMDRSP_GET_PP_PARAMS: + break; + case ASM_SESSION_EVENT_TX_OVERFLOW: + pr_aud_err("%s:session id %d: ASM_SESSION_EVENT_TX_OVERFLOW\n", + __func__, audio->ac->session); + break; + default: + pr_aud_err("%s:session id %d: Ignore opcode[0x%x]\n", __func__, + audio->ac->session, opcode); + break; + } + spin_unlock_irqrestore(&audio->dsp_lock, flags); +} + +/* ------------------- device --------------------- */ +static long evrc_in_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct q6audio_in *audio = file->private_data; + int rc = 0; + int cnt = 0; + + switch (cmd) { + case AUDIO_START: { + struct msm_audio_evrc_enc_config *enc_cfg; + enc_cfg = audio->enc_cfg; + pr_debug("%s:session id %d: default buf alloc[%d]\n", __func__, + audio->ac->session, audio->buf_alloc); + if (audio->enabled == 1) { + pr_aud_info("%s:AUDIO_START already over\n", __func__); + rc = 0; + break; + } + rc = audio_in_buf_alloc(audio); + if (rc < 0) { + pr_aud_err("%s:session id %d: buffer allocation failed\n", + __func__, audio->ac->session); + break; + } + + /* rate_modulation_cmd set to zero + currently not configurable from user space */ + rc = q6asm_enc_cfg_blk_evrc(audio->ac, + audio->buf_cfg.frames_per_buf, + enc_cfg->min_bit_rate, + enc_cfg->max_bit_rate, 0); + + if (rc < 0) { + pr_aud_err("%s:session id %d: cmd evrc media format block\ + failed\n", __func__, audio->ac->session); + break; + } + if (audio->feedback == NON_TUNNEL_MODE) { + rc = q6asm_media_format_block_pcm(audio->ac, + audio->pcm_cfg.sample_rate, + audio->pcm_cfg.channel_count); + + if (rc < 0) { + pr_aud_err("%s:session id %d: media format block\ + failed\n", __func__, audio->ac->session); + break; + } + } + pr_debug("%s:session id %d: AUDIO_START enable[%d]\n", + __func__, audio->ac->session, audio->enabled); + rc = audio_in_enable(audio); + if (!rc) { + audio->enabled = 1; + } else { + audio->enabled = 0; + pr_aud_err("%s:session id %d: Audio Start procedure failed\ + rc=%d\n", __func__, audio->ac->session, rc); + break; + } + while (cnt++ < audio->str_cfg.buffer_count) + q6asm_read(audio->ac); /* Push buffer to DSP */ + rc = 0; + pr_debug("%s:session id %d: AUDIO_START success enable[%d]\n", + __func__, audio->ac->session, audio->enabled); + break; + } + case AUDIO_STOP: { + pr_debug("%s:session id %d: AUDIO_STOP\n", __func__, + audio->ac->session); + rc = audio_in_disable(audio); + if (rc < 0) { + pr_aud_err("%s:session id %d: Audio Stop procedure failed\ + rc=%d\n", __func__, audio->ac->session, rc); + break; + } + break; + } + case AUDIO_GET_EVRC_ENC_CONFIG: { + if (copy_to_user((void *)arg, audio->enc_cfg, + sizeof(struct msm_audio_evrc_enc_config))) + rc = -EFAULT; + break; + } + case AUDIO_SET_EVRC_ENC_CONFIG: { + struct msm_audio_evrc_enc_config cfg; + struct msm_audio_evrc_enc_config *enc_cfg; + enc_cfg = audio->enc_cfg; + + if (copy_from_user(&cfg, (void *) arg, + sizeof(struct msm_audio_evrc_enc_config))) { + rc = -EFAULT; + break; + } + + if (cfg.min_bit_rate > 4 || + cfg.min_bit_rate < 1 || + (cfg.min_bit_rate == 2)) { + pr_aud_err("%s:session id %d: invalid min bitrate\n", + __func__, audio->ac->session); + rc = -EINVAL; + break; + } + if (cfg.max_bit_rate > 4 || + cfg.max_bit_rate < 1 || + (cfg.max_bit_rate == 2)) { + pr_aud_err("%s:session id %d: invalid max bitrate\n", + __func__, audio->ac->session); + rc = -EINVAL; + break; + } + enc_cfg->min_bit_rate = cfg.min_bit_rate; + enc_cfg->max_bit_rate = cfg.max_bit_rate; + pr_debug("%s:session id %d: min_bit_rate= 0x%x\ + max_bit_rate=0x%x\n", __func__, + audio->ac->session, enc_cfg->min_bit_rate, + enc_cfg->max_bit_rate); + break; + } + default: + rc = -EINVAL; + } + return rc; +} + +static int evrc_in_open(struct inode *inode, struct file *file) +{ + struct q6audio_in *audio = NULL; + struct msm_audio_evrc_enc_config *enc_cfg; + int rc = 0; + + audio = kzalloc(sizeof(struct q6audio_in), GFP_KERNEL); + + if (audio == NULL) { + pr_aud_err("%s:session id %d: Could not allocate memory for evrc\ + driver\n", __func__, audio->ac->session); + return -ENOMEM; + } + /* Allocate memory for encoder config param */ + audio->enc_cfg = kzalloc(sizeof(struct msm_audio_evrc_enc_config), + GFP_KERNEL); + if (audio->enc_cfg == NULL) { + pr_aud_err("%s:session id %d: Could not allocate memory for aac\ + config param\n", __func__, audio->ac->session); + kfree(audio); + return -ENOMEM; + } + enc_cfg = audio->enc_cfg; + mutex_init(&audio->lock); + mutex_init(&audio->read_lock); + mutex_init(&audio->write_lock); + spin_lock_init(&audio->dsp_lock); + init_waitqueue_head(&audio->read_wait); + init_waitqueue_head(&audio->write_wait); + + /* Settings will be re-config at AUDIO_SET_CONFIG, + * but at least we need to have initial config + */ + audio->str_cfg.buffer_size = FRAME_SIZE; + audio->str_cfg.buffer_count = FRAME_NUM; + audio->min_frame_size = 23; + audio->max_frames_per_buf = 10; + audio->pcm_cfg.buffer_size = PCM_BUF_SIZE; + audio->pcm_cfg.buffer_count = PCM_BUF_COUNT; + enc_cfg->min_bit_rate = 4; + enc_cfg->max_bit_rate = 4; + audio->pcm_cfg.channel_count = 1; + audio->pcm_cfg.sample_rate = 8000; + audio->buf_cfg.meta_info_enable = 0x01; + audio->buf_cfg.frames_per_buf = 0x01; + + audio->ac = q6asm_audio_client_alloc((app_cb)q6asm_evrc_in_cb, + (void *)audio); + + if (!audio->ac) { + pr_aud_err("%s:session id %d: Could not allocate memory for audio\ + client\n", __func__, audio->ac->session); + kfree(audio->enc_cfg); + kfree(audio); + return -ENOMEM; + } + + /* open evrc encoder in T/NT mode */ + if ((file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + audio->feedback = NON_TUNNEL_MODE; + rc = q6asm_open_read_write(audio->ac, FORMAT_EVRC, + FORMAT_LINEAR_PCM); + if (rc < 0) { + pr_aud_err("%s:session id %d: NT mode Open failed rc=%d\n", + __func__, audio->ac->session, rc); + rc = -ENODEV; + goto fail; + } + pr_aud_info("%s:session id %d: NT mode encoder success\n", + __func__, audio->ac->session); + } else if (!(file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + audio->feedback = TUNNEL_MODE; + rc = q6asm_open_read(audio->ac, FORMAT_EVRC); + if (rc < 0) { + pr_aud_err("%s:session id %d: T mode Open failed rc=%d\n", + __func__, audio->ac->session, rc); + rc = -ENODEV; + goto fail; + } + /* register for tx overflow (valid for tunnel mode only) */ + rc = q6asm_reg_tx_overflow(audio->ac, 0x01); + if (rc < 0) { + pr_aud_err("%s:session id %d: TX Overflow registration\ + failed rc=%d\n", __func__, + audio->ac->session, rc); + rc = -ENODEV; + goto fail; + } + pr_aud_info("%s:session id %d: T mode encoder success\n", __func__, + audio->ac->session); + } else { + pr_aud_err("%s:session id %d: Unexpected mode\n", __func__, + audio->ac->session); + rc = -EACCES; + goto fail; + } + + audio->opened = 1; + atomic_set(&audio->in_count, PCM_BUF_COUNT); + atomic_set(&audio->out_count, 0x00); + audio->enc_ioctl = evrc_in_ioctl; + file->private_data = audio; + + pr_aud_info("%s:session id %d: success\n", __func__, audio->ac->session); + return 0; +fail: + q6asm_audio_client_free(audio->ac); + kfree(audio->enc_cfg); + kfree(audio); + return rc; +} + +static const struct file_operations audio_in_fops = { + .owner = THIS_MODULE, + .open = evrc_in_open, + .release = audio_in_release, + .read = audio_in_read, + .write = audio_in_write, + .unlocked_ioctl = audio_in_ioctl, +}; + +struct miscdevice audio_evrc_in_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_evrc_in", + .fops = &audio_in_fops, +}; + +static int __init evrc_in_init(void) +{ + return misc_register(&audio_evrc_in_misc); +} + +device_initcall(evrc_in_init); diff --git a/arch/arm/mach-msm/qdsp6v3/fm.c b/arch/arm/mach-msm/qdsp6v3/fm.c new file mode 100644 index 00000000000..8bbd4d558bc --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v3/fm.c @@ -0,0 +1,259 @@ +/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + * + * Based on the mp3 native driver in arch/arm/mach-msm/qdsp5v2/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 +#include +#include +#include + +#define SESSION_ID_FM (MAX_SESSIONS + 1) +#define FM_ENABLE 0x1 +#define FM_DISABLE 0x0 +#define FM_COPP 0x7 + +struct audio { + struct mutex lock; + + int opened; + int enabled; + int running; + + uint16_t fm_source; + uint16_t fm_src_copp_id; + uint16_t fm_dest; + uint16_t fm_dst_copp_id; + uint16_t dec_id; + uint32_t device_events; + uint16_t volume; +}; + +static struct audio fm_audio; +static int fm_audio_enable(struct audio *audio) +{ + if (audio->enabled) + return 0; + + pr_aud_info("%s: fm dest= %08x fm_source = %08x\n", __func__, + audio->fm_dst_copp_id, audio->fm_src_copp_id); + + /* do afe loopback here */ + + if (audio->fm_dest && audio->fm_source) { + if (afe_loopback(FM_ENABLE, audio->fm_dst_copp_id, + audio->fm_src_copp_id) < 0) { + pr_aud_err("%s: afe_loopback failed\n", __func__); + } + + audio->running = 1; + } + + audio->enabled = 1; + return 0; +} + +static void fm_audio_listner(u32 evt_id, union auddev_evt_data *evt_payload, + void *private_data) +{ + struct audio *audio = (struct audio *) private_data; + switch (evt_id) { + case AUDDEV_EVT_DEV_RDY: + pr_aud_info("%s :AUDDEV_EVT_DEV_RDY\n", __func__); + if (evt_payload->routing_id == FM_COPP) { + audio->fm_source = 1; + audio->fm_src_copp_id = FM_COPP; + } else { + audio->fm_dest = 1; + audio->fm_dst_copp_id = evt_payload->routing_id; + } + + if (audio->enabled && + audio->fm_dest && + audio->fm_source && !audio->running) { + + afe_loopback(FM_ENABLE, audio->fm_dst_copp_id, + audio->fm_src_copp_id); + audio->running = 1; + } + break; + case AUDDEV_EVT_DEV_RLS: + pr_aud_info("%s: AUDDEV_EVT_DEV_RLS\n", __func__); + if (evt_payload->routing_id == audio->fm_src_copp_id) + audio->fm_source = 0; + else + audio->fm_dest = 0; + if (audio->running + && (!audio->fm_dest && !audio->fm_source)) { + afe_loopback(FM_DISABLE, audio->fm_dst_copp_id, + audio->fm_src_copp_id); + audio->running = 0; + } else { + pr_aud_err("%s: device switch happened\n", __func__); + } + break; + case AUDDEV_EVT_STREAM_VOL_CHG: + pr_debug("%s: AUDDEV_EVT_STREAM_VOL_CHG\n", __func__); + if (audio->fm_source) { + audio->volume = evt_payload->session_vol; + afe_loopback_gain(audio->fm_src_copp_id, + audio->volume); + } + break; + + default: + pr_aud_err("%s: ERROR:wrong event %08x\n", __func__, evt_id); + break; + } +} + +static int fm_audio_disable(struct audio *audio) +{ + + /* break the AFE loopback here */ + afe_loopback(FM_DISABLE, audio->fm_dst_copp_id, audio->fm_src_copp_id); + return 0; +} + +static long fm_audio_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct audio *audio = file->private_data; + int rc = -EINVAL; + + + mutex_lock(&audio->lock); + switch (cmd) { + case AUDIO_START: + pr_aud_info("%s: AUDIO_START\n", __func__); + rc = fm_audio_enable(audio); + break; + case AUDIO_STOP: + pr_aud_info("%s: AUDIO_STOP\n", __func__); + rc = fm_audio_disable(audio); + audio->running = 0; + audio->enabled = 0; + break; + case AUDIO_GET_SESSION_ID: + if (copy_to_user((void *) arg, &audio->dec_id, + sizeof(unsigned short))) + rc = -EFAULT; + else + rc = 0; + break; + default: + rc = -EINVAL; + pr_aud_err("%s: Un supported IOCTL\n", __func__); + } + mutex_unlock(&audio->lock); + return rc; +} + +static int fm_audio_release(struct inode *inode, struct file *file) +{ + struct audio *audio = file->private_data; + + pr_debug("audio instance 0x%08x freeing\n", (int)audio); + mutex_lock(&audio->lock); + auddev_unregister_evt_listner(AUDDEV_CLNT_DEC, audio->dec_id); + fm_audio_disable(audio); + audio->running = 0; + audio->enabled = 0; + audio->opened = 0; + mutex_unlock(&audio->lock); + return 0; +} + +static int fm_audio_open(struct inode *inode, struct file *file) +{ + struct audio *audio = &fm_audio; + int rc = 0; + + + if (audio->opened) + return -EPERM; + + /* Allocate the decoder */ + audio->dec_id = SESSION_ID_FM; + + audio->running = 0; + audio->fm_source = 0; + audio->fm_dest = 0; + + audio->device_events = AUDDEV_EVT_DEV_RDY + |AUDDEV_EVT_DEV_RLS| + AUDDEV_EVT_STREAM_VOL_CHG; + + rc = auddev_register_evt_listner(audio->device_events, + AUDDEV_CLNT_DEC, + audio->dec_id, + fm_audio_listner, + (void *)audio); + + if (rc) { + pr_aud_err("%s: failed to register listnet\n", __func__); + goto event_err; + } + + audio->opened = 1; + file->private_data = audio; + +event_err: + return rc; +} + +static const struct file_operations audio_fm_fops = { + .owner = THIS_MODULE, + .open = fm_audio_open, + .release = fm_audio_release, + .unlocked_ioctl = fm_audio_ioctl, +}; + +struct miscdevice audio_fm_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_fm", + .fops = &audio_fm_fops, +}; + +static int __init fm_audio_init(void) +{ + struct audio *audio = &fm_audio; + + mutex_init(&audio->lock); + return misc_register(&audio_fm_misc); +} + +device_initcall(fm_audio_init); + +MODULE_DESCRIPTION("MSM FM driver"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/qdsp6v3/pcm_in.c b/arch/arm/mach-msm/qdsp6v3/pcm_in.c new file mode 100644 index 00000000000..7282e2de982 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v3/pcm_in.c @@ -0,0 +1,504 @@ +/* + * Copyright (C) 2009 Google, Inc. + * Copyright (C) 2009 HTC Corporation + * Copyright (c) 2010-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 + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT 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 + +#define MAX_BUF 4 +#define BUFSZ (480 * 8) +#define BUFFER_SIZE_MULTIPLE 4 +#define MIN_BUFFER_SIZE 160 + +#define VOC_REC_NONE 0xFF + +struct pcm { + struct mutex lock; + struct mutex read_lock; + wait_queue_head_t wait; + spinlock_t dsp_lock; + struct audio_client *ac; + uint32_t sample_rate; + uint32_t channel_count; + uint32_t buffer_size; + uint32_t buffer_count; + uint32_t rec_mode; + uint32_t in_frame_info[MAX_BUF][2]; + atomic_t in_count; + atomic_t in_enabled; + atomic_t in_opened; + atomic_t in_stopped; +}; + +static atomic_t pcm_opened = ATOMIC_INIT(0); + +static void pcm_in_get_dsp_buffers(struct pcm*, + uint32_t token, uint32_t *payload); + +void pcm_in_cb(uint32_t opcode, uint32_t token, + uint32_t *payload, void *priv) +{ + struct pcm *pcm = (struct pcm *) priv; + unsigned long flags; + + spin_lock_irqsave(&pcm->dsp_lock, flags); + switch (opcode) { + case ASM_DATA_EVENT_READ_DONE: + pcm_in_get_dsp_buffers(pcm, token, payload); + break; + default: + break; + } + spin_unlock_irqrestore(&pcm->dsp_lock, flags); +} + +static void pcm_in_get_dsp_buffers(struct pcm *pcm, + uint32_t token, uint32_t *payload) +{ + pcm->in_frame_info[token][0] = payload[7]; + pcm->in_frame_info[token][1] = payload[3]; + if (atomic_read(&pcm->in_count) <= pcm->buffer_count) + atomic_inc(&pcm->in_count); + wake_up(&pcm->wait); +} + +static int pcm_in_enable(struct pcm *pcm) +{ + if (atomic_read(&pcm->in_enabled)) + return 0; + return q6asm_run(pcm->ac, 0, 0, 0); +} + +static int pcm_in_disable(struct pcm *pcm) +{ + int rc = 0; + + if (atomic_read(&pcm->in_opened)) { + atomic_set(&pcm->in_enabled, 0); + atomic_set(&pcm->in_opened, 0); + rc = q6asm_cmd(pcm->ac, CMD_CLOSE); + + atomic_set(&pcm->in_stopped, 1); + memset(pcm->in_frame_info, 0, + sizeof(char) * pcm->buffer_count * 2); + wake_up(&pcm->wait); + } + return rc; +} + +static int config(struct pcm *pcm) +{ + int rc = 0; + + pr_debug("%s: pcm prefill, buffer_size = %d\n", __func__, + pcm->buffer_size); + rc = q6asm_audio_client_buf_alloc(OUT, pcm->ac, + pcm->buffer_size, pcm->buffer_count); + if (rc < 0) { + pr_aud_err("Audio Start: Buffer Allocation failed \ + rc = %d\n", rc); + goto fail; + } + + rc = q6asm_enc_cfg_blk_pcm(pcm->ac, pcm->sample_rate, + pcm->channel_count); + if (rc < 0) { + pr_aud_err("%s: cmd media format block failed", __func__); + goto fail; + } +fail: + return rc; +} + +static long pcm_in_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct pcm *pcm = file->private_data; + int rc = 0; + + mutex_lock(&pcm->lock); + 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))) + rc = -EFAULT; + break; + } + case AUDIO_START: { + int cnt = 0; + if (atomic_read(&pcm->in_enabled)) { + pr_aud_info("%s:AUDIO_START already over\n", __func__); + rc = 0; + break; + } + rc = config(pcm); + if (rc) { + pr_aud_err("%s: IN Configuration failed\n", __func__); + rc = -EFAULT; + break; + } + + rc = pcm_in_enable(pcm); + if (rc) { + pr_aud_err("%s: In Enable failed\n", __func__); + rc = -EFAULT; + break; + } + + atomic_set(&pcm->in_enabled, 1); + + while (cnt++ < pcm->buffer_count) + q6asm_read(pcm->ac); + pr_aud_info("%s: AUDIO_START session id[%d]\n", __func__, + pcm->ac->session); + + if (pcm->rec_mode != VOC_REC_NONE) + msm_enable_incall_recording(pcm->ac->session, + pcm->rec_mode, pcm->sample_rate, pcm->channel_count); + + break; + } + case AUDIO_GET_SESSION_ID: { + if (copy_to_user((void *) arg, &pcm->ac->session, + sizeof(unsigned short))) + rc = -EFAULT; + 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; + } + pr_debug("%s: SET_CONFIG: buffer_size:%d channel_count:%d" + "sample_rate:%d, buffer_count:%d\n", __func__, + config.buffer_size, config.channel_count, + config.sample_rate, config.buffer_count); + + 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 % (config.channel_count * + BUFFER_SIZE_MULTIPLE)) || + (config.buffer_size < MIN_BUFFER_SIZE)) { + pr_aud_err("%s: Buffer Size should be multiple of " + "[4 * no. of channels] and greater than 160\n", + __func__); + rc = -EINVAL; + break; + } + + pcm->sample_rate = config.sample_rate; + pcm->channel_count = config.channel_count; + pcm->buffer_size = config.buffer_size; + pcm->buffer_count = config.buffer_count; + break; + } + case AUDIO_GET_CONFIG: { + struct msm_audio_config config; + config.buffer_size = pcm->buffer_size; + config.buffer_count = pcm->buffer_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; + if (copy_to_user((void *) arg, &config, sizeof(config))) + rc = -EFAULT; + break; + } + case AUDIO_ENABLE_AUDPRE: { + + uint16_t enable_mask; + + if (copy_from_user(&enable_mask, (void *) arg, + sizeof(enable_mask))) { + rc = -EFAULT; + break; + } + if (enable_mask & FLUENCE_ENABLE) + rc = auddev_cfg_tx_copp_topology(pcm->ac->session, + VPM_TX_DM_FLUENCE_COPP_TOPOLOGY); + else if (enable_mask & STEREO_RECORD_ENABLE) + rc = auddev_cfg_tx_copp_topology(pcm->ac->session, + HTC_STEREO_RECORD_TOPOLOGY); + else + rc = auddev_cfg_tx_copp_topology(pcm->ac->session, + DEFAULT_COPP_TOPOLOGY); + break; + } + case AUDIO_SET_INCALL: { + if (copy_from_user(&pcm->rec_mode, + (void *) arg, + sizeof(pcm->rec_mode))) { + rc = -EFAULT; + pr_aud_err("%s: Error copying in-call mode\n", __func__); + break; + } + + if (pcm->rec_mode != VOC_REC_UPLINK && + pcm->rec_mode != VOC_REC_DOWNLINK && + pcm->rec_mode != VOC_REC_BOTH) { + rc = -EINVAL; + pcm->rec_mode = VOC_REC_NONE; + + pr_aud_err("%s: Invalid %d in-call rec_mode\n", + __func__, pcm->rec_mode); + break; + } + + pr_debug("%s: In-call rec_mode %d\n", __func__, pcm->rec_mode); + break; + } + + + default: + rc = -EINVAL; + break; + } + mutex_unlock(&pcm->lock); + return rc; +} + +static int pcm_in_open(struct inode *inode, struct file *file) +{ + struct pcm *pcm; + int rc = 0; + struct timespec ts; + struct rtc_time tm; + + if (atomic_cmpxchg(&pcm_opened, 0, 1) != 0) { + rc = -EBUSY; + 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->buffer_count = MAX_BUF; + + pcm->ac = q6asm_audio_client_alloc((app_cb)pcm_in_cb, (void *)pcm); + if (!pcm->ac) { + pr_aud_err("%s: Could not allocate memory\n", __func__); + rc = -ENOMEM; + goto fail; + } + + mutex_init(&pcm->lock); + mutex_init(&pcm->read_lock); + spin_lock_init(&pcm->dsp_lock); + init_waitqueue_head(&pcm->wait); + + rc = q6asm_open_read(pcm->ac, FORMAT_LINEAR_PCM); + if (rc < 0) { + pr_aud_err("%s: Cmd Open Failed\n", __func__); + goto fail; + } + + atomic_set(&pcm->in_stopped, 0); + atomic_set(&pcm->in_enabled, 0); + atomic_set(&pcm->in_count, 0); + atomic_set(&pcm->in_opened, 1); + + pcm->rec_mode = VOC_REC_NONE; + + file->private_data = pcm; + getnstimeofday(&ts); + rtc_time_to_tm(ts.tv_sec, &tm); + pr_aud_info1("[ATS][start_recording][successful] 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); + pr_aud_info("%s: pcm in open session id[%d]\n", __func__, pcm->ac->session); + + return 0; +fail: + if (pcm->ac) + q6asm_audio_client_free(pcm->ac); + kfree(pcm); + atomic_set(&pcm_opened, 0); + return rc; +} + +static ssize_t pcm_in_read(struct file *file, char __user *buf, + size_t count, loff_t *pos) +{ + struct pcm *pcm = file->private_data; + const char __user *start = buf; + void *data; + uint32_t offset = 0; + uint32_t size = 0; + uint32_t idx; + int rc = 0; + int len = 0; + + if (!atomic_read(&pcm->in_enabled)) + return -EFAULT; + mutex_lock(&pcm->read_lock); + while (count > 0) { + rc = wait_event_timeout(pcm->wait, + (atomic_read(&pcm->in_count) || + atomic_read(&pcm->in_stopped)), 5 * HZ); + if (!rc) { + pr_aud_err("%s: wait_event_timeout failed\n", __func__); + goto fail; + } + + if (atomic_read(&pcm->in_stopped) && + !atomic_read(&pcm->in_count)) { + mutex_unlock(&pcm->read_lock); + return 0; + } + + data = q6asm_is_cpu_buf_avail(OUT, pcm->ac, &size, &idx); + if (count >= size) + len = size; + else { + len = count; + pr_aud_err("%s: short read data[%p]bytesavail[%d]" + "bytesrequest[%d]" + "bytesrejected%d]\n",\ + __func__, data, size, + count, (size - count)); + } + if ((len) && data) { + offset = pcm->in_frame_info[idx][1]; + if (copy_to_user(buf, data+offset, len)) { + pr_aud_err("%s copy_to_user failed len[%d]\n", + __func__, len); + rc = -EFAULT; + goto fail; + } + count -= len; + buf += len; + } + atomic_dec(&pcm->in_count); + memset(&pcm->in_frame_info[idx], 0, + sizeof(uint32_t) * 2); + + rc = q6asm_read(pcm->ac); + if (rc < 0) { + pr_aud_err("%s q6asm_read faile\n", __func__); + goto fail; + } + rmb(); + break; + } + rc = buf-start; +fail: + mutex_unlock(&pcm->read_lock); + return rc; +} + +static int pcm_in_release(struct inode *inode, struct file *file) +{ + int rc = 0; + struct timespec ts; + struct rtc_time tm; + struct pcm *pcm = file->private_data; + + if (pcm == NULL) { + pr_aud_err("%s: Nothing need to be released.\n", __func__); + return 0; + } + if (pcm->ac) { + mutex_lock(&pcm->lock); + + if ((pcm->rec_mode != VOC_REC_NONE) && atomic_read(&pcm->in_enabled)) { + msm_disable_incall_recording(pcm->ac->session, pcm->rec_mode); + + pcm->rec_mode = VOC_REC_NONE; + } + + + /* remove this session from topology list */ + auddev_cfg_tx_copp_topology(pcm->ac->session, + DEFAULT_COPP_TOPOLOGY); + mutex_unlock(&pcm->lock); + } + + rc = pcm_in_disable(pcm); + if (pcm->ac) { + pr_aud_info("[%s:%s] release session id[%d]\n", __MM_FILE__, + __func__, pcm->ac->session); + msm_clear_session_id(pcm->ac->session); + q6asm_audio_client_free(pcm->ac); + } + + kfree(pcm); + getnstimeofday(&ts); + rtc_time_to_tm(ts.tv_sec, &tm); + atomic_set(&pcm_opened, 0); + pr_aud_info1("[ATS][stop_recording][successful] 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); + return rc; +} + +static const struct file_operations pcm_in_fops = { + .owner = THIS_MODULE, + .open = pcm_in_open, + .read = pcm_in_read, + .release = pcm_in_release, + .unlocked_ioctl = pcm_in_ioctl, +}; + +struct miscdevice pcm_in_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_pcm_in", + .fops = &pcm_in_fops, +}; + +static int __init pcm_in_init(void) +{ + return misc_register(&pcm_in_misc); +} + +device_initcall(pcm_in_init); diff --git a/arch/arm/mach-msm/qdsp6v3/pcm_out.c b/arch/arm/mach-msm/qdsp6v3/pcm_out.c new file mode 100644 index 00000000000..4ba5c287e4a --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v3/pcm_out.c @@ -0,0 +1,491 @@ +/* + * Copyright (C) 2009 Google, Inc. + * Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + * 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 + +#define MAX_BUF 2 +#define BUFSZ (4800) + +struct pcm { + struct mutex lock; + struct mutex write_lock; + spinlock_t dsp_lock; + wait_queue_head_t write_wait; + struct audio_client *ac; + uint32_t sample_rate; + uint32_t channel_count; + uint32_t buffer_size; + uint32_t buffer_count; + uint32_t rec_mode; + uint32_t stream_event; + uint32_t volume; + atomic_t out_count; + atomic_t out_enabled; + atomic_t out_opened; + atomic_t out_stopped; + atomic_t out_prefill; + struct wake_lock wakelock; +}; + +void pcm_out_cb(uint32_t opcode, uint32_t token, + uint32_t *payload, void *priv) +{ + struct pcm *pcm = (struct pcm *) priv; + unsigned long flags; + + spin_lock_irqsave(&pcm->dsp_lock, flags); + switch (opcode) { + case ASM_DATA_EVENT_WRITE_DONE: + atomic_inc(&pcm->out_count); + wake_up(&pcm->write_wait); + break; + default: + break; + } + spin_unlock_irqrestore(&pcm->dsp_lock, flags); +} + +static void audio_prevent_sleep(struct pcm *audio) +{ + pr_debug("%s:\n", __func__); + wake_lock(&audio->wakelock); +} + +static void audio_allow_sleep(struct pcm *audio) +{ + pr_debug("%s:\n", __func__); + wake_unlock(&audio->wakelock); +} + +static int pcm_out_enable(struct pcm *pcm) +{ + if (atomic_read(&pcm->out_enabled)) + return 0; + return q6asm_run(pcm->ac, 0, 0, 0); +} + +static int pcm_out_disable(struct pcm *pcm) +{ + int rc = 0; + + if (atomic_read(&pcm->out_opened)) { + atomic_set(&pcm->out_enabled, 0); + atomic_set(&pcm->out_opened, 0); + rc = q6asm_cmd(pcm->ac, CMD_CLOSE); + + atomic_set(&pcm->out_stopped, 1); + wake_up(&pcm->write_wait); + } + return rc; +} + +static int config(struct pcm *pcm) +{ + int rc = 0; + if (!atomic_read(&pcm->out_prefill)) { + pr_debug("%s: pcm prefill\n", __func__); + rc = q6asm_audio_client_buf_alloc(IN, pcm->ac, + pcm->buffer_size, pcm->buffer_count); + if (rc < 0) { + pr_aud_err("Audio Start: Buffer Allocation failed \ + rc = %d\n", rc); + goto fail; + } + + rc = q6asm_media_format_block_pcm(pcm->ac, pcm->sample_rate, + pcm->channel_count); + if (rc < 0) + pr_aud_err("%s: CMD Format block failed\n", __func__); + + atomic_set(&pcm->out_prefill, 1); + atomic_set(&pcm->out_count, pcm->buffer_count); + } +fail: + return rc; +} + +static void pcm_event_listner(u32 evt_id, union auddev_evt_data *evt_payload, + void *private_data) +{ + struct pcm *pcm = (struct pcm *) private_data; + int rc = 0; + + switch (evt_id) { + case AUDDEV_EVT_STREAM_VOL_CHG: + pcm->volume = evt_payload->session_vol; + pr_debug("%s: AUDDEV_EVT_STREAM_VOL_CHG, stream vol %d, " + "enabled = %d\n", __func__, pcm->volume, + atomic_read(&pcm->out_enabled)); + if (atomic_read(&pcm->out_enabled)) { + if (pcm->ac) { + rc = q6asm_set_volume(pcm->ac, pcm->volume); + if (rc < 0) + pr_aud_err("%s: Send Volume command" + "failed rc=%d\n", __func__, rc); + } + } + break; + default: + pr_aud_err("%s:ERROR:wrong event\n", __func__); + break; + } +} + +static long pcm_out_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: { + pr_aud_info("%s: AUDIO_SET_VOLUME, vol %lu\n", __func__, arg); + rc = q6asm_set_volume(pcm->ac, arg); + if (rc < 0) + pr_aud_err("%s: Send Volume command failed rc=%d\n", + __func__, rc); + break; + } + case AUDIO_START: { + pr_aud_info("%s: AUDIO_START\n", __func__); + rc = config(pcm); + if (rc) { + pr_aud_err("%s: Out Configuration failed\n", __func__); + rc = -EFAULT; + break; + } + + rc = pcm_out_enable(pcm); + if (rc) { + pr_aud_err("Out enable failed\n"); + rc = -EFAULT; + break; + } + audio_prevent_sleep(pcm); + atomic_set(&pcm->out_enabled, 1); + + rc = q6asm_set_volume(pcm->ac, pcm->volume); + if (rc < 0) + pr_aud_err("%s: Send Volume command failed rc=%d\n", + __func__, rc); + rc = q6asm_set_lrgain(pcm->ac, 0x2000, 0x2000); + if (rc < 0) + pr_aud_err("%s: Send channel gain failed rc=%d\n", + __func__, rc); + /* disable mute by default */ + rc = q6asm_set_mute(pcm->ac, 0); + if (rc < 0) + pr_aud_err("%s: Send mute command failed rc=%d\n", + __func__, rc); + break; + } + case AUDIO_GET_SESSION_ID: { + if (copy_to_user((void *) arg, &pcm->ac->session, + sizeof(unsigned short))) + rc = -EFAULT; + break; + } + case AUDIO_STOP: + break; + case AUDIO_FLUSH: + break; + case AUDIO_SET_CONFIG: { + struct msm_audio_config config; + pr_aud_info("AUDIO_SET_CONFIG\n"); + 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) { + rc = -EINVAL; + break; + } + pcm->sample_rate = config.sample_rate; + pcm->channel_count = config.channel_count; + pcm->buffer_size = config.buffer_size; + pcm->buffer_count = config.buffer_count; + pr_debug("%s:buffer_size:%d buffer_count:%d sample_rate:%d \ + channel_count:%d\n", __func__, pcm->buffer_size, + pcm->buffer_count, pcm->sample_rate, + pcm->channel_count); + break; + } + case AUDIO_GET_CONFIG: { + struct msm_audio_config config; + pr_aud_info("AUDIO_GET_CONFIG\n"); + config.buffer_size = pcm->buffer_size; + config.buffer_count = pcm->buffer_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; + if (copy_to_user((void *) arg, &config, sizeof(config))) + rc = -EFAULT; + break; + } + case AUDIO_SET_EQ: { + struct msm_audio_eq_stream_config eq_config; + if (copy_from_user(&eq_config, (void *) arg, + sizeof(eq_config))) { + rc = -EFAULT; + break; + } + rc = q6asm_equalizer(pcm->ac, (void *) &eq_config); + if (rc < 0) + pr_aud_err("%s: EQUALIZER FAILED\n", __func__); + break; + } + default: + rc = -EINVAL; + } + mutex_unlock(&pcm->lock); + return rc; +} + +static int pcm_out_open(struct inode *inode, struct file *file) +{ + struct pcm *pcm; + int rc = 0; + + struct timespec ts; + struct rtc_time tm; + + pr_aud_info("[%s:%s] open\n", __MM_FILE__, __func__); + pcm = kzalloc(sizeof(struct pcm), GFP_KERNEL); + if (!pcm) { + pr_aud_info("%s: Failed to allocated memory\n", __func__); + return -ENOMEM; + } + + pcm->channel_count = 2; + pcm->sample_rate = 44100; + pcm->buffer_size = BUFSZ; + pcm->buffer_count = MAX_BUF; + pcm->stream_event = AUDDEV_EVT_STREAM_VOL_CHG; + pcm->volume = 0x2000; + + pcm->ac = q6asm_audio_client_alloc((app_cb)pcm_out_cb, (void *)pcm); + if (!pcm->ac) { + pr_aud_err("%s: Could not allocate memory\n", __func__); + rc = -ENOMEM; + goto fail; + } + + rc = q6asm_open_write(pcm->ac, FORMAT_LINEAR_PCM); + if (rc < 0) { + pr_aud_err("%s: pcm out open failed for session %d\n", __func__, + pcm->ac->session); + rc = -EINVAL; + goto fail; + } + + mutex_init(&pcm->lock); + mutex_init(&pcm->write_lock); + init_waitqueue_head(&pcm->write_wait); + spin_lock_init(&pcm->dsp_lock); + atomic_set(&pcm->out_enabled, 0); + atomic_set(&pcm->out_stopped, 0); + atomic_set(&pcm->out_count, pcm->buffer_count); + atomic_set(&pcm->out_prefill, 0); + atomic_set(&pcm->out_opened, 1); + wake_lock_init(&pcm->wakelock, WAKE_LOCK_SUSPEND, "audio_pcm"); + + rc = auddev_register_evt_listner(pcm->stream_event, + AUDDEV_CLNT_DEC, + pcm->ac->session, + pcm_event_listner, + (void *)pcm); + if (rc < 0) { + pr_aud_err("%s: failed to register listner\n", __func__); + goto fail; + } + + file->private_data = pcm; + + getnstimeofday(&ts); + rtc_time_to_tm(ts.tv_sec, &tm); + pr_aud_info1("[ATS][play_music][successful] 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); + pr_debug("[%s:%s] open session id[%d]\n", __MM_FILE__, + __func__, pcm->ac->session); + return 0; +fail: + if (pcm->ac) + q6asm_audio_client_free(pcm->ac); + kfree(pcm); + return rc; +} + +static ssize_t pcm_out_write(struct file *file, const char __user *buf, + size_t count, loff_t *pos) +{ + struct pcm *pcm = file->private_data; + const char __user *start = buf; + int xfer; + char *bufptr; + uint32_t idx; + void *data; + int rc = 0; + uint32_t size; + + if (!pcm->ac) + return -ENODEV; + + if (!atomic_read(&pcm->out_enabled)) { + rc = config(pcm); + if (rc < 0) + return rc; + } + + mutex_lock(&pcm->write_lock); + while (count > 0) { + rc = wait_event_timeout(pcm->write_wait, + (atomic_read(&pcm->out_count) || + atomic_read(&pcm->out_stopped)), 5 * HZ); + if (!rc) { + pr_aud_info("%s: wait_event_timeout failed for session %d\n", + __func__, pcm->ac->session); + goto fail; + } + + if (atomic_read(&pcm->out_stopped) && + !atomic_read(&pcm->out_count)) { + pr_aud_info("%s: pcm stopped out_count 0\n", __func__); + mutex_unlock(&pcm->write_lock); + return 0; + } + + data = q6asm_is_cpu_buf_avail(IN, pcm->ac, &size, &idx); + bufptr = data; + if (bufptr) { + xfer = count; + if (xfer > BUFSZ) + xfer = BUFSZ; + + if (copy_from_user(bufptr, buf, xfer)) { + rc = -EFAULT; + goto fail; + } + buf += xfer; + count -= xfer; + rc = q6asm_write(pcm->ac, xfer, 0, 0, NO_TIMESTAMP); + wmb(); + if (rc < 0) { + rc = -EFAULT; + goto fail; + } + } + atomic_dec(&pcm->out_count); + } + + rc = buf - start; +fail: + mutex_unlock(&pcm->write_lock); + return rc; +} + +static int pcm_out_release(struct inode *inode, struct file *file) +{ + struct pcm *pcm = file->private_data; + struct timespec ts; + struct rtc_time tm; + + if (pcm == NULL) { + pr_aud_err("%s: Nothing need to be released.\n", __func__); + return 0; + } + if (pcm->ac) { + pr_aud_info("[%s:%s] release session id[%d]\n", __MM_FILE__, + __func__, pcm->ac->session); + pcm_out_disable(pcm); + msm_clear_session_id(pcm->ac->session); + auddev_unregister_evt_listner(AUDDEV_CLNT_DEC, pcm->ac->session); + q6asm_audio_client_free(pcm->ac); + } + audio_allow_sleep(pcm); + wake_lock_destroy(&pcm->wakelock); + mutex_destroy(&pcm->lock); + mutex_destroy(&pcm->write_lock); + + kfree(pcm); + pr_aud_info("[%s:%s] release\n", __MM_FILE__, __func__); + + getnstimeofday(&ts); + rtc_time_to_tm(ts.tv_sec, &tm); + pr_aud_info1("[ATS][stop_music][successful] 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); + return 0; +} + +static const struct file_operations pcm_out_fops = { + .owner = THIS_MODULE, + .open = pcm_out_open, + .write = pcm_out_write, + .release = pcm_out_release, + .unlocked_ioctl = pcm_out_ioctl, +}; + +struct miscdevice pcm_out_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_pcm_out", + .fops = &pcm_out_fops, +}; + +static int __init pcm_out_init(void) +{ + return misc_register(&pcm_out_misc); +} + +device_initcall(pcm_out_init); diff --git a/arch/arm/mach-msm/qdsp6v3/q6adm.c b/arch/arm/mach-msm/qdsp6v3/q6adm.c new file mode 100644 index 00000000000..b2e015180f2 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v3/q6adm.c @@ -0,0 +1,651 @@ +/* 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. + * + * 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 "audio_acdb.h" +#include "rtac.h" + +#define TIMEOUT_MS 1000 +#define AUDIO_RX 0x0 +#define AUDIO_TX 0x1 +#define RESET_COPP_ID 99 +#define INVALID_COPP_ID 0xFF + +struct adm_ctl { + void *apr; + atomic_t copp_id[AFE_MAX_PORTS]; + atomic_t copp_cnt[AFE_MAX_PORTS]; + atomic_t copp_stat[AFE_MAX_PORTS]; + wait_queue_head_t wait; +}; + +static struct adm_ctl this_adm; + +static int32_t adm_callback(struct apr_client_data *data, void *priv) +{ + uint32_t *payload; + int i, index; + payload = data->payload; + + if (data->opcode == RESET_EVENTS) { + pr_debug("adm_callback: Reset event is received: %d %d apr[%p]\n", + data->reset_event, data->reset_proc, + this_adm.apr); + if (this_adm.apr) { + apr_reset(this_adm.apr); + for (i = 0; i < AFE_MAX_PORTS; i++) { + atomic_set(&this_adm.copp_id[i], RESET_COPP_ID); + atomic_set(&this_adm.copp_cnt[i], 0); + atomic_set(&this_adm.copp_stat[i], 0); + } + this_adm.apr = NULL; + } + return 0; + } + + pr_debug("%s: code = 0x%x %x %x size = %d\n", __func__, + data->opcode, payload[0], payload[1], + data->payload_size); + + if (data->payload_size) { + index = afe_get_port_index(data->token); + + if (index < 0 || index >= AFE_MAX_PORTS) { + pr_aud_err("%s: invalid index %d\n", __func__, index); + return -EINVAL; + } + + pr_debug("%s: Port ID %d, index %d\n", __func__, + data->token, index); + + if (data->opcode == APR_BASIC_RSP_RESULT) { + pr_debug("APR_BASIC_RSP_RESULT\n"); + switch (payload[0]) { + case ADM_CMD_SET_PARAMS: +#ifdef CONFIG_MSM8X60_RTAC + if (rtac_make_adm_callback(payload, + data->payload_size)) + break; +#endif + case ADM_CMD_COPP_CLOSE: + case ADM_CMD_MEMORY_MAP: + case ADM_CMD_MEMORY_UNMAP: + case ADM_CMD_MEMORY_MAP_REGIONS: + case ADM_CMD_MEMORY_UNMAP_REGIONS: + case ADM_CMD_MATRIX_MAP_ROUTINGS: + pr_debug("ADM_CMD_MATRIX_MAP_ROUTINGS\n"); + atomic_set(&this_adm.copp_stat[index], 1); + wake_up(&this_adm.wait); + break; + default: + pr_aud_err("%s: Unknown Cmd: 0x%x\n", __func__, + payload[0]); + break; + } + return 0; + } + + switch (data->opcode) { + case ADM_CMDRSP_COPP_OPEN: { + struct adm_copp_open_respond *open = data->payload; + if (open->copp_id == INVALID_COPP_ID) { + pr_aud_err("%s: invalid coppid rxed %d\n", + __func__, open->copp_id); + atomic_set(&this_adm.copp_stat[index], 1); + wake_up(&this_adm.wait); + break; + } + atomic_set(&this_adm.copp_id[index], open->copp_id); + atomic_set(&this_adm.copp_stat[index], 1); + pr_debug("%s: coppid rxed=%d\n", __func__, + open->copp_id); + wake_up(&this_adm.wait); + } + break; +#ifdef CONFIG_MSM8X60_RTAC + case ADM_CMDRSP_GET_PARAMS: + pr_debug("ADM_CMDRSP_GET_PARAMS\n"); + rtac_make_adm_callback(payload, + data->payload_size); + break; +#endif + default: + pr_aud_err("%s: Unknown cmd:0x%x\n", __func__, + data->opcode); + break; + } + } + return 0; +} + +void send_cal(int port_id, struct acdb_cal_block *aud_cal) +{ + s32 result; + struct adm_set_params_command adm_params; + int index = afe_get_port_index(port_id); + + pr_debug("%s: Port id %d, index %d\n", __func__, port_id, index); + + if (!aud_cal || aud_cal->cal_size == 0) { + pr_aud_info("%s: No calibration data to send!\n", __func__); + goto done; + } + + if (index < 0 || index >= AFE_MAX_PORTS) { + pr_aud_err("%s: invalid index %d\n", __func__, index); + goto done; + } + + adm_params.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(20), APR_PKT_VER); + adm_params.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(adm_params)); + adm_params.hdr.src_svc = APR_SVC_ADM; + adm_params.hdr.src_domain = APR_DOMAIN_APPS; + adm_params.hdr.src_port = port_id; + adm_params.hdr.dest_svc = APR_SVC_ADM; + adm_params.hdr.dest_domain = APR_DOMAIN_ADSP; + adm_params.hdr.dest_port = atomic_read(&this_adm.copp_id[index]); + adm_params.hdr.token = port_id; + adm_params.hdr.opcode = ADM_CMD_SET_PARAMS; + adm_params.payload = aud_cal->cal_paddr; + adm_params.payload_size = aud_cal->cal_size; + + atomic_set(&this_adm.copp_stat[index], 0); + pr_aud_info("%s: Sending SET_PARAMS payload = 0x%x, size = %d\n", + __func__, adm_params.payload, adm_params.payload_size); + result = apr_send_pkt(this_adm.apr, (uint32_t *)&adm_params); + if (result < 0) { + pr_aud_err("%s: Set params failed port = %d payload = 0x%x\n", + __func__, port_id, aud_cal->cal_paddr); + goto done; + } + /* Wait for the callback */ + result = wait_event_timeout(this_adm.wait, + atomic_read(&this_adm.copp_stat[index]), + msecs_to_jiffies(TIMEOUT_MS)); + if (!result) + pr_aud_err("%s: Set params timed out port = %d, payload = 0x%x\n", + __func__, port_id, aud_cal->cal_paddr); +done: + return; +} + +void send_adm_cal(int port_id, int path) +{ + s32 acdb_path; + struct acdb_cal_block aud_cal; + + pr_aud_info("%s\n", __func__); + + /* Maps audio_dev_ctrl path definition to ACDB definition */ + acdb_path = path - 1; + if ((acdb_path >= NUM_AUDPROC_BUFFERS) || + (acdb_path < 0)) { + pr_aud_err("%s: Path is not RX or TX, path = %d\n", + __func__, path); + goto done; + } + + pr_aud_info("%s: Sending audproc cal, acdb_path %d\n", + __func__, acdb_path); + get_audproc_cal(acdb_path, &aud_cal); + send_cal(port_id, &aud_cal); + + pr_aud_info("%s: Sending audvol cal, acdb_path %d\n", + __func__, acdb_path); + get_audvol_cal(acdb_path, &aud_cal); + send_cal(port_id, &aud_cal); +done: + return; +} + +int adm_open(int port_id, int path, int rate, int channel_mode, int topology) +{ + struct adm_copp_open_command open; + int ret = 0; + int index; + + pr_debug("%s: port %d path:%d rate:%d mode:%d\n", __func__, + port_id, path, rate, channel_mode); + pr_aud_info("Topology = %x\n", topology); + + if (afe_validate_port(port_id) < 0) { + pr_aud_err("%s port idi[%d] is invalid\n", __func__, port_id); + return -ENODEV; + } + + index = afe_get_port_index(port_id); + pr_debug("%s: Port ID %d, index %d\n", __func__, port_id, index); + + if (this_adm.apr == NULL) { + this_adm.apr = apr_register("ADSP", "ADM", adm_callback, + 0xFFFFFFFF, &this_adm); + if (this_adm.apr == NULL) { + pr_aud_err("%s: Unable to register ADM\n", __func__); + ret = -ENODEV; + return ret; + } +#ifdef CONFIG_MSM8X60_RTAC + rtac_set_adm_handle(this_adm.apr); +#endif + } + + + /* Create a COPP if port id are not enabled */ + if (atomic_read(&this_adm.copp_cnt[index]) == 0) { + + open.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + open.hdr.pkt_size = sizeof(open); + open.hdr.src_svc = APR_SVC_ADM; + open.hdr.src_domain = APR_DOMAIN_APPS; + open.hdr.src_port = port_id; + open.hdr.dest_svc = APR_SVC_ADM; + open.hdr.dest_domain = APR_DOMAIN_ADSP; + open.hdr.dest_port = port_id; + open.hdr.token = port_id; + open.hdr.opcode = ADM_CMD_COPP_OPEN; + + open.mode = path; + open.endpoint_id1 = port_id; + open.endpoint_id2 = 0xFFFF; + open.topology_id = topology; + + open.channel_config = channel_mode & 0x00FF; + open.rate = rate; + + pr_debug("%s: channel_config=%d port_id=%d rate=%d\ + topology_id=0x%X\n", __func__, open.channel_config,\ + open.endpoint_id1, open.rate,\ + open.topology_id); + + atomic_set(&this_adm.copp_stat[index], 0); + + ret = apr_send_pkt(this_adm.apr, (uint32_t *)&open); + if (ret < 0) { + pr_aud_err("%s:ADM enable for port %d failed\n", + __func__, port_id); + ret = -EINVAL; + goto fail_cmd; + } + /* Wait for the callback with copp id */ + ret = wait_event_timeout(this_adm.wait, + atomic_read(&this_adm.copp_stat[index]), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_aud_err("%s ADM open failed for port %d\n", __func__, + port_id); + ret = -EINVAL; + goto fail_cmd; + } + } + atomic_inc(&this_adm.copp_cnt[index]); + return 0; + +fail_cmd: + + return ret; +} + +int adm_matrix_map(int session_id, int path, int num_copps, + unsigned int *port_id, int copp_id) +{ + struct adm_routings_command route; + int ret = 0, i = 0; + /* Assumes port_ids have already been validated during adm_open */ + int index = afe_get_port_index(copp_id); + + if (index < 0 || index >= AFE_MAX_PORTS) { + pr_aud_err("%s: invalid index %d\n", __func__, index); + return -EINVAL; + } + + pr_debug("%s: session 0x%x path:%d num_copps:%d port_id[0]:%d\n", + __func__, session_id, path, num_copps, port_id[0]); + + route.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + route.hdr.pkt_size = sizeof(route); + route.hdr.src_svc = 0; + route.hdr.src_domain = APR_DOMAIN_APPS; + route.hdr.src_port = copp_id; + route.hdr.dest_svc = APR_SVC_ADM; + route.hdr.dest_domain = APR_DOMAIN_ADSP; + route.hdr.dest_port = atomic_read(&this_adm.copp_id[index]); + route.hdr.token = copp_id; + route.hdr.opcode = ADM_CMD_MATRIX_MAP_ROUTINGS; + route.num_sessions = 1; + route.session[0].id = session_id; + route.session[0].num_copps = num_copps; + + for (i = 0; i < num_copps; i++) { + int tmp; + tmp = afe_get_port_index(port_id[i]); + + pr_debug("%s: port_id[%d]: %d, index: %d\n", __func__, i, + port_id[i], tmp); + if (tmp < 0 || tmp >= AFE_MAX_PORTS) { + pr_aud_err("afe_get_port_index return invalid %d\n", tmp); + ret = -EINVAL; + goto fail_cmd; + } + route.session[0].copp_id[i] = + atomic_read(&this_adm.copp_id[tmp]); + } + if (num_copps % 2) + route.session[0].copp_id[i] = 0; + + switch (path) { + case 0x1: + route.path = AUDIO_RX; + break; + case 0x2: + case 0x3: + route.path = AUDIO_TX; + break; + default: + pr_aud_err("%s: Wrong path set[%d]\n", __func__, path); + break; + } + atomic_set(&this_adm.copp_stat[index], 0); + + ret = apr_send_pkt(this_adm.apr, (uint32_t *)&route); + if (ret < 0) { + pr_aud_err("%s: ADM routing for port %d failed\n", + __func__, port_id[0]); + ret = -EINVAL; + goto fail_cmd; + } + ret = wait_event_timeout(this_adm.wait, + atomic_read(&this_adm.copp_stat[index]), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_aud_err("%s: ADM cmd Route failed for port %d\n", + __func__, port_id[0]); + ret = -EINVAL; + goto fail_cmd; + } + + for (i = 0; i < num_copps; i++) + send_adm_cal(port_id[i], path); + +#ifdef CONFIG_MSM8X60_RTAC + for (i = 0; i < num_copps; i++) + rtac_add_adm_device(port_id[i], session_id); +#endif + return 0; + +fail_cmd: + + return ret; +} + +int adm_memory_map_regions(uint32_t *buf_add, uint32_t mempool_id, + uint32_t *bufsz, uint32_t bufcnt) +{ + struct adm_cmd_memory_map_regions *mmap_regions = NULL; + struct adm_memory_map_regions *mregions = NULL; + void *mmap_region_cmd = NULL; + void *payload = NULL; + int ret = 0; + int i = 0; + int cmd_size = 0; + + pr_aud_info("%s\n", __func__); + if (this_adm.apr == NULL) { + this_adm.apr = apr_register("ADSP", "ADM", adm_callback, + 0xFFFFFFFF, &this_adm); + if (this_adm.apr == NULL) { + pr_aud_err("%s: Unable to register ADM\n", __func__); + ret = -ENODEV; + return ret; + } +#ifdef CONFIG_MSM8X60_RTAC + rtac_set_adm_handle(this_adm.apr); +#endif + } + + cmd_size = sizeof(struct adm_cmd_memory_map_regions) + + sizeof(struct adm_memory_map_regions) * bufcnt; + + mmap_region_cmd = kzalloc(cmd_size, GFP_KERNEL); + if (!mmap_region_cmd) { + pr_aud_err("%s: allocate mmap_region_cmd failed\n", __func__); + return -ENOMEM; + } + mmap_regions = (struct adm_cmd_memory_map_regions *)mmap_region_cmd; + mmap_regions->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + mmap_regions->hdr.pkt_size = cmd_size; + mmap_regions->hdr.src_port = 0; + mmap_regions->hdr.dest_port = 0; + mmap_regions->hdr.token = 0; + mmap_regions->hdr.opcode = ADM_CMD_MEMORY_MAP_REGIONS; + mmap_regions->mempool_id = mempool_id & 0x00ff; + mmap_regions->nregions = bufcnt & 0x00ff; + pr_debug("%s: map_regions->nregions = %d\n", __func__, + mmap_regions->nregions); + payload = ((u8 *) mmap_region_cmd + + sizeof(struct adm_cmd_memory_map_regions)); + mregions = (struct adm_memory_map_regions *)payload; + + for (i = 0; i < bufcnt; i++) { + mregions->phys = buf_add[i]; + mregions->buf_size = bufsz[i]; + ++mregions; + } + + atomic_set(&this_adm.copp_stat[0], 0); + ret = apr_send_pkt(this_adm.apr, (uint32_t *) mmap_region_cmd); + if (ret < 0) { + pr_aud_err("%s: mmap_regions op[0x%x]rc[%d]\n", __func__, + mmap_regions->hdr.opcode, ret); + ret = -EINVAL; + goto fail_cmd; + } + + ret = wait_event_timeout(this_adm.wait, + atomic_read(&this_adm.copp_stat[0]), 5 * HZ); + if (!ret) { + pr_aud_err("%s: timeout. waited for memory_map\n", __func__); + ret = -EINVAL; + goto fail_cmd; + } +fail_cmd: + kfree(mmap_region_cmd); + return ret; +} + +int adm_memory_unmap_regions(uint32_t *buf_add, uint32_t *bufsz, + uint32_t bufcnt) +{ + struct adm_cmd_memory_unmap_regions *unmap_regions = NULL; + struct adm_memory_unmap_regions *mregions = NULL; + void *unmap_region_cmd = NULL; + void *payload = NULL; + int ret = 0; + int i = 0; + int cmd_size = 0; + + pr_aud_info("%s\n", __func__); + + if (this_adm.apr == NULL) { + pr_aud_err("%s APR handle NULL\n", __func__); + return -EINVAL; + } + + cmd_size = sizeof(struct adm_cmd_memory_unmap_regions) + + sizeof(struct adm_memory_unmap_regions) * bufcnt; + + unmap_region_cmd = kzalloc(cmd_size, GFP_KERNEL); + if (!unmap_region_cmd) { + pr_aud_err("%s: allocate unmap_region_cmd failed\n", __func__); + return -ENOMEM; + } + unmap_regions = (struct adm_cmd_memory_unmap_regions *) + unmap_region_cmd; + unmap_regions->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + unmap_regions->hdr.pkt_size = cmd_size; + unmap_regions->hdr.src_port = 0; + unmap_regions->hdr.dest_port = 0; + unmap_regions->hdr.token = 0; + unmap_regions->hdr.opcode = ADM_CMD_MEMORY_UNMAP_REGIONS; + unmap_regions->nregions = bufcnt & 0x00ff; + unmap_regions->reserved = 0; + pr_debug("%s: unmap_regions->nregions = %d\n", __func__, + unmap_regions->nregions); + payload = ((u8 *) unmap_region_cmd + + sizeof(struct adm_cmd_memory_unmap_regions)); + mregions = (struct adm_memory_unmap_regions *)payload; + + for (i = 0; i < bufcnt; i++) { + mregions->phys = buf_add[i]; + ++mregions; + } + atomic_set(&this_adm.copp_stat[0], 0); + ret = apr_send_pkt(this_adm.apr, (uint32_t *) unmap_region_cmd); + if (ret < 0) { + pr_aud_err("%s: mmap_regions op[0x%x]rc[%d]\n", __func__, + unmap_regions->hdr.opcode, ret); + ret = -EINVAL; + goto fail_cmd; + } + + ret = wait_event_timeout(this_adm.wait, + atomic_read(&this_adm.copp_stat[0]), 5 * HZ); + if (!ret) { + pr_aud_err("%s: timeout. waited for memory_unmap\n", __func__); + ret = -EINVAL; + goto fail_cmd; + } +fail_cmd: + kfree(unmap_region_cmd); + return ret; +} + +#ifdef CONFIG_MSM8X60_RTAC +int adm_get_copp_id(int port_id) +{ + pr_debug("%s\n", __func__); + + if (port_id < 0) { + pr_aud_err("%s: invalid port_id = %d\n", __func__, port_id); + return -EINVAL; + } + + return atomic_read(&this_adm.copp_id[port_id]); +} +#endif + +int adm_close(int port_id) +{ + struct apr_hdr close; + + int ret = 0; + int index = afe_get_port_index(port_id); + + if (index < 0 || index >= AFE_MAX_PORTS) { + pr_aud_err("%s: invalid index %d\n", __func__, index); + return -EINVAL; + } + + pr_aud_info("%s port_id=%d index %d\n", __func__, port_id, index); + + if (!(atomic_read(&this_adm.copp_cnt[index]))) { + pr_aud_err("%s: copp count for port[%d]is 0\n", __func__, port_id); + + goto fail_cmd; + } + atomic_dec(&this_adm.copp_cnt[index]); + if (!(atomic_read(&this_adm.copp_cnt[index]))) { + + close.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + close.pkt_size = sizeof(close); + close.src_svc = APR_SVC_ADM; + close.src_domain = APR_DOMAIN_APPS; + close.src_port = port_id; + close.dest_svc = APR_SVC_ADM; + close.dest_domain = APR_DOMAIN_ADSP; + close.dest_port = atomic_read(&this_adm.copp_id[index]); + close.token = port_id; + close.opcode = ADM_CMD_COPP_CLOSE; + + atomic_set(&this_adm.copp_id[index], RESET_COPP_ID); + atomic_set(&this_adm.copp_stat[index], 0); + + + pr_debug("%s:coppid %d portid=%d index=%d coppcnt=%d\n", + __func__, + atomic_read(&this_adm.copp_id[index]), + port_id, index, + atomic_read(&this_adm.copp_cnt[index])); + + ret = apr_send_pkt(this_adm.apr, (uint32_t *)&close); + if (ret < 0) { + pr_aud_err("%s ADM close failed\n", __func__); + ret = -EINVAL; + goto fail_cmd; + } + + ret = wait_event_timeout(this_adm.wait, + atomic_read(&this_adm.copp_stat[index]), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_aud_info("%s: ADM cmd Route failed for port %d\n", + __func__, port_id); + ret = -EINVAL; + goto fail_cmd; + } + +#ifdef CONFIG_MSM8X60_RTAC + rtac_remove_adm_device(port_id); +#endif + } + +fail_cmd: + return ret; +} + +static int __init adm_init(void) +{ + int i = 0; + pr_aud_info("%s\n", __func__); + init_waitqueue_head(&this_adm.wait); + this_adm.apr = NULL; + + for (i = 0; i < AFE_MAX_PORTS; i++) { + atomic_set(&this_adm.copp_id[i], RESET_COPP_ID); + atomic_set(&this_adm.copp_cnt[i], 0); + atomic_set(&this_adm.copp_stat[i], 0); + } + return 0; +} + +device_initcall(adm_init); diff --git a/arch/arm/mach-msm/qdsp6v3/q6adm.h b/arch/arm/mach-msm/qdsp6v3/q6adm.h new file mode 100644 index 00000000000..6d01738b7a8 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v3/q6adm.h @@ -0,0 +1,56 @@ +/* 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 __Q6_ADM_H__ +#define __Q6_ADM_H__ +#include + +/* multiple copp per stream. */ +struct route_payload { + unsigned int copp_ids[AFE_MAX_PORTS]; + unsigned short num_copps; + unsigned int session_id; +}; + +int adm_open(int port, int path, int rate, int mode, int topology); + +int adm_memory_map_regions(uint32_t *buf_add, uint32_t mempool_id, + uint32_t *bufsz, uint32_t bufcnt); + +int adm_memory_unmap_regions(uint32_t *buf_add, uint32_t *bufsz, + uint32_t bufcnt); + +int adm_close(int port); + +int adm_matrix_map(int session_id, int path, int num_copps, + unsigned int *port_id, int copp_id); + +#ifdef CONFIG_MSM8X60_RTAC +int adm_get_copp_id(int port_id); +#endif + +#endif /* __Q6_ADM_H__ */ diff --git a/arch/arm/mach-msm/qdsp6v3/q6afe.c b/arch/arm/mach-msm/qdsp6v3/q6afe.c new file mode 100644 index 00000000000..a51f6ab83a7 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v3/q6afe.c @@ -0,0 +1,687 @@ +/* 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. + * + * 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 + +struct afe_ctl { + void *apr; + atomic_t state; + wait_queue_head_t wait; + struct task_struct *task; +}; + +static struct afe_ctl this_afe; + +#define TIMEOUT_MS 1000 +#define Q6AFE_MAX_VOLUME 0x3FFF + + +static int32_t afe_callback(struct apr_client_data *data, void *priv) +{ + if (data->opcode == RESET_EVENTS) { + pr_debug("q6afe: reset event = %d %d apr[%p]\n", + data->reset_event, data->reset_proc, this_afe.apr); + if (this_afe.apr) { + apr_reset(this_afe.apr); + atomic_set(&this_afe.state, 0); + this_afe.apr = NULL; + } + /* send info to user */ + pr_debug("task_name = %s pid = %d\n", + this_afe.task->comm, this_afe.task->pid); + send_sig(SIGUSR1, this_afe.task, 0); + } + if (data->payload_size) { + uint32_t *payload; + payload = data->payload; + pr_debug("%s: cmd = 0x%x status = 0x%x\n", __func__, + payload[0], payload[1]); + if (data->opcode == APR_BASIC_RSP_RESULT) { + switch (payload[0]) { + case AFE_PORT_AUDIO_IF_CONFIG: + case AFE_PORT_CMD_STOP: + case AFE_PORT_CMD_START: + case AFE_PORT_CMD_LOOPBACK: + case AFE_PORT_CMD_SIDETONE_CTL: + case AFE_PORT_CMD_SET_PARAM: + case AFE_PSEUDOPORT_CMD_START: + case AFE_PSEUDOPORT_CMD_STOP: + atomic_set(&this_afe.state, 0); + wake_up(&this_afe.wait); + break; + default: + pr_aud_err("Unknown cmd 0x%x\n", + payload[0]); + break; + } + } + } + return 0; +} + +int afe_validate_port(u16 port_id) +{ + int ret; + + switch (port_id) { + case PRIMARY_I2S_RX: + case PRIMARY_I2S_TX: + case PCM_RX: + case PCM_TX: + case SECONDARY_I2S_RX: + case SECONDARY_I2S_TX: + case MI2S_RX: + case MI2S_TX: + case HDMI_RX: + case RSVD_2: + case RSVD_3: + case DIGI_MIC_TX: + case VOICE_RECORD_RX: + case VOICE_RECORD_TX: + case VOICE_PLAYBACK_TX: + { + ret = 0; + break; + } + + default: + ret = -EINVAL; + } + + return ret; +} + +int afe_get_port_index(u16 port_id) +{ + switch (port_id) { + case PRIMARY_I2S_RX: return IDX_PRIMARY_I2S_RX; + case PRIMARY_I2S_TX: return IDX_PRIMARY_I2S_TX; + case PCM_RX: return IDX_PCM_RX; + case PCM_TX: return IDX_PCM_TX; + case SECONDARY_I2S_RX: return IDX_SECONDARY_I2S_RX; + case SECONDARY_I2S_TX: return IDX_SECONDARY_I2S_TX; + case MI2S_RX: return IDX_MI2S_RX; + case MI2S_TX: return IDX_MI2S_TX; + case HDMI_RX: return IDX_HDMI_RX; + case RSVD_2: return IDX_RSVD_2; + case RSVD_3: return IDX_RSVD_3; + case DIGI_MIC_TX: return IDX_DIGI_MIC_TX; + case VOICE_RECORD_RX: return IDX_VOICE_RECORD_RX; + case VOICE_RECORD_TX: return IDX_VOICE_RECORD_TX; + case VOICE_PLAYBACK_TX: return IDX_VOICE_PLAYBACK_TX; + default: return -EINVAL; + } +} + +int afe_open(u16 port_id, union afe_port_config *afe_config, int rate) +{ + struct afe_port_start_command start; + struct afe_audioif_config_command config; + int ret = 0; + + if (!afe_config) { + pr_aud_err("%s: Error, no configuration data\n", __func__); + ret = -EINVAL; + return ret; + } + + pr_aud_info("%s: %d %d\n", __func__, port_id, rate); + + if (this_afe.apr == NULL) { + this_afe.apr = apr_register("ADSP", "AFE", afe_callback, + 0xFFFFFFFF, &this_afe); + pr_aud_info("%s: Register AFE\n", __func__); + if (this_afe.apr == NULL) { + pr_aud_err("%s: Unable to register AFE\n", __func__); + ret = -ENODEV; + return ret; + } + } + + config.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + config.hdr.pkt_size = sizeof(config); + config.hdr.src_port = 0; + config.hdr.dest_port = 0; + config.hdr.token = 0; + config.hdr.opcode = AFE_PORT_AUDIO_IF_CONFIG; + + if (afe_validate_port(port_id) < 0) { + + pr_aud_err("%s: Failed : Invalid Port id = %d\n", __func__, + port_id); + ret = -EINVAL; + goto fail_cmd; + } + + config.port_id = port_id; + config.port = *afe_config; + + atomic_set(&this_afe.state, 1); + ret = apr_send_pkt(this_afe.apr, (uint32_t *) &config); + if (ret < 0) { + pr_aud_err("%s: AFE enable for port %d failed\n", __func__, + port_id); + ret = -EINVAL; + goto fail_cmd; + } + + ret = wait_event_timeout(this_afe.wait, + (atomic_read(&this_afe.state) == 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_aud_err("%s: wait_event timeout\n", __func__); + ret = -EINVAL; + goto fail_cmd; + } + + start.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + start.hdr.pkt_size = sizeof(start); + start.hdr.src_port = 0; + start.hdr.dest_port = 0; + start.hdr.token = 0; + start.hdr.opcode = AFE_PORT_CMD_START; + start.port_id = port_id; + start.gain = 0x2000; + start.sample_rate = rate; + + atomic_set(&this_afe.state, 1); + ret = apr_send_pkt(this_afe.apr, (uint32_t *) &start); + if (ret < 0) { + pr_aud_err("%s: AFE enable for port %d failed\n", __func__, + port_id); + ret = -EINVAL; + goto fail_cmd; + } + ret = wait_event_timeout(this_afe.wait, + (atomic_read(&this_afe.state) == 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_aud_err("%s: wait_event timeout\n", __func__); + ret = -EINVAL; + goto fail_cmd; + } + + if (this_afe.task != current) + this_afe.task = current; + + pr_debug("task_name = %s pid = %d\n", + this_afe.task->comm, this_afe.task->pid); + return 0; +fail_cmd: + return ret; +} + +int afe_loopback(u16 enable, u16 rx_port, u16 tx_port) +{ + struct afe_loopback_command lb_cmd; + int ret = 0; + if (this_afe.apr == NULL) { + pr_aud_err("%s:AFE is not opened\n", __func__); + ret = -1; + goto done; + } + lb_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(20), APR_PKT_VER); + lb_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(lb_cmd) - APR_HDR_SIZE); + lb_cmd.hdr.src_port = 0; + lb_cmd.hdr.dest_port = 0; + lb_cmd.hdr.token = 0; + lb_cmd.hdr.opcode = AFE_PORT_CMD_LOOPBACK; + lb_cmd.tx_port_id = tx_port; + lb_cmd.rx_port_id = rx_port; + lb_cmd.mode = 0xFFFF; + lb_cmd.enable = (enable ? 1 : 0); + atomic_set(&this_afe.state, 1); + + ret = apr_send_pkt(this_afe.apr, (uint32_t *) &lb_cmd); + if (ret < 0) { + pr_aud_err("AFE loopback failed\n"); + ret = -EINVAL; + goto done; + } + ret = wait_event_timeout(this_afe.wait, + (atomic_read(&this_afe.state) == 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_aud_err("%s: wait_event timeout\n", __func__); + ret = -EINVAL; + } +done: + return ret; +} + + +int afe_loopback_gain(u16 port_id, u16 volume) +{ + struct afe_port_cmd_set_param set_param; + int ret = 0; + + if (this_afe.apr == NULL) { + pr_aud_err("%s: AFE is not opened\n", __func__); + ret = -EPERM; + goto fail_cmd; + } + + if (afe_validate_port(port_id) < 0) { + + pr_aud_err("%s: Failed : Invalid Port id = %d\n", __func__, + port_id); + ret = -EINVAL; + goto fail_cmd; + } + + /* RX ports numbers are even .TX ports numbers are odd. */ + if (port_id % 2 == 0) { + pr_aud_err("%s: Failed : afe loopback gain only for TX ports." + " port_id %d\n", __func__, port_id); + ret = -EINVAL; + goto fail_cmd; + } + + pr_debug("%s: %d %hX\n", __func__, port_id, volume); + + set_param.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + set_param.hdr.pkt_size = sizeof(set_param); + set_param.hdr.src_port = 0; + set_param.hdr.dest_port = 0; + set_param.hdr.token = 0; + set_param.hdr.opcode = AFE_PORT_CMD_SET_PARAM; + + set_param.port_id = port_id; + set_param.payload_size = sizeof(struct afe_param_payload); + set_param.payload_address = 0; + + set_param.payload.module_id = AFE_MODULE_ID_PORT_INFO; + set_param.payload.param_id = AFE_PARAM_ID_LOOPBACK_GAIN; + set_param.payload.param_size = sizeof(struct afe_param_loopback_gain); + set_param.payload.reserved = 0; + + set_param.payload.param.loopback_gain.gain = volume; + set_param.payload.param.loopback_gain.reserved = 0; + + atomic_set(&this_afe.state, 1); + ret = apr_send_pkt(this_afe.apr, (uint32_t *) &set_param); + if (ret < 0) { + pr_aud_err("%s: AFE param set failed for port %d\n", + __func__, port_id); + ret = -EINVAL; + goto fail_cmd; + } + + ret = wait_event_timeout(this_afe.wait, + (atomic_read(&this_afe.state) == 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (ret < 0) { + pr_aud_err("%s: wait_event timeout\n", __func__); + ret = -EINVAL; + goto fail_cmd; + } + return 0; +fail_cmd: + return ret; +} + +int afe_start_pseudo_port(u16 port_id) +{ + int ret = 0; + struct afe_pseudoport_start_command start; + + pr_aud_info("%s: port_id=%d\n", __func__, port_id); + + if (this_afe.apr == NULL) { + this_afe.apr = apr_register("ADSP", "AFE", afe_callback, + 0xFFFFFFFF, &this_afe); + pr_aud_info("%s: Register AFE\n", __func__); + if (this_afe.apr == NULL) { + pr_aud_err("%s: Unable to register AFE\n", __func__); + ret = -ENODEV; + return ret; + } + } + + start.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + start.hdr.pkt_size = sizeof(start); + start.hdr.src_port = 0; + start.hdr.dest_port = 0; + start.hdr.token = 0; + start.hdr.opcode = AFE_PSEUDOPORT_CMD_START; + start.port_id = port_id; + start.timing = 1; + + atomic_set(&this_afe.state, 1); + ret = apr_send_pkt(this_afe.apr, (uint32_t *) &start); + if (ret < 0) { + pr_aud_err("%s: AFE enable for port %d failed %d\n", + __func__, port_id, ret); + ret = -EINVAL; + return ret; + } + + ret = wait_event_timeout(this_afe.wait, + (atomic_read(&this_afe.state) == 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_aud_err("%s: wait_event timeout\n", __func__); + ret = -EINVAL; + return ret; + } + + return 0; +} + +int afe_stop_pseudo_port(u16 port_id) +{ + int ret = 0; + struct afe_pseudoport_stop_command stop; + + pr_aud_info("%s: port_id=%d\n", __func__, port_id); + + if (this_afe.apr == NULL) { + pr_aud_err("%s: AFE is already closed\n", __func__); + ret = -EINVAL; + return ret; + } + + stop.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + stop.hdr.pkt_size = sizeof(stop); + stop.hdr.src_port = 0; + stop.hdr.dest_port = 0; + stop.hdr.token = 0; + stop.hdr.opcode = AFE_PSEUDOPORT_CMD_STOP; + stop.port_id = port_id; + stop.reserved = 0; + + atomic_set(&this_afe.state, 1); + ret = apr_send_pkt(this_afe.apr, (uint32_t *) &stop); + if (ret < 0) { + pr_aud_err("%s: AFE close failed %d\n", __func__, ret); + ret = -EINVAL; + return ret; + } + + ret = wait_event_timeout(this_afe.wait, + (atomic_read(&this_afe.state) == 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_aud_err("%s: wait_event timeout\n", __func__); + ret = -EINVAL; + return ret; + } + + return 0; +} + + +#ifdef CONFIG_DEBUG_FS +static struct dentry *debugfs_afelb; +static struct dentry *debugfs_afelb_gain; + +static int afe_debug_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + pr_aud_info("debug intf %s\n", (char *) file->private_data); + return 0; +} + +static int afe_get_parameters(char *buf, long int *param1, int num_of_par) +{ + char *token; + int base, cnt; + + token = strsep(&buf, " "); + + for (cnt = 0; cnt < num_of_par; cnt++) { + if (token != NULL) { + if ((token[1] == 'x') || (token[1] == 'X')) + base = 16; + else + base = 10; + + if (strict_strtoul(token, base, ¶m1[cnt]) != 0) + return -EINVAL; + + token = strsep(&buf, " "); + } else + return -EINVAL; + } + return 0; +} +#define AFE_LOOPBACK_ON (1) +#define AFE_LOOPBACK_OFF (0) +static ssize_t afe_debug_write(struct file *filp, + const char __user *ubuf, size_t cnt, loff_t *ppos) +{ + char *lb_str = filp->private_data; + char lbuf[32]; + int rc; + unsigned long param[5]; + + if (cnt > sizeof(lbuf) - 1) + return -EINVAL; + + rc = copy_from_user(lbuf, ubuf, cnt); + if (rc) + return -EFAULT; + + lbuf[cnt] = '\0'; + + if (!strcmp(lb_str, "afe_loopback")) { + rc = afe_get_parameters(lbuf, param, 3); + if (!rc) { + pr_aud_info("%s %lu %lu %lu\n", lb_str, param[0], param[1], + param[2]); + + if ((param[0] != AFE_LOOPBACK_ON) && (param[0] != + AFE_LOOPBACK_OFF)) { + pr_aud_err("%s: Error, parameter 0 incorrect\n", + __func__); + rc = -EINVAL; + goto afe_error; + } + if ((afe_validate_port(param[1]) < 0) || + (afe_validate_port(param[2])) < 0) { + pr_aud_err("%s: Error, invalid afe port\n", + __func__); + } + if (this_afe.apr == NULL) { + pr_aud_err("%s: Error, AFE not opened\n", __func__); + rc = -EINVAL; + } else { + rc = afe_loopback(param[0], param[1], param[2]); + } + } else { + pr_aud_err("%s: Error, invalid parameters\n", __func__); + rc = -EINVAL; + } + + } else if (!strcmp(lb_str, "afe_loopback_gain")) { + rc = afe_get_parameters(lbuf, param, 2); + if (!rc) { + pr_aud_info("%s %lu %lu\n", lb_str, param[0], param[1]); + + if (afe_validate_port(param[0]) < 0) { + pr_aud_err("%s: Error, invalid afe port\n", + __func__); + rc = -EINVAL; + goto afe_error; + } + + if (param[1] < 0 || param[1] > 100) { + pr_aud_err("%s: Error, volume shoud be 0 to 100" + " percentage param = %lu\n", + __func__, param[1]); + rc = -EINVAL; + goto afe_error; + } + + param[1] = (Q6AFE_MAX_VOLUME * param[1]) / 100; + + if (this_afe.apr == NULL) { + pr_aud_err("%s: Error, AFE not opened\n", __func__); + rc = -EINVAL; + } else { + rc = afe_loopback_gain(param[0], param[1]); + } + } else { + pr_aud_err("%s: Error, invalid parameters\n", __func__); + rc = -EINVAL; + } + } + +afe_error: + if (rc == 0) + rc = cnt; + else + pr_aud_err("%s: rc = %d\n", __func__, rc); + + return rc; +} + +static const struct file_operations afe_debug_fops = { + .open = afe_debug_open, + .write = afe_debug_write +}; +#endif +int afe_sidetone(u16 tx_port_id, u16 rx_port_id, u16 enable, uint16_t gain) +{ + struct afe_port_sidetone_command cmd_sidetone; + int ret = 0; + + pr_aud_info("%s: tx_port_id:%d rx_port_id:%d enable:%d gain:%d\n", __func__, + tx_port_id, rx_port_id, enable, gain); + cmd_sidetone.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + cmd_sidetone.hdr.pkt_size = sizeof(cmd_sidetone); + cmd_sidetone.hdr.src_port = 0; + cmd_sidetone.hdr.dest_port = 0; + cmd_sidetone.hdr.token = 0; + cmd_sidetone.hdr.opcode = AFE_PORT_CMD_SIDETONE_CTL; + cmd_sidetone.tx_port_id = tx_port_id; + cmd_sidetone.rx_port_id = rx_port_id; + cmd_sidetone.gain = gain; + cmd_sidetone.enable = enable; + + atomic_set(&this_afe.state, 1); + ret = apr_send_pkt(this_afe.apr, (uint32_t *) &cmd_sidetone); + if (ret < 0) { + pr_aud_err("%s: AFE sidetone failed for tx_port:%d rx_port:%d\n", + __func__, tx_port_id, rx_port_id); + ret = -EINVAL; + goto fail_cmd; + } + + ret = wait_event_timeout(this_afe.wait, + (atomic_read(&this_afe.state) == 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (ret < 0) { + pr_aud_err("%s: wait_event timeout\n", __func__); + ret = -EINVAL; + goto fail_cmd; + } + return 0; +fail_cmd: + return ret; +} + +int afe_close(int port_id) +{ + struct afe_port_stop_command stop; + int ret = 0; + + if (this_afe.apr == NULL) { + pr_aud_err("AFE is already closed\n"); + ret = -EINVAL; + goto fail_cmd; + } + pr_debug("%s: port_id=%d\n", __func__, port_id); + stop.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + stop.hdr.pkt_size = sizeof(stop); + stop.hdr.src_port = 0; + stop.hdr.dest_port = 0; + stop.hdr.token = 0; + stop.hdr.opcode = AFE_PORT_CMD_STOP; + stop.port_id = port_id; + stop.reserved = 0; + + atomic_set(&this_afe.state, 1); + ret = apr_send_pkt(this_afe.apr, (uint32_t *) &stop); + + if (ret < 0) { + pr_aud_err("AFE close failed\n"); + ret = -EINVAL; + goto fail_cmd; + } + + ret = wait_event_timeout(this_afe.wait, + (atomic_read(&this_afe.state) == 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_aud_err("%s: wait_event timeout\n", __func__); + ret = -EINVAL; + goto fail_cmd; + } +fail_cmd: + return ret; +} + +static int __init afe_init(void) +{ + pr_aud_info("%s:\n", __func__); + init_waitqueue_head(&this_afe.wait); + atomic_set(&this_afe.state, 0); + this_afe.apr = NULL; +#ifdef CONFIG_DEBUG_FS + debugfs_afelb = debugfs_create_file("afe_loopback", + 0644, NULL, (void *) "afe_loopback", + &afe_debug_fops); + + debugfs_afelb_gain = debugfs_create_file("afe_loopback_gain", + 0644, NULL, (void *) "afe_loopback_gain", + &afe_debug_fops); + + +#endif + return 0; +} + +static void __exit afe_exit(void) +{ +#ifdef CONFIG_DEBUG_FS + if (debugfs_afelb) + debugfs_remove(debugfs_afelb); + if (debugfs_afelb_gain) + debugfs_remove(debugfs_afelb_gain); +#endif +} + +device_initcall(afe_init); +__exitcall(afe_exit); diff --git a/arch/arm/mach-msm/qdsp6v3/q6asm.c b/arch/arm/mach-msm/qdsp6v3/q6asm.c new file mode 100644 index 00000000000..33004bfc4a1 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v3/q6asm.c @@ -0,0 +1,2548 @@ + +/* + * Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + * 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 +#include "rtac.h" + +#define TRUE 0x01 +#define FALSE 0x00 +#define READDONE_IDX_STATUS 0 +#define READDONE_IDX_BUFFER 1 +#define READDONE_IDX_SIZE 2 +#define READDONE_IDX_OFFSET 3 +#define READDONE_IDX_MSW_TS 4 +#define READDONE_IDX_LSW_TS 5 +#define READDONE_IDX_FLAGS 6 +#define READDONE_IDX_NUMFRAMES 7 +#define READDONE_IDX_ID 8 + +static DEFINE_MUTEX(session_lock); + +/* session id: 0 reserved */ +static struct audio_client *session[SESSION_MAX+1]; +static int32_t q6asm_mmapcallback(struct apr_client_data *data, void *priv); +static int32_t q6asm_callback(struct apr_client_data *data, void *priv); +static void q6asm_add_hdr(struct audio_client *ac, struct apr_hdr *hdr, + uint32_t pkt_size, uint32_t cmd_flg); +static void q6asm_add_hdr_async(struct audio_client *ac, struct apr_hdr *hdr, + uint32_t pkt_size, uint32_t cmd_flg); +static int q6asm_memory_map_regions(struct audio_client *ac, int dir, + uint32_t bufsz, uint32_t bufcnt); +static int q6asm_memory_unmap_regions(struct audio_client *ac, int dir, + uint32_t bufsz, uint32_t bufcnt); + +static void q6asm_reset_buf_state(struct audio_client *ac); + +struct asm_mmap { + atomic_t ref_cnt; + atomic_t cmd_state; + wait_queue_head_t cmd_wait; + void *apr; +}; + +static struct asm_mmap this_mmap; + +static int q6asm_session_alloc(struct audio_client *ac) +{ + int n; + mutex_lock(&session_lock); + for (n = 1; n <= SESSION_MAX; n++) { + if (!session[n]) { + session[n] = ac; + mutex_unlock(&session_lock); + return n; + } + } + mutex_unlock(&session_lock); + return -ENOMEM; +} + +static void q6asm_session_free(struct audio_client *ac) +{ + pr_debug("%s: sessionid[%d]\n", __func__, ac->session); + mutex_lock(&session_lock); + session[ac->session] = 0; + mutex_unlock(&session_lock); + ac->session = 0; + return; +} + +int q6asm_audio_client_buf_free(unsigned int dir, + struct audio_client *ac) +{ + struct audio_port_data *port; + int cnt = 0; + int rc = 0; + pr_debug("%s: Session id %d\n", __func__, ac->session); + mutex_lock(&ac->cmd_lock); + if (ac->io_mode == SYNC_IO_MODE) { + port = &ac->port[dir]; + if (!port->buf) { + mutex_unlock(&ac->cmd_lock); + return 0; + } + cnt = port->max_buf_cnt - 1; + + if (cnt >= 0) { + rc = q6asm_memory_unmap_regions(ac, dir, + port->buf[0].size, + port->max_buf_cnt); + if (rc < 0) + pr_aud_err("%s CMD Memory_unmap_regions failed\n", + __func__); + } + + while (cnt >= 0) { + if (port->buf[cnt].data) { + pr_debug("data[%p]phys[%p][%p] cnt[%d]\n", + (void *)port->buf[cnt].data, + (void *)port->buf[cnt].phys, + (void *)&port->buf[cnt].phys, cnt); + dma_free_coherent(NULL, port->buf[cnt].size, + port->buf[cnt].data, + port->buf[cnt].phys); + port->buf[cnt].data = NULL; + port->buf[cnt].phys = 0; + --(port->max_buf_cnt); + } + --cnt; + } + kfree(port->buf); + port->buf = NULL; + } + mutex_unlock(&ac->cmd_lock); + return 0; +} + +int q6asm_audio_client_buf_free_contiguous(unsigned int dir, + struct audio_client *ac) +{ + struct audio_port_data *port; + int cnt = 0; + int rc = 0; + pr_debug("%s: Session id %d\n", __func__, ac->session); + mutex_lock(&ac->cmd_lock); + if (ac->io_mode == SYNC_IO_MODE) { + port = &ac->port[dir]; + if (!port->buf) { + mutex_unlock(&ac->cmd_lock); + return 0; + } + cnt = port->max_buf_cnt - 1; + + if (cnt >= 0) { + rc = q6asm_memory_unmap(ac, port->buf[0].phys, dir); + if (rc < 0) + pr_aud_err("%s CMD Memory_unmap_regions failed\n", + __func__); + } + + if (port->buf[0].data) { + pr_debug("%s:data[%p]phys[%p][%p] cnt[%d]\n", + __func__, + (void *)port->buf[0].data, + (void *)port->buf[0].phys, + (void *)&port->buf[0].phys, cnt); + dma_free_coherent(NULL, + port->buf[0].size * port->max_buf_cnt, + port->buf[0].data, + port->buf[0].phys); + } + while (cnt >= 0) { + port->buf[cnt].data = NULL; + port->buf[cnt].phys = 0; + cnt--; + } + port->max_buf_cnt = 0; + kfree(port->buf); + port->buf = NULL; + } + mutex_unlock(&ac->cmd_lock); + return 0; +} + +void q6asm_audio_client_free(struct audio_client *ac) +{ + int loopcnt; + struct audio_port_data *port; + if (!ac || !ac->session) + return; + pr_debug("%s: Session id %d\n", __func__, ac->session); + if (ac->io_mode == SYNC_IO_MODE) { + for (loopcnt = 0; loopcnt <= OUT; loopcnt++) { + port = &ac->port[loopcnt]; + if (!port->buf) + continue; + pr_debug("%s:loopcnt = %d\n", __func__, loopcnt); + q6asm_audio_client_buf_free(loopcnt, ac); + } + } + + apr_deregister(ac->apr); + q6asm_session_free(ac); + + pr_debug("%s: APR De-Register\n", __func__); + if (atomic_read(&this_mmap.ref_cnt) <= 0) { + pr_aud_err("%s: APR Common Port Already Closed\n", __func__); + goto done; + } + + atomic_dec(&this_mmap.ref_cnt); + if (atomic_read(&this_mmap.ref_cnt) == 0) { + apr_deregister(this_mmap.apr); + pr_debug("%s:APR De-Register common port\n", __func__); + } +done: + kfree(ac); + return; +} + +int q6asm_set_io_mode(struct audio_client *ac, uint32_t mode) +{ + if (ac == NULL) { + pr_aud_err("%s APR handle NULL\n", __func__); + return -EINVAL; + } + if ((mode == ASYNC_IO_MODE) || (mode == SYNC_IO_MODE)) { + ac->io_mode = mode; + pr_debug("%s:Set Mode to %d\n", __func__, ac->io_mode); + return 0; + } else { + pr_aud_err("%s:Not an valid IO Mode:%d\n", __func__, ac->io_mode); + return -EINVAL; + } +} + +struct audio_client *q6asm_audio_client_alloc(app_cb cb, void *priv) +{ + struct audio_client *ac; + int n; + int lcnt = 0; + + ac = kzalloc(sizeof(struct audio_client), GFP_KERNEL); + if (!ac) + return NULL; + n = q6asm_session_alloc(ac); + if (n <= 0) + goto fail_session; + ac->session = n; + ac->cb = cb; + ac->priv = priv; + ac->io_mode = SYNC_IO_MODE; + ac->apr = apr_register("ADSP", "ASM", \ + (apr_fn)q6asm_callback,\ + ((ac->session) << 8 | 0x0001),\ + ac); + + if (ac->apr == NULL) { + pr_aud_err("%s Registration with APR failed\n", __func__); + goto fail; + } +#ifdef CONFIG_MSM8X60_RTAC + rtac_set_asm_handle(n, ac->apr); +#endif + pr_debug("%s Registering the common port with APR\n", __func__); + if (atomic_read(&this_mmap.ref_cnt) == 0) { + this_mmap.apr = apr_register("ADSP", "ASM", \ + (apr_fn)q6asm_mmapcallback,\ + 0x0FFFFFFFF, &this_mmap); + if (this_mmap.apr == NULL) { + pr_debug("%s Unable to register \ + APR ASM common port \n", __func__); + goto fail; + } + } + + atomic_inc(&this_mmap.ref_cnt); + init_waitqueue_head(&ac->cmd_wait); + init_waitqueue_head(&ac->time_wait); + atomic_set(&ac->time_flag, 1); + mutex_init(&ac->cmd_lock); + for (lcnt = 0; lcnt <= OUT; lcnt++) { + mutex_init(&ac->port[lcnt].lock); + spin_lock_init(&ac->port[lcnt].dsp_lock); + } + atomic_set(&ac->cmd_state, 0); + + pr_debug("%s: session[%d]\n", __func__, ac->session); + + return ac; +fail: + q6asm_audio_client_free(ac); + return NULL; +fail_session: + kfree(ac); + return NULL; +} + +int q6asm_audio_client_buf_alloc(unsigned int dir, + struct audio_client *ac, + unsigned int bufsz, + unsigned int bufcnt) +{ + int cnt = 0; + int rc = 0; + struct audio_buffer *buf; + + if (!(ac) || ((dir != IN) && (dir != OUT))) + return -EINVAL; + + pr_debug("%s: session[%d]bufsz[%d]bufcnt[%d]\n", __func__, ac->session, + bufsz, bufcnt); + + if (ac->session <= 0 || ac->session > 8) + goto fail; + + if (ac->io_mode == SYNC_IO_MODE) { + if (ac->port[dir].buf) { + pr_debug("%s: buffer already allocated\n", __func__); + return 0; + } + mutex_lock(&ac->cmd_lock); + buf = kzalloc(((sizeof(struct audio_buffer))*bufcnt), + GFP_KERNEL); + + if (!buf) { + mutex_unlock(&ac->cmd_lock); + goto fail; + } + + ac->port[dir].buf = buf; + + while (cnt < bufcnt) { + if (bufsz > 0) { + buf[cnt].data = dma_alloc_coherent(NULL, bufsz, + &buf[cnt].phys, + GFP_KERNEL); + if (!buf[cnt].data) { + pr_aud_err("%s Buf alloc failed for" + " size=%d\n", __func__, + bufsz); + mutex_unlock(&ac->cmd_lock); + goto fail; + } + buf[cnt].used = 1; + buf[cnt].size = bufsz; + buf[cnt].actual_size = bufsz; + pr_debug("%s data[%p]phys[%p][%p]\n", __func__, + (void *)buf[cnt].data, + (void *)buf[cnt].phys, + (void *)&buf[cnt].phys); + } + cnt++; + } + ac->port[dir].max_buf_cnt = cnt; + + mutex_unlock(&ac->cmd_lock); + rc = q6asm_memory_map_regions(ac, dir, bufsz, cnt); + if (rc < 0) { + pr_aud_err("%s:CMD Memory_map_regions failed\n", __func__); + goto fail; + } + } + return 0; +fail: + q6asm_audio_client_buf_free(dir, ac); + return -EINVAL; +} + +int q6asm_audio_client_buf_alloc_contiguous(unsigned int dir, + struct audio_client *ac, + unsigned int bufsz, + unsigned int bufcnt) +{ + int cnt = 0; + int rc = 0; + struct audio_buffer *buf; + + if (!(ac) || ((dir != IN) && (dir != OUT))) + return -EINVAL; + + pr_debug("%s: session[%d]bufsz[%d]bufcnt[%d]\n", + __func__, ac->session, + bufsz, bufcnt); + + if (ac->session <= 0 || ac->session > 8) + goto fail; + + if (ac->io_mode == SYNC_IO_MODE) { + if (ac->port[dir].buf) { + pr_debug("%s: buffer already allocated\n", __func__); + return 0; + } + mutex_lock(&ac->cmd_lock); + buf = kzalloc(((sizeof(struct audio_buffer))*bufcnt), + GFP_KERNEL); + + if (!buf) { + mutex_unlock(&ac->cmd_lock); + goto fail; + } + + ac->port[dir].buf = buf; + + buf[0].data = dma_alloc_coherent(NULL, bufsz * bufcnt, + &buf[0].phys, GFP_KERNEL); + buf[0].used = dir ^ 1; + buf[0].size = bufsz; + buf[0].actual_size = bufsz; + cnt = 1; + while (cnt < bufcnt) { + if (bufsz > 0) { + buf[cnt].data = buf[0].data + (cnt * bufsz); + buf[cnt].phys = buf[0].phys + (cnt * bufsz); + + if (!buf[cnt].data) { + pr_aud_err("%s Buf alloc failed\n", + __func__); + mutex_unlock(&ac->cmd_lock); + goto fail; + } + buf[cnt].used = dir ^ 1; + buf[cnt].size = bufsz; + buf[cnt].actual_size = bufsz; + pr_debug("%s data[%p]phys[%p][%p]\n", __func__, + (void *)buf[cnt].data, + (void *)buf[cnt].phys, + (void *)&buf[cnt].phys); + } + cnt++; + } + ac->port[dir].max_buf_cnt = cnt; + + mutex_unlock(&ac->cmd_lock); + rc = q6asm_memory_map(ac, buf[0].phys, dir, bufsz, cnt); + if (rc < 0) { + pr_aud_err("%s:CMD Memory_map_regions failed\n", __func__); + goto fail; + } + } + return 0; +fail: + q6asm_audio_client_buf_free_contiguous(dir, ac); + return -EINVAL; +} + +static int32_t q6asm_mmapcallback(struct apr_client_data *data, void *priv) +{ + uint32_t token; + uint32_t *payload = data->payload; + + if (data->opcode == RESET_EVENTS) { + pr_debug("%s: Reset event is received: %d %d apr[%p]\n", + __func__, + data->reset_event, + data->reset_proc, + this_mmap.apr); + apr_reset(this_mmap.apr); + this_mmap.apr = NULL; + atomic_set(&this_mmap.cmd_state, 0); + return 0; + } + + pr_debug("%s:ptr0[0x%x]ptr1[0x%x]opcode[0x%x]" + "token[0x%x]payload_s[%d] src[%d] dest[%d]\n", __func__, + payload[0], payload[1], data->opcode, data->token, + data->payload_size, data->src_port, data->dest_port); + + if (data->opcode == APR_BASIC_RSP_RESULT) { + token = data->token; + switch (payload[0]) { + case ASM_SESSION_CMD_MEMORY_MAP: + case ASM_SESSION_CMD_MEMORY_UNMAP: + case ASM_SESSION_CMD_MEMORY_MAP_REGIONS: + case ASM_SESSION_CMD_MEMORY_UNMAP_REGIONS: + pr_debug("%s:command[0x%x]success [0x%x]\n", + __func__, payload[0], payload[1]); + if (atomic_read(&this_mmap.cmd_state)) { + atomic_set(&this_mmap.cmd_state, 0); + wake_up(&this_mmap.cmd_wait); + } + break; + default: + pr_debug("%s:command[0x%x] not expecting rsp\n", + __func__, payload[0]); + break; + } + } + return 0; +} + + +static int32_t q6asm_callback(struct apr_client_data *data, void *priv) +{ + int i = 0; + struct audio_client *ac = (struct audio_client *)priv; + uint32_t token; + unsigned long dsp_flags; + uint32_t *payload; + + + if ((ac == NULL) || (data == NULL)) { + pr_aud_err("ac or priv NULL\n"); + return -EINVAL; + } + if (ac->session <= 0 || ac->session > 8) { + pr_aud_err("%s:Session ID is invalid, session = %d\n", __func__, + ac->session); + return -EINVAL; + } + + payload = data->payload; + + if (data->opcode == RESET_EVENTS) { + pr_debug("q6asm_callback: Reset event is received: %d %d apr[%p]\n", + data->reset_event, data->reset_proc, ac->apr); + apr_reset(ac->apr); + return 0; + } + + pr_debug("%s: session[%d]opcode[0x%x] \ + token[0x%x]payload_s[%d] src[%d] dest[%d]\n", __func__, + ac->session, data->opcode, + data->token, data->payload_size, data->src_port, + data->dest_port); + + if (data->opcode == APR_BASIC_RSP_RESULT) { + token = data->token; + switch (payload[0]) { + case ASM_STREAM_CMD_SET_PP_PARAMS: +#ifdef CONFIG_MSM8X60_RTAC + if (rtac_make_asm_callback(ac->session, payload, + data->payload_size)) + break; +#endif + case ASM_SESSION_CMD_PAUSE: + case ASM_DATA_CMD_EOS: + case ASM_STREAM_CMD_CLOSE: + case ASM_STREAM_CMD_FLUSH: + case ASM_SESSION_CMD_RUN: + case ASM_SESSION_CMD_REGISTER_FOR_TX_OVERFLOW_EVENTS: + pr_debug("%s:Payload = [0x%x]\n", __func__, payload[0]); + if (token != ac->session) { + pr_aud_err("%s:Invalid session[%d] rxed expected[%d]", + __func__, token, ac->session); + return -EINVAL; + } + case ASM_STREAM_CMD_OPEN_READ: + case ASM_STREAM_CMD_OPEN_WRITE: + case ASM_STREAM_CMD_OPEN_READWRITE: + case ASM_DATA_CMD_MEDIA_FORMAT_UPDATE: + case ASM_STREAM_CMD_SET_ENCDEC_PARAM: + if (atomic_read(&ac->cmd_state)) { + atomic_set(&ac->cmd_state, 0); + wake_up(&ac->cmd_wait); + } + if (ac->cb) + ac->cb(data->opcode, data->token, + (uint32_t *)data->payload, ac->priv); + break; + default: + pr_debug("%s:command[0x%x] not expecting rsp\n", + __func__, payload[0]); + break; + } + return 0; + } + + switch (data->opcode) { + case ASM_DATA_EVENT_WRITE_DONE:{ + struct audio_port_data *port = &ac->port[IN]; + pr_debug("%s: Rxed opcode[0x%x] status[0x%x] token[%d]", + __func__, payload[0], payload[1], + data->token); + if (ac->io_mode == SYNC_IO_MODE) { + if (port->buf == NULL) { + pr_aud_err("%s: Unexpected Write Done\n", + __func__); + return -EINVAL; + } + spin_lock_irqsave(&port->dsp_lock, dsp_flags); + if (port->buf[data->token].phys != + payload[0]) { + pr_aud_err("Buf expected[%p]rxed[%p]\n",\ + (void *)port->buf[data->token].phys,\ + (void *)payload[0]); + spin_unlock_irqrestore(&port->dsp_lock, + dsp_flags); + return -EINVAL; + } + token = data->token; + port->buf[token].used = 1; + spin_unlock_irqrestore(&port->dsp_lock, dsp_flags); + for (i = 0; i < port->max_buf_cnt; i++) + pr_debug("%d ", port->buf[i].used); + + } + break; + } +#ifdef CONFIG_MSM8X60_RTAC + case ASM_STREAM_CMDRSP_GET_PP_PARAMS: + rtac_make_asm_callback(ac->session, payload, + data->payload_size); + break; +#endif + case ASM_DATA_EVENT_READ_DONE:{ + + struct audio_port_data *port = &ac->port[OUT]; + + pr_debug("%s:R-D: status=%d buff_add=%x act_size=%d offset=%d\n", + __func__, payload[READDONE_IDX_STATUS], + payload[READDONE_IDX_BUFFER], + payload[READDONE_IDX_SIZE], + payload[READDONE_IDX_OFFSET]); + pr_debug("%s:R-D:msw_ts=%d lsw_ts=%d flags=%d id=%d num=%d\n", + __func__, payload[READDONE_IDX_MSW_TS], + payload[READDONE_IDX_LSW_TS], + payload[READDONE_IDX_FLAGS], + payload[READDONE_IDX_ID], + payload[READDONE_IDX_NUMFRAMES]); + + if (ac->io_mode == SYNC_IO_MODE) { + if (port->buf == NULL) { + pr_aud_err("%s: Unexpected Write Done\n", __func__); + return -EINVAL; + } + spin_lock_irqsave(&port->dsp_lock, dsp_flags); + token = data->token; + port->buf[token].used = 0; + if (port->buf[token].phys != + payload[READDONE_IDX_BUFFER]) { + pr_aud_err("Buf expected[%p]rxed[%p]\n",\ + (void *)port->buf[token].phys,\ + (void *)payload[READDONE_IDX_BUFFER]); + spin_unlock_irqrestore(&port->dsp_lock, + dsp_flags); + break; + } + port->buf[token].actual_size = + payload[READDONE_IDX_SIZE]; + spin_unlock_irqrestore(&port->dsp_lock, dsp_flags); + } + break; + } + case ASM_DATA_EVENT_EOS: + case ASM_DATA_CMDRSP_EOS: + pr_debug("%s:EOS ACK received: rxed opcode[0x%x]\n", + __func__, data->opcode); + break; + case ASM_STREAM_CMDRSP_GET_ENCDEC_PARAM: + break; + case ASM_SESSION_EVENT_TX_OVERFLOW: + pr_aud_err("ASM_SESSION_EVENT_TX_OVERFLOW\n"); + break; + case ASM_SESSION_CMDRSP_GET_SESSION_TIME: + pr_debug("%s: ASM_SESSION_CMDRSP_GET_SESSION_TIME, " + "payload[0] = %d, payload[1] = %d, " + "payload[2] = %d\n", __func__, + payload[0], payload[1], payload[2]); + ac->time_stamp = (uint64_t)(((uint64_t)payload[1] << 32) | + payload[2]); + if (atomic_read(&ac->time_flag)) { + atomic_set(&ac->time_flag, 0); + wake_up(&ac->time_wait); + } + break; + + } + if (ac->cb) + ac->cb(data->opcode, data->token, + data->payload, ac->priv); + + return 0; +} + +void *q6asm_is_cpu_buf_avail(int dir, struct audio_client *ac, uint32_t *size, + uint32_t *index) +{ + void *data; + unsigned char idx; + struct audio_port_data *port; + + if (!ac || ((dir != IN) && (dir != OUT))) + return NULL; + + if (ac->io_mode == SYNC_IO_MODE) { + port = &ac->port[dir]; + + mutex_lock(&port->lock); + idx = port->cpu_buf; + if (port->buf == NULL) { + pr_debug("%s:Buffer pointer null\n", __func__); + return NULL; + } + /* dir 0: used = 0 means buf in use + dir 1: used = 1 means buf in use */ + if (port->buf[idx].used == dir) { + /* To make it more robust, we could loop and get the + next avail buf, its risky though */ + pr_debug("%s:Next buf idx[0x%x] not available,\ + dir[%d]\n", __func__, idx, dir); + mutex_unlock(&port->lock); + return NULL; + } + *size = port->buf[idx].actual_size; + *index = port->cpu_buf; + data = port->buf[idx].data; + pr_debug("%s:session[%d]index[%d] data[%p]size[%d]\n", + __func__, + ac->session, + port->cpu_buf, + data, *size); + /* By default increase the cpu_buf cnt + user accesses this function,increase cpu + buf(to avoid another api)*/ + port->buf[idx].used = dir; + port->cpu_buf = ((port->cpu_buf + 1) & (port->max_buf_cnt - 1)); + mutex_unlock(&port->lock); + return data; + } + return NULL; +} + +int q6asm_is_dsp_buf_avail(int dir, struct audio_client *ac) +{ + int ret = -1; + struct audio_port_data *port; + uint32_t idx; + + if (!ac || (dir != OUT)) + return ret; + + if (ac->io_mode == SYNC_IO_MODE) { + port = &ac->port[dir]; + + mutex_lock(&port->lock); + idx = port->dsp_buf; + + if (port->buf[idx].used == (dir ^ 1)) { + /* To make it more robust, we could loop and get the + next avail buf, its risky though */ + pr_aud_err("Next buf idx[0x%x] not available, dir[%d]\n", + idx, dir); + mutex_unlock(&port->lock); + return ret; + } + pr_debug("%s: session[%d]dsp_buf=%d cpu_buf=%d\n", __func__, + ac->session, port->dsp_buf, port->cpu_buf); + ret = ((port->dsp_buf != port->cpu_buf) ? 0 : -1); + mutex_unlock(&port->lock); + } + return ret; +} + +static void q6asm_add_hdr(struct audio_client *ac, struct apr_hdr *hdr, + uint32_t pkt_size, uint32_t cmd_flg) +{ + pr_debug("%s:session=%d pkt size=%d cmd_flg=%d\n", __func__, pkt_size, + cmd_flg, ac->session); + mutex_lock(&ac->cmd_lock); + hdr->hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, \ + APR_HDR_LEN(sizeof(struct apr_hdr)),\ + APR_PKT_VER); + hdr->src_svc = ((struct apr_svc *)ac->apr)->id; + hdr->src_domain = APR_DOMAIN_APPS; + hdr->dest_svc = APR_SVC_ASM; + hdr->dest_domain = APR_DOMAIN_ADSP; + hdr->src_port = ((ac->session << 8) & 0xFF00) | 0x01; + hdr->dest_port = ((ac->session << 8) & 0xFF00) | 0x01; + if (cmd_flg) { + hdr->token = ac->session; + atomic_set(&ac->cmd_state, 1); + } + hdr->pkt_size = pkt_size; + mutex_unlock(&ac->cmd_lock); + return; +} + +static void q6asm_add_mmaphdr(struct apr_hdr *hdr, uint32_t pkt_size, + uint32_t cmd_flg) +{ + pr_debug("%s:pkt size=%d cmd_flg=%d\n", __func__, pkt_size, cmd_flg); + hdr->hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, \ + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + hdr->src_port = 0; + hdr->dest_port = 0; + if (cmd_flg) { + hdr->token = 0; + atomic_set(&this_mmap.cmd_state, 1); + } + hdr->pkt_size = pkt_size; + return; +} + +int q6asm_open_read(struct audio_client *ac, + uint32_t format) +{ + int rc = 0x00; + struct asm_stream_cmd_open_read open; + + if ((ac == NULL) || (ac->apr == NULL)) { + pr_aud_err("%s: APR handle NULL\n", __func__); + return -EINVAL; + } + pr_debug("%s:session[%d]", __func__, ac->session); + + q6asm_add_hdr(ac, &open.hdr, sizeof(open), TRUE); + open.hdr.opcode = ASM_STREAM_CMD_OPEN_READ; + /* Stream prio : High, provide meta info with encoded frames */ + open.src_endpoint = ASM_END_POINT_DEVICE_MATRIX; + open.pre_proc_top = DEFAULT_POPP_TOPOLOGY; + + switch (format) { + case FORMAT_LINEAR_PCM: + open.uMode = STREAM_PRIORITY_HIGH; + open.format = LINEAR_PCM; + break; + case FORMAT_MPEG4_AAC: + open.uMode = BUFFER_META_ENABLE | STREAM_PRIORITY_HIGH; + open.format = MPEG4_AAC; + break; + case FORMAT_V13K: + open.uMode = BUFFER_META_ENABLE | STREAM_PRIORITY_HIGH; + open.format = V13K_FS; + break; + case FORMAT_EVRC: + open.uMode = BUFFER_META_ENABLE | STREAM_PRIORITY_HIGH; + open.format = EVRC_FS; + break; + case FORMAT_AMRNB: + open.uMode = BUFFER_META_ENABLE | STREAM_PRIORITY_HIGH; + open.format = AMRNB_FS; + break; + default: + pr_aud_err("Invalid format[%d]\n", format); + goto fail_cmd; + } + rc = apr_send_pkt(ac->apr, (uint32_t *) &open); + if (rc < 0) { + pr_aud_err("open failed op[0x%x]rc[%d]\n", \ + open.hdr.opcode, rc); + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) == 0), 5*HZ); + if (!rc) { + pr_aud_err("%s: timeout. waited for OPEN_WRITE rc[%d]\n", __func__, + rc); + goto fail_cmd; + } + return 0; +fail_cmd: + return -EINVAL; +} + +int q6asm_open_write(struct audio_client *ac, uint32_t format) +{ + int rc = 0x00; + struct asm_stream_cmd_open_write open; + + if ((ac == NULL) || (ac->apr == NULL)) { + pr_aud_err("%s: APR handle NULL\n", __func__); + return -EINVAL; + } + pr_debug("%s: session[%d] wr_format[0x%x]", __func__, ac->session, + format); + + q6asm_add_hdr(ac, &open.hdr, sizeof(open), TRUE); + + open.hdr.opcode = ASM_STREAM_CMD_OPEN_WRITE; + open.uMode = STREAM_PRIORITY_HIGH; + /* source endpoint : matrix */ + open.sink_endpoint = ASM_END_POINT_DEVICE_MATRIX; + open.stream_handle = 0x00; + open.post_proc_top = DEFAULT_POPP_TOPOLOGY; + + switch (format) { + case FORMAT_LINEAR_PCM: + open.format = LINEAR_PCM; + break; + case FORMAT_MPEG4_AAC: + open.format = MPEG4_AAC; + break; + case FORMAT_WMA_V9: + open.format = WMA_V9; + break; + case FORMAT_WMA_V10PRO: + open.format = WMA_V10PRO; + break; + default: + pr_aud_err("%s: Invalid format[%d]\n", __func__, format); + goto fail_cmd; + } + rc = apr_send_pkt(ac->apr, (uint32_t *) &open); + if (rc < 0) { + pr_aud_err("%s: open failed op[0x%x]rc[%d]\n", \ + __func__, open.hdr.opcode, rc); + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) == 0), 5*HZ); + if (!rc) { + pr_aud_err("%s: timeout. waited for OPEN_WRITR rc[%d]\n", __func__, + rc); + goto fail_cmd; + } + return 0; +fail_cmd: + return -EINVAL; +} + +int q6asm_open_read_write(struct audio_client *ac, + uint32_t rd_format, + uint32_t wr_format) +{ + int rc = 0x00; + struct asm_stream_cmd_open_read_write open; + + if ((ac == NULL) || (ac->apr == NULL)) { + pr_aud_err("APR handle NULL\n"); + return -EINVAL; + } + pr_debug("%s: session[%d]", __func__, ac->session); + pr_debug("wr_format[0x%x]rd_format[0x%x]", + wr_format, rd_format); + + q6asm_add_hdr(ac, &open.hdr, sizeof(open), TRUE); + open.hdr.opcode = ASM_STREAM_CMD_OPEN_READWRITE; + + open.uMode = BUFFER_META_ENABLE | STREAM_PRIORITY_NORMAL; + /* source endpoint : matrix */ + open.post_proc_top = DEFAULT_POPP_TOPOLOGY; + switch (wr_format) { + case FORMAT_LINEAR_PCM: + open.write_format = LINEAR_PCM; + break; + case FORMAT_MPEG4_AAC: + open.write_format = MPEG4_AAC; + break; + case FORMAT_WMA_V9: + open.write_format = WMA_V9; + break; + case FORMAT_WMA_V10PRO: + open.write_format = WMA_V10PRO; + break; + default: + pr_aud_err("Invalid format[%d]\n", wr_format); + goto fail_cmd; + } + + switch (rd_format) { + case FORMAT_LINEAR_PCM: + open.read_format = LINEAR_PCM; + break; + case FORMAT_MPEG4_AAC: + open.read_format = MPEG4_AAC; + break; + case FORMAT_V13K: + open.read_format = V13K_FS; + break; + case FORMAT_EVRC: + open.read_format = EVRC_FS; + break; + case FORMAT_AMRNB: + open.read_format = AMRNB_FS; + break; + default: + pr_aud_err("Invalid format[%d]\n", rd_format); + goto fail_cmd; + } + pr_debug("%s:rdformat[0x%x]wrformat[0x%x]\n", __func__, + open.read_format, open.write_format); + + rc = apr_send_pkt(ac->apr, (uint32_t *) &open); + if (rc < 0) { + pr_aud_err("open failed op[0x%x]rc[%d]\n", \ + open.hdr.opcode, rc); + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) == 0), 5*HZ); + if (!rc) { + pr_aud_err("timeout. waited for OPEN_WRITR rc[%d]\n", rc); + goto fail_cmd; + } + return 0; +fail_cmd: + return -EINVAL; +} + +int q6asm_run(struct audio_client *ac, uint32_t flags, + uint32_t msw_ts, uint32_t lsw_ts) +{ + struct asm_stream_cmd_run run; + int rc; + if (!ac || ac->apr == NULL) { + pr_aud_err("APR handle NULL\n"); + return -EINVAL; + } + pr_debug("%s session[%d]", __func__, ac->session); + q6asm_add_hdr(ac, &run.hdr, sizeof(run), TRUE); + + run.hdr.opcode = ASM_SESSION_CMD_RUN; + run.flags = flags; + run.msw_ts = msw_ts; + run.lsw_ts = lsw_ts; + + rc = apr_send_pkt(ac->apr, (uint32_t *) &run); + if (rc < 0) { + pr_aud_err("Commmand run failed[%d]", rc); + goto fail_cmd; + } + + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) == 0), 5*HZ); + if (!rc) { + pr_aud_err("timeout. waited for run success rc[%d]", rc); + goto fail_cmd; + } + + return 0; +fail_cmd: + return -EINVAL; +} + +int q6asm_run_nowait(struct audio_client *ac, uint32_t flags, + uint32_t msw_ts, uint32_t lsw_ts) +{ + struct asm_stream_cmd_run run; + int rc; + if (!ac || ac->apr == NULL) { + pr_aud_err("%s:APR handle NULL\n", __func__); + return -EINVAL; + } + pr_debug("session[%d]", ac->session); + q6asm_add_hdr_async(ac, &run.hdr, sizeof(run), TRUE); + + run.hdr.opcode = ASM_SESSION_CMD_RUN; + run.flags = flags; + run.msw_ts = msw_ts; + run.lsw_ts = lsw_ts; + + rc = apr_send_pkt(ac->apr, (uint32_t *) &run); + if (rc < 0) { + pr_aud_err("%s:Commmand run failed[%d]", __func__, rc); + return -EINVAL; + } + return 0; +} + + +int q6asm_enc_cfg_blk_aac(struct audio_client *ac, + uint32_t frames_per_buf, + uint32_t sample_rate, uint32_t channels, + uint32_t bit_rate, uint32_t mode, uint32_t format) +{ + struct asm_stream_cmd_encdec_cfg_blk enc_cfg; + int rc = 0; + + pr_debug("%s:session[%d]frames[%d]SR[%d]ch[%d]bitrate[%d]mode[%d]" + "format[%d]", __func__, ac->session, frames_per_buf, + sample_rate, channels, bit_rate, mode, format); + + q6asm_add_hdr(ac, &enc_cfg.hdr, sizeof(enc_cfg), TRUE); + + enc_cfg.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM; + enc_cfg.param_id = ASM_ENCDEC_CFG_BLK_ID; + enc_cfg.param_size = sizeof(struct asm_encode_cfg_blk); + enc_cfg.enc_blk.frames_per_buf = frames_per_buf; + enc_cfg.enc_blk.format_id = MPEG4_AAC; + enc_cfg.enc_blk.cfg_size = sizeof(struct asm_aac_read_cfg); + enc_cfg.enc_blk.cfg.aac.bitrate = bit_rate; + enc_cfg.enc_blk.cfg.aac.enc_mode = mode; + enc_cfg.enc_blk.cfg.aac.format = format; + enc_cfg.enc_blk.cfg.aac.ch_cfg = channels; + enc_cfg.enc_blk.cfg.aac.sample_rate = sample_rate; + + rc = apr_send_pkt(ac->apr, (uint32_t *) &enc_cfg); + if (rc < 0) { + pr_aud_err("Comamnd %d failed\n", ASM_STREAM_CMD_SET_ENCDEC_PARAM); + rc = -EINVAL; + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) == 0), 5*HZ); + if (!rc) { + pr_aud_err("timeout. waited for FORMAT_UPDATE\n"); + goto fail_cmd; + } + return 0; +fail_cmd: + return -EINVAL; +} + +int q6asm_enc_cfg_blk_pcm(struct audio_client *ac, + uint32_t rate, uint32_t channels) +{ + struct asm_stream_cmd_encdec_cfg_blk enc_cfg; + + int rc = 0; + + pr_debug("%s: Session %d\n", __func__, ac->session); + + q6asm_add_hdr(ac, &enc_cfg.hdr, sizeof(enc_cfg), TRUE); + + enc_cfg.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM; + enc_cfg.param_id = ASM_ENCDEC_CFG_BLK_ID; + enc_cfg.param_size = sizeof(struct asm_encode_cfg_blk); + enc_cfg.enc_blk.frames_per_buf = 1; + enc_cfg.enc_blk.format_id = LINEAR_PCM; + enc_cfg.enc_blk.cfg_size = sizeof(struct asm_pcm_cfg); + enc_cfg.enc_blk.cfg.pcm.ch_cfg = channels; + enc_cfg.enc_blk.cfg.pcm.bits_per_sample = 16; + enc_cfg.enc_blk.cfg.pcm.sample_rate = rate; + enc_cfg.enc_blk.cfg.pcm.is_signed = 1; + enc_cfg.enc_blk.cfg.pcm.interleaved = 1; + + rc = apr_send_pkt(ac->apr, (uint32_t *) &enc_cfg); + if (rc < 0) { + pr_aud_err("Comamnd open failed\n"); + rc = -EINVAL; + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) == 0), 5*HZ); + if (!rc) { + pr_aud_err("timeout opcode[0x%x] ", enc_cfg.hdr.opcode); + goto fail_cmd; + } + return 0; +fail_cmd: + return -EINVAL; +} + +int q6asm_enable_sbrps(struct audio_client *ac, + uint32_t sbr_ps_enable) +{ + struct asm_stream_cmd_encdec_sbr sbrps; + + int rc = 0; + + pr_debug("%s: Session %d\n", __func__, ac->session); + + q6asm_add_hdr(ac, &sbrps.hdr, sizeof(sbrps), TRUE); + + sbrps.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM; + sbrps.param_id = ASM_ENABLE_SBR_PS; + sbrps.param_size = sizeof(struct asm_sbr_ps); + sbrps.sbr_ps.enable = sbr_ps_enable; + + rc = apr_send_pkt(ac->apr, (uint32_t *) &sbrps); + if (rc < 0) { + pr_aud_err("Command opcode[0x%x]paramid[0x%x] failed\n", + ASM_STREAM_CMD_SET_ENCDEC_PARAM, + ASM_ENABLE_SBR_PS); + rc = -EINVAL; + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) == 0), 5*HZ); + if (!rc) { + pr_aud_err("timeout opcode[0x%x] ", sbrps.hdr.opcode); + goto fail_cmd; + } + return 0; +fail_cmd: + return -EINVAL; +} + +int q6asm_enc_cfg_blk_qcelp(struct audio_client *ac, uint32_t frames_per_buf, + uint16_t min_rate, uint16_t max_rate, + uint16_t reduced_rate_level, uint16_t rate_modulation_cmd) +{ + struct asm_stream_cmd_encdec_cfg_blk enc_cfg; + int rc = 0; + + pr_debug("%s:session[%d]frames[%d]min_rate[0x%4x]max_rate[0x%4x] \ + reduced_rate_level[0x%4x]rate_modulation_cmd[0x%4x]", __func__, + ac->session, frames_per_buf, min_rate, max_rate, + reduced_rate_level, rate_modulation_cmd); + + q6asm_add_hdr(ac, &enc_cfg.hdr, sizeof(enc_cfg), TRUE); + + enc_cfg.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM; + + enc_cfg.param_id = ASM_ENCDEC_CFG_BLK_ID; + enc_cfg.param_size = sizeof(struct asm_encode_cfg_blk); + + enc_cfg.enc_blk.frames_per_buf = frames_per_buf; + enc_cfg.enc_blk.format_id = V13K_FS; + enc_cfg.enc_blk.cfg_size = sizeof(struct asm_qcelp13_read_cfg); + enc_cfg.enc_blk.cfg.qcelp13.min_rate = min_rate; + enc_cfg.enc_blk.cfg.qcelp13.max_rate = max_rate; + enc_cfg.enc_blk.cfg.qcelp13.reduced_rate_level = reduced_rate_level; + enc_cfg.enc_blk.cfg.qcelp13.rate_modulation_cmd = rate_modulation_cmd; + + rc = apr_send_pkt(ac->apr, (uint32_t *) &enc_cfg); + if (rc < 0) { + pr_aud_err("Comamnd %d failed\n", ASM_STREAM_CMD_SET_ENCDEC_PARAM); + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) == 0), 5*HZ); + if (!rc) { + pr_aud_err("timeout. waited for FORMAT_UPDATE\n"); + goto fail_cmd; + } + return 0; +fail_cmd: + return -EINVAL; +} + +int q6asm_enc_cfg_blk_evrc(struct audio_client *ac, uint32_t frames_per_buf, + uint16_t min_rate, uint16_t max_rate, + uint16_t rate_modulation_cmd) +{ + struct asm_stream_cmd_encdec_cfg_blk enc_cfg; + int rc = 0; + + pr_debug("%s:session[%d]frames[%d]min_rate[0x%4x]max_rate[0x%4x] \ + rate_modulation_cmd[0x%4x]", __func__, ac->session, + frames_per_buf, min_rate, max_rate, rate_modulation_cmd); + + q6asm_add_hdr(ac, &enc_cfg.hdr, sizeof(enc_cfg), TRUE); + + enc_cfg.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM; + + enc_cfg.param_id = ASM_ENCDEC_CFG_BLK_ID; + enc_cfg.param_size = sizeof(struct asm_encode_cfg_blk); + + enc_cfg.enc_blk.frames_per_buf = frames_per_buf; + enc_cfg.enc_blk.format_id = EVRC_FS; + enc_cfg.enc_blk.cfg_size = sizeof(struct asm_evrc_read_cfg); + enc_cfg.enc_blk.cfg.evrc.min_rate = min_rate; + enc_cfg.enc_blk.cfg.evrc.max_rate = max_rate; + enc_cfg.enc_blk.cfg.evrc.rate_modulation_cmd = rate_modulation_cmd; + enc_cfg.enc_blk.cfg.evrc.reserved = 0; + + rc = apr_send_pkt(ac->apr, (uint32_t *) &enc_cfg); + if (rc < 0) { + pr_aud_err("Comamnd %d failed\n", ASM_STREAM_CMD_SET_ENCDEC_PARAM); + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) == 0), 5*HZ); + if (!rc) { + pr_aud_err("timeout. waited for FORMAT_UPDATE\n"); + goto fail_cmd; + } + return 0; +fail_cmd: + return -EINVAL; +} + +int q6asm_enc_cfg_blk_amrnb(struct audio_client *ac, uint32_t frames_per_buf, + uint16_t band_mode, uint16_t dtx_enable) +{ + struct asm_stream_cmd_encdec_cfg_blk enc_cfg; + int rc = 0; + + pr_debug("%s:session[%d]frames[%d]band_mode[0x%4x]dtx_enable[0x%4x]", + __func__, ac->session, frames_per_buf, band_mode, dtx_enable); + + q6asm_add_hdr(ac, &enc_cfg.hdr, sizeof(enc_cfg), TRUE); + + enc_cfg.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM; + + enc_cfg.param_id = ASM_ENCDEC_CFG_BLK_ID; + enc_cfg.param_size = sizeof(struct asm_encode_cfg_blk); + + enc_cfg.enc_blk.frames_per_buf = frames_per_buf; + enc_cfg.enc_blk.format_id = AMRNB_FS; + enc_cfg.enc_blk.cfg_size = sizeof(struct asm_amrnb_read_cfg); + enc_cfg.enc_blk.cfg.amrnb.mode = band_mode; + enc_cfg.enc_blk.cfg.amrnb.dtx_mode = dtx_enable; + + rc = apr_send_pkt(ac->apr, (uint32_t *) &enc_cfg); + if (rc < 0) { + pr_aud_err("Comamnd %d failed\n", ASM_STREAM_CMD_SET_ENCDEC_PARAM); + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) == 0), 5*HZ); + if (!rc) { + pr_aud_err("timeout. waited for FORMAT_UPDATE\n"); + goto fail_cmd; + } + return 0; +fail_cmd: + return -EINVAL; +} + +int q6asm_media_format_block_pcm(struct audio_client *ac, + uint32_t rate, uint32_t channels) +{ + struct asm_stream_media_format_update fmt; + int rc = 0; + + pr_debug("%s:session[%d]rate[%d]ch[%d]\n", __func__, ac->session, rate, + channels); + + q6asm_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE); + + fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FORMAT_UPDATE; + + fmt.format = LINEAR_PCM; + fmt.cfg_size = sizeof(struct asm_pcm_cfg); + fmt.write_cfg.pcm_cfg.ch_cfg = channels; + fmt.write_cfg.pcm_cfg.bits_per_sample = 16; + fmt.write_cfg.pcm_cfg.sample_rate = rate; + fmt.write_cfg.pcm_cfg.is_signed = 1; + fmt.write_cfg.pcm_cfg.interleaved = 1; + + rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt); + if (rc < 0) { + pr_aud_err("%s:Comamnd open failed\n", __func__); + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) == 0), 5*HZ); + if (!rc) { + pr_aud_err("%s:timeout. waited for FORMAT_UPDATE\n", __func__); + goto fail_cmd; + } + return 0; +fail_cmd: + return -EINVAL; +} + +int q6asm_media_format_block_aac(struct audio_client *ac, + struct asm_aac_cfg *cfg) +{ + struct asm_stream_media_format_update fmt; + int rc = 0; + + pr_debug("%s:session[%d]rate[%d]ch[%d]\n", __func__, ac->session, + cfg->sample_rate, cfg->ch_cfg); + + q6asm_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE); + + fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FORMAT_UPDATE; + + fmt.format = MPEG4_AAC; + fmt.cfg_size = sizeof(struct asm_aac_cfg); + fmt.write_cfg.aac_cfg.format = cfg->format; + fmt.write_cfg.aac_cfg.aot = cfg->aot; + fmt.write_cfg.aac_cfg.ep_config = cfg->ep_config; + fmt.write_cfg.aac_cfg.section_data_resilience = + cfg->section_data_resilience; + fmt.write_cfg.aac_cfg.scalefactor_data_resilience = + cfg->scalefactor_data_resilience; + fmt.write_cfg.aac_cfg.spectral_data_resilience = + cfg->spectral_data_resilience; + fmt.write_cfg.aac_cfg.ch_cfg = cfg->ch_cfg; + fmt.write_cfg.aac_cfg.sample_rate = cfg->sample_rate; + pr_aud_info("%s:format=%x cfg_size=%d aac-cfg=%x aot=%d ch=%d sr=%d\n", + __func__, fmt.format, fmt.cfg_size, + fmt.write_cfg.aac_cfg.format, + fmt.write_cfg.aac_cfg.aot, + fmt.write_cfg.aac_cfg.ch_cfg, + fmt.write_cfg.aac_cfg.sample_rate); + rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt); + if (rc < 0) { + pr_aud_err("%s:Comamnd open failed\n", __func__); + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) == 0), 5*HZ); + if (!rc) { + pr_aud_err("%s:timeout. waited for FORMAT_UPDATE\n", __func__); + goto fail_cmd; + } + return 0; +fail_cmd: + return -EINVAL; +} + +int q6asm_media_format_block_wma(struct audio_client *ac, + void *cfg) +{ + struct asm_stream_media_format_update fmt; + struct asm_wma_cfg *wma_cfg = (struct asm_wma_cfg *)cfg; + int rc = 0; + + pr_debug("session[%d]format_tag[0x%4x] rate[%d] ch[0x%4x] bps[%d],\ + balign[0x%4x], bit_sample[0x%4x], ch_msk[%d], enc_opt[0x%4x]\n", + ac->session, wma_cfg->format_tag, wma_cfg->sample_rate, + wma_cfg->ch_cfg, wma_cfg->avg_bytes_per_sec, + wma_cfg->block_align, wma_cfg->valid_bits_per_sample, + wma_cfg->ch_mask, wma_cfg->encode_opt); + + q6asm_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE); + + fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FORMAT_UPDATE; + + fmt.format = WMA_V9; + fmt.cfg_size = sizeof(struct asm_wma_cfg); + fmt.write_cfg.wma_cfg.format_tag = wma_cfg->format_tag; + fmt.write_cfg.wma_cfg.ch_cfg = wma_cfg->ch_cfg; + fmt.write_cfg.wma_cfg.sample_rate = wma_cfg->sample_rate; + fmt.write_cfg.wma_cfg.avg_bytes_per_sec = wma_cfg->avg_bytes_per_sec; + fmt.write_cfg.wma_cfg.block_align = wma_cfg->block_align; + fmt.write_cfg.wma_cfg.valid_bits_per_sample = + wma_cfg->valid_bits_per_sample; + fmt.write_cfg.wma_cfg.ch_mask = wma_cfg->ch_mask; + fmt.write_cfg.wma_cfg.encode_opt = wma_cfg->encode_opt; + fmt.write_cfg.wma_cfg.adv_encode_opt = 0; + fmt.write_cfg.wma_cfg.adv_encode_opt2 = 0; + fmt.write_cfg.wma_cfg.drc_peak_ref = 0; + fmt.write_cfg.wma_cfg.drc_peak_target = 0; + fmt.write_cfg.wma_cfg.drc_ave_ref = 0; + fmt.write_cfg.wma_cfg.drc_ave_target = 0; + + rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt); + if (rc < 0) { + pr_aud_err("%s:Comamnd open failed\n", __func__); + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) == 0), 5*HZ); + if (!rc) { + pr_aud_err("%s:timeout. waited for FORMAT_UPDATE\n", __func__); + goto fail_cmd; + } + return 0; +fail_cmd: + return -EINVAL; +} + +int q6asm_media_format_block_wmapro(struct audio_client *ac, + void *cfg) +{ + struct asm_stream_media_format_update fmt; + struct asm_wmapro_cfg *wmapro_cfg = (struct asm_wmapro_cfg *)cfg; + int rc = 0; + + pr_debug("session[%d]format_tag[0x%4x] rate[%d] ch[0x%4x] bps[%d]," + "balign[0x%4x], bit_sample[0x%4x], ch_msk[%d], enc_opt[0x%4x],\ + adv_enc_opt[0x%4x], adv_enc_opt2[0x%8x]\n", + ac->session, wmapro_cfg->format_tag, wmapro_cfg->sample_rate, + wmapro_cfg->ch_cfg, wmapro_cfg->avg_bytes_per_sec, + wmapro_cfg->block_align, wmapro_cfg->valid_bits_per_sample, + wmapro_cfg->ch_mask, wmapro_cfg->encode_opt, + wmapro_cfg->adv_encode_opt, wmapro_cfg->adv_encode_opt2); + + q6asm_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE); + + fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FORMAT_UPDATE; + + fmt.format = WMA_V10PRO; + fmt.cfg_size = sizeof(struct asm_wmapro_cfg); + fmt.write_cfg.wmapro_cfg.format_tag = wmapro_cfg->format_tag; + fmt.write_cfg.wmapro_cfg.ch_cfg = wmapro_cfg->ch_cfg; + fmt.write_cfg.wmapro_cfg.sample_rate = wmapro_cfg->sample_rate; + fmt.write_cfg.wmapro_cfg.avg_bytes_per_sec = + wmapro_cfg->avg_bytes_per_sec; + fmt.write_cfg.wmapro_cfg.block_align = wmapro_cfg->block_align; + fmt.write_cfg.wmapro_cfg.valid_bits_per_sample = + wmapro_cfg->valid_bits_per_sample; + fmt.write_cfg.wmapro_cfg.ch_mask = wmapro_cfg->ch_mask; + fmt.write_cfg.wmapro_cfg.encode_opt = wmapro_cfg->encode_opt; + fmt.write_cfg.wmapro_cfg.adv_encode_opt = wmapro_cfg->adv_encode_opt; + fmt.write_cfg.wmapro_cfg.adv_encode_opt2 = wmapro_cfg->adv_encode_opt2; + fmt.write_cfg.wmapro_cfg.drc_peak_ref = 0; + fmt.write_cfg.wmapro_cfg.drc_peak_target = 0; + fmt.write_cfg.wmapro_cfg.drc_ave_ref = 0; + fmt.write_cfg.wmapro_cfg.drc_ave_target = 0; + + rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt); + if (rc < 0) { + pr_aud_err("%s:Comamnd open failed\n", __func__); + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) == 0), 5*HZ); + if (!rc) { + pr_aud_err("%s:timeout. waited for FORMAT_UPDATE\n", __func__); + goto fail_cmd; + } + return 0; +fail_cmd: + return -EINVAL; +} + +int q6asm_memory_map(struct audio_client *ac, uint32_t buf_add, int dir, + uint32_t bufsz, uint32_t bufcnt) +{ + struct asm_stream_cmd_memory_map mem_map; + int rc = 0; + + if (!ac || ac->apr == NULL || this_mmap.apr == NULL) { + pr_aud_err("APR handle NULL\n"); + return -EINVAL; + } + + pr_debug("%s: Session[%d]\n", __func__, ac->session); + + mem_map.hdr.opcode = ASM_SESSION_CMD_MEMORY_MAP; + + mem_map.buf_add = buf_add; + mem_map.buf_size = bufsz * bufcnt; + mem_map.mempool_id = 0; /* EBI */ + mem_map.reserved = 0; + + q6asm_add_mmaphdr(&mem_map.hdr, + sizeof(struct asm_stream_cmd_memory_map), TRUE); + + pr_debug("buf add[%x] buf_add_parameter[%x]\n", + mem_map.buf_add, buf_add); + + rc = apr_send_pkt(this_mmap.apr, (uint32_t *) &mem_map); + if (rc < 0) { + pr_aud_err("mem_map op[0x%x]rc[%d]\n", + mem_map.hdr.opcode, rc); + rc = -EINVAL; + goto fail_cmd; + } + + rc = wait_event_timeout(this_mmap.cmd_wait, + (atomic_read(&this_mmap.cmd_state) == 0), 5 * HZ); + if (!rc) { + pr_aud_err("timeout. waited for memory_map\n"); + rc = -EINVAL; + goto fail_cmd; + } + rc = 0; +fail_cmd: + return rc; +} + +int q6asm_memory_unmap(struct audio_client *ac, uint32_t buf_add, int dir) +{ + struct asm_stream_cmd_memory_unmap mem_unmap; + int rc = 0; + + if (!ac || ac->apr == NULL || this_mmap.apr == NULL) { + pr_aud_err("APR handle NULL\n"); + return -EINVAL; + } + pr_debug("%s: Session[%d]\n", __func__, ac->session); + + q6asm_add_mmaphdr(&mem_unmap.hdr, + sizeof(struct asm_stream_cmd_memory_unmap), TRUE); + mem_unmap.hdr.opcode = ASM_SESSION_CMD_MEMORY_UNMAP; + mem_unmap.buf_add = buf_add; + + rc = apr_send_pkt(this_mmap.apr, (uint32_t *) &mem_unmap); + if (rc < 0) { + pr_aud_err("mem_unmap op[0x%x]rc[%d]\n", + mem_unmap.hdr.opcode, rc); + rc = -EINVAL; + goto fail_cmd; + } + + rc = wait_event_timeout(this_mmap.cmd_wait, + (atomic_read(&this_mmap.cmd_state) == 0), 5 * HZ); + if (!rc) { + pr_aud_err("timeout. waited for memory_map\n"); + rc = -EINVAL; + goto fail_cmd; + } + rc = 0; +fail_cmd: + return rc; +} + +int q6asm_set_lrgain(struct audio_client *ac, int left_gain, int right_gain) +{ + void *vol_cmd = NULL; + void *payload = NULL; + struct asm_pp_params_command *cmd = NULL; + struct asm_lrchannel_gain_params *lrgain = NULL; + int sz = 0; + int rc = 0; + + sz = sizeof(struct asm_pp_params_command) + + + sizeof(struct asm_lrchannel_gain_params); + vol_cmd = kzalloc(sz, GFP_KERNEL); + if (vol_cmd == NULL) { + pr_aud_err("%s[%d]: Mem alloc failed\n", __func__, ac->session); + rc = -EINVAL; + return rc; + } + cmd = (struct asm_pp_params_command *)vol_cmd; + q6asm_add_hdr_async(ac, &cmd->hdr, sz, TRUE); + cmd->hdr.opcode = ASM_STREAM_CMD_SET_PP_PARAMS; + cmd->payload = NULL; + cmd->payload_size = sizeof(struct asm_pp_param_data_hdr) + + sizeof(struct asm_lrchannel_gain_params); + cmd->params.module_id = VOLUME_CONTROL_MODULE_ID; + cmd->params.param_id = L_R_CHANNEL_GAIN_PARAM_ID; + cmd->params.param_size = sizeof(struct asm_lrchannel_gain_params); + cmd->params.reserved = 0; + + payload = (u8 *)(vol_cmd + sizeof(struct asm_pp_params_command)); + lrgain = (struct asm_lrchannel_gain_params *)payload; + + lrgain->left_gain = left_gain; + lrgain->right_gain = right_gain; + rc = apr_send_pkt(ac->apr, (uint32_t *) vol_cmd); + if (rc < 0) { + pr_aud_err("%s: Volume Command failed\n", __func__); + rc = -EINVAL; + goto fail_cmd; + } + + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) == 0), 5*HZ); + if (!rc) { + pr_aud_err("%s: timeout in sending volume command to apr\n", + __func__); + rc = -EINVAL; + goto fail_cmd; + } + rc = 0; +fail_cmd: + kfree(vol_cmd); + return rc; +} + +static int q6asm_memory_map_regions(struct audio_client *ac, int dir, + uint32_t bufsz, uint32_t bufcnt) +{ + struct asm_stream_cmd_memory_map_regions *mmap_regions = NULL; + struct asm_memory_map_regions *mregions = NULL; + struct audio_port_data *port = NULL; + struct audio_buffer *ab = NULL; + void *mmap_region_cmd = NULL; + void *payload = NULL; + int rc = 0; + int i = 0; + int cmd_size = 0; + + if (!ac || ac->apr == NULL || this_mmap.apr == NULL) { + pr_aud_err("APR handle NULL\n"); + return -EINVAL; + } + pr_debug("%s: Session[%d]\n", __func__, ac->session); + + cmd_size = sizeof(struct asm_stream_cmd_memory_map_regions) + + sizeof(struct asm_memory_map_regions) * bufcnt; + + mmap_region_cmd = kzalloc(cmd_size, GFP_KERNEL); + if (mmap_region_cmd == NULL) { + pr_aud_info("mmap_region_cmd == NULL\n"); + return -EINVAL; + } + mmap_regions = (struct asm_stream_cmd_memory_map_regions *) + mmap_region_cmd; + q6asm_add_mmaphdr(&mmap_regions->hdr, cmd_size, TRUE); + mmap_regions->hdr.opcode = ASM_SESSION_CMD_MEMORY_MAP_REGIONS; + mmap_regions->mempool_id = 0; + mmap_regions->nregions = bufcnt & 0x00ff; + pr_debug("map_regions->nregions = %d\n", mmap_regions->nregions); + payload = ((u8 *) mmap_region_cmd + + sizeof(struct asm_stream_cmd_memory_map_regions)); + mregions = (struct asm_memory_map_regions *)payload; + + port = &ac->port[dir]; + for (i = 0; i < bufcnt; i++) { + ab = &port->buf[i]; + mregions->phys = ab->phys; + mregions->buf_size = ab->size; + ++mregions; + } + + rc = apr_send_pkt(this_mmap.apr, (uint32_t *) mmap_region_cmd); + if (rc < 0) { + pr_aud_err("mmap_regions op[0x%x]rc[%d]\n", + mmap_regions->hdr.opcode, rc); + rc = -EINVAL; + goto fail_cmd; + } + + rc = wait_event_timeout(this_mmap.cmd_wait, + (atomic_read(&this_mmap.cmd_state) == 0), 5*HZ); + if (!rc) { + pr_aud_err("timeout. waited for memory_map\n"); + rc = -EINVAL; + goto fail_cmd; + } + rc = 0; +fail_cmd: + kfree(mmap_region_cmd); + return rc; +} + +static int q6asm_memory_unmap_regions(struct audio_client *ac, int dir, + uint32_t bufsz, uint32_t bufcnt) +{ + struct asm_stream_cmd_memory_unmap_regions *unmap_regions = NULL; + struct asm_memory_unmap_regions *mregions = NULL; + struct audio_port_data *port = NULL; + struct audio_buffer *ab = NULL; + void *unmap_region_cmd = NULL; + void *payload = NULL; + int rc = 0; + int i = 0; + int cmd_size = 0; + + if (!ac || ac->apr == NULL || this_mmap.apr == NULL) { + pr_aud_err("APR handle NULL\n"); + return -EINVAL; + } + pr_debug("%s: Session[%d]\n", __func__, ac->session); + + cmd_size = sizeof(struct asm_stream_cmd_memory_unmap_regions) + + sizeof(struct asm_memory_unmap_regions) * bufcnt; + + unmap_region_cmd = kzalloc(cmd_size, GFP_KERNEL); + if (unmap_region_cmd == NULL) { + pr_aud_info("unmap_region_cmd == NULL\n"); + return -EINVAL; + } + unmap_regions = (struct asm_stream_cmd_memory_unmap_regions *) + unmap_region_cmd; + q6asm_add_mmaphdr(&unmap_regions->hdr, cmd_size, TRUE); + unmap_regions->hdr.opcode = ASM_SESSION_CMD_MEMORY_UNMAP_REGIONS; + unmap_regions->nregions = bufcnt & 0x00ff; + pr_debug("unmap_regions->nregions = %d\n", unmap_regions->nregions); + payload = ((u8 *) unmap_region_cmd + + sizeof(struct asm_stream_cmd_memory_unmap_regions)); + mregions = (struct asm_memory_unmap_regions *)payload; + port = &ac->port[dir]; + for (i = 0; i < bufcnt; i++) { + ab = &port->buf[i]; + mregions->phys = ab->phys; + ++mregions; + } + + rc = apr_send_pkt(this_mmap.apr, (uint32_t *) unmap_region_cmd); + if (rc < 0) { + pr_aud_err("mmap_regions op[0x%x]rc[%d]\n", + unmap_regions->hdr.opcode, rc); + goto fail_cmd; + } + + rc = wait_event_timeout(this_mmap.cmd_wait, + (atomic_read(&this_mmap.cmd_state) == 0), 5*HZ); + if (!rc) { + pr_aud_err("timeout. waited for memory_unmap\n"); + goto fail_cmd; + } + rc = 0; + +fail_cmd: + kfree(unmap_region_cmd); + return rc; +} + +int q6asm_set_mute(struct audio_client *ac, int muteflag) +{ + void *vol_cmd = NULL; + void *payload = NULL; + struct asm_pp_params_command *cmd = NULL; + struct asm_mute_params *mute = NULL; + int sz = 0; + int rc = 0; + + sz = sizeof(struct asm_pp_params_command) + + + sizeof(struct asm_mute_params); + vol_cmd = kzalloc(sz, GFP_KERNEL); + if (vol_cmd == NULL) { + pr_aud_err("%s[%d]: Mem alloc failed\n", __func__, ac->session); + rc = -EINVAL; + return rc; + } + cmd = (struct asm_pp_params_command *)vol_cmd; + q6asm_add_hdr_async(ac, &cmd->hdr, sz, TRUE); + cmd->hdr.opcode = ASM_STREAM_CMD_SET_PP_PARAMS; + cmd->payload = NULL; + cmd->payload_size = sizeof(struct asm_pp_param_data_hdr) + + sizeof(struct asm_mute_params); + cmd->params.module_id = VOLUME_CONTROL_MODULE_ID; + cmd->params.param_id = MUTE_CONFIG_PARAM_ID; + cmd->params.param_size = sizeof(struct asm_mute_params); + cmd->params.reserved = 0; + + payload = (u8 *)(vol_cmd + sizeof(struct asm_pp_params_command)); + mute = (struct asm_mute_params *)payload; + + mute->muteflag = muteflag; + rc = apr_send_pkt(ac->apr, (uint32_t *) vol_cmd); + if (rc < 0) { + pr_aud_err("%s: Mute Command failed\n", __func__); + rc = -EINVAL; + goto fail_cmd; + } + + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) == 0), 5*HZ); + if (!rc) { + pr_aud_err("%s: timeout in sending mute command to apr\n", + __func__); + rc = -EINVAL; + goto fail_cmd; + } + rc = 0; +fail_cmd: + kfree(vol_cmd); + return rc; +} + +int q6asm_set_volume(struct audio_client *ac, int volume) +{ + void *vol_cmd = NULL; + void *payload = NULL; + struct asm_pp_params_command *cmd = NULL; + struct asm_master_gain_params *mgain = NULL; + int sz = 0; + int rc = 0; + + sz = sizeof(struct asm_pp_params_command) + + + sizeof(struct asm_master_gain_params); + vol_cmd = kzalloc(sz, GFP_KERNEL); + if (vol_cmd == NULL) { + pr_aud_err("%s[%d]: Mem alloc failed\n", __func__, ac->session); + rc = -EINVAL; + return rc; + } + cmd = (struct asm_pp_params_command *)vol_cmd; + q6asm_add_hdr_async(ac, &cmd->hdr, sz, TRUE); + cmd->hdr.opcode = ASM_STREAM_CMD_SET_PP_PARAMS; + cmd->payload = NULL; + cmd->payload_size = sizeof(struct asm_pp_param_data_hdr) + + sizeof(struct asm_master_gain_params); + cmd->params.module_id = VOLUME_CONTROL_MODULE_ID; + cmd->params.param_id = MASTER_GAIN_PARAM_ID; + cmd->params.param_size = sizeof(struct asm_master_gain_params); + cmd->params.reserved = 0; + + payload = (u8 *)(vol_cmd + sizeof(struct asm_pp_params_command)); + mgain = (struct asm_master_gain_params *)payload; + + mgain->master_gain = volume; + mgain->padding = 0x00; + rc = apr_send_pkt(ac->apr, (uint32_t *) vol_cmd); + if (rc < 0) { + pr_aud_err("%s: Volume Command failed\n", __func__); + rc = -EINVAL; + goto fail_cmd; + } + + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) == 0), 5*HZ); + if (!rc) { + pr_aud_err("%s: timeout in sending volume command to apr\n", + __func__); + rc = -EINVAL; + goto fail_cmd; + } + rc = 0; +fail_cmd: + kfree(vol_cmd); + return rc; +} + +int q6asm_set_softpause(struct audio_client *ac, + struct asm_softpause_params *pause_param) +{ + void *vol_cmd = NULL; + void *payload = NULL; + struct asm_pp_params_command *cmd = NULL; + struct asm_softpause_params *params = NULL; + int sz = 0; + int rc = 0; + + sz = sizeof(struct asm_pp_params_command) + + + sizeof(struct asm_softpause_params); + vol_cmd = kzalloc(sz, GFP_KERNEL); + if (vol_cmd == NULL) { + pr_aud_err("%s[%d]: Mem alloc failed\n", __func__, ac->session); + rc = -EINVAL; + return rc; + } + cmd = (struct asm_pp_params_command *)vol_cmd; + q6asm_add_hdr_async(ac, &cmd->hdr, sz, TRUE); + cmd->hdr.opcode = ASM_STREAM_CMD_SET_PP_PARAMS; + cmd->payload = NULL; + cmd->payload_size = sizeof(struct asm_pp_param_data_hdr) + + sizeof(struct asm_softpause_params); + cmd->params.module_id = VOLUME_CONTROL_MODULE_ID; + cmd->params.param_id = SOFT_PAUSE_PARAM_ID; + cmd->params.param_size = sizeof(struct asm_softpause_params); + cmd->params.reserved = 0; + + payload = (u8 *)(vol_cmd + sizeof(struct asm_pp_params_command)); + params = (struct asm_softpause_params *)payload; + + params->enable = pause_param->enable; + params->period = pause_param->period; + params->step = pause_param->step; + params->rampingcurve = pause_param->rampingcurve; + pr_debug("%s: soft Pause Command: enable = %d, period = %d," + "step = %d, curve = %d\n", __func__, params->enable, + params->period, params->step, params->rampingcurve); + rc = apr_send_pkt(ac->apr, (uint32_t *) vol_cmd); + if (rc < 0) { + pr_aud_err("%s: Volume Command(soft_pause) failed\n", __func__); + rc = -EINVAL; + goto fail_cmd; + } + + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) == 0), 5*HZ); + if (!rc) { + pr_aud_err("%s: timeout in sending volume command(soft_pause)" + "to apr\n", __func__); + rc = -EINVAL; + goto fail_cmd; + } + rc = 0; +fail_cmd: + kfree(vol_cmd); + return rc; +} + +int q6asm_equalizer(struct audio_client *ac, void *eq) +{ + void *eq_cmd = NULL; + void *payload = NULL; + struct asm_pp_params_command *cmd = NULL; + struct asm_equalizer_params *equalizer = NULL; + struct msm_audio_eq_stream_config *eq_params = NULL; + int i = 0; + int sz = 0; + int rc = 0; + + sz = sizeof(struct asm_pp_params_command) + + + sizeof(struct asm_equalizer_params); + eq_cmd = kzalloc(sz, GFP_KERNEL); + if (eq_cmd == NULL) { + pr_aud_err("%s[%d]: Mem alloc failed\n", __func__, ac->session); + rc = -EINVAL; + goto fail_cmd; + } + eq_params = (struct msm_audio_eq_stream_config *) eq; + cmd = (struct asm_pp_params_command *)eq_cmd; + q6asm_add_hdr(ac, &cmd->hdr, sz, TRUE); + cmd->hdr.opcode = ASM_STREAM_CMD_SET_PP_PARAMS; + cmd->payload = NULL; + cmd->payload_size = sizeof(struct asm_pp_param_data_hdr) + + sizeof(struct asm_equalizer_params); + cmd->params.module_id = EQUALIZER_MODULE_ID; + cmd->params.param_id = EQUALIZER_PARAM_ID; + cmd->params.param_size = sizeof(struct asm_equalizer_params); + cmd->params.reserved = 0; + payload = (u8 *)(eq_cmd + sizeof(struct asm_pp_params_command)); + equalizer = (struct asm_equalizer_params *)payload; + + equalizer->enable = eq_params->enable; + equalizer->num_bands = eq_params->num_bands; + pr_debug("%s: enable:%d numbands:%d\n", __func__, eq_params->enable, + eq_params->num_bands); + for (i = 0; i < eq_params->num_bands; i++) { + equalizer->eq_bands[i].band_idx = + eq_params->eq_bands[i].band_idx; + equalizer->eq_bands[i].filter_type = + eq_params->eq_bands[i].filter_type; + equalizer->eq_bands[i].center_freq_hz = + eq_params->eq_bands[i].center_freq_hz; + equalizer->eq_bands[i].filter_gain = + eq_params->eq_bands[i].filter_gain; + equalizer->eq_bands[i].q_factor = + eq_params->eq_bands[i].q_factor; + pr_debug("%s: filter_type:%u bandnum:%d\n", __func__, + eq_params->eq_bands[i].filter_type, i); + pr_debug("%s: center_freq_hz:%u bandnum:%d\n", __func__, + eq_params->eq_bands[i].center_freq_hz, i); + pr_debug("%s: filter_gain:%d bandnum:%d\n", __func__, + eq_params->eq_bands[i].filter_gain, i); + pr_debug("%s: q_factor:%d bandnum:%d\n", __func__, + eq_params->eq_bands[i].q_factor, i); + } + rc = apr_send_pkt(ac->apr, (uint32_t *) eq_cmd); + if (rc < 0) { + pr_aud_err("%s: Equalizer Command failed\n", __func__); + rc = -EINVAL; + goto fail_cmd; + } + + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) == 0), 5*HZ); + if (!rc) { + pr_aud_err("%s: timeout in sending equalizer command to apr\n", + __func__); + rc = -EINVAL; + goto fail_cmd; + } + rc = 0; +fail_cmd: + kfree(eq_cmd); + return rc; +} + +int q6asm_read(struct audio_client *ac) +{ + struct asm_stream_cmd_read read; + struct audio_buffer *ab; + int dsp_buf; + struct audio_port_data *port; + int rc; + if (!ac || ac->apr == NULL) { + pr_aud_err("APR handle NULL\n"); + return -EINVAL; + } + if (ac->io_mode == SYNC_IO_MODE) { + port = &ac->port[OUT]; + + q6asm_add_hdr(ac, &read.hdr, sizeof(read), FALSE); + + mutex_lock(&port->lock); + + dsp_buf = port->dsp_buf; + ab = &port->buf[dsp_buf]; + pr_debug("%s:session[%d]dsp-buf[%d][%p]cpu_buf[%d][%p]\n", + __func__, + ac->session, + dsp_buf, + (void *)port->buf[dsp_buf].data, + port->cpu_buf, + (void *)port->buf[port->cpu_buf].phys); + + read.hdr.opcode = ASM_DATA_CMD_READ; + read.buf_add = ab->phys; + read.buf_size = ab->size; + read.uid = port->dsp_buf; + read.hdr.token = port->dsp_buf; + + port->dsp_buf = (port->dsp_buf + 1) & (port->max_buf_cnt - 1); + mutex_unlock(&port->lock); + pr_debug("%s:buf add[0x%x] token[%d] uid[%d]\n", __func__, + read.buf_add, + read.hdr.token, + read.uid); + rc = apr_send_pkt(ac->apr, (uint32_t *) &read); + if (rc < 0) { + pr_aud_err("read op[0x%x]rc[%d]\n", read.hdr.opcode, rc); + goto fail_cmd; + } + return 0; + } +fail_cmd: + return -EINVAL; +} + +int q6asm_read_nolock(struct audio_client *ac) +{ + struct asm_stream_cmd_read read; + struct audio_buffer *ab; + int dsp_buf; + struct audio_port_data *port; + int rc; + if (!ac || ac->apr == NULL) { + pr_aud_err("APR handle NULL\n"); + return -EINVAL; + } + if (ac->io_mode == SYNC_IO_MODE) { + port = &ac->port[OUT]; + + q6asm_add_hdr_async(ac, &read.hdr, sizeof(read), FALSE); + + + dsp_buf = port->dsp_buf; + ab = &port->buf[dsp_buf]; + + pr_debug("%s:session[%d]dsp-buf[%d][%p]cpu_buf[%d][%p]\n", + __func__, + ac->session, + dsp_buf, + (void *)port->buf[dsp_buf].data, + port->cpu_buf, + (void *)port->buf[port->cpu_buf].phys); + + read.hdr.opcode = ASM_DATA_CMD_READ; + read.buf_add = ab->phys; + read.buf_size = ab->size; + read.uid = port->dsp_buf; + read.hdr.token = port->dsp_buf; + + port->dsp_buf = (port->dsp_buf + 1) & (port->max_buf_cnt - 1); + pr_debug("%s:buf add[0x%x] token[%d] uid[%d]\n", __func__, + read.buf_add, + read.hdr.token, + read.uid); + rc = apr_send_pkt(ac->apr, (uint32_t *) &read); + if (rc < 0) { + pr_aud_err("read op[0x%x]rc[%d]\n", read.hdr.opcode, rc); + goto fail_cmd; + } + return 0; + } +fail_cmd: + return -EINVAL; +} + +static void q6asm_add_hdr_async(struct audio_client *ac, struct apr_hdr *hdr, + uint32_t pkt_size, uint32_t cmd_flg) +{ + pr_debug("session=%d pkt size=%d cmd_flg=%d\n", pkt_size, cmd_flg, + ac->session); + hdr->hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, \ + APR_HDR_LEN(sizeof(struct apr_hdr)),\ + APR_PKT_VER); + hdr->src_svc = ((struct apr_svc *)ac->apr)->id; + hdr->src_domain = APR_DOMAIN_APPS; + hdr->dest_svc = APR_SVC_ASM; + hdr->dest_domain = APR_DOMAIN_ADSP; + hdr->src_port = ((ac->session << 8) & 0xFF00) | 0x01; + hdr->dest_port = ((ac->session << 8) & 0xFF00) | 0x01; + if (cmd_flg) { + hdr->token = ac->session; + atomic_set(&ac->cmd_state, 1); + } + hdr->pkt_size = pkt_size; + return; +} + + +int q6asm_async_write(struct audio_client *ac, + struct audio_aio_write_param *param) +{ + int rc = 0; + struct asm_stream_cmd_write write; + + if (!ac || ac->apr == NULL) { + pr_aud_err("%s: APR handle NULL\n", __func__); + return -EINVAL; + } + + q6asm_add_hdr_async(ac, &write.hdr, sizeof(write), FALSE); + + /* Pass physical address as token for AIO scheme */ + write.hdr.token = param->uid; + write.hdr.opcode = ASM_DATA_CMD_WRITE; + write.buf_add = param->paddr; + write.avail_bytes = param->len; + write.uid = param->uid; + write.msw_ts = param->msw_ts; + write.lsw_ts = param->lsw_ts; + /* Use 0xFF00 for disabling timestamps */ + if (param->flags == 0xFF00) + write.uflags = (0x00000000 | (param->flags & 0x800000FF)); + else + write.uflags = (0x80000000 | param->flags); + + pr_debug("%s: session[%d] bufadd[0x%x]len[0x%x]", __func__, ac->session, + write.buf_add, write.avail_bytes); + + rc = apr_send_pkt(ac->apr, (uint32_t *) &write); + if (rc < 0) { + pr_debug("[%s] write op[0x%x]rc[%d]\n", __func__, + write.hdr.opcode, rc); + goto fail_cmd; + } + return 0; +fail_cmd: + return -EINVAL; +} + +int q6asm_async_read(struct audio_client *ac, + struct audio_aio_read_param *param) +{ + int rc = 0; + struct asm_stream_cmd_read read; + + if (!ac || ac->apr == NULL) { + pr_aud_err("%s: APR handle NULL\n", __func__); + return -EINVAL; + } + + q6asm_add_hdr_async(ac, &read.hdr, sizeof(read), FALSE); + + /* Pass physical address as token for AIO scheme */ + read.hdr.token = param->paddr; + read.hdr.opcode = ASM_DATA_CMD_READ; + read.buf_add = param->paddr; + read.buf_size = param->len; + read.uid = param->uid; + + pr_debug("%s: session[%d] bufadd[0x%x]len[0x%x]", __func__, ac->session, + read.buf_add, read.buf_size); + + rc = apr_send_pkt(ac->apr, (uint32_t *) &read); + if (rc < 0) { + pr_debug("[%s] read op[0x%x]rc[%d]\n", __func__, + read.hdr.opcode, rc); + goto fail_cmd; + } + return 0; +fail_cmd: + return -EINVAL; +} + +int q6asm_write(struct audio_client *ac, uint32_t len, uint32_t msw_ts, + uint32_t lsw_ts, uint32_t flags) +{ + int rc = 0; + struct asm_stream_cmd_write write; + struct audio_port_data *port; + struct audio_buffer *ab; + int dsp_buf = 0; + + if (!ac || ac->apr == NULL) { + pr_aud_err("APR handle NULL\n"); + return -EINVAL; + } + pr_debug("%s: session[%d] len=%d", __func__, ac->session, len); + if (ac->io_mode == SYNC_IO_MODE) { + port = &ac->port[IN]; + + q6asm_add_hdr(ac, &write.hdr, sizeof(write), + FALSE); + mutex_lock(&port->lock); + + dsp_buf = port->dsp_buf; + ab = &port->buf[dsp_buf]; + + write.hdr.token = port->dsp_buf; + write.hdr.opcode = ASM_DATA_CMD_WRITE; + write.buf_add = ab->phys; + write.avail_bytes = len; + write.uid = port->dsp_buf; + write.msw_ts = msw_ts; + write.lsw_ts = lsw_ts; + /* Use 0xFF00 for disabling timestamps */ + if (flags == 0xFF00) + write.uflags = (0x00000000 | (flags & 0x800000FF)); + else + write.uflags = (0x80000000 | flags); + port->dsp_buf = (port->dsp_buf + 1) & (port->max_buf_cnt - 1); + + pr_debug("%s:ab->phys[0x%x]bufadd[0x%x]token[0x%x]buf_id[0x%x]" + , __func__, + ab->phys, + write.buf_add, + write.hdr.token, + write.uid); + mutex_unlock(&port->lock); + + rc = apr_send_pkt(ac->apr, (uint32_t *) &write); + if (rc < 0) { + pr_aud_err("write op[0x%x]rc[%d]\n", write.hdr.opcode, rc); + goto fail_cmd; + } + pr_debug("%s: WRITE SUCCESS\n", __func__); + return 0; + } +fail_cmd: + return -EINVAL; +} + +int q6asm_write_nolock(struct audio_client *ac, uint32_t len, uint32_t msw_ts, + uint32_t lsw_ts, uint32_t flags) +{ + int rc = 0; + struct asm_stream_cmd_write write; + struct audio_port_data *port; + struct audio_buffer *ab; + int dsp_buf = 0; + + if (!ac || ac->apr == NULL) { + pr_aud_err("APR handle NULL\n"); + return -EINVAL; + } + pr_debug("%s: session[%d] len=%d", __func__, ac->session, len); + if (ac->io_mode == SYNC_IO_MODE) { + port = &ac->port[IN]; + + q6asm_add_hdr_async(ac, &write.hdr, sizeof(write), + FALSE); + + dsp_buf = port->dsp_buf; + ab = &port->buf[dsp_buf]; + + write.hdr.token = port->dsp_buf; + write.hdr.opcode = ASM_DATA_CMD_WRITE; + write.buf_add = ab->phys; + write.avail_bytes = len; + write.uid = port->dsp_buf; + write.msw_ts = msw_ts; + write.lsw_ts = lsw_ts; + /* Use 0xFF00 for disabling timestamps */ + if (flags == 0xFF00) + write.uflags = (0x00000000 | (flags & 0x800000FF)); + else + write.uflags = (0x80000000 | flags); + port->dsp_buf = (port->dsp_buf + 1) & (port->max_buf_cnt - 1); + + pr_debug("%s:ab->phys[0x%x]bufadd[0x%x]token[0x%x]buf_id[0x%x]" + , __func__, + ab->phys, + write.buf_add, + write.hdr.token, + write.uid); + + rc = apr_send_pkt(ac->apr, (uint32_t *) &write); + if (rc < 0) { + pr_aud_err("write op[0x%x]rc[%d]\n", write.hdr.opcode, rc); + goto fail_cmd; + } + pr_debug("%s: WRITE SUCCESS\n", __func__); + return 0; + } +fail_cmd: + return -EINVAL; +} + +uint64_t q6asm_get_session_time(struct audio_client *ac) +{ + struct apr_hdr hdr; + int rc; + + if (!ac || ac->apr == NULL) { + pr_aud_err("APR handle NULL\n"); + return -EINVAL; + } + q6asm_add_hdr(ac, &hdr, sizeof(hdr), TRUE); + hdr.opcode = ASM_SESSION_CMD_GET_SESSION_TIME; + atomic_set(&ac->time_flag, 1); + + pr_debug("%s: session[%d]opcode[0x%x]\n", __func__, + ac->session, + hdr.opcode); + rc = apr_send_pkt(ac->apr, (uint32_t *) &hdr); + if (rc < 0) { + pr_aud_err("Commmand 0x%x failed\n", hdr.opcode); + goto fail_cmd; + } + rc = wait_event_timeout(ac->time_wait, + (atomic_read(&ac->time_flag) == 0), 5*HZ); + if (!rc) { + pr_aud_err("%s: timeout in getting session time from DSP\n", + __func__); + goto fail_cmd; + } + return ac->time_stamp; + +fail_cmd: + return -EINVAL; +} + +int q6asm_cmd(struct audio_client *ac, int cmd) +{ + struct apr_hdr hdr; + int rc; + atomic_t *state; + int cnt = 0; + + if (!ac || ac->apr == NULL) { + pr_aud_err("APR handle NULL\n"); + return -EINVAL; + } + q6asm_add_hdr(ac, &hdr, sizeof(hdr), TRUE); + switch (cmd) { + case CMD_PAUSE: + pr_debug("%s:CMD_PAUSE\n", __func__); + hdr.opcode = ASM_SESSION_CMD_PAUSE; + state = &ac->cmd_state; + break; + case CMD_FLUSH: + pr_debug("%s:CMD_FLUSH\n", __func__); + hdr.opcode = ASM_STREAM_CMD_FLUSH; + state = &ac->cmd_state; + break; + case CMD_EOS: + pr_debug("%s:CMD_EOS\n", __func__); + hdr.opcode = ASM_DATA_CMD_EOS; + atomic_set(&ac->cmd_state, 0); + state = &ac->cmd_state; + break; + case CMD_CLOSE: + pr_debug("%s:CMD_CLOSE\n", __func__); + hdr.opcode = ASM_STREAM_CMD_CLOSE; + state = &ac->cmd_state; + break; + default: + pr_aud_err("Invalid format[%d]\n", cmd); + goto fail_cmd; + } + pr_debug("%s:session[%d]opcode[0x%x] ", __func__, + ac->session, + hdr.opcode); + rc = apr_send_pkt(ac->apr, (uint32_t *) &hdr); + if (rc < 0) { + pr_aud_err("Commmand 0x%x failed\n", hdr.opcode); + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, (atomic_read(state) == 0), 5*HZ); + if (!rc) { + pr_aud_err("timeout. waited for response opcode[0x%x]\n", + hdr.opcode); + goto fail_cmd; + } + if (cmd == CMD_FLUSH) + q6asm_reset_buf_state(ac); + if (cmd == CMD_CLOSE) { + /* check if DSP return all buffers */ + if (ac->port[IN].buf) { + for (cnt = 0; cnt < ac->port[IN].max_buf_cnt; + cnt++) { + if (ac->port[IN].buf[cnt].used == IN) { + pr_aud_info("Write Buf[%d] not returned\n", + cnt); + } + } + } + if (ac->port[OUT].buf) { + for (cnt = 0; cnt < ac->port[OUT].max_buf_cnt; cnt++) { + if (ac->port[OUT].buf[cnt].used == OUT) { + pr_aud_info("Read Buf[%d] not returned\n", + cnt); + } + } + } + } + return 0; +fail_cmd: + return -EINVAL; +} + +int q6asm_cmd_nowait(struct audio_client *ac, int cmd) +{ + struct apr_hdr hdr; + int rc; + + if (!ac || ac->apr == NULL) { + pr_aud_err("%s:APR handle NULL\n", __func__); + return -EINVAL; + } + q6asm_add_hdr_async(ac, &hdr, sizeof(hdr), TRUE); + switch (cmd) { + case CMD_PAUSE: + pr_debug("%s:CMD_PAUSE\n", __func__); + hdr.opcode = ASM_SESSION_CMD_PAUSE; + break; + case CMD_EOS: + pr_debug("%s:CMD_EOS\n", __func__); + hdr.opcode = ASM_DATA_CMD_EOS; + break; + default: + pr_aud_err("%s:Invalid format[%d]\n", __func__, cmd); + goto fail_cmd; + } + pr_debug("%s:session[%d]opcode[0x%x] ", __func__, + ac->session, + hdr.opcode); + rc = apr_send_pkt(ac->apr, (uint32_t *) &hdr); + if (rc < 0) { + pr_aud_err("%s:Commmand 0x%x failed\n", __func__, hdr.opcode); + goto fail_cmd; + } + return 0; +fail_cmd: + return -EINVAL; +} + +static void q6asm_reset_buf_state(struct audio_client *ac) +{ + int cnt = 0; + int loopcnt = 0; + struct audio_port_data *port = NULL; + + if (ac->io_mode == SYNC_IO_MODE) { + mutex_lock(&ac->cmd_lock); + for (loopcnt = 0; loopcnt <= OUT; loopcnt++) { + port = &ac->port[loopcnt]; + cnt = port->max_buf_cnt - 1; + port->dsp_buf = 0; + port->cpu_buf = 0; + while (cnt >= 0) { + if (!port->buf) + continue; + port->buf[cnt].used = 1; + cnt--; + } + } + mutex_unlock(&ac->cmd_lock); + } +} + +int q6asm_reg_tx_overflow(struct audio_client *ac, uint16_t enable) +{ + struct asm_stream_cmd_reg_tx_overflow_event tx_overflow; + int rc; + + if (!ac || ac->apr == NULL) { + pr_aud_err("APR handle NULL\n"); + return -EINVAL; + } + pr_debug("%s:session[%d]enable[%d]\n", __func__, + ac->session, enable); + q6asm_add_hdr(ac, &tx_overflow.hdr, sizeof(tx_overflow), TRUE); + + tx_overflow.hdr.opcode = \ + ASM_SESSION_CMD_REGISTER_FOR_TX_OVERFLOW_EVENTS; + /* tx overflow event: enable */ + tx_overflow.enable = enable; + + rc = apr_send_pkt(ac->apr, (uint32_t *) &tx_overflow); + if (rc < 0) { + pr_aud_err("tx overflow op[0x%x]rc[%d]\n", \ + tx_overflow.hdr.opcode, rc); + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) == 0), 5*HZ); + if (!rc) { + pr_aud_err("timeout. waited for tx overflow\n"); + goto fail_cmd; + } + return 0; +fail_cmd: + return -EINVAL; +} + +#ifdef CONFIG_MSM8X60_RTAC +int q6asm_get_apr_service_id(int session_id) +{ + pr_debug("%s\n", __func__); + + if (session_id < 0) { + pr_aud_err("%s: invalid session_id = %d\n", __func__, session_id); + return -EINVAL; + } + + return ((struct apr_svc *)session[session_id]->apr)->id; +} +#endif + + +static int __init q6asm_init(void) +{ + pr_debug("%s\n", __func__); + init_waitqueue_head(&this_mmap.cmd_wait); + memset(session, 0, sizeof(session)); + return 0; +} + +device_initcall(q6asm_init); diff --git a/arch/arm/mach-msm/qdsp6v3/q6core.c b/arch/arm/mach-msm/qdsp6v3/q6core.c new file mode 100644 index 00000000000..8d739347f3d --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v3/q6core.c @@ -0,0 +1,348 @@ +/* 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. + * + * 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 + +struct dentry *dentry; +struct apr_svc_ch_dev *handle; +struct apr_svc *apr_handle_q; +struct apr_svc *apr_handle_m; +struct apr_svc *core_handle_q; +struct apr_client_data clnt_data; +char l_buf[4096]; + +#define TIMEOUT_MS 1000 +int32_t query_adsp_ver; +wait_queue_head_t adsp_version_wait; +uint32_t adsp_version; + +static int32_t aprv2_core_fn_q(struct apr_client_data *data, void *priv) +{ + struct adsp_get_version *payload; + uint32_t *payload1; + struct adsp_service_info *svc_info; + int i; + + pr_aud_info("core msg: payload len = %d\n", data->payload_size); + switch (data->opcode) { + case APR_BASIC_RSP_RESULT:{ + payload1 = data->payload; + if (payload1[0] == ADSP_CMD_SET_POWER_COLLAPSE_STATE) { + pr_aud_info("Cmd[0x%x] status[0x%x]\n", payload1[0], + payload1[1]); + break; + } else + pr_aud_err("Invalid cmd rsp[0x%x][0x%x]\n", payload1[0], + payload1[1]); + break; + } + case ADSP_GET_VERSION_RSP:{ + if (data->payload_size) { + payload = data->payload; + if (query_adsp_ver == 1) { + query_adsp_ver = 0; + adsp_version = payload->build_id; + wake_up(&adsp_version_wait); + } + svc_info = (struct adsp_service_info *) + ((char *)payload + sizeof(struct adsp_get_version)); + pr_aud_info("----------------------------------------\n"); + pr_aud_info("Build id = %x\n", payload->build_id); + pr_aud_info("Number of services= %x\n", payload->svc_cnt); + pr_aud_info("----------------------------------------\n"); + for (i = 0; i < payload->svc_cnt; i++) { + pr_aud_info("svc-id[%d]\tver[%x.%x]\n", + svc_info[i].svc_id, + (svc_info[i].svc_ver & 0xFFFF0000) >> 16, + (svc_info[i].svc_ver & 0xFFFF)); + } + pr_aud_info("-----------------------------------------\n"); + } else + pr_aud_info("zero payload for ADSP_GET_VERSION_RSP\n"); + break; + } + case RESET_EVENTS:{ + pr_debug("Reset event received in Core service"); + apr_reset(core_handle_q); + core_handle_q = NULL; + break; + } + + default: + pr_aud_err("Message id from adsp core svc: %d\n", data->opcode); + break; + } + + return 0; +} + +static int32_t aprv2_debug_fn_q(struct apr_client_data *data, void *priv) +{ + pr_debug("Q6_Payload Length = %d\n", data->payload_size); + if (memcmp(data->payload, l_buf + 20, data->payload_size)) + pr_aud_info("FAIL: %d\n", data->payload_size); + else + pr_aud_info("SUCCESS: %d\n", data->payload_size); + return 0; +} + +static int32_t aprv2_debug_fn_m(struct apr_client_data *data, void *priv) +{ + pr_aud_info("M_Payload Length = %d\n", data->payload_size); + return 0; +} + +static ssize_t apr_debug_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + pr_debug("apr debugfs opened\n"); + return 0; +} + +void *core_open(void) +{ + if (core_handle_q == NULL) { + core_handle_q = apr_register("ADSP", "CORE", + aprv2_core_fn_q, 0xFFFFFFFF, NULL); + } + pr_aud_info("Open_q %p\n", core_handle_q); + if (core_handle_q == NULL) { + pr_aud_err("%s: Unable to register CORE\n", __func__); + return NULL; + } + return core_handle_q; +} +EXPORT_SYMBOL(core_open); + +int32_t core_close(void) +{ + int ret = 0; + if (core_handle_q == NULL) { + pr_aud_err("CORE is already closed\n"); + ret = -EINVAL; + return ret; + } + apr_deregister(core_handle_q); + return ret; +} +EXPORT_SYMBOL(core_close); + +uint32_t core_get_adsp_version(void) +{ + struct apr_hdr *hdr; + int32_t rc = 0, ret = 0; + core_open(); + if (core_handle_q) { + hdr = (struct apr_hdr *)l_buf; + hdr->hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_EVENT, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + hdr->pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, 0); + hdr->src_port = 0; + hdr->dest_port = 0; + hdr->token = 0; + hdr->opcode = ADSP_GET_VERSION; + + apr_send_pkt(core_handle_q, (uint32_t *)l_buf); + query_adsp_ver = 1; + pr_aud_info("Write_q\n"); + ret = wait_event_timeout(adsp_version_wait, + (query_adsp_ver == 0), + msecs_to_jiffies(TIMEOUT_MS)); + rc = adsp_version; + if (!ret) { + pr_aud_err("%s: wait_event timeout\n", __func__); + rc = -ENODEV; + } + } else + pr_aud_info("apr registration failed\n"); + return rc; +} +EXPORT_SYMBOL(core_get_adsp_version); + +static ssize_t apr_debug_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + int len; + static int t_len; + + if (count < 0) + return 0; + len = count > 63 ? 63 : count; + if (copy_from_user(l_buf + 20 , buf, len)) { + pr_aud_info("Unable to copy data from user space\n"); + return -EFAULT; + } + l_buf[len + 20] = 0; + if (l_buf[len + 20 - 1] == '\n') { + l_buf[len + 20 - 1] = 0; + len--; + } + if (!strncmp(l_buf + 20, "open_q", 64)) { + apr_handle_q = apr_register("ADSP", "TEST", aprv2_debug_fn_q, + 0xFFFFFFFF, NULL); + pr_aud_info("Open_q %p\n", apr_handle_q); + } else if (!strncmp(l_buf + 20, "open_m", 64)) { + apr_handle_m = apr_register("MODEM", "TEST", aprv2_debug_fn_m, + 0xFFFFFFFF, NULL); + pr_aud_info("Open_m %p\n", apr_handle_m); + } else if (!strncmp(l_buf + 20, "write_q", 64)) { + struct apr_hdr *hdr; + + t_len++; + t_len = t_len % 450; + if (!t_len % 99) + msleep(2000); + hdr = (struct apr_hdr *)l_buf; + hdr->hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_EVENT, + APR_HDR_LEN(20), APR_PKT_VER); + hdr->pkt_size = APR_PKT_SIZE(20, t_len); + hdr->src_port = 0; + hdr->dest_port = 0; + hdr->token = 0; + hdr->opcode = 0x12345678; + memset(l_buf + 20, 9, 4060); + + apr_send_pkt(apr_handle_q, (uint32_t *)l_buf); + pr_debug("Write_q\n"); + } else if (!strncmp(l_buf + 20, "write_m", 64)) { + struct apr_hdr *hdr; + + hdr = (struct apr_hdr *)l_buf; + hdr->hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_EVENT, + APR_HDR_LEN(20), APR_PKT_VER); + hdr->pkt_size = APR_PKT_SIZE(20, 8); + hdr->src_port = 0; + hdr->dest_port = 0; + hdr->token = 0; + hdr->opcode = 0x12345678; + memset(l_buf + 30, 9, 4060); + + apr_send_pkt(apr_handle_m, (uint32_t *)l_buf); + pr_aud_info("Write_m\n"); + } else if (!strncmp(l_buf + 20, "write_q4", 64)) { + struct apr_hdr *hdr; + + hdr = (struct apr_hdr *)l_buf; + hdr->hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_EVENT, + APR_HDR_LEN(20), APR_PKT_VER); + hdr->pkt_size = APR_PKT_SIZE(20, 4076); + hdr->src_port = 0; + hdr->dest_port = 0; + hdr->token = 0; + hdr->opcode = 0x12345678; + memset(l_buf + 30, 9, 4060); + + apr_send_pkt(apr_handle_q, (uint32_t *)l_buf); + pr_aud_info("Write_q\n"); + } else if (!strncmp(l_buf + 20, "write_m4", 64)) { + struct apr_hdr *hdr; + + hdr = (struct apr_hdr *)l_buf; + hdr->hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_EVENT, + APR_HDR_LEN(20), APR_PKT_VER); + hdr->pkt_size = APR_PKT_SIZE(20, 4076); + hdr->src_port = 0; + hdr->dest_port = 0; + hdr->token = 0; + hdr->opcode = 0x12345678; + memset(l_buf + 30, 9, 4060); + + apr_send_pkt(apr_handle_m, (uint32_t *)l_buf); + pr_aud_info("Write_m\n"); + } else if (!strncmp(l_buf + 20, "close", 64)) { + if (apr_handle_q) + apr_deregister(apr_handle_q); + } else if (!strncmp(l_buf + 20, "loaded", 64)) { + change_q6_state(APR_Q6_LOADED); + } else if (!strncmp(l_buf + 20, "boom", 64)) { + q6audio_dsp_not_responding(); + } else if (!strncmp(l_buf + 20, "dsp_ver", 64)) { + core_get_adsp_version(); + } else if (!strncmp(l_buf + 20, "en_pwr_col", 64)) { + struct adsp_power_collapse pc; + + core_handle_q = core_open(); + if (core_handle_q) { + pc.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_EVENT, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + pc.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(uint32_t));; + pc.hdr.src_port = 0; + pc.hdr.dest_port = 0; + pc.hdr.token = 0; + pc.hdr.opcode = ADSP_CMD_SET_POWER_COLLAPSE_STATE; + pc.power_collapse = 0x00000000; + apr_send_pkt(core_handle_q, (uint32_t *)&pc); + pr_aud_info("Write_q :enable power collapse\n"); + } + } else if (!strncmp(l_buf + 20, "dis_pwr_col", 64)) { + struct adsp_power_collapse pc; + + core_handle_q = core_open(); + if (core_handle_q) { + pc.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_EVENT, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + pc.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(uint32_t)); + pc.hdr.src_port = 0; + pc.hdr.dest_port = 0; + pc.hdr.token = 0; + pc.hdr.opcode = ADSP_CMD_SET_POWER_COLLAPSE_STATE; + pc.power_collapse = 0x00000001; + apr_send_pkt(core_handle_q, (uint32_t *)&pc); + pr_aud_info("Write_q:disable power collapse\n"); + } + } else + pr_aud_info("Unknown Command\n"); + + return count; +} + +static const struct file_operations apr_debug_fops = { + .write = apr_debug_write, + .open = apr_debug_open, +}; + +static int __init core_init(void) +{ +#ifdef CONFIG_DEBUG_FS + dentry = debugfs_create_file("apr", 0644, + NULL, (void *) NULL, &apr_debug_fops); +#endif /* CONFIG_DEBUG_FS */ + query_adsp_ver = 0; + init_waitqueue_head(&adsp_version_wait); + adsp_version = 0; + core_handle_q = NULL; + return 0; +} +device_initcall(core_init); + diff --git a/arch/arm/mach-msm/qdsp6v3/q6voice.c b/arch/arm/mach-msm/qdsp6v3/q6voice.c new file mode 100644 index 00000000000..e320184d0d9 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v3/q6voice.c @@ -0,0 +1,2812 @@ +/* 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. + * + * 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 "audio_acdb.h" +#include "rtac.h" +#include + +#define TIMEOUT_MS 3000 +#define SNDDEV_CAP_TTY 0x20 + +#define CMD_STATUS_SUCCESS 0 +#define CMD_STATUS_FAIL 1 + +#define VOC_PATH_PASSIVE 0 +#define VOC_PATH_FULL 1 +#define ADSP_VERSION_CVD 0x60300000 + +#define BUFFER_PAYLOAD_SIZE 4000 + +#define VOC_REC_NONE 0xFF + +#define AUD_LOG(x...) do { \ +struct timespec ts; \ +struct rtc_time tm; \ +getnstimeofday(&ts); \ +rtc_time_to_tm(ts.tv_sec, &tm); \ +printk(KERN_INFO "[AUD] " x); \ +printk("[AUD] 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) + + +struct voice_data voice; + +static bool is_adsp_support_cvd(void) +{ + return (voice.adsp_version >= ADSP_VERSION_CVD); +} +static int voice_send_enable_vocproc_cmd(struct voice_data *v); +static int voice_send_netid_timing_cmd(struct voice_data *v); + +static void *voice_get_apr_mvm(struct voice_data *v) +{ + void *apr_mvm = NULL; + + if (v->voc_path == VOC_PATH_PASSIVE && + !(is_adsp_support_cvd())) + apr_mvm = v->apr_mvm; + else + apr_mvm = v->apr_q6_mvm; + + pr_debug("%s: apr_mvm 0x%x\n", __func__, (unsigned int)apr_mvm); + + return apr_mvm; +} + +static void voice_set_apr_mvm(struct voice_data *v, void *apr_mvm) +{ + pr_debug("%s: apr_mvm 0x%x\n", __func__, (unsigned int)apr_mvm); + + if (v->voc_path == VOC_PATH_PASSIVE && + !(is_adsp_support_cvd())) + v->apr_mvm = apr_mvm; + else + v->apr_q6_mvm = apr_mvm; +} + +static void *voice_get_apr_cvs(struct voice_data *v) +{ + void *apr_cvs = NULL; + + if (v->voc_path == VOC_PATH_PASSIVE && + !(is_adsp_support_cvd())) + apr_cvs = v->apr_cvs; + else + apr_cvs = v->apr_q6_cvs; + + pr_debug("%s: apr_cvs 0x%x\n", __func__, (unsigned int)apr_cvs); + + return apr_cvs; +} + +static void voice_set_apr_cvs(struct voice_data *v, void *apr_cvs) +{ + pr_debug("%s: apr_cvs 0x%x\n", __func__, (unsigned int)apr_cvs); + + if (v->voc_path == VOC_PATH_PASSIVE && + !(is_adsp_support_cvd())) + v->apr_cvs = apr_cvs; + else + v->apr_q6_cvs = apr_cvs; +#ifdef CONFIG_MSM8X60_RTAC + rtac_set_voice_handle(RTAC_CVS, apr_cvs); +#endif +} + +static void *voice_get_apr_cvp(struct voice_data *v) +{ + void *apr_cvp = NULL; + + if (v->voc_path == VOC_PATH_PASSIVE && + !(is_adsp_support_cvd())) + apr_cvp = v->apr_cvp; + else + apr_cvp = v->apr_q6_cvp; + + pr_debug("%s: apr_cvp 0x%x\n", __func__, (unsigned int)apr_cvp); + + return apr_cvp; +} + +static void voice_set_apr_cvp(struct voice_data *v, void *apr_cvp) +{ + pr_debug("%s: apr_cvp 0x%x\n", __func__, (unsigned int)apr_cvp); + + if (v->voc_path == VOC_PATH_PASSIVE && + !(is_adsp_support_cvd())) + v->apr_cvp = apr_cvp; + else + v->apr_q6_cvp = apr_cvp; +#ifdef CONFIG_MSM8X60_RTAC + rtac_set_voice_handle(RTAC_CVP, apr_cvp); +#endif +} + +static u16 voice_get_mvm_handle(struct voice_data *v) +{ + u16 mvm_handle = 0; + + if (v->voc_path == VOC_PATH_PASSIVE) + mvm_handle = v->mvm_handle; + else + mvm_handle = v->mvm_q6_handle; + + pr_debug("%s: mvm_handle %d\n", __func__, mvm_handle); + + return mvm_handle; +} + +static void voice_set_mvm_handle(struct voice_data *v, u16 mvm_handle) +{ + pr_debug("%s: mvm_handle %d\n", __func__, mvm_handle); + + if (v->voc_path == VOC_PATH_PASSIVE) + v->mvm_handle = mvm_handle; + else + v->mvm_q6_handle = mvm_handle; +} + +static u16 voice_get_cvs_handle(struct voice_data *v) +{ + u16 cvs_handle = 0; + + if (v->voc_path == VOC_PATH_PASSIVE) + cvs_handle = v->cvs_handle; + else + cvs_handle = v->cvs_q6_handle; + + pr_debug("%s: cvs_handle %d\n", __func__, cvs_handle); + + return cvs_handle; +} + +static void voice_set_cvs_handle(struct voice_data *v, u16 cvs_handle) +{ + pr_debug("%s: cvs_handle %d\n", __func__, cvs_handle); + + if (v->voc_path == VOC_PATH_PASSIVE) + v->cvs_handle = cvs_handle; + else + v->cvs_q6_handle = cvs_handle; +} + +static u16 voice_get_cvp_handle(struct voice_data *v) +{ + u16 cvp_handle = 0; + + if (v->voc_path == VOC_PATH_PASSIVE) + cvp_handle = v->cvp_handle; + else + cvp_handle = v->cvp_q6_handle; + + pr_debug("%s: cvp_handle %d\n", __func__, cvp_handle); + + return cvp_handle; +} + +static void voice_set_cvp_handle(struct voice_data *v, u16 cvp_handle) +{ + pr_debug("%s: cvp_handle %d\n", __func__, cvp_handle); + + if (v->voc_path == VOC_PATH_PASSIVE) + v->cvp_handle = cvp_handle; + else + v->cvp_q6_handle = cvp_handle; +} + +static void voice_auddev_cb_function(u32 evt_id, + union auddev_evt_data *evt_payload, + void *private_data); + +static int32_t modem_mvm_callback(struct apr_client_data *data, void *priv); +static int32_t modem_cvs_callback(struct apr_client_data *data, void *priv); +static int32_t modem_cvp_callback(struct apr_client_data *data, void *priv); + +static int voice_apr_register(struct voice_data *v) +{ + int rc = 0; + void *apr_mvm; + void *apr_cvs; + void *apr_cvp; + + if (v->adsp_version == 0) { + core_open(); + v->adsp_version = core_get_adsp_version(); + pr_aud_info("adsp_ver fetched:%x\n", v->adsp_version); + } + apr_mvm = voice_get_apr_mvm(v); + apr_cvs = voice_get_apr_cvs(v); + apr_cvp = voice_get_apr_cvp(v); + + + pr_debug("into voice_apr_register_callback\n"); + /* register callback to APR */ + if (apr_mvm == NULL) { + pr_debug("start to register MVM callback\n"); + + if (v->voc_path == VOC_PATH_PASSIVE && + !(is_adsp_support_cvd())) { + apr_mvm = apr_register("MODEM", "MVM", + modem_mvm_callback, 0xFFFFFFFF, + v); + } else { + apr_mvm = apr_register("ADSP", "MVM", + modem_mvm_callback, 0xFFFFFFFF, + v); + } + + if (apr_mvm == NULL) { + pr_aud_err("Unable to register MVM %d\n", + is_adsp_support_cvd()); + rc = -ENODEV; + goto done; + } + + voice_set_apr_mvm(v, apr_mvm); + } + + if (apr_cvs == NULL) { + pr_debug("start to register CVS callback\n"); + + if (v->voc_path == VOC_PATH_PASSIVE && + !(is_adsp_support_cvd())) { + apr_cvs = apr_register("MODEM", "CVS", + modem_cvs_callback, 0xFFFFFFFF, + v); + } else { + apr_cvs = apr_register("ADSP", "CVS", + modem_cvs_callback, 0xFFFFFFFF, + v); + } + + if (apr_cvs == NULL) { + pr_aud_err("Unable to register CVS %d\n", + is_adsp_support_cvd()); + rc = -ENODEV; + goto err; + } + + voice_set_apr_cvs(v, apr_cvs); + } + + if (apr_cvp == NULL) { + pr_debug("start to register CVP callback\n"); + + if (v->voc_path == VOC_PATH_PASSIVE && + !(is_adsp_support_cvd())) { + apr_cvp = apr_register("MODEM", "CVP", + modem_cvp_callback, 0xFFFFFFFF, + v); + } else { + apr_cvp = apr_register("ADSP", "CVP", + modem_cvp_callback, 0xFFFFFFFF, + v); + } + + if (apr_cvp == NULL) { + pr_aud_err("Unable to register CVP %d\n", + is_adsp_support_cvd()); + rc = -ENODEV; + goto err1; + } + + voice_set_apr_cvp(v, apr_cvp); + } + return 0; + +err1: + apr_deregister(apr_cvs); + apr_cvs = NULL; + voice_set_apr_cvs(v, apr_cvs); +err: + apr_deregister(apr_mvm); + apr_mvm = NULL; + voice_set_apr_mvm(v, apr_mvm); + +done: + return rc; +} + +static int voice_create_mvm_cvs_session(struct voice_data *v) +{ + int ret = 0; + struct mvm_create_ctl_session_cmd mvm_session_cmd; + struct cvs_create_passive_ctl_session_cmd cvs_session_cmd; + struct cvs_create_full_ctl_session_cmd cvs_full_ctl_cmd; + struct mvm_attach_stream_cmd attach_stream_cmd; + void *apr_mvm = voice_get_apr_mvm(v); + void *apr_cvs = voice_get_apr_cvs(v); + void *apr_cvp = voice_get_apr_cvp(v); + u16 mvm_handle = voice_get_mvm_handle(v); + u16 cvs_handle = voice_get_cvs_handle(v); + u16 cvp_handle = voice_get_cvp_handle(v); + + pr_aud_info("%s:\n", __func__); + + /* start to ping if modem service is up */ + pr_debug("in voice_create_mvm_cvs_session, mvm_hdl=%d, cvs_hdl=%d\n", + mvm_handle, cvs_handle); + /* send cmd to create mvm session and wait for response */ + + if (!mvm_handle) { + if (v->voc_path == VOC_PATH_PASSIVE) { + mvm_session_cmd.hdr.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + mvm_session_cmd.hdr.pkt_size = + APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(mvm_session_cmd) - APR_HDR_SIZE); + pr_debug("Send mvm create session pkt size = %d\n", + mvm_session_cmd.hdr.pkt_size); + mvm_session_cmd.hdr.src_port = 0; + mvm_session_cmd.hdr.dest_port = 0; + mvm_session_cmd.hdr.token = 0; + pr_debug("%s: Creating MVM passive ctrl\n", __func__); + mvm_session_cmd.hdr.opcode = + VSS_IMVM_CMD_CREATE_PASSIVE_CONTROL_SESSION; + strncpy(mvm_session_cmd.mvm_session.name, + "default modem voice", SESSION_NAME_LEN); + v->mvm_state = CMD_STATUS_FAIL; + + ret = apr_send_pkt(apr_mvm, + (uint32_t *) &mvm_session_cmd); + if (ret < 0) { + pr_aud_err("Error sending MVM_CONTROL_SESSION\n"); + goto fail; + } + ret = wait_event_timeout(v->mvm_wait, + (v->mvm_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_aud_err("%s: wait_event timeout\n", __func__); + goto fail; + } + } else { + mvm_session_cmd.hdr.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + mvm_session_cmd.hdr.pkt_size = + APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(mvm_session_cmd) - APR_HDR_SIZE); + pr_debug("Send mvm create session pkt size = %d\n", + mvm_session_cmd.hdr.pkt_size); + mvm_session_cmd.hdr.src_port = 0; + mvm_session_cmd.hdr.dest_port = 0; + mvm_session_cmd.hdr.token = 0; + pr_debug("%s: Creating MVM full ctrl\n", __func__); + mvm_session_cmd.hdr.opcode = + VSS_IMVM_CMD_CREATE_FULL_CONTROL_SESSION; + strcpy(mvm_session_cmd.mvm_session.name, + "default voip"); + + v->mvm_state = CMD_STATUS_FAIL; + + ret = apr_send_pkt(apr_mvm, + (uint32_t *) &mvm_session_cmd); + if (ret < 0) { + pr_aud_err("Error sending MVM_FULL_CTL_SESSION\n"); + goto fail; + } + ret = wait_event_timeout(v->mvm_wait, + (v->mvm_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_aud_err("%s: wait_event timeout\n", __func__); + goto fail; + } + } + + /* Get the created MVM handle. */ + mvm_handle = voice_get_mvm_handle(v); + } + + /* send cmd to create cvs session */ + if (!cvs_handle) { + if (v->voc_path == VOC_PATH_PASSIVE) { + pr_aud_info("%s:creating CVS passive session\n", __func__); + + cvs_session_cmd.hdr.hdr_field = APR_HDR_FIELD( + APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + cvs_session_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvs_session_cmd) - APR_HDR_SIZE); + pr_aud_info("send stream create session pkt size = %d\n", + cvs_session_cmd.hdr.pkt_size); + cvs_session_cmd.hdr.src_port = 0; + cvs_session_cmd.hdr.dest_port = 0; + cvs_session_cmd.hdr.token = 0; + cvs_session_cmd.hdr.opcode = + VSS_ISTREAM_CMD_CREATE_PASSIVE_CONTROL_SESSION; + strncpy(cvs_session_cmd.cvs_session.name, + "default modem voice", SESSION_NAME_LEN); + v->cvs_state = CMD_STATUS_FAIL; + + pr_aud_info("%s: CVS create\n", __func__); + ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_session_cmd); + if (ret < 0) { + pr_aud_err("Fail in sending STREAM_CONTROL_SESSION\n"); + goto fail; + } + ret = wait_event_timeout(v->cvs_wait, + (v->cvs_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_aud_err("%s: wait_event timeout\n", __func__); + goto fail; + } + + /* Get the created CVS handle. */ + cvs_handle = voice_get_cvs_handle(v); + } else { + pr_aud_info("%s:creating CVS full session\n", __func__); + + cvs_full_ctl_cmd.hdr.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + + cvs_full_ctl_cmd.hdr.pkt_size = + APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvs_full_ctl_cmd) - APR_HDR_SIZE); + + cvs_full_ctl_cmd.hdr.src_port = 0; + cvs_full_ctl_cmd.hdr.dest_port = 0; + cvs_full_ctl_cmd.hdr.token = 0; + cvs_full_ctl_cmd.hdr.opcode = + VSS_ISTREAM_CMD_CREATE_FULL_CONTROL_SESSION; + cvs_full_ctl_cmd.cvs_session.direction = 2; + + cvs_full_ctl_cmd.cvs_session.enc_media_type = + v->mvs_info.media_type; + cvs_full_ctl_cmd.cvs_session.dec_media_type = + v->mvs_info.media_type; + cvs_full_ctl_cmd.cvs_session.network_id = + v->mvs_info.network_type; + strncpy(cvs_full_ctl_cmd.cvs_session.name, + "default voip", SESSION_NAME_LEN); + v->cvs_state = CMD_STATUS_FAIL; + + ret = apr_send_pkt(apr_cvs, + (uint32_t *) &cvs_full_ctl_cmd); + + if (ret < 0) { + pr_aud_err("%s: Err %d sending CREATE_FULL_CTRL\n", + __func__, ret); + goto fail; + } + ret = wait_event_timeout(v->cvs_wait, + (v->cvs_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_aud_err("%s: wait_event timeout\n", __func__); + goto fail; + } + + /* Get the created CVS handle. */ + cvs_handle = voice_get_cvs_handle(v); + + /* Attach MVM to CVS. */ + pr_aud_info("%s: Attach MVM to stream\n", __func__); + + attach_stream_cmd.hdr.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + + attach_stream_cmd.hdr.pkt_size = + APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(attach_stream_cmd) - APR_HDR_SIZE); + attach_stream_cmd.hdr.src_port = 0; + attach_stream_cmd.hdr.dest_port = mvm_handle; + attach_stream_cmd.hdr.token = 0; + attach_stream_cmd.hdr.opcode = + VSS_IMVM_CMD_ATTACH_STREAM; + attach_stream_cmd.attach_stream.handle = cvs_handle; + + v->mvm_state = CMD_STATUS_FAIL; + ret = apr_send_pkt(apr_mvm, + (uint32_t *) &attach_stream_cmd); + if (ret < 0) { + pr_aud_err("%s: Error %d sending ATTACH_STREAM\n", + __func__, ret); + goto fail; + } + ret = wait_event_timeout(v->mvm_wait, + (v->mvm_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_aud_err("%s: wait_event timeout\n", __func__); + goto fail; + } + } + } + + return 0; + +fail: + apr_deregister(apr_mvm); + apr_mvm = NULL; + voice_set_apr_mvm(v, apr_mvm); + + apr_deregister(apr_cvs); + apr_cvs = NULL; + voice_set_apr_cvs(v, apr_cvs); + + apr_deregister(apr_cvp); + apr_cvp = NULL; + voice_set_apr_cvp(v, apr_cvp); + + cvp_handle = 0; + voice_set_cvp_handle(v, cvp_handle); + + cvs_handle = 0; + voice_set_cvs_handle(v, cvs_handle); + + return -EINVAL; +} + +static int voice_destroy_mvm_cvs_session(struct voice_data *v) +{ + int ret = 0; + struct mvm_detach_stream_cmd detach_stream; + struct apr_hdr mvm_destroy; + struct apr_hdr cvs_destroy; + void *apr_mvm = voice_get_apr_mvm(v); + void *apr_cvs = voice_get_apr_cvs(v); + u16 mvm_handle = voice_get_mvm_handle(v); + u16 cvs_handle = voice_get_cvs_handle(v); + + /* MVM, CVS sessions are destroyed only for Full control sessions. */ + if (v->voc_path == VOC_PATH_FULL) { + pr_aud_info("%s: MVM detach stream\n", __func__); + + /* Detach voice stream. */ + detach_stream.hdr.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + detach_stream.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(detach_stream) - APR_HDR_SIZE); + detach_stream.hdr.src_port = 0; + detach_stream.hdr.dest_port = mvm_handle; + detach_stream.hdr.token = 0; + detach_stream.hdr.opcode = VSS_IMVM_CMD_DETACH_STREAM; + detach_stream.detach_stream.handle = cvs_handle; + + v->mvm_state = CMD_STATUS_FAIL; + + ret = apr_send_pkt(apr_mvm, (uint32_t *) &detach_stream); + if (ret < 0) { + pr_aud_err("%s: Error %d sending DETACH_STREAM\n", + __func__, ret); + + goto fail; + } + + ret = wait_event_timeout(v->mvm_wait, + (v->mvm_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_aud_err("%s: wait event timeout\n", __func__); + goto fail; + } + + /* Destroy CVS. */ + pr_aud_info("%s: CVS destroy session\n", __func__); + + cvs_destroy.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + cvs_destroy.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvs_destroy) - APR_HDR_SIZE); + cvs_destroy.src_port = 0; + cvs_destroy.dest_port = cvs_handle; + cvs_destroy.token = 0; + cvs_destroy.opcode = APRV2_IBASIC_CMD_DESTROY_SESSION; + + v->cvs_state = CMD_STATUS_FAIL; + + ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_destroy); + if (ret < 0) { + pr_aud_err("%s: Error %d sending CVS DESTROY\n", + __func__, ret); + + goto fail; + } + + ret = wait_event_timeout(v->cvs_wait, + (v->cvs_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_aud_err("%s: wait event timeout\n", __func__); + + goto fail; + } + cvs_handle = 0; + voice_set_cvs_handle(v, cvs_handle); + + /* Destroy MVM. */ + pr_aud_info("%s: MVM destroy session\n", __func__); + + mvm_destroy.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + mvm_destroy.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(mvm_destroy) - APR_HDR_SIZE); + mvm_destroy.src_port = 0; + mvm_destroy.dest_port = mvm_handle; + mvm_destroy.token = 0; + mvm_destroy.opcode = APRV2_IBASIC_CMD_DESTROY_SESSION; + + v->mvm_state = CMD_STATUS_FAIL; + + ret = apr_send_pkt(apr_mvm, (uint32_t *) &mvm_destroy); + if (ret < 0) { + pr_aud_err("%s: Error %d sending MVM DESTROY\n", + __func__, ret); + + goto fail; + } + + ret = wait_event_timeout(v->mvm_wait, + (v->mvm_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_aud_err("%s: wait event timeout\n", __func__); + + goto fail; + } + mvm_handle = 0; + voice_set_mvm_handle(v, mvm_handle); + } + +fail: + return 0; +} + +static int voice_send_tty_mode_to_modem(struct voice_data *v) +{ + struct msm_snddev_info *dev_tx_info; + struct msm_snddev_info *dev_rx_info; + int tty_mode = 0; + int ret = 0; + struct mvm_set_tty_mode_cmd mvm_tty_mode_cmd; + void *apr_mvm = voice_get_apr_mvm(v); + u16 mvm_handle = voice_get_mvm_handle(v); + + dev_rx_info = audio_dev_ctrl_find_dev(v->dev_rx.dev_id); + if (IS_ERR(dev_rx_info)) { + pr_aud_err("bad dev_id %d\n", v->dev_rx.dev_id); + goto done; + } + + dev_tx_info = audio_dev_ctrl_find_dev(v->dev_tx.dev_id); + if (IS_ERR(dev_tx_info)) { + pr_aud_err("bad dev_id %d\n", v->dev_tx.dev_id); + goto done; + } + + if ((dev_rx_info->capability & SNDDEV_CAP_TTY) && + (dev_tx_info->capability & SNDDEV_CAP_TTY)) + tty_mode = 3; /* FULL */ + else if (!(dev_tx_info->capability & SNDDEV_CAP_TTY) && + (dev_rx_info->capability & SNDDEV_CAP_TTY)) + tty_mode = 2; /* VCO */ + else if ((dev_tx_info->capability & SNDDEV_CAP_TTY) && + !(dev_rx_info->capability & SNDDEV_CAP_TTY)) + tty_mode = 1; /* HCO */ + + if (tty_mode) { + /* send tty mode cmd to mvm */ + mvm_tty_mode_cmd.hdr.hdr_field = APR_HDR_FIELD( + APR_MSG_TYPE_SEQ_CMD, APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + mvm_tty_mode_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(mvm_tty_mode_cmd) - APR_HDR_SIZE); + pr_debug("pkt size = %d\n", mvm_tty_mode_cmd.hdr.pkt_size); + mvm_tty_mode_cmd.hdr.src_port = 0; + mvm_tty_mode_cmd.hdr.dest_port = mvm_handle; + mvm_tty_mode_cmd.hdr.token = 0; + mvm_tty_mode_cmd.hdr.opcode = VSS_ISTREAM_CMD_SET_TTY_MODE; + mvm_tty_mode_cmd.tty_mode.mode = tty_mode; + pr_aud_info("tty mode =%d\n", mvm_tty_mode_cmd.tty_mode.mode); + + v->mvm_state = CMD_STATUS_FAIL; + pr_aud_info("%s: MVM set tty\n", __func__); + ret = apr_send_pkt(apr_mvm, (uint32_t *) &mvm_tty_mode_cmd); + if (ret < 0) { + pr_aud_err("Fail: sending VSS_ISTREAM_CMD_SET_TTY_MODE\n"); + goto done; + } + ret = wait_event_timeout(v->mvm_wait, + (v->mvm_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_aud_err("%s: wait_event timeout\n", __func__); + goto done; + } + } + return 0; +done: + return -EINVAL; +} + +static int voice_send_cvs_cal_to_modem(struct voice_data *v) +{ + struct apr_hdr cvs_cal_cmd_hdr = {0}; + uint32_t *cmd_buf; + struct acdb_cal_data cal_data; + struct acdb_cal_block *cal_blk; + int32_t cal_size_per_network; + uint32_t *cal_data_per_network; + int index = 0; + int ret = 0; + void *apr_cvs = voice_get_apr_cvs(v); + u16 cvs_handle = voice_get_cvs_handle(v); + + /* fill the header */ + cvs_cal_cmd_hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + cvs_cal_cmd_hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvs_cal_cmd_hdr) - APR_HDR_SIZE); + cvs_cal_cmd_hdr.src_port = 0; + cvs_cal_cmd_hdr.dest_port = cvs_handle; + cvs_cal_cmd_hdr.token = 0; + cvs_cal_cmd_hdr.opcode = + VSS_ISTREAM_CMD_CACHE_CALIBRATION_DATA; + + pr_debug("voice_send_cvs_cal_to_modem\n"); + /* get the cvs cal data */ + get_vocstrm_cal(&cal_data); + if (cal_data.num_cal_blocks == 0) { + pr_aud_err("%s: No calibration data to send!\n", __func__); + goto done; + } + + /* send cvs cal to modem */ + cmd_buf = kzalloc((sizeof(struct apr_hdr) + BUFFER_PAYLOAD_SIZE), + GFP_KERNEL); + if (!cmd_buf) { + pr_aud_err("No memory is allocated.\n"); + return -ENOMEM; + } + pr_debug("----- num_cal_blocks=%d\n", (s32)cal_data.num_cal_blocks); + cal_blk = cal_data.cal_blocks; + pr_debug("cal_blk =%x\n", (uint32_t)cal_data.cal_blocks); + + for (; index < cal_data.num_cal_blocks; index++) { + cal_size_per_network = cal_blk[index].cal_size; + pr_debug(" cal size =%d\n", cal_size_per_network); + if (cal_size_per_network >= BUFFER_PAYLOAD_SIZE) + pr_aud_err("Cal size is too big\n"); + cal_data_per_network = (u32 *)cal_blk[index].cal_kvaddr; + pr_debug(" cal data=%x\n", (uint32_t)cal_data_per_network); + cvs_cal_cmd_hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + cal_size_per_network); + pr_debug("header size =%d, pkt_size =%d\n", + APR_HDR_SIZE, cvs_cal_cmd_hdr.pkt_size); + memcpy(cmd_buf, &cvs_cal_cmd_hdr, APR_HDR_SIZE); + memcpy(cmd_buf + (APR_HDR_SIZE / sizeof(uint32_t)), + cal_data_per_network, cal_size_per_network); + pr_debug("send cvs cal: index =%d\n", index); + v->cvs_state = CMD_STATUS_FAIL; + ret = apr_send_pkt(apr_cvs, cmd_buf); + if (ret < 0) { + pr_aud_err("Fail: sending cvs cal, idx=%d\n", index); + continue; + } + ret = wait_event_timeout(v->cvs_wait, + (v->cvs_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_aud_err("%s: wait_event timeout\n", __func__); + return -EINVAL; + } + } + kfree(cmd_buf); +done: + return 0; +} + +static int voice_send_cvp_cal_to_modem(struct voice_data *v) +{ + struct apr_hdr cvp_cal_cmd_hdr = {0}; + uint32_t *cmd_buf; + struct acdb_cal_data cal_data; + struct acdb_cal_block *cal_blk; + int32_t cal_size_per_network; + uint32_t *cal_data_per_network; + int index = 0; + int ret = 0; + void *apr_cvp = voice_get_apr_cvp(v); + u16 cvp_handle = voice_get_cvp_handle(v); + + + /* fill the header */ + cvp_cal_cmd_hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + cvp_cal_cmd_hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvp_cal_cmd_hdr) - APR_HDR_SIZE); + cvp_cal_cmd_hdr.src_port = 0; + cvp_cal_cmd_hdr.dest_port = cvp_handle; + cvp_cal_cmd_hdr.token = 0; + cvp_cal_cmd_hdr.opcode = + VSS_IVOCPROC_CMD_CACHE_CALIBRATION_DATA; + + /* get cal data */ + get_vocproc_cal(&cal_data); + if (cal_data.num_cal_blocks == 0) { + pr_aud_err("%s: No calibration data to send!\n", __func__); + goto done; + } + + /* send cal to modem */ + cmd_buf = kzalloc((sizeof(struct apr_hdr) + BUFFER_PAYLOAD_SIZE), + GFP_KERNEL); + if (!cmd_buf) { + pr_aud_err("No memory is allocated.\n"); + return -ENOMEM; + } + pr_debug("----- num_cal_blocks=%d\n", (s32)cal_data.num_cal_blocks); + cal_blk = cal_data.cal_blocks; + pr_debug(" cal_blk =%x\n", (uint32_t)cal_data.cal_blocks); + + for (; index < cal_data.num_cal_blocks; index++) { + cal_size_per_network = cal_blk[index].cal_size; + if (cal_size_per_network >= BUFFER_PAYLOAD_SIZE) + pr_aud_err("Cal size is too big\n"); + pr_debug(" cal size =%d\n", cal_size_per_network); + cal_data_per_network = (u32 *)cal_blk[index].cal_kvaddr; + pr_debug(" cal data=%x\n", (uint32_t)cal_data_per_network); + + cvp_cal_cmd_hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + cal_size_per_network); + memcpy(cmd_buf, &cvp_cal_cmd_hdr, APR_HDR_SIZE); + memcpy(cmd_buf + (APR_HDR_SIZE / sizeof(*cmd_buf)), + cal_data_per_network, cal_size_per_network); + pr_debug("Send cvp cal\n"); + v->cvp_state = CMD_STATUS_FAIL; + pr_aud_info("%s: CVP calib\n", __func__); + ret = apr_send_pkt(apr_cvp, cmd_buf); + if (ret < 0) { + pr_aud_err("Fail: sending cvp cal, idx=%d\n", index); + continue; + } + ret = wait_event_timeout(v->cvp_wait, + (v->cvp_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_aud_err("%s: wait_event timeout\n", __func__); + return -EINVAL; + } + } + kfree(cmd_buf); +done: + return 0; +} + +static int voice_send_cvp_vol_tbl_to_modem(struct voice_data *v) +{ + struct apr_hdr cvp_vol_cal_cmd_hdr = {0}; + uint32_t *cmd_buf; + struct acdb_cal_data cal_data; + struct acdb_cal_block *cal_blk; + int32_t cal_size_per_network; + uint32_t *cal_data_per_network; + int index = 0; + int ret = 0; + void *apr_cvp = voice_get_apr_cvp(v); + u16 cvp_handle = voice_get_cvp_handle(v); + + + /* fill the header */ + cvp_vol_cal_cmd_hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + cvp_vol_cal_cmd_hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvp_vol_cal_cmd_hdr) - APR_HDR_SIZE); + cvp_vol_cal_cmd_hdr.src_port = 0; + cvp_vol_cal_cmd_hdr.dest_port = cvp_handle; + cvp_vol_cal_cmd_hdr.token = 0; + cvp_vol_cal_cmd_hdr.opcode = + VSS_IVOCPROC_CMD_CACHE_VOLUME_CALIBRATION_TABLE; + + /* get cal data */ + get_vocvol_cal(&cal_data); + if (cal_data.num_cal_blocks == 0) { + pr_aud_err("%s: No calibration data to send!\n", __func__); + goto done; + } + + /* send cal to modem */ + cmd_buf = kzalloc((sizeof(struct apr_hdr) + BUFFER_PAYLOAD_SIZE), + GFP_KERNEL); + if (!cmd_buf) { + pr_aud_err("No memory is allocated.\n"); + return -ENOMEM; + } + pr_debug("----- num_cal_blocks=%d\n", (s32)cal_data.num_cal_blocks); + cal_blk = cal_data.cal_blocks; + pr_debug("Cal_blk =%x\n", (uint32_t)cal_data.cal_blocks); + + for (; index < cal_data.num_cal_blocks; index++) { + cal_size_per_network = cal_blk[index].cal_size; + cal_data_per_network = (u32 *)cal_blk[index].cal_kvaddr; + pr_debug("Cal size =%d, index=%d\n", cal_size_per_network, + index); + pr_debug("Cal data=%x\n", (uint32_t)cal_data_per_network); + cvp_vol_cal_cmd_hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + cal_size_per_network); + memcpy(cmd_buf, &cvp_vol_cal_cmd_hdr, APR_HDR_SIZE); + memcpy(cmd_buf + (APR_HDR_SIZE / sizeof(uint32_t)), + cal_data_per_network, cal_size_per_network); + pr_debug("Send vol table\n"); + + v->cvp_state = CMD_STATUS_FAIL; + ret = apr_send_pkt(apr_cvp, cmd_buf); + if (ret < 0) { + pr_aud_err("Fail: sending cvp vol cal, idx=%d\n", index); + continue; + } + ret = wait_event_timeout(v->cvp_wait, + (v->cvp_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_aud_err("%s: wait_event timeout\n", __func__); + return -EINVAL; + } + } + kfree(cmd_buf); +done: + return 0; +} + +static int voice_set_dtx(struct voice_data *v) +{ + int ret = 0; + void *apr_cvs = voice_get_apr_cvs(v); + u16 cvs_handle = voice_get_cvs_handle(v); + + /* Set DTX */ + struct cvs_set_enc_dtx_mode_cmd cvs_set_dtx = { + .hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER), + .hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvs_set_dtx) - APR_HDR_SIZE), + .hdr.src_port = 0, + .hdr.dest_port = cvs_handle, + .hdr.token = 0, + .hdr.opcode = VSS_ISTREAM_CMD_SET_ENC_DTX_MODE, + .dtx_mode.enable = v->mvs_info.dtx_mode, + }; + + pr_debug("%s: Setting DTX %d\n", __func__, v->mvs_info.dtx_mode); + + v->cvs_state = CMD_STATUS_FAIL; + + ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_set_dtx); + if (ret < 0) { + pr_aud_err("%s: Error %d sending SET_DTX\n", __func__, ret); + + goto done; + } + + ret = wait_event_timeout(v->cvs_wait, + (v->cvs_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_aud_err("%s: wait_event timeout\n", __func__); + + ret = -EINVAL; + } + +done: + return ret; +} + +static int voice_config_cvs_vocoder(struct voice_data *v) +{ + int ret = 0; + void *apr_cvs = voice_get_apr_cvs(v); + u16 cvs_handle = voice_get_cvs_handle(v); + + /* Set media type. */ + struct cvs_set_media_type_cmd cvs_set_media_cmd; + + pr_aud_info("%s: Setting media type\n", __func__); + + cvs_set_media_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + cvs_set_media_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvs_set_media_cmd) - APR_HDR_SIZE); + cvs_set_media_cmd.hdr.src_port = 0; + cvs_set_media_cmd.hdr.dest_port = cvs_handle; + cvs_set_media_cmd.hdr.token = 0; + cvs_set_media_cmd.hdr.opcode = VSS_ISTREAM_CMD_SET_MEDIA_TYPE; + cvs_set_media_cmd.media_type.tx_media_id = v->mvs_info.media_type; + cvs_set_media_cmd.media_type.rx_media_id = v->mvs_info.media_type; + + v->cvs_state = CMD_STATUS_FAIL; + + ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_set_media_cmd); + if (ret < 0) { + pr_aud_err("%s: Error %d sending SET_MEDIA_TYPE\n", + __func__, ret); + + goto done; + } + + ret = wait_event_timeout(v->cvs_wait, + (v->cvs_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_aud_err("%s: wait_event timeout\n", __func__); + + ret = -EINVAL; + goto done; + } + + /* Set encoder properties. */ + switch (v->mvs_info.media_type) { + case VSS_MEDIA_ID_EVRC_MODEM: { + struct cvs_set_cdma_enc_minmax_rate_cmd cvs_set_cdma_rate; + + pr_aud_info("%s: Setting EVRC min-max rate\n", __func__); + + cvs_set_cdma_rate.hdr.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + cvs_set_cdma_rate.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvs_set_cdma_rate) - APR_HDR_SIZE); + cvs_set_cdma_rate.hdr.src_port = 0; + cvs_set_cdma_rate.hdr.dest_port = cvs_handle; + cvs_set_cdma_rate.hdr.token = 0; + cvs_set_cdma_rate.hdr.opcode = + VSS_ISTREAM_CMD_CDMA_SET_ENC_MINMAX_RATE; + cvs_set_cdma_rate.cdma_rate.min_rate = v->mvs_info.rate; + cvs_set_cdma_rate.cdma_rate.max_rate = v->mvs_info.rate; + + v->cvs_state = CMD_STATUS_FAIL; + + ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_set_cdma_rate); + if (ret < 0) { + pr_aud_err("%s: Error %d sending SET_EVRC_MINMAX_RATE\n", + __func__, ret); + + goto done; + } + + ret = wait_event_timeout(v->cvs_wait, + (v->cvs_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_aud_err("%s: wait_event timeout\n", __func__); + + ret = -EINVAL; + goto done; + } + + break; + } + + case VSS_MEDIA_ID_AMR_NB_MODEM: { + struct cvs_set_amr_enc_rate_cmd cvs_set_amr_rate; + + pr_aud_info("%s: Setting AMR rate\n", __func__); + + cvs_set_amr_rate.hdr.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + cvs_set_amr_rate.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvs_set_amr_rate) - APR_HDR_SIZE); + cvs_set_amr_rate.hdr.src_port = 0; + cvs_set_amr_rate.hdr.dest_port = cvs_handle; + cvs_set_amr_rate.hdr.token = 0; + cvs_set_amr_rate.hdr.opcode = + VSS_ISTREAM_CMD_VOC_AMR_SET_ENC_RATE; + cvs_set_amr_rate.amr_rate.mode = v->mvs_info.rate; + + v->cvs_state = CMD_STATUS_FAIL; + + ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_set_amr_rate); + if (ret < 0) { + pr_aud_err("%s: Error %d sending SET_AMR_RATE\n", + __func__, ret); + + goto done; + } + + ret = wait_event_timeout(v->cvs_wait, + (v->cvs_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_aud_err("%s: wait_event timeout\n", __func__); + + ret = -EINVAL; + goto done; + } + + ret = voice_set_dtx(v); + + break; + } + + case VSS_MEDIA_ID_AMR_WB_MODEM: { + struct cvs_set_amrwb_enc_rate_cmd cvs_set_amrwb_rate; + + pr_aud_info("%s: Setting AMR WB rate\n", __func__); + + cvs_set_amrwb_rate.hdr.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + cvs_set_amrwb_rate.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvs_set_amrwb_rate) - APR_HDR_SIZE); + cvs_set_amrwb_rate.hdr.src_port = 0; + cvs_set_amrwb_rate.hdr.dest_port = cvs_handle; + cvs_set_amrwb_rate.hdr.token = 0; + cvs_set_amrwb_rate.hdr.opcode = + VSS_ISTREAM_CMD_VOC_AMRWB_SET_ENC_RATE; + cvs_set_amrwb_rate.amrwb_rate.mode = v->mvs_info.rate; + + v->cvs_state = CMD_STATUS_FAIL; + + ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_set_amrwb_rate); + if (ret < 0) { + pr_aud_err("%s: Error %d sending SET_AMRWB_RATE\n", + __func__, ret); + + goto done; + } + + ret = wait_event_timeout(v->cvs_wait, + (v->cvs_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_aud_err("%s: wait_event timeout\n", __func__); + + ret = -EINVAL; + goto done; + } + + ret = voice_set_dtx(v); + + break; + } + + case VSS_MEDIA_ID_G729: + case VSS_MEDIA_ID_G711_ALAW: + case VSS_MEDIA_ID_G711_MULAW: { + ret = voice_set_dtx(v); + + break; + } + + default: { + /* Do nothing. */ + } + } + +done: + return ret; +} + +static int voice_send_start_voice_cmd(struct voice_data *v) +{ + struct apr_hdr mvm_start_voice_cmd; + int ret = 0; + void *apr_mvm = voice_get_apr_mvm(v); + u16 mvm_handle = voice_get_mvm_handle(v); + + mvm_start_voice_cmd.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + mvm_start_voice_cmd.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(mvm_start_voice_cmd) - APR_HDR_SIZE); + AUD_LOG("send mvm_start_voice_cmd pkt size = %d\n", + mvm_start_voice_cmd.pkt_size); + mvm_start_voice_cmd.src_port = 0; + mvm_start_voice_cmd.dest_port = mvm_handle; + mvm_start_voice_cmd.token = 0; + mvm_start_voice_cmd.opcode = VSS_IMVM_CMD_START_VOICE; + + v->mvm_state = CMD_STATUS_FAIL; + ret = apr_send_pkt(apr_mvm, (uint32_t *) &mvm_start_voice_cmd); + if (ret < 0) { + pr_aud_err("Fail in sending VSS_IMVM_CMD_START_VOICE\n"); + goto fail; + } + ret = wait_event_timeout(v->mvm_wait, + (v->mvm_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_aud_err("%s: wait_event timeout\n", __func__); + goto fail; + } + + return 0; +fail: + return -EINVAL; +} + +static int voice_disable_vocproc(struct voice_data *v) +{ + struct apr_hdr cvp_disable_cmd; + int ret = 0; + void *apr_cvp = voice_get_apr_cvp(v); + u16 cvp_handle = voice_get_cvp_handle(v); + + /* disable vocproc and wait for respose */ + cvp_disable_cmd.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + cvp_disable_cmd.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvp_disable_cmd) - APR_HDR_SIZE); + pr_debug("cvp_disable_cmd pkt size = %d, cvp_handle=%d\n", + cvp_disable_cmd.pkt_size, cvp_handle); + cvp_disable_cmd.src_port = 0; + cvp_disable_cmd.dest_port = cvp_handle; + cvp_disable_cmd.token = 0; + cvp_disable_cmd.opcode = VSS_IVOCPROC_CMD_DISABLE; + + v->cvp_state = CMD_STATUS_FAIL; + ret = apr_send_pkt(apr_cvp, (uint32_t *) &cvp_disable_cmd); + if (ret < 0) { + pr_aud_err("Fail in sending VSS_IVOCPROC_CMD_DISABLE\n"); + goto fail; + } + ret = wait_event_timeout(v->cvp_wait, + (v->cvp_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_aud_err("%s: wait_event timeout\n", __func__); + goto fail; + } +#ifdef CONFIG_MSM8X60_RTAC + rtac_remove_voice(v); +#endif + + return 0; +fail: + return -EINVAL; +} + +static int voice_set_device(struct voice_data *v) +{ + struct cvp_set_device_cmd cvp_setdev_cmd; + struct msm_snddev_info *dev_tx_info; + int ret = 0; + void *apr_cvp = voice_get_apr_cvp(v); + u16 cvp_handle = voice_get_cvp_handle(v); + + + /* set device and wait for response */ + cvp_setdev_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + cvp_setdev_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvp_setdev_cmd) - APR_HDR_SIZE); + pr_debug(" send create cvp setdev, pkt size = %d\n", + cvp_setdev_cmd.hdr.pkt_size); + cvp_setdev_cmd.hdr.src_port = 0; + cvp_setdev_cmd.hdr.dest_port = cvp_handle; + cvp_setdev_cmd.hdr.token = 0; + cvp_setdev_cmd.hdr.opcode = VSS_IVOCPROC_CMD_SET_DEVICE; + + dev_tx_info = audio_dev_ctrl_find_dev(v->dev_tx.dev_id); + if (IS_ERR(dev_tx_info)) { + pr_aud_err("bad dev_id %d\n", v->dev_tx.dev_id); + goto fail; + } + + if (dev_tx_info->channel_mode > 1) + cvp_setdev_cmd.cvp_set_device.tx_topology_id = + VSS_IVOCPROC_TOPOLOGY_ID_TX_DM_FLUENCE; + else + cvp_setdev_cmd.cvp_set_device.tx_topology_id = + VSS_IVOCPROC_TOPOLOGY_ID_TX_SM_ECNS; + cvp_setdev_cmd.cvp_set_device.rx_topology_id = + VSS_IVOCPROC_TOPOLOGY_ID_RX_DEFAULT; + cvp_setdev_cmd.cvp_set_device.tx_port_id = v->dev_tx.dev_port_id; + cvp_setdev_cmd.cvp_set_device.rx_port_id = v->dev_rx.dev_port_id; + pr_aud_info("topology=%d , tx_port_id=%d, rx_port_id=%d\n", + cvp_setdev_cmd.cvp_set_device.tx_topology_id, + cvp_setdev_cmd.cvp_set_device.tx_port_id, + cvp_setdev_cmd.cvp_set_device.rx_port_id); + + v->cvp_state = CMD_STATUS_FAIL; + ret = apr_send_pkt(apr_cvp, (uint32_t *) &cvp_setdev_cmd); + if (ret < 0) { + pr_aud_err("Fail in sending VOCPROC_FULL_CONTROL_SESSION\n"); + goto fail; + } + pr_debug("wait for cvp create session event\n"); + ret = wait_event_timeout(v->cvp_wait, + (v->cvp_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_aud_err("%s: wait_event timeout\n", __func__); + goto fail; + } + + /* send cvs cal */ + voice_send_cvs_cal_to_modem(v); + + /* send cvp cal */ + voice_send_cvp_cal_to_modem(v); + + /* send cvp vol table cal */ + voice_send_cvp_vol_tbl_to_modem(v); + + /* enable vocproc and wait for respose */ + voice_send_enable_vocproc_cmd(v); + + /* send tty mode if tty device is used */ + voice_send_tty_mode_to_modem(v); + + if (v->voc_path == VOC_PATH_FULL) + voice_send_netid_timing_cmd(v); + +#ifdef CONFIG_MSM8X60_RTAC + rtac_add_voice(v); +#endif + + return 0; +fail: + return -EINVAL; +} + +static int voice_send_stop_voice_cmd(struct voice_data *v) +{ + struct apr_hdr mvm_stop_voice_cmd; + int ret = 0; + void *apr_mvm = voice_get_apr_mvm(v); + u16 mvm_handle = voice_get_mvm_handle(v); + + mvm_stop_voice_cmd.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + mvm_stop_voice_cmd.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(mvm_stop_voice_cmd) - APR_HDR_SIZE); + AUD_LOG("send mvm_stop_voice_cmd pkt size = %d\n", + mvm_stop_voice_cmd.pkt_size); + mvm_stop_voice_cmd.src_port = 0; + mvm_stop_voice_cmd.dest_port = mvm_handle; + mvm_stop_voice_cmd.token = 0; + mvm_stop_voice_cmd.opcode = VSS_IMVM_CMD_STOP_VOICE; + + v->mvm_state = CMD_STATUS_FAIL; + ret = apr_send_pkt(apr_mvm, (uint32_t *) &mvm_stop_voice_cmd); + if (ret < 0) { + pr_aud_err("Fail in sending VSS_IMVM_CMD_STOP_VOICE\n"); + goto fail; + } + ret = wait_event_timeout(v->mvm_wait, + (v->mvm_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_aud_err("%s: wait_event timeout\n", __func__); + goto fail; + } + + return 0; +fail: + return -EINVAL; +} + +static int voice_setup_modem_voice(struct voice_data *v) +{ + struct cvp_create_full_ctl_session_cmd cvp_session_cmd; + int ret = 0; + struct msm_snddev_info *dev_tx_info; + void *apr_cvp = voice_get_apr_cvp(v); + + /* create cvp session and wait for response */ + cvp_session_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + cvp_session_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvp_session_cmd) - APR_HDR_SIZE); + pr_aud_info(" send create cvp session, pkt size = %d\n", + cvp_session_cmd.hdr.pkt_size); + cvp_session_cmd.hdr.src_port = 0; + cvp_session_cmd.hdr.dest_port = 0; + cvp_session_cmd.hdr.token = 0; + cvp_session_cmd.hdr.opcode = + VSS_IVOCPROC_CMD_CREATE_FULL_CONTROL_SESSION; + + dev_tx_info = audio_dev_ctrl_find_dev(v->dev_tx.dev_id); + if (IS_ERR(dev_tx_info)) { + pr_aud_err("bad dev_id %d\n", v->dev_tx.dev_id); + goto fail; + } + + if (dev_tx_info->channel_mode > 1) + cvp_session_cmd.cvp_session.tx_topology_id = + VSS_IVOCPROC_TOPOLOGY_ID_TX_DM_FLUENCE; + else + cvp_session_cmd.cvp_session.tx_topology_id = + VSS_IVOCPROC_TOPOLOGY_ID_TX_SM_ECNS; + cvp_session_cmd.cvp_session.rx_topology_id = + VSS_IVOCPROC_TOPOLOGY_ID_RX_DEFAULT; + cvp_session_cmd.cvp_session.direction = 2; /*tx and rx*/ + cvp_session_cmd.cvp_session.network_id = VSS_NETWORK_ID_DEFAULT; + cvp_session_cmd.cvp_session.tx_port_id = v->dev_tx.dev_port_id; + cvp_session_cmd.cvp_session.rx_port_id = v->dev_rx.dev_port_id; + pr_aud_info("topology=%d net_id=%d, dir=%d tx_port_id=%d, rx_port_id=%d\n", + cvp_session_cmd.cvp_session.tx_topology_id, + cvp_session_cmd.cvp_session.network_id, + cvp_session_cmd.cvp_session.direction, + cvp_session_cmd.cvp_session.tx_port_id, + cvp_session_cmd.cvp_session.rx_port_id); + + v->cvp_state = CMD_STATUS_FAIL; + ret = apr_send_pkt(apr_cvp, (uint32_t *) &cvp_session_cmd); + if (ret < 0) { + pr_aud_err("Fail in sending VOCPROC_FULL_CONTROL_SESSION\n"); + goto fail; + } + pr_debug("wait for cvp create session event\n"); + ret = wait_event_timeout(v->cvp_wait, + (v->cvp_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_aud_err("%s: wait_event timeout\n", __func__); + goto fail; + } + + /* send cvs cal */ + voice_send_cvs_cal_to_modem(v); + + /* send cvp cal */ + voice_send_cvp_cal_to_modem(v); + + /* send cvp vol table cal */ + voice_send_cvp_vol_tbl_to_modem(v); + + return 0; + +fail: + return -EINVAL; +} + +static int voice_send_enable_vocproc_cmd(struct voice_data *v) +{ + int ret = 0; + struct apr_hdr cvp_enable_cmd; + + u16 cvp_handle = voice_get_cvp_handle(v); + void *apr_cvp = voice_get_apr_cvp(v); + + /* enable vocproc and wait for respose */ + cvp_enable_cmd.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + cvp_enable_cmd.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvp_enable_cmd) - APR_HDR_SIZE); + pr_debug("cvp_enable_cmd pkt size = %d, cvp_handle=%d\n", + cvp_enable_cmd.pkt_size, cvp_handle); + cvp_enable_cmd.src_port = 0; + cvp_enable_cmd.dest_port = cvp_handle; + cvp_enable_cmd.token = 0; + cvp_enable_cmd.opcode = VSS_IVOCPROC_CMD_ENABLE; + + v->cvp_state = CMD_STATUS_FAIL; + ret = apr_send_pkt(apr_cvp, (uint32_t *) &cvp_enable_cmd); + if (ret < 0) { + pr_aud_err("Fail in sending VSS_IVOCPROC_CMD_ENABLE\n"); + goto fail; + } + ret = wait_event_timeout(v->cvp_wait, + (v->cvp_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_aud_err("%s: wait_event timeout\n", __func__); + goto fail; + } + + return 0; +fail: + return -EINVAL; +} + +static int voice_send_netid_timing_cmd(struct voice_data *v) +{ + int ret = 0; + void *apr_mvm = voice_get_apr_mvm(v); + struct mvm_set_network_cmd mvm_set_network; + struct mvm_set_voice_timing_cmd mvm_set_voice_timing; + u16 mvm_handle = voice_get_mvm_handle(v); + + ret = voice_config_cvs_vocoder(v); + if (ret < 0) { + pr_aud_err("%s: Error %d configuring CVS voc", + __func__, ret); + goto fail; + } + /* Set network ID. */ + pr_debug("%s: Setting network ID\n", __func__); + + mvm_set_network.hdr.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + mvm_set_network.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(mvm_set_network) - APR_HDR_SIZE); + mvm_set_network.hdr.src_port = 0; + mvm_set_network.hdr.dest_port = mvm_handle; + mvm_set_network.hdr.token = 0; + mvm_set_network.hdr.opcode = VSS_ICOMMON_CMD_SET_NETWORK; + mvm_set_network.network.network_id = v->mvs_info.network_type; + + v->mvm_state = CMD_STATUS_FAIL; + ret = apr_send_pkt(apr_mvm, (uint32_t *) &mvm_set_network); + if (ret < 0) { + pr_aud_err("%s: Error %d sending SET_NETWORK\n", __func__, ret); + goto fail; + } + + ret = wait_event_timeout(v->mvm_wait, + (v->mvm_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_aud_err("%s: wait_event timeout\n", __func__); + goto fail; + } + + /* Set voice timing. */ + pr_debug("%s: Setting voice timing\n", __func__); + + mvm_set_voice_timing.hdr.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + mvm_set_voice_timing.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(mvm_set_voice_timing) - APR_HDR_SIZE); + mvm_set_voice_timing.hdr.src_port = 0; + mvm_set_voice_timing.hdr.dest_port = mvm_handle; + mvm_set_voice_timing.hdr.token = 0; + mvm_set_voice_timing.hdr.opcode = + VSS_ICOMMON_CMD_SET_VOICE_TIMING; + mvm_set_voice_timing.timing.mode = 0; + mvm_set_voice_timing.timing.enc_offset = 8000; + mvm_set_voice_timing.timing.dec_req_offset = 3300; + mvm_set_voice_timing.timing.dec_offset = 8300; + + v->mvm_state = CMD_STATUS_FAIL; + + ret = apr_send_pkt(apr_mvm, (uint32_t *) &mvm_set_voice_timing); + if (ret < 0) { + pr_aud_err("%s: Error %d sending SET_TIMING\n", __func__, ret); + goto fail; + } + + ret = wait_event_timeout(v->mvm_wait, + (v->mvm_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_aud_err("%s: wait_event timeout\n", __func__); + goto fail; + } + + return 0; +fail: + return -EINVAL; +} + +static int voice_attach_vocproc(struct voice_data *v) +{ + int ret = 0; + struct mvm_attach_vocproc_cmd mvm_a_vocproc_cmd; + void *apr_mvm = voice_get_apr_mvm(v); + u16 mvm_handle = voice_get_mvm_handle(v); + u16 cvp_handle = voice_get_cvp_handle(v); + + /* send enable vocproc */ + voice_send_enable_vocproc_cmd(v); + + /* attach vocproc and wait for response */ + mvm_a_vocproc_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + mvm_a_vocproc_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(mvm_a_vocproc_cmd) - APR_HDR_SIZE); + pr_aud_info("send mvm_a_vocproc_cmd pkt size = %d\n", + mvm_a_vocproc_cmd.hdr.pkt_size); + mvm_a_vocproc_cmd.hdr.src_port = 0; + mvm_a_vocproc_cmd.hdr.dest_port = mvm_handle; + mvm_a_vocproc_cmd.hdr.token = 0; + mvm_a_vocproc_cmd.hdr.opcode = VSS_ISTREAM_CMD_ATTACH_VOCPROC; + mvm_a_vocproc_cmd.mvm_attach_cvp_handle.handle = cvp_handle; + + v->mvm_state = CMD_STATUS_FAIL; + ret = apr_send_pkt(apr_mvm, (uint32_t *) &mvm_a_vocproc_cmd); + if (ret < 0) { + pr_aud_err("Fail in sending VSS_ISTREAM_CMD_ATTACH_VOCPROC\n"); + goto fail; + } + ret = wait_event_timeout(v->mvm_wait, + (v->mvm_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_aud_err("%s: wait_event timeout\n", __func__); + goto fail; + } + + /* send tty mode if tty device is used */ + voice_send_tty_mode_to_modem(v); + + if (v->voc_path == VOC_PATH_FULL) + voice_send_netid_timing_cmd(v); + +#ifdef CONFIG_MSM8X60_RTAC + rtac_add_voice(v); +#endif + return 0; +fail: + return -EINVAL; +} + +static int voice_destroy_modem_voice(struct voice_data *v) +{ + struct mvm_detach_vocproc_cmd mvm_d_vocproc_cmd; + struct apr_hdr cvp_destroy_session_cmd; + int ret = 0; + void *apr_mvm = voice_get_apr_mvm(v); + void *apr_cvp = voice_get_apr_cvp(v); + u16 mvm_handle = voice_get_mvm_handle(v); + u16 cvp_handle = voice_get_cvp_handle(v); + + /* detach VOCPROC and wait for response from mvm */ + mvm_d_vocproc_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + mvm_d_vocproc_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(mvm_d_vocproc_cmd) - APR_HDR_SIZE); + pr_aud_info("mvm_d_vocproc_cmd pkt size = %d\n", + mvm_d_vocproc_cmd.hdr.pkt_size); + mvm_d_vocproc_cmd.hdr.src_port = 0; + mvm_d_vocproc_cmd.hdr.dest_port = mvm_handle; + mvm_d_vocproc_cmd.hdr.token = 0; + mvm_d_vocproc_cmd.hdr.opcode = VSS_ISTREAM_CMD_DETACH_VOCPROC; + mvm_d_vocproc_cmd.mvm_detach_cvp_handle.handle = cvp_handle; + + v->mvm_state = CMD_STATUS_FAIL; + ret = apr_send_pkt(apr_mvm, (uint32_t *) &mvm_d_vocproc_cmd); + if (ret < 0) { + pr_aud_err("Fail in sending VSS_ISTREAM_CMD_DETACH_VOCPROC\n"); + goto fail; + } + ret = wait_event_timeout(v->mvm_wait, + (v->mvm_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_aud_err("%s: wait_event timeout\n", __func__); + goto fail; + } + + /* destrop cvp session */ + cvp_destroy_session_cmd.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + cvp_destroy_session_cmd.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvp_destroy_session_cmd) - APR_HDR_SIZE); + pr_aud_info("cvp_destroy_session_cmd pkt size = %d\n", + cvp_destroy_session_cmd.pkt_size); + cvp_destroy_session_cmd.src_port = 0; + cvp_destroy_session_cmd.dest_port = cvp_handle; + cvp_destroy_session_cmd.token = 0; + cvp_destroy_session_cmd.opcode = APRV2_IBASIC_CMD_DESTROY_SESSION; + + v->cvp_state = CMD_STATUS_FAIL; + ret = apr_send_pkt(apr_cvp, (uint32_t *) &cvp_destroy_session_cmd); + if (ret < 0) { + pr_aud_err("Fail in sending APRV2_IBASIC_CMD_DESTROY_SESSION\n"); + goto fail; + } + ret = wait_event_timeout(v->cvp_wait, + (v->cvp_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_aud_err("%s: wait_event timeout\n", __func__); + goto fail; + } + +#ifdef CONFIG_MSM8X60_RTAC + rtac_remove_voice(v); +#endif + cvp_handle = 0; + voice_set_cvp_handle(v, cvp_handle); + + return 0; + +fail: + return -EINVAL; +} + +static int voice_send_mute_cmd_to_modem(struct voice_data *v) +{ + struct cvs_set_mute_cmd cvs_mute_cmd; + int ret = 0; + void *apr_cvs = voice_get_apr_cvs(v); + u16 cvs_handle = voice_get_cvs_handle(v); + + /* send mute/unmute to cvs */ + cvs_mute_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + cvs_mute_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvs_mute_cmd) - APR_HDR_SIZE); + cvs_mute_cmd.hdr.src_port = 0; + cvs_mute_cmd.hdr.dest_port = cvs_handle; + cvs_mute_cmd.hdr.token = 0; + cvs_mute_cmd.hdr.opcode = VSS_ISTREAM_CMD_SET_MUTE; + cvs_mute_cmd.cvs_set_mute.direction = 0; /*tx*/ + cvs_mute_cmd.cvs_set_mute.mute_flag = v->dev_tx.mute; + + pr_aud_info(" mute value =%d\n", cvs_mute_cmd.cvs_set_mute.mute_flag); + v->cvs_state = CMD_STATUS_FAIL; + ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_mute_cmd); + if (ret < 0) { + pr_aud_err("Fail: send STREAM SET MUTE\n"); + goto fail; + } + ret = wait_event_timeout(v->cvs_wait, + (v->cvs_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) + pr_aud_err("%s: wait_event timeout\n", __func__); + +fail: + return 0; +} + +static int voice_send_vol_index_to_modem(struct voice_data *v) +{ + struct cvp_set_rx_volume_index_cmd cvp_vol_cmd; + int ret = 0; + void *apr_cvp = voice_get_apr_cvp(v); + u16 cvp_handle = voice_get_cvp_handle(v); + + /* send volume index to cvp */ + cvp_vol_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + cvp_vol_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvp_vol_cmd) - APR_HDR_SIZE); + cvp_vol_cmd.hdr.src_port = 0; + cvp_vol_cmd.hdr.dest_port = cvp_handle; + cvp_vol_cmd.hdr.token = 0; + cvp_vol_cmd.hdr.opcode = + VSS_IVOCPROC_CMD_SET_RX_VOLUME_INDEX; + cvp_vol_cmd.cvp_set_vol_idx.vol_index = v->dev_rx.volume; + v->cvp_state = CMD_STATUS_FAIL; + ret = apr_send_pkt(apr_cvp, (uint32_t *) &cvp_vol_cmd); + if (ret < 0) { + pr_aud_err("Fail in sending RX VOL INDEX\n"); + return -EINVAL; + } + ret = wait_event_timeout(v->cvp_wait, + (v->cvp_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_aud_err("%s: wait_event timeout\n", __func__); + return -EINVAL; + } + return 0; +} + +static int voice_cvs_start_record(struct voice_data *v, uint32_t rec_mode) +{ + int ret = 0; + void *apr_cvs = voice_get_apr_cvs(v); + u16 cvs_handle = voice_get_cvs_handle(v); + struct cvs_start_record_cmd cvs_start_record; + + pr_debug("%s: Start record %d\n", __func__, rec_mode); + + cvs_start_record.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + cvs_start_record.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvs_start_record) - APR_HDR_SIZE); + cvs_start_record.hdr.src_port = 0; + cvs_start_record.hdr.dest_port = cvs_handle; + cvs_start_record.hdr.token = 0; + cvs_start_record.hdr.opcode = VSS_ISTREAM_CMD_START_RECORD; + + if (rec_mode == VOC_REC_UPLINK) { + cvs_start_record.rec_mode.rx_tap_point = VSS_TAP_POINT_NONE; + cvs_start_record.rec_mode.tx_tap_point = + VSS_TAP_POINT_STREAM_END; + } else if (rec_mode == VOC_REC_DOWNLINK) { + cvs_start_record.rec_mode.rx_tap_point = + VSS_TAP_POINT_STREAM_END; + cvs_start_record.rec_mode.tx_tap_point = VSS_TAP_POINT_NONE; + } else if (rec_mode == VOC_REC_BOTH) { + cvs_start_record.rec_mode.rx_tap_point = + VSS_TAP_POINT_STREAM_END; + cvs_start_record.rec_mode.tx_tap_point = + VSS_TAP_POINT_STREAM_END; + } else { + pr_aud_err("%s: Invalid in-call rec_mode %d\n", __func__, rec_mode); + + ret = -EINVAL; + goto fail; + } + + v->cvs_state = CMD_STATUS_FAIL; + + ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_start_record); + if (ret < 0) { + pr_aud_err("%s: Error %d sending START_RECORD\n", __func__, ret); + + goto fail; + } + + ret = wait_event_timeout(v->cvs_wait, + (v->cvs_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_aud_err("%s: wait_event timeout\n", __func__); + + goto fail; + } + + return 0; + +fail: + return ret; +} + +static int voice_cvs_stop_record(struct voice_data *v) +{ + int ret = 0; + void *apr_cvs = voice_get_apr_cvs(v); + u16 cvs_handle = voice_get_cvs_handle(v); + struct apr_hdr cvs_stop_record; + + pr_debug("%s: Stop record\n", __func__); + + cvs_stop_record.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + cvs_stop_record.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvs_stop_record) - APR_HDR_SIZE); + cvs_stop_record.src_port = 0; + cvs_stop_record.dest_port = cvs_handle; + cvs_stop_record.token = 0; + cvs_stop_record.opcode = VSS_ISTREAM_CMD_STOP_RECORD; + + v->cvs_state = CMD_STATUS_FAIL; + + ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_stop_record); + if (ret < 0) { + pr_aud_err("%s: Error %d sending STOP_RECORD\n", __func__, ret); + + goto fail; + } + + ret = wait_event_timeout(v->cvs_wait, + (v->cvs_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_aud_err("%s: wait_event timeout\n", __func__); + + goto fail; + } + + return 0; + +fail: + return ret; +} + +int voice_start_record(uint32_t rec_mode, uint32_t set) +{ + int ret = 0; + u16 cvs_handle; + + pr_debug("%s: rec_mode %d, set %d\n", __func__, rec_mode, set); + + mutex_lock(&voice.lock); + + cvs_handle = voice_get_cvs_handle(&voice); + + if (cvs_handle != 0) { + if (set) + ret = voice_cvs_start_record(&voice, rec_mode); + else + ret = voice_cvs_stop_record(&voice); + } else { + /* Cache the value for later. */ + voice.rec_info.pending = set; + voice.rec_info.rec_mode = rec_mode; + } + + mutex_unlock(&voice.lock); + + return ret; +} + +static int voice_cvs_start_playback(struct voice_data *v) +{ + int ret = 0; + void *apr_cvs = voice_get_apr_cvs(v); + u16 cvs_handle = voice_get_cvs_handle(v); + struct apr_hdr cvs_start_playback; + + pr_debug("%s: Start playback\n", __func__); + + cvs_start_playback.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + cvs_start_playback.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvs_start_playback) - APR_HDR_SIZE); + cvs_start_playback.src_port = 0; + cvs_start_playback.dest_port = cvs_handle; + cvs_start_playback.token = 0; + cvs_start_playback.opcode = VSS_ISTREAM_CMD_START_PLAYBACK; + + v->cvs_state = CMD_STATUS_FAIL; + + ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_start_playback); + if (ret < 0) { + pr_aud_err("%s: Error %d sending START_PLAYBACK\n", + __func__, ret); + + goto fail; + } + + ret = wait_event_timeout(v->cvs_wait, + (v->cvs_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_aud_err("%s: wait_event timeout\n", __func__); + + goto fail; + } + + v->music_info.playing = 1; + + return 0; + +fail: + return ret; +} + +static int voice_cvs_stop_playback(struct voice_data *v) +{ + int ret = 0; + void *apr_cvs = voice_get_apr_cvs(v); + u16 cvs_handle = voice_get_cvs_handle(v); + struct apr_hdr cvs_stop_playback; + + pr_debug("%s: Stop playback\n", __func__); + + if (v->music_info.playing) { + cvs_stop_playback.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + cvs_stop_playback.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvs_stop_playback) - APR_HDR_SIZE); + cvs_stop_playback.src_port = 0; + cvs_stop_playback.dest_port = cvs_handle; + cvs_stop_playback.token = 0; + + cvs_stop_playback.opcode = VSS_ISTREAM_CMD_STOP_PLAYBACK; + + v->cvs_state = CMD_STATUS_FAIL; + + ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_stop_playback); + if (ret < 0) { + pr_aud_err("%s: Error %d sending STOP_PLAYBACK\n", + __func__, ret); + + goto fail; + } + + ret = wait_event_timeout(v->cvs_wait, + (v->cvs_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_aud_err("%s: wait_event timeout\n", __func__); + + goto fail; + } + + v->music_info.playing = 0; + } else { + pr_aud_err("%s: Stop playback already sent\n", __func__); + } + + return 0; + +fail: + return ret; +} + +int voice_start_playback(uint32_t set) +{ + int ret = 0; + u16 cvs_handle; + + pr_debug("%s: Start playback %d\n", __func__, set); + + mutex_lock(&voice.lock); + + cvs_handle = voice_get_cvs_handle(&voice); + + if (cvs_handle != 0) { + if (set) + ret = voice_cvs_start_playback(&voice); + else + ret = voice_cvs_stop_playback(&voice); + } else { + /* Cache the value for later. */ + pr_debug("%s: Caching ICP value", __func__); + + voice.music_info.pending = set; + } + + mutex_unlock(&voice.lock); + + return ret; +} + +static void voice_auddev_cb_function(u32 evt_id, + union auddev_evt_data *evt_payload, + void *private_data) +{ + struct voice_data *v = &voice; + struct sidetone_cal sidetone_cal_data; + int rc = 0; + pr_aud_info("auddev_cb_function, evt_id=%d,\n", evt_id); + if ((evt_id != AUDDEV_EVT_START_VOICE) || + (evt_id != AUDDEV_EVT_END_VOICE)) { + if (evt_payload == NULL) { + pr_aud_err(" evt_payload is NULL pointer\n"); + return; + } + } + + switch (evt_id) { + case AUDDEV_EVT_START_VOICE: + mutex_lock(&v->lock); + + if ((v->voc_state == VOC_INIT) || + (v->voc_state == VOC_RELEASE)) { + v->v_call_status = VOICE_CALL_START; + if ((v->dev_rx.enabled == VOICE_DEV_ENABLED) + && (v->dev_tx.enabled == VOICE_DEV_ENABLED)) { + rc = voice_apr_register(v); + if (rc < 0) { + mutex_unlock(&v->lock); + pr_aud_err("%s: voice apr registration" + "failed\n", __func__); + return; + } + voice_create_mvm_cvs_session(v); + voice_setup_modem_voice(v); + voice_attach_vocproc(v); + voice_send_start_voice_cmd(v); + get_sidetone_cal(&sidetone_cal_data); + msm_snddev_enable_sidetone( + v->dev_rx.dev_id, + sidetone_cal_data.enable, + sidetone_cal_data.gain); + v->voc_state = VOC_RUN; + + /* Start in-call recording if command was + * pending. */ + if (v->rec_info.pending) { + voice_cvs_start_record(v, + v->rec_info.rec_mode); + + v->rec_info.pending = 0; + } + + /* Start in-call music delivery if command was + * pending. */ + if (v->music_info.pending) { + voice_cvs_start_playback(v); + + v->music_info.pending = 0; + } + } + } + + mutex_unlock(&v->lock); + break; + case AUDDEV_EVT_DEV_CHG_VOICE: + if (v->dev_rx.enabled == VOICE_DEV_ENABLED) + msm_snddev_enable_sidetone(v->dev_rx.dev_id, 0, 0); + v->dev_rx.enabled = VOICE_DEV_DISABLED; + v->dev_tx.enabled = VOICE_DEV_DISABLED; + + mutex_lock(&v->lock); + + if (v->voc_state == VOC_RUN) { + /* send cmd to modem to do voice device change */ + voice_disable_vocproc(v); + v->voc_state = VOC_CHANGE; + } + + mutex_unlock(&v->lock); + break; + case AUDDEV_EVT_DEV_RDY: + mutex_lock(&v->lock); + + if (v->voc_state == VOC_CHANGE) { + /* get port Ids */ + if (evt_payload->voc_devinfo.dev_type == DIR_RX) { + v->dev_rx.dev_port_id = + evt_payload->voc_devinfo.dev_port_id; + v->dev_rx.sample = + evt_payload->voc_devinfo.dev_sample; + v->dev_rx.dev_id = + evt_payload->voc_devinfo.dev_id; + v->dev_rx.enabled = VOICE_DEV_ENABLED; + } else { + v->dev_tx.dev_port_id = + evt_payload->voc_devinfo.dev_port_id; + v->dev_tx.sample = + evt_payload->voc_devinfo.dev_sample; + v->dev_tx.enabled = VOICE_DEV_ENABLED; + v->dev_tx.dev_id = + evt_payload->voc_devinfo.dev_id; + } + if ((v->dev_rx.enabled == VOICE_DEV_ENABLED) && + (v->dev_tx.enabled == VOICE_DEV_ENABLED)) { + voice_set_device(v); + get_sidetone_cal(&sidetone_cal_data); + msm_snddev_enable_sidetone( + v->dev_rx.dev_id, + sidetone_cal_data.enable, + sidetone_cal_data.gain); + v->voc_state = VOC_RUN; + } + } else if ((v->voc_state == VOC_INIT) || + (v->voc_state == VOC_RELEASE)) { + /* get AFE ports */ + if (evt_payload->voc_devinfo.dev_type == DIR_RX) { + /* get rx port id */ + v->dev_rx.dev_port_id = + evt_payload->voc_devinfo.dev_port_id; + v->dev_rx.sample = + evt_payload->voc_devinfo.dev_sample; + v->dev_rx.dev_id = + evt_payload->voc_devinfo.dev_id; + v->dev_rx.enabled = VOICE_DEV_ENABLED; + } else { + /* get tx port id */ + v->dev_tx.dev_port_id = + evt_payload->voc_devinfo.dev_port_id; + v->dev_tx.sample = + evt_payload->voc_devinfo.dev_sample; + v->dev_tx.dev_id = + evt_payload->voc_devinfo.dev_id; + v->dev_tx.enabled = VOICE_DEV_ENABLED; + } + if ((v->dev_rx.enabled == VOICE_DEV_ENABLED) && + (v->dev_tx.enabled == VOICE_DEV_ENABLED) && + (v->v_call_status == VOICE_CALL_START)) { + rc = voice_apr_register(v); + if (rc < 0) { + mutex_unlock(&v->lock); + pr_aud_err("%s: voice apr registration" + "failed\n", __func__); + return; + } + voice_create_mvm_cvs_session(v); + voice_setup_modem_voice(v); + voice_attach_vocproc(v); + voice_send_start_voice_cmd(v); + get_sidetone_cal(&sidetone_cal_data); + msm_snddev_enable_sidetone( + v->dev_rx.dev_id, + sidetone_cal_data.enable, + sidetone_cal_data.gain); + v->voc_state = VOC_RUN; + + /* Start in-call recording if command was + * pending. */ + if (v->rec_info.pending) { + voice_cvs_start_record(v, + v->rec_info.rec_mode); + + v->rec_info.pending = 0; + } + + /* Start in-call music delivery if command was + * pending. */ + if (v->music_info.pending) { + voice_cvs_start_playback(v); + + v->music_info.pending = 0; + } + } + } + + mutex_unlock(&v->lock); + break; + case AUDDEV_EVT_DEVICE_VOL_MUTE_CHG: + /* cache the mute and volume index value */ + if (evt_payload->voc_devinfo.dev_type == DIR_TX) { + v->dev_tx.mute = + evt_payload->voc_vm_info.dev_vm_val.mute; + + mutex_lock(&v->lock); + + if (v->voc_state == VOC_RUN) + voice_send_mute_cmd_to_modem(v); + + mutex_unlock(&v->lock); + } else { + v->dev_rx.volume = evt_payload-> + voc_vm_info.dev_vm_val.vol; + + mutex_lock(&v->lock); + + if (v->voc_state == VOC_RUN) + voice_send_vol_index_to_modem(v); + + mutex_unlock(&v->lock); + } + break; + case AUDDEV_EVT_REL_PENDING: + + mutex_lock(&v->lock); + + if (v->voc_state == VOC_RUN) { + voice_disable_vocproc(v); + v->voc_state = VOC_CHANGE; + } + + mutex_unlock(&v->lock); + + if (evt_payload->voc_devinfo.dev_type == DIR_RX) + v->dev_rx.enabled = VOICE_DEV_DISABLED; + else + v->dev_tx.enabled = VOICE_DEV_DISABLED; + + break; + case AUDDEV_EVT_END_VOICE: + /* recover the tx mute and rx volume to the default values */ + v->dev_tx.mute = v->default_mute_val; + v->dev_rx.volume = v->default_vol_val; + if (v->dev_rx.enabled == VOICE_DEV_ENABLED) + msm_snddev_enable_sidetone(v->dev_rx.dev_id, 0, 0); + + mutex_lock(&v->lock); + + if (v->voc_state == VOC_RUN) { + /* call stop modem voice */ + voice_send_stop_voice_cmd(v); + voice_destroy_modem_voice(v); + voice_destroy_mvm_cvs_session(v); + v->voc_state = VOC_RELEASE; + } else if (v->voc_state == VOC_CHANGE) { + voice_send_stop_voice_cmd(v); + voice_destroy_mvm_cvs_session(v); + v->voc_state = VOC_RELEASE; + } + + mutex_unlock(&v->lock); + + v->v_call_status = VOICE_CALL_END; + + break; + default: + pr_aud_err("UNKNOWN EVENT\n"); + } + return; +} +EXPORT_SYMBOL(voice_auddev_cb_function); + +int voice_set_voc_path_full(uint32_t set) +{ + int rc = 0; + + pr_aud_info("%s: %d\n", __func__, set); + + mutex_lock(&voice.lock); + + if (voice.voc_state == VOC_INIT || voice.voc_state == VOC_RELEASE) { + if (set) + voice.voc_path = VOC_PATH_FULL; + else + voice.voc_path = VOC_PATH_PASSIVE; + } else { + pr_aud_err("%s: Invalid voc path set to %d, in state %d\n", + __func__, set, voice.voc_state); + + rc = -EPERM; + } + + mutex_unlock(&voice.lock); + + return rc; +} +EXPORT_SYMBOL(voice_set_voc_path_full); + +void voice_register_mvs_cb(ul_cb_fn ul_cb, + dl_cb_fn dl_cb, + void *private_data) +{ + voice.mvs_info.ul_cb = ul_cb; + voice.mvs_info.dl_cb = dl_cb; + voice.mvs_info.private_data = private_data; +} + +void voice_config_vocoder(uint32_t media_type, + uint32_t rate, + uint32_t network_type, + uint32_t dtx_mode) +{ + voice.mvs_info.media_type = media_type; + voice.mvs_info.rate = rate; + voice.mvs_info.network_type = network_type; + voice.mvs_info.dtx_mode = dtx_mode; +} + +static int32_t modem_mvm_callback(struct apr_client_data *data, void *priv) +{ + uint32_t *ptr; + struct voice_data *v = priv; + + pr_debug("%s\n", __func__); + pr_debug("%s: Payload Length = %d, opcode=%x\n", __func__, + data->payload_size, data->opcode); + + if (data->opcode == RESET_EVENTS) { + pr_debug("%s:Reset event received in Voice service MVM\n", + __func__); + apr_reset(v->apr_mvm); + apr_reset(v->apr_q6_mvm); + v->apr_q6_mvm = NULL; + v->apr_mvm = NULL; + v->mvm_handle = 0; + v->mvm_q6_handle = 0; + return 0; + } + + if (data->opcode == APR_BASIC_RSP_RESULT) { + if (data->payload_size) { + ptr = data->payload; + + pr_debug("%x %x\n", ptr[0], ptr[1]); + /* ping mvm service ACK */ + + if (ptr[0] == + VSS_IMVM_CMD_CREATE_PASSIVE_CONTROL_SESSION || + ptr[0] == + VSS_IMVM_CMD_CREATE_FULL_CONTROL_SESSION) { + /* Passive session is used for voice call + * through modem. Full session is used for voice + * call through Q6. */ + pr_debug("%s: cmd = 0x%x\n", __func__, ptr[0]); + if (!ptr[1]) { + pr_debug("%s: MVM handle is %d\n", + __func__, data->src_port); + + voice_set_mvm_handle(v, data->src_port); + } else + pr_aud_info("got NACK for sending \ + MVM create session \n"); + v->mvm_state = CMD_STATUS_SUCCESS; + wake_up(&v->mvm_wait); + } else if (ptr[0] == VSS_IMVM_CMD_START_VOICE) { + pr_debug("%s: cmd = 0x%x\n", __func__, ptr[0]); + v->mvm_state = CMD_STATUS_SUCCESS; + wake_up(&v->mvm_wait); + } else if (ptr[0] == VSS_ISTREAM_CMD_ATTACH_VOCPROC) { + pr_debug("%s: cmd = 0x%x\n", __func__, ptr[0]); + v->mvm_state = CMD_STATUS_SUCCESS; + wake_up(&v->mvm_wait); + } else if (ptr[0] == VSS_IMVM_CMD_STOP_VOICE) { + v->mvm_state = CMD_STATUS_SUCCESS; + wake_up(&v->mvm_wait); + } else if (ptr[0] == VSS_ISTREAM_CMD_DETACH_VOCPROC) { + v->mvm_state = CMD_STATUS_SUCCESS; + wake_up(&v->mvm_wait); + } else if (ptr[0] == VSS_ISTREAM_CMD_SET_TTY_MODE) { + v->mvm_state = CMD_STATUS_SUCCESS; + wake_up(&v->mvm_wait); + } else if (ptr[0] == APRV2_IBASIC_CMD_DESTROY_SESSION) { + pr_aud_info("%s: DESTROY resp\n", __func__); + + v->mvm_state = CMD_STATUS_SUCCESS; + wake_up(&v->mvm_wait); + } else if (ptr[0] == VSS_IMVM_CMD_ATTACH_STREAM) { + pr_aud_info("%s: ATTACH_STREAM resp 0x%x\n", + __func__, ptr[1]); + + v->mvm_state = CMD_STATUS_SUCCESS; + wake_up(&v->mvm_wait); + } else if (ptr[0] == VSS_IMVM_CMD_DETACH_STREAM) { + pr_debug("%s: DETACH_STREAM resp 0x%x\n", + __func__, ptr[1]); + + v->mvm_state = CMD_STATUS_SUCCESS; + wake_up(&v->mvm_wait); + } else if (ptr[0] == VSS_ICOMMON_CMD_SET_NETWORK) { + pr_debug("%s: SET_NETWORK resp 0x%x\n", + __func__, ptr[1]); + + v->mvm_state = CMD_STATUS_SUCCESS; + wake_up(&v->mvm_wait); + } else if (ptr[0] == VSS_ICOMMON_CMD_SET_VOICE_TIMING) { + pr_debug("%s: SET_VOICE_TIMING resp 0x%x\n", + __func__, ptr[1]); + + v->mvm_state = CMD_STATUS_SUCCESS; + wake_up(&v->mvm_wait); + } else + pr_debug("%s: not match cmd = 0x%x\n", + __func__, ptr[0]); + } + } + + return 0; +} + +static int32_t modem_cvs_callback(struct apr_client_data *data, void *priv) +{ + uint32_t *ptr; + struct voice_data *v = priv; + + pr_debug("%s\n", __func__); + pr_debug("%s: Payload Length = %d, opcode=%x\n", __func__, + data->payload_size, data->opcode); + + if (data->opcode == RESET_EVENTS) { + pr_debug("%s:Reset event received in Voice service CVS\n", + __func__); + apr_reset(v->apr_cvs); + apr_reset(v->apr_q6_cvs); + v->apr_q6_cvs = NULL; + v->apr_cvs = NULL; + v->cvs_handle = 0; + v->cvs_q6_handle = 0; + return 0; + } + + if (data->opcode == APR_BASIC_RSP_RESULT) { + if (data->payload_size) { + ptr = data->payload; + + pr_aud_info("%x %x\n", ptr[0], ptr[1]); + /*response from modem CVS */ + if (ptr[0] == + VSS_ISTREAM_CMD_CREATE_PASSIVE_CONTROL_SESSION || + ptr[0] == + VSS_ISTREAM_CMD_CREATE_FULL_CONTROL_SESSION) { + if (!ptr[1]) { + pr_debug("%s: CVS handle is %d\n", + __func__, data->src_port); + voice_set_cvs_handle(v, data->src_port); + } else + pr_aud_info("got NACK for sending \ + CVS create session \n"); + v->cvs_state = CMD_STATUS_SUCCESS; + wake_up(&v->cvs_wait); + } else if (ptr[0] == + VSS_ISTREAM_CMD_CACHE_CALIBRATION_DATA) { + v->cvs_state = CMD_STATUS_SUCCESS; + wake_up(&v->cvs_wait); + } else if (ptr[0] == + VSS_ISTREAM_CMD_SET_MUTE) { + v->cvs_state = CMD_STATUS_SUCCESS; + wake_up(&v->cvs_wait); + } else if (ptr[0] == VSS_ISTREAM_CMD_SET_MEDIA_TYPE) { + pr_debug("%s: SET_MEDIA resp 0x%x\n", + __func__, ptr[1]); + + v->cvs_state = CMD_STATUS_SUCCESS; + wake_up(&v->cvs_wait); + } else if (ptr[0] == + VSS_ISTREAM_CMD_VOC_AMR_SET_ENC_RATE) { + pr_debug("%s: SET_AMR_RATE resp 0x%x\n", + __func__, ptr[1]); + + v->cvs_state = CMD_STATUS_SUCCESS; + wake_up(&v->cvs_wait); + } else if (ptr[0] == + VSS_ISTREAM_CMD_VOC_AMRWB_SET_ENC_RATE) { + pr_debug("%s: SET_AMR_WB_RATE resp 0x%x\n", + __func__, ptr[1]); + + v->cvs_state = CMD_STATUS_SUCCESS; + wake_up(&v->cvs_wait); + } else if (ptr[0] == VSS_ISTREAM_CMD_SET_ENC_DTX_MODE) { + pr_debug("%s: SET_DTX resp 0x%x\n", + __func__, ptr[1]); + + v->cvs_state = CMD_STATUS_SUCCESS; + wake_up(&v->cvs_wait); + } else if (ptr[0] == + VSS_ISTREAM_CMD_CDMA_SET_ENC_MINMAX_RATE) { + pr_debug("%s: SET_CDMA_RATE resp 0x%x\n", + __func__, ptr[1]); + + v->cvs_state = CMD_STATUS_SUCCESS; + wake_up(&v->cvs_wait); + } else if (ptr[0] == APRV2_IBASIC_CMD_DESTROY_SESSION) { + pr_debug("%s: DESTROY resp\n", __func__); + + v->cvs_state = CMD_STATUS_SUCCESS; + wake_up(&v->cvs_wait); + } else if (ptr[0] == VSS_ISTREAM_CMD_START_RECORD) { + pr_debug("%s: START_RECORD resp 0x%x\n", + __func__, ptr[1]); + + v->cvs_state = CMD_STATUS_SUCCESS; + wake_up(&v->cvs_wait); + } else if (ptr[0] == VSS_ISTREAM_CMD_STOP_RECORD) { + pr_debug("%s: STOP_RECORD resp 0x%x\n", + __func__, ptr[1]); + + v->cvs_state = CMD_STATUS_SUCCESS; + wake_up(&v->cvs_wait); +#ifdef CONFIG_MSM8X60_RTAC + } else if (ptr[0] == VOICE_CMD_SET_PARAM) { + rtac_make_voice_callback(RTAC_CVS, ptr, + data->payload_size); +#endif + } else if (ptr[0] == VSS_ISTREAM_CMD_START_PLAYBACK) { + pr_debug("%s: START_PLAYBACK resp 0x%x\n", + __func__, ptr[1]); + + v->cvs_state = CMD_STATUS_SUCCESS; + wake_up(&v->cvs_wait); + } else if (ptr[0] == VSS_ISTREAM_CMD_STOP_PLAYBACK) { + pr_debug("%s: STOP_PLAYBACK resp 0x%x\n", + __func__, ptr[1]); + + v->cvs_state = CMD_STATUS_SUCCESS; + wake_up(&v->cvs_wait); + } else + pr_debug("%s: cmd = 0x%x\n", __func__, ptr[0]); + } + } else if (data->opcode == VSS_ISTREAM_EVT_SEND_ENC_BUFFER) { + uint32_t *voc_pkt = data->payload; + uint32_t pkt_len = data->payload_size; + + if (voc_pkt != NULL && v->mvs_info.ul_cb != NULL) { + pr_debug("%s: Media type is 0x%x\n", + __func__, voc_pkt[0]); + + /* Remove media ID from payload. */ + voc_pkt++; + pkt_len = pkt_len - 4; + + v->mvs_info.ul_cb((uint8_t *)voc_pkt, + pkt_len, + v->mvs_info.private_data); + } else { + pr_aud_err("%s: voc_pkt is 0x%x ul_cb is 0x%x\n", + __func__, (unsigned int)voc_pkt, + (unsigned int) v->mvs_info.ul_cb); + } + } else if (data->opcode == VSS_ISTREAM_EVT_SEND_DEC_BUFFER) { + pr_debug("%s: Send dec buf resp\n", __func__); + } else if (data->opcode == VSS_ISTREAM_EVT_REQUEST_DEC_BUFFER) { + struct cvs_send_dec_buf_cmd send_dec_buf; + int ret = 0; + uint32_t pkt_len = 0; + + if (v->mvs_info.dl_cb != NULL) { + send_dec_buf.dec_buf.media_id = v->mvs_info.media_type; + + v->mvs_info.dl_cb( + (uint8_t *)&send_dec_buf.dec_buf.packet_data, + &pkt_len, + v->mvs_info.private_data); + + send_dec_buf.hdr.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + send_dec_buf.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(send_dec_buf.dec_buf.media_id) + pkt_len); + send_dec_buf.hdr.src_port = 0; + send_dec_buf.hdr.dest_port = voice_get_cvs_handle(v); + send_dec_buf.hdr.token = 0; + send_dec_buf.hdr.opcode = + VSS_ISTREAM_EVT_SEND_DEC_BUFFER; + + ret = apr_send_pkt(voice_get_apr_cvs(v), + (uint32_t *) &send_dec_buf); + if (ret < 0) { + pr_aud_err("%s: Error %d sending DEC_BUF\n", + __func__, ret); + goto fail; + } + } else { + pr_aud_err("%s: ul_cb is NULL\n", __func__); + } +#ifdef CONFIG_MSM8X60_RTAC + } else if (data->opcode == VOICE_EVT_GET_PARAM_ACK) { + rtac_make_voice_callback(RTAC_CVS, data->payload, + data->payload_size); +#endif + + } else { + pr_debug("%s: Unknown opcode 0x%x\n", __func__, data->opcode); + } + +fail: + return 0; +} + +static int32_t modem_cvp_callback(struct apr_client_data *data, void *priv) +{ + uint32_t *ptr; + struct voice_data *v = priv; + + pr_debug("%s\n", __func__); + pr_debug("%s: Payload Length = %d, opcode=%x\n", __func__, + data->payload_size, data->opcode); + + if (data->opcode == RESET_EVENTS) { + pr_debug("%s:Reset event received in Voice service CVP\n", + __func__); + apr_reset(v->apr_cvp); + apr_reset(v->apr_q6_cvp); + v->apr_q6_cvp = NULL; + v->apr_cvp = NULL; + v->cvp_handle = 0; + v->cvp_q6_handle = 0; + return 0; + } + + if (data->opcode == APR_BASIC_RSP_RESULT) { + if (data->payload_size) { + ptr = data->payload; + + pr_aud_info("%x %x\n", ptr[0], ptr[1]); + /*response from modem CVP */ + if (ptr[0] == + VSS_IVOCPROC_CMD_CREATE_FULL_CONTROL_SESSION) { + pr_debug("%s: cmd = 0x%x\n", __func__, ptr[0]); + if (!ptr[1]) { + voice_set_cvp_handle(v, data->src_port); + pr_debug("cvphdl=%d\n", data->src_port); + } else + pr_aud_info("got NACK from CVP create \ + session response\n"); + v->cvp_state = CMD_STATUS_SUCCESS; + wake_up(&v->cvp_wait); + } else if (ptr[0] == + VSS_IVOCPROC_CMD_CACHE_CALIBRATION_DATA) { + pr_debug("%s: cmd = 0x%x\n", __func__, ptr[0]); + v->cvp_state = CMD_STATUS_SUCCESS; + wake_up(&v->cvp_wait); + } else if (ptr[0] == VSS_IVOCPROC_CMD_SET_DEVICE) { + v->cvp_state = CMD_STATUS_SUCCESS; + wake_up(&v->cvp_wait); + } else if (ptr[0] == + VSS_IVOCPROC_CMD_SET_RX_VOLUME_INDEX) { + v->cvp_state = CMD_STATUS_SUCCESS; + wake_up(&v->cvp_wait); + } else if (ptr[0] == VSS_IVOCPROC_CMD_ENABLE) { + v->cvp_state = CMD_STATUS_SUCCESS; + wake_up(&v->cvp_wait); + } else if (ptr[0] == VSS_IVOCPROC_CMD_DISABLE) { + v->cvp_state = CMD_STATUS_SUCCESS; + wake_up(&v->cvp_wait); + } else if (ptr[0] == APRV2_IBASIC_CMD_DESTROY_SESSION) { + v->cvp_state = CMD_STATUS_SUCCESS; + wake_up(&v->cvp_wait); + } else if (ptr[0] == + VSS_IVOCPROC_CMD_CACHE_VOLUME_CALIBRATION_TABLE + ) { + + pr_debug("%s: cmd = 0x%x\n", __func__, ptr[0]); + v->cvp_state = CMD_STATUS_SUCCESS; + wake_up(&v->cvp_wait); +#ifdef CONFIG_MSM8X60_RTAC + } else if (ptr[0] == VOICE_CMD_SET_PARAM) { + rtac_make_voice_callback(RTAC_CVP, ptr, + data->payload_size); +#endif + } else + pr_debug("%s: not match cmd = 0x%x\n", + __func__, ptr[0]); + } +#ifdef CONFIG_MSM8X60_RTAC + } else if (data->opcode == VOICE_EVT_GET_PARAM_ACK) { + rtac_make_voice_callback(RTAC_CVP, data->payload, + data->payload_size); +#endif + } + return 0; +} + + +static int __init voice_init(void) +{ + int rc = 0; + struct voice_data *v = &voice; + pr_aud_info("%s\n", __func__); /* Macro prints the file name and function */ + /* set default value */ + v->default_mute_val = 1; /* default is mute */ + v->default_vol_val = 0; + v->default_sample_val = 8000; + + /* initialize dev_rx and dev_tx */ + memset(&v->dev_tx, 0, sizeof(struct device_data)); + memset(&v->dev_rx, 0, sizeof(struct device_data)); + v->dev_rx.volume = v->default_vol_val; + v->dev_tx.mute = v->default_mute_val; + + v->voc_state = VOC_INIT; + v->voc_path = VOC_PATH_PASSIVE; + v->adsp_version = 0; + init_waitqueue_head(&v->mvm_wait); + init_waitqueue_head(&v->cvs_wait); + init_waitqueue_head(&v->cvp_wait); + + mutex_init(&v->lock); + + v->mvm_handle = 0; + v->cvs_handle = 0; + v->cvp_handle = 0; + + v->mvm_q6_handle = 0; + v->cvs_q6_handle = 0; + v->cvp_q6_handle = 0; + + v->apr_mvm = NULL; + v->apr_cvs = NULL; + v->apr_cvp = NULL; + + v->apr_q6_mvm = NULL; + v->apr_q6_cvs = NULL; + v->apr_q6_cvp = NULL; + + /* Initialize MVS info. */ + memset(&v->mvs_info, 0, sizeof(v->mvs_info)); + v->mvs_info.network_type = VSS_NETWORK_ID_DEFAULT; + + v->rec_info.pending = 0; + v->rec_info.rec_mode = VOC_REC_NONE; + + memset(&v->music_info, 0, sizeof(v->music_info)); + + v->device_events = AUDDEV_EVT_DEV_CHG_VOICE | + AUDDEV_EVT_DEV_RDY | + AUDDEV_EVT_REL_PENDING | + AUDDEV_EVT_START_VOICE | + AUDDEV_EVT_END_VOICE | + AUDDEV_EVT_DEVICE_VOL_MUTE_CHG | + AUDDEV_EVT_FREQ_CHG; + + pr_debug("to register call back\n"); + /* register callback to auddev */ + auddev_register_evt_listner(v->device_events, AUDDEV_CLNT_VOC, + 0, voice_auddev_cb_function, v); + + return rc; +} + +device_initcall(voice_init); diff --git a/arch/arm/mach-msm/qdsp6v3/qcelp_in.c b/arch/arm/mach-msm/qdsp6v3/qcelp_in.c new file mode 100644 index 00000000000..f1986cef7ad --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v3/qcelp_in.c @@ -0,0 +1,334 @@ +/* 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 "audio_utils.h" + +/* Buffer with meta*/ +#define PCM_BUF_SIZE (4096 + sizeof(struct meta_in)) + +/* Maximum 10 frames in buffer with meta */ +#define FRAME_SIZE (1 + ((35+sizeof(struct meta_out_dsp)) * 10)) + +void q6asm_qcelp_in_cb(uint32_t opcode, uint32_t token, + uint32_t *payload, void *priv) +{ + struct q6audio_in * audio = (struct q6audio_in *)priv; + unsigned long flags; + + pr_debug("%s:session id %d: opcode - %d\n", __func__, + audio->ac->session, opcode); + + spin_lock_irqsave(&audio->dsp_lock, flags); + switch (opcode) { + case ASM_DATA_EVENT_READ_DONE: + audio_in_get_dsp_frames(audio, token, payload); + break; + case ASM_DATA_EVENT_WRITE_DONE: + atomic_inc(&audio->in_count); + wake_up(&audio->write_wait); + break; + case ASM_DATA_CMDRSP_EOS: + audio->eos_rsp = 1; + wake_up(&audio->read_wait); + break; + case ASM_STREAM_CMDRSP_GET_ENCDEC_PARAM: + break; + case ASM_STREAM_CMDRSP_GET_PP_PARAMS: + break; + case ASM_SESSION_EVENT_TX_OVERFLOW: + pr_aud_err("%s:session id %d:ASM_SESSION_EVENT_TX_OVERFLOW\n", + __func__, audio->ac->session); + break; + default: + pr_aud_err("%s:session id %d: Ignore opcode[0x%x]\n", __func__, + audio->ac->session, opcode); + break; + } + spin_unlock_irqrestore(&audio->dsp_lock, flags); +} +/* ------------------- device --------------------- */ +static long qcelp_in_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct q6audio_in *audio = file->private_data; + int rc = 0; + int cnt = 0; + + switch (cmd) { + case AUDIO_START: { + struct msm_audio_qcelp_enc_config *enc_cfg; + enc_cfg = audio->enc_cfg; + pr_debug("%s:session id %d: default buf alloc[%d]\n", __func__, + audio->ac->session, audio->buf_alloc); + if (audio->enabled == 1) { + pr_aud_info("%s:AUDIO_START already over\n", __func__); + rc = 0; + break; + } + rc = audio_in_buf_alloc(audio); + if (rc < 0) { + pr_aud_err("%s:session id %d: buffer allocation failed\n", + __func__, audio->ac->session); + break; + } + + /* reduced_rate_level, rate_modulation_cmd set to zero + currently not configurable from user space */ + rc = q6asm_enc_cfg_blk_qcelp(audio->ac, + audio->buf_cfg.frames_per_buf, + enc_cfg->min_bit_rate, + enc_cfg->max_bit_rate, 0, 0); + + if (rc < 0) { + pr_aud_err("%s:session id %d: cmd qcelp media format block\ + failed\n", __func__, audio->ac->session); + break; + } + if (audio->feedback == NON_TUNNEL_MODE) { + rc = q6asm_media_format_block_pcm(audio->ac, + audio->pcm_cfg.sample_rate, + audio->pcm_cfg.channel_count); + + if (rc < 0) { + pr_aud_err("%s:session id %d: media format block\ + failed\n", __func__, audio->ac->session); + break; + } + } + pr_debug("%s:session id %d: AUDIO_START enable[%d]\n", __func__, + audio->ac->session, audio->enabled); + rc = audio_in_enable(audio); + if (!rc) { + audio->enabled = 1; + } else { + audio->enabled = 0; + pr_aud_err("%s:session id %d: Audio Start procedure failed\ + rc=%d\n", __func__, audio->ac->session, rc); + break; + } + while (cnt++ < audio->str_cfg.buffer_count) + q6asm_read(audio->ac); /* Push buffer to DSP */ + rc = 0; + pr_debug("%s:session id %d: AUDIO_START success enable[%d]\n", + __func__, audio->ac->session, audio->enabled); + break; + } + case AUDIO_STOP: { + pr_debug("%s:session id %d: AUDIO_STOP\n", __func__, + audio->ac->session); + rc = audio_in_disable(audio); + if (rc < 0) { + pr_aud_err("%s:session id %d: Audio Stop procedure failed\ + rc=%d\n", __func__, audio->ac->session, + rc); + break; + } + break; + } + case AUDIO_GET_QCELP_ENC_CONFIG: { + if (copy_to_user((void *)arg, audio->enc_cfg, + sizeof(struct msm_audio_qcelp_enc_config))) + rc = -EFAULT; + break; + } + case AUDIO_SET_QCELP_ENC_CONFIG: { + struct msm_audio_qcelp_enc_config cfg; + struct msm_audio_qcelp_enc_config *enc_cfg; + enc_cfg = audio->enc_cfg; + if (copy_from_user(&cfg, (void *) arg, + sizeof(struct msm_audio_qcelp_enc_config))) { + rc = -EFAULT; + break; + } + + if (cfg.min_bit_rate > 4 || + cfg.min_bit_rate < 1) { + pr_aud_err("%s:session id %d: invalid min bitrate\n", + __func__, audio->ac->session); + rc = -EINVAL; + break; + } + if (cfg.max_bit_rate > 4 || + cfg.max_bit_rate < 1) { + pr_aud_err("%s:session id %d: invalid max bitrate\n", + __func__, audio->ac->session); + rc = -EINVAL; + break; + } + enc_cfg->min_bit_rate = cfg.min_bit_rate; + enc_cfg->max_bit_rate = cfg.max_bit_rate; + pr_debug("%s:session id %d: min_bit_rate= 0x%x\ + max_bit_rate=0x%x\n", __func__, + audio->ac->session, enc_cfg->min_bit_rate, + enc_cfg->max_bit_rate); + break; + } + default: + rc = -EINVAL; + } + return rc; +} + +static int qcelp_in_open(struct inode *inode, struct file *file) +{ + struct q6audio_in *audio = NULL; + struct msm_audio_qcelp_enc_config *enc_cfg; + int rc = 0; + + audio = kzalloc(sizeof(struct q6audio_in), GFP_KERNEL); + + if (audio == NULL) { + pr_aud_err("%s:session id %d: Could not allocate memory for qcelp\ + driver\n", __func__, audio->ac->session); + return -ENOMEM; + } + /* Allocate memory for encoder config param */ + audio->enc_cfg = kzalloc(sizeof(struct msm_audio_qcelp_enc_config), + GFP_KERNEL); + if (audio->enc_cfg == NULL) { + pr_aud_err("%s:session id %d: Could not allocate memory for aac\ + config param\n", __func__, audio->ac->session); + kfree(audio); + return -ENOMEM; + } + enc_cfg = audio->enc_cfg; + + mutex_init(&audio->lock); + mutex_init(&audio->read_lock); + mutex_init(&audio->write_lock); + spin_lock_init(&audio->dsp_lock); + init_waitqueue_head(&audio->read_wait); + init_waitqueue_head(&audio->write_wait); + + /* Settings will be re-config at AUDIO_SET_CONFIG, + * but at least we need to have initial config + */ + audio->str_cfg.buffer_size = FRAME_SIZE; + audio->str_cfg.buffer_count = FRAME_NUM; + audio->min_frame_size = 35; + audio->max_frames_per_buf = 10; + audio->pcm_cfg.buffer_size = PCM_BUF_SIZE; + audio->pcm_cfg.buffer_count = PCM_BUF_COUNT; + enc_cfg->min_bit_rate = 4; + enc_cfg->max_bit_rate = 4; + audio->pcm_cfg.channel_count = 1; + audio->pcm_cfg.sample_rate = 8000; + audio->buf_cfg.meta_info_enable = 0x01; + audio->buf_cfg.frames_per_buf = 0x01; + + audio->ac = q6asm_audio_client_alloc((app_cb)q6asm_qcelp_in_cb, + (void *)audio); + + if (!audio->ac) { + pr_aud_err("%s:session id %d: Could not allocate memory for audio\ + client\n", __func__, audio->ac->session); + kfree(audio->enc_cfg); + kfree(audio); + return -ENOMEM; + } + + /* open qcelp encoder in T/NT mode */ + if ((file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + audio->feedback = NON_TUNNEL_MODE; + rc = q6asm_open_read_write(audio->ac, FORMAT_V13K, + FORMAT_LINEAR_PCM); + if (rc < 0) { + pr_aud_err("%s:session id %d: NT mode Open failed rc=%d\n", + __func__, audio->ac->session, rc); + rc = -ENODEV; + goto fail; + } + pr_aud_info("%s:session id %d: NT mode encoder success\n", __func__, + audio->ac->session); + } else if (!(file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + audio->feedback = TUNNEL_MODE; + rc = q6asm_open_read(audio->ac, FORMAT_V13K); + if (rc < 0) { + pr_aud_err("%s:session id %d: T mode Open failed rc=%d\n", + __func__, audio->ac->session, rc); + rc = -ENODEV; + goto fail; + } + /* register for tx overflow (valid for tunnel mode only) */ + rc = q6asm_reg_tx_overflow(audio->ac, 0x01); + if (rc < 0) { + pr_aud_err("%s:session id %d: TX Overflow registration\ + failed rc=%d\n", __func__, audio->ac->session, rc); + rc = -ENODEV; + goto fail; + } + pr_aud_info("%s:session id %d: T mode encoder success\n", __func__, + audio->ac->session); + } else { + pr_aud_err("%s:session id %d: Unexpected mode\n", __func__, + audio->ac->session); + rc = -EACCES; + goto fail; + } + + audio->opened = 1; + atomic_set(&audio->in_count, PCM_BUF_COUNT); + atomic_set(&audio->out_count, 0x00); + audio->enc_ioctl = qcelp_in_ioctl; + file->private_data = audio; + + pr_aud_info("%s:session id %d: success\n", __func__, audio->ac->session); + return 0; +fail: + q6asm_audio_client_free(audio->ac); + kfree(audio->enc_cfg); + kfree(audio); + return rc; +} + +static const struct file_operations audio_in_fops = { + .owner = THIS_MODULE, + .open = qcelp_in_open, + .release = audio_in_release, + .read = audio_in_read, + .write = audio_in_write, + .unlocked_ioctl = audio_in_ioctl, +}; + +struct miscdevice audio_qcelp_in_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_qcelp_in", + .fops = &audio_in_fops, +}; + +static int __init qcelp_in_init(void) +{ + return misc_register(&audio_qcelp_in_misc); +} + +device_initcall(qcelp_in_init); diff --git a/arch/arm/mach-msm/qdsp6v3/rtac.h b/arch/arm/mach-msm/qdsp6v3/rtac.h new file mode 100644 index 00000000000..fd7f85f0e51 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v3/rtac.h @@ -0,0 +1,45 @@ +/* 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 __RTAC_H__ +#define __RTAC_H__ + +#ifdef CONFIG_MSM8X60_RTAC + +#include +#include + +/* Voice Modes */ +#define RTAC_CVP 0 +#define RTAC_CVS 1 +#define RTAC_VOICE_MODES 2 + +void update_rtac(u32 evt_id, u32 dev_id, struct msm_snddev_info *dev_info); +void rtac_add_adm_device(u32 port_id, u32 popp_id); +void rtac_remove_adm_device(u32 port_id); +void rtac_add_voice(struct voice_data *v); +void rtac_remove_voice(struct voice_data *v); +void rtac_set_adm_handle(void *handle); +bool rtac_make_adm_callback(uint32_t *payload, u32 payload_size); +void rtac_copy_adm_payload_to_user(void *payload, u32 payload_size); +void rtac_set_asm_handle(u32 session_id, void *handle); +bool rtac_make_asm_callback(u32 session_id, uint32_t *payload, + u32 payload_size); +void rtac_copy_asm_payload_to_user(void *payload, u32 payload_size); +void rtac_set_voice_handle(u32 mode, void *handle); +bool rtac_make_voice_callback(u32 mode, uint32_t *payload, u32 payload_size); +void rtac_copy_voice_payload_to_user(void *payload, u32 payload_size); + +#endif + +#endif diff --git a/arch/arm/mach-msm/qdsp6v3/snddev_ecodec.c b/arch/arm/mach-msm/qdsp6v3/snddev_ecodec.c new file mode 100644 index 00000000000..fc944ef75d3 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v3/snddev_ecodec.c @@ -0,0 +1,390 @@ +/* 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. + * + * 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 + +#define ECODEC_SAMPLE_RATE 8000 +static struct q6v2audio_ecodec_ops default_audio_ops; +static struct q6v2audio_ecodec_ops *audio_ops = &default_audio_ops; + +/* Context for each external codec device */ +struct snddev_ecodec_state { + struct snddev_ecodec_data *data; + u32 sample_rate; +}; + +/* Global state for the driver */ +struct snddev_ecodec_drv_state { + struct mutex dev_lock; + int ref_cnt; /* ensure one rx device at a time */ + struct clk *ecodec_clk; +}; + +static struct snddev_ecodec_drv_state snddev_ecodec_drv; + +struct aux_pcm_state { + unsigned int dout; + unsigned int din; + unsigned int syncout; + unsigned int clkin_a; +}; + +static struct aux_pcm_state the_aux_pcm_state; + +static int aux_pcm_gpios_request(void) +{ + int rc = 0; + uint32_t bt_config_gpio[] = { + GPIO_CFG(the_aux_pcm_state.dout, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), + GPIO_CFG(the_aux_pcm_state.din, 1, GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), + GPIO_CFG(the_aux_pcm_state.syncout, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), + GPIO_CFG(the_aux_pcm_state.clkin_a, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), + }; + pr_debug("%s\n", __func__); + gpio_tlmm_config(bt_config_gpio[0], GPIO_CFG_ENABLE); + gpio_tlmm_config(bt_config_gpio[1], GPIO_CFG_ENABLE); + gpio_tlmm_config(bt_config_gpio[2], GPIO_CFG_ENABLE); + gpio_tlmm_config(bt_config_gpio[3], GPIO_CFG_ENABLE); + + return rc; +} + +static void aux_pcm_gpios_free(void) +{ + uint32_t bt_config_gpio[] = { + GPIO_CFG(the_aux_pcm_state.dout, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), + GPIO_CFG(the_aux_pcm_state.din, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + GPIO_CFG(the_aux_pcm_state.syncout, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), + GPIO_CFG(the_aux_pcm_state.clkin_a, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), + }; + pr_debug("%s\n", __func__); + gpio_tlmm_config(bt_config_gpio[0], GPIO_CFG_DISABLE); + gpio_tlmm_config(bt_config_gpio[1], GPIO_CFG_DISABLE); + gpio_tlmm_config(bt_config_gpio[2], GPIO_CFG_DISABLE); + gpio_tlmm_config(bt_config_gpio[3], GPIO_CFG_DISABLE); +} + +static int get_aux_pcm_gpios(struct platform_device *pdev) +{ + int rc = 0; + struct resource *res; + + /* Claim all of the GPIOs. */ + res = platform_get_resource_byname(pdev, IORESOURCE_IO, "aux_pcm_dout"); + if (!res) { + pr_aud_err("%s: failed to get gpio AUX PCM DOUT\n", __func__); + return -ENODEV; + } + + the_aux_pcm_state.dout = res->start; + + res = platform_get_resource_byname(pdev, IORESOURCE_IO, "aux_pcm_din"); + if (!res) { + pr_aud_err("%s: failed to get gpio AUX PCM DIN\n", __func__); + return -ENODEV; + } + + the_aux_pcm_state.din = res->start; + + res = platform_get_resource_byname(pdev, IORESOURCE_IO, + "aux_pcm_syncout"); + if (!res) { + pr_aud_err("%s: failed to get gpio AUX PCM SYNC OUT\n", __func__); + return -ENODEV; + } + + the_aux_pcm_state.syncout = res->start; + + res = platform_get_resource_byname(pdev, IORESOURCE_IO, + "aux_pcm_clkin_a"); + if (!res) { + pr_aud_err("%s: failed to get gpio AUX PCM CLKIN A\n", __func__); + return -ENODEV; + } + + the_aux_pcm_state.clkin_a = res->start; + + pr_aud_info("%s: dout = %u, din = %u , syncout = %u, clkin_a =%u\n", + __func__, the_aux_pcm_state.dout, the_aux_pcm_state.din, + the_aux_pcm_state.syncout, the_aux_pcm_state.clkin_a); + + return rc; +} + +static int aux_pcm_probe(struct platform_device *pdev) +{ + int rc = 0; + + pr_aud_info("%s:\n", __func__); + + rc = get_aux_pcm_gpios(pdev); + if (rc < 0) { + pr_aud_err("%s: GPIO configuration failed\n", __func__); + return -ENODEV; + } + return rc; +} + +static struct platform_driver aux_pcm_driver = { + .probe = aux_pcm_probe, + .driver = { .name = "msm_aux_pcm"} +}; + +static int snddev_ecodec_open(struct msm_snddev_info *dev_info) +{ + int rc; + struct snddev_ecodec_drv_state *drv = &snddev_ecodec_drv; + union afe_port_config afe_config; + + pr_debug("%s\n", __func__); + + mutex_lock(&drv->dev_lock); + + if (dev_info->opened) { + pr_aud_err("%s: ERROR: %s already opened\n", __func__, + dev_info->name); + mutex_unlock(&drv->dev_lock); + return -EBUSY; + } + + if (drv->ref_cnt != 0) { + pr_debug("%s: opened %s\n", __func__, dev_info->name); + drv->ref_cnt++; + mutex_unlock(&drv->dev_lock); + return 0; + } + + pr_aud_info("%s: opening %s\n", __func__, dev_info->name); + + rc = aux_pcm_gpios_request(); + if (rc < 0) { + pr_aud_err("%s: GPIO request failed\n", __func__); + return rc; + } + + clk_reset(drv->ecodec_clk, CLK_RESET_ASSERT); + + afe_config.pcm.mode = AFE_PCM_CFG_MODE_PCM; + afe_config.pcm.sync = AFE_PCM_CFG_SYNC_INT; + afe_config.pcm.frame = AFE_PCM_CFG_FRM_256BPF; + afe_config.pcm.quant = AFE_PCM_CFG_QUANT_LINEAR_NOPAD; + afe_config.pcm.slot = 0; + afe_config.pcm.data = AFE_PCM_CFG_CDATAOE_MASTER; + + rc = afe_open(PCM_RX, &afe_config, ECODEC_SAMPLE_RATE); + if (rc < 0) { + pr_aud_err("%s: afe open failed for PCM_RX\n", __func__); + goto err_rx_afe; + } + + rc = afe_open(PCM_TX, &afe_config, ECODEC_SAMPLE_RATE); + if (rc < 0) { + pr_aud_err("%s: afe open failed for PCM_TX\n", __func__); + goto err_tx_afe; + } + + rc = clk_set_rate(drv->ecodec_clk, 2048000); + if (rc < 0) { + pr_aud_err("%s: clk_set_rate failed\n", __func__); + goto err_clk; + } + + clk_enable(drv->ecodec_clk); + + clk_reset(drv->ecodec_clk, CLK_RESET_DEASSERT); + + drv->ref_cnt++; + mutex_unlock(&drv->dev_lock); + + return 0; + +err_clk: + afe_close(PCM_TX); +err_tx_afe: + afe_close(PCM_RX); +err_rx_afe: + aux_pcm_gpios_free(); + mutex_unlock(&drv->dev_lock); + return -ENODEV; +} + +int snddev_ecodec_close(struct msm_snddev_info *dev_info) +{ + struct snddev_ecodec_drv_state *drv = &snddev_ecodec_drv; + + pr_debug("%s: closing %s\n", __func__, dev_info->name); + + mutex_lock(&drv->dev_lock); + + if (!dev_info->opened) { + pr_aud_err("%s: ERROR: %s is not opened\n", __func__, + dev_info->name); + mutex_unlock(&drv->dev_lock); + return -EPERM; + } + + drv->ref_cnt--; + + if (drv->ref_cnt == 0) { + + pr_aud_info("%s: closing all devices\n", __func__); + + clk_disable(drv->ecodec_clk); + aux_pcm_gpios_free(); + + afe_close(PCM_RX); + afe_close(PCM_TX); + } + + mutex_unlock(&drv->dev_lock); + + return 0; +} + +int snddev_ecodec_set_freq(struct msm_snddev_info *dev_info, u32 rate) +{ + int rc = 0; + + if (!dev_info) { + rc = -EINVAL; + goto error; + } + return ECODEC_SAMPLE_RATE; + +error: + return rc; +} + +void htc_8x60_register_ecodec_ops(struct q6v2audio_ecodec_ops *ops) +{ + audio_ops = ops; +} + +static int snddev_ecodec_probe(struct platform_device *pdev) +{ + int rc = 0; + struct snddev_ecodec_data *pdata; + struct msm_snddev_info *dev_info; + struct snddev_ecodec_state *ecodec; + + pr_aud_info("%s:\n", __func__); + + if (!pdev || !pdev->dev.platform_data) { + printk(KERN_ALERT "Invalid caller\n"); + rc = -1; + goto error; + } + pdata = pdev->dev.platform_data; + + ecodec = kzalloc(sizeof(struct snddev_ecodec_state), GFP_KERNEL); + if (!ecodec) { + rc = -ENOMEM; + goto error; + } + + dev_info = kzalloc(sizeof(struct msm_snddev_info), GFP_KERNEL); + if (!dev_info) { + kfree(ecodec); + rc = -ENOMEM; + goto error; + } + + dev_info->name = pdata->name; + dev_info->copp_id = pdata->copp_id; + dev_info->private_data = (void *)ecodec; + dev_info->dev_ops.open = snddev_ecodec_open; + dev_info->dev_ops.close = snddev_ecodec_close; + dev_info->dev_ops.set_freq = snddev_ecodec_set_freq; + dev_info->dev_ops.enable_sidetone = NULL; + dev_info->capability = pdata->capability; + dev_info->opened = 0; + + msm_snddev_register(dev_info); + + ecodec->data = pdata; + ecodec->sample_rate = ECODEC_SAMPLE_RATE; /* Default to 8KHz */ +error: + return rc; +} + +struct platform_driver snddev_ecodec_driver = { + .probe = snddev_ecodec_probe, + .driver = {.name = "msm_snddev_ecodec"} +}; + +int __init snddev_ecodec_init(void) +{ + int rc = 0; + struct snddev_ecodec_drv_state *drv = &snddev_ecodec_drv; + + pr_aud_info("%s:\n", __func__); + + mutex_init(&drv->dev_lock); + drv->ref_cnt = 0; + + drv->ecodec_clk = clk_get(NULL, "pcm_clk"); + if (IS_ERR(drv->ecodec_clk)) { + pr_aud_err("%s: could not get pcm_clk\n", __func__); + return PTR_ERR(drv->ecodec_clk); + } + + rc = platform_driver_register(&aux_pcm_driver); + if (IS_ERR_VALUE(rc)) { + pr_aud_err("%s: platform_driver_register for aux pcm failed\n", + __func__); + goto error_aux_pcm_platform_driver; + } + + rc = platform_driver_register(&snddev_ecodec_driver); + if (IS_ERR_VALUE(rc)) { + pr_aud_err("%s: platform_driver_register for ecodec failed\n", + __func__); + goto error_ecodec_platform_driver; + } + pr_aud_info("%s: done\n", __func__); + + return 0; + +error_ecodec_platform_driver: + platform_driver_unregister(&aux_pcm_driver); +error_aux_pcm_platform_driver: + clk_put(drv->ecodec_clk); + + pr_aud_err("%s: encounter error\n", __func__); + return -ENODEV; +} + +device_initcall(snddev_ecodec_init); + +MODULE_DESCRIPTION("ECodec Sound Device driver"); +MODULE_VERSION("1.0"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/qdsp6v3/snddev_hdmi.c b/arch/arm/mach-msm/qdsp6v3/snddev_hdmi.c new file mode 100644 index 00000000000..9e661c8b1db --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v3/snddev_hdmi.c @@ -0,0 +1,182 @@ +/* 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 + +static DEFINE_MUTEX(snddev_hdmi_lock); +static int snddev_hdmi_active; + +static int snddev_hdmi_open(struct msm_snddev_info *dev_info) +{ + int rc = 0; + union afe_port_config afe_config; + struct snddev_hdmi_data *snddev_hdmi_data; + + if (!dev_info) { + pr_aud_err("msm_snddev_info is null\n"); + return -EINVAL; + } + + snddev_hdmi_data = dev_info->private_data; + + mutex_lock(&snddev_hdmi_lock); + + if (snddev_hdmi_active) { + pr_aud_err("HDMI snddev already active\n"); + mutex_unlock(&snddev_hdmi_lock); + return -EBUSY; + } + afe_config.hdmi.channel_mode = snddev_hdmi_data->channel_mode; + afe_config.hdmi.bitwidth = 16; + afe_config.hdmi.data_type = 0; + rc = afe_open(snddev_hdmi_data->copp_id, &afe_config, + dev_info->sample_rate); + + if (rc < 0) { + pr_aud_err("afe_open failed\n"); + mutex_unlock(&snddev_hdmi_lock); + return -EINVAL; + } + snddev_hdmi_active = 1; + + pr_debug("%s open done\n", dev_info->name); + + mutex_unlock(&snddev_hdmi_lock); + + return 0; +} + +static int snddev_hdmi_close(struct msm_snddev_info *dev_info) +{ + if (!dev_info) { + pr_aud_err("msm_snddev_info is null\n"); + return -EINVAL; + } + + if (!dev_info->opened) { + pr_aud_err("calling close device with out opening the" + " device\n"); + return -EPERM; + } + mutex_lock(&snddev_hdmi_lock); + + if (!snddev_hdmi_active) { + pr_aud_err("HDMI snddev not active\n"); + mutex_unlock(&snddev_hdmi_lock); + return -EPERM; + } + snddev_hdmi_active = 0; + + afe_close(HDMI_RX); + + pr_debug("%s closed\n", dev_info->name); + mutex_unlock(&snddev_hdmi_lock); + + return 0; +} + +static int snddev_hdmi_set_freq(struct msm_snddev_info *dev_info, u32 req_freq) +{ + if (req_freq != 48000) { + pr_debug("Unsupported Frequency:%d\n", req_freq); + return -EINVAL; + } + return 48000; +} + +static int snddev_hdmi_probe(struct platform_device *pdev) +{ + int rc = 0; + struct snddev_hdmi_data *pdata; + struct msm_snddev_info *dev_info; + + if (!pdev || !pdev->dev.platform_data) { + printk(KERN_ALERT "Invalid caller\n"); + return -ENODEV; + } + + pdata = pdev->dev.platform_data; + if (!(pdata->capability & SNDDEV_CAP_RX)) { + pr_aud_err("invalid device data either RX or TX\n"); + return -ENODEV; + } + + dev_info = kzalloc(sizeof(struct msm_snddev_info), GFP_KERNEL); + if (!dev_info) { + pr_aud_err("unable to allocate memeory for msm_snddev_info\n"); + return -ENOMEM; + } + + dev_info->name = pdata->name; + dev_info->copp_id = pdata->copp_id; + dev_info->acdb_id = pdata->acdb_id; + dev_info->private_data = (void *)pdata; + dev_info->dev_ops.open = snddev_hdmi_open; + dev_info->dev_ops.close = snddev_hdmi_close; + dev_info->dev_ops.set_freq = snddev_hdmi_set_freq; + dev_info->capability = pdata->capability; + dev_info->opened = 0; + msm_snddev_register(dev_info); + dev_info->sample_rate = pdata->default_sample_rate; + + pr_debug("probe done for %s\n", pdata->name); + return rc; +} + +static struct platform_driver snddev_hdmi_driver = { + .probe = snddev_hdmi_probe, + .driver = {.name = "snddev_hdmi"} +}; + +static int __init snddev_hdmi_init(void) +{ + s32 rc; + + rc = platform_driver_register(&snddev_hdmi_driver); + if (IS_ERR_VALUE(rc)) { + + pr_aud_err("platform_driver_register failed.\n"); + goto error_platform_driver; + } + + pr_debug("snddev_hdmi_init : done\n"); + + return 0; + +error_platform_driver: + + pr_aud_err("encounterd error\n"); + return -ENODEV; +} + +module_init(snddev_hdmi_init); + +MODULE_DESCRIPTION("HDMI Sound Device driver"); +MODULE_VERSION("1.0"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/qdsp6v3/snddev_icodec.c b/arch/arm/mach-msm/qdsp6v3/snddev_icodec.c new file mode 100644 index 00000000000..f23797cf3ba --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v3/snddev_icodec.c @@ -0,0 +1,1189 @@ +/* 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. + * + * 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 "audio_acdb.h" + +#define SNDDEV_ICODEC_PCM_SZ 32 /* 16 bit / sample stereo mode */ +#define SNDDEV_ICODEC_MUL_FACTOR 3 /* Multi by 8 Shift by 3 */ +#define SNDDEV_ICODEC_CLK_RATE(freq) \ + (((freq) * (SNDDEV_ICODEC_PCM_SZ)) << (SNDDEV_ICODEC_MUL_FACTOR)) +#define SNDDEV_LOW_POWER_MODE 0 +#define SNDDEV_HIGH_POWER_MODE 1 +/* Voltage required for S4 in microVolts, 2.2V or 2200000microvolts */ +#define SNDDEV_VREG_8058_S4_VOLTAGE (2200000) +/* Load Current required for S4 in microAmps, + 36mA - 56mA */ +#define SNDDEV_VREG_LOW_POWER_LOAD (36000) +#define SNDDEV_VREG_HIGH_POWER_LOAD (56000) + +static struct q6v2audio_icodec_ops default_audio_ops; +static struct q6v2audio_icodec_ops *audio_ops = &default_audio_ops; +static int support_aic3254; +static int support_adie; +static int support_aic3254_use_mclk; +static int aic3254_use_mclk_counter; +int msm_codec_i2s_slave_mode = 1; +static struct q6v2audio_aic3254_ops default_aic3254_ops; +static struct q6v2audio_aic3254_ops *aic3254_ops = &default_aic3254_ops; + +/* Global state for the driver */ +struct snddev_icodec_drv_state { + struct mutex rx_lock; + struct mutex tx_lock; + u32 rx_active; /* ensure one rx device at a time */ + u32 tx_active; /* ensure one tx device at a time */ + struct clk *rx_osrclk; + struct clk *rx_bitclk; + struct clk *tx_osrclk; + struct clk *tx_bitclk; + + struct wake_lock rx_idlelock; + struct wake_lock tx_idlelock; + + /* handle to pmic8058 regulator smps4 */ + struct regulator *snddev_vreg; + + struct mutex rx_mclk_lock; +}; + +static struct snddev_icodec_drv_state snddev_icodec_drv; + +struct regulator *vreg_init(void) +{ + int rc; + struct regulator *vreg_ptr; + + vreg_ptr = regulator_get(NULL, "8058_s4"); + if (IS_ERR(vreg_ptr)) { + pr_aud_err("%s: regulator_get 8058_s4 failed\n", __func__); + return NULL; + } + + rc = regulator_set_voltage(vreg_ptr, SNDDEV_VREG_8058_S4_VOLTAGE, + SNDDEV_VREG_8058_S4_VOLTAGE); + if (rc == 0) + return vreg_ptr; + else + return NULL; +} + +static void vreg_deinit(struct regulator *vreg) +{ + regulator_put(vreg); +} + +static void vreg_mode_vote(struct regulator *vreg, int enable, int mode) +{ + int rc; + if (enable) { + rc = regulator_enable(vreg); + if (rc != 0) + pr_aud_err("%s:Enabling regulator failed\n", __func__); + else { + if (mode) + regulator_set_optimum_mode(vreg, + SNDDEV_VREG_HIGH_POWER_LOAD); + else + regulator_set_optimum_mode(vreg, + SNDDEV_VREG_LOW_POWER_LOAD); + } + } else { + rc = regulator_disable(vreg); + if (rc != 0) + pr_aud_err("%s:Disabling regulator failed\n", __func__); + } +} + +struct msm_cdcclk_ctl_state { + unsigned int rx_mclk; + unsigned int rx_mclk_requested; + unsigned int tx_mclk; + unsigned int tx_mclk_requested; +}; + +static struct msm_cdcclk_ctl_state the_msm_cdcclk_ctl_state; + +static int msm_snddev_rx_mclk_request(void) +{ + int rc = 0; +/* + rc = gpio_request(the_msm_cdcclk_ctl_state.rx_mclk, + "MSM_SNDDEV_RX_MCLK"); + if (rc < 0) { + pr_aud_err("%s: GPIO request for MSM SNDDEV RX failed\n", __func__); + return rc; + } + the_msm_cdcclk_ctl_state.rx_mclk_requested = 1; +*/ + return rc; +} +static int msm_snddev_tx_mclk_request(void) +{ + int rc = 0; +/* + rc = gpio_request(the_msm_cdcclk_ctl_state.tx_mclk, + "MSM_SNDDEV_TX_MCLK"); + if (rc < 0) { + pr_aud_err("%s: GPIO request for MSM SNDDEV TX failed\n", __func__); + return rc; + } + the_msm_cdcclk_ctl_state.tx_mclk_requested = 1; +*/ + return rc; +} +static void msm_snddev_rx_mclk_free(void) +{ + if (the_msm_cdcclk_ctl_state.rx_mclk_requested) { + gpio_free(the_msm_cdcclk_ctl_state.rx_mclk); + the_msm_cdcclk_ctl_state.rx_mclk_requested = 0; + } +} +static void msm_snddev_tx_mclk_free(void) +{ + if (the_msm_cdcclk_ctl_state.tx_mclk_requested) { + gpio_free(the_msm_cdcclk_ctl_state.tx_mclk); + the_msm_cdcclk_ctl_state.tx_mclk_requested = 0; + } +} + +static int get_msm_cdcclk_ctl_gpios(struct platform_device *pdev) +{ + int rc = 0; + struct resource *res; + + /* Claim all of the GPIOs. */ + res = platform_get_resource_byname(pdev, IORESOURCE_IO, + "msm_snddev_rx_mclk"); + if (!res) { + pr_aud_err("%s: failed to get gpio MSM SNDDEV RX\n", __func__); + return -ENODEV; + } + the_msm_cdcclk_ctl_state.rx_mclk = res->start; + the_msm_cdcclk_ctl_state.rx_mclk_requested = 0; + + res = platform_get_resource_byname(pdev, IORESOURCE_IO, + "msm_snddev_tx_mclk"); + if (!res) { + pr_aud_err("%s: failed to get gpio MSM SNDDEV TX\n", __func__); + return -ENODEV; + } + the_msm_cdcclk_ctl_state.tx_mclk = res->start; + the_msm_cdcclk_ctl_state.tx_mclk_requested = 0; + + return rc; +} +static int msm_cdcclk_ctl_probe(struct platform_device *pdev) +{ + int rc = 0; + + pr_aud_info("%s:\n", __func__); + + rc = get_msm_cdcclk_ctl_gpios(pdev); + if (rc < 0) { + pr_aud_err("%s: GPIO configuration failed\n", __func__); + return -ENODEV; + } + return rc; +} +static struct platform_driver msm_cdcclk_ctl_driver = { + .probe = msm_cdcclk_ctl_probe, + .driver = { .name = "msm_cdcclk_ctl"} +}; + +static int snddev_icodec_rxclk_enable(struct snddev_icodec_state *icodec, + int en) +{ + int trc; + struct snddev_icodec_drv_state *drv = &snddev_icodec_drv; + + mutex_lock(&drv->rx_mclk_lock); + if (en) { + if (aic3254_use_mclk_counter == 0) { + drv->rx_osrclk = clk_get(0, "i2s_spkr_osr_clk"); + if (IS_ERR(drv->rx_osrclk)) { + pr_aud_err("%s turning on RX MCLK Error\n", \ + __func__); + goto error_invalid_osrclk; + } + + trc = clk_set_rate(drv->rx_osrclk, \ + SNDDEV_ICODEC_CLK_RATE(\ + icodec->sample_rate)); + if (IS_ERR_VALUE(trc)) { + pr_aud_err("ERROR setting RX m clock1\n"); + goto error_invalid_freq; + } + clk_enable(drv->rx_osrclk); + } + + aic3254_use_mclk_counter++; + + } else { + if (aic3254_use_mclk_counter > 0) { + aic3254_use_mclk_counter--; + if (aic3254_use_mclk_counter == 0) + clk_disable(drv->rx_osrclk); + } else + pr_aud_info("%s: counter error!\n", __func__); + } + + mutex_unlock(&drv->rx_mclk_lock); + + pr_aud_info("%s: en: %d counter: %d\n", __func__, en, \ + aic3254_use_mclk_counter); + + return 0; + +error_invalid_osrclk: +error_invalid_freq: + pr_aud_err("%s: encounter error\n", __func__); + msm_snddev_rx_mclk_free(); + + mutex_unlock(&drv->rx_mclk_lock); + return -ENODEV; +} + +static int snddev_icodec_open_rx(struct snddev_icodec_state *icodec) +{ + int trc; + int rc_clk; + int afe_channel_mode; + union afe_port_config afe_config; + struct snddev_icodec_drv_state *drv = &snddev_icodec_drv; + + wake_lock(&drv->rx_idlelock); + + if (drv->snddev_vreg) { + if (!strcmp(icodec->data->name, "headset_stereo_rx")) + vreg_mode_vote(drv->snddev_vreg, 1, + SNDDEV_LOW_POWER_MODE); + else + vreg_mode_vote(drv->snddev_vreg, 1, + SNDDEV_HIGH_POWER_MODE); + } + + if (support_aic3254_use_mclk) { + rc_clk = snddev_icodec_rxclk_enable(icodec, 1); + if (IS_ERR_VALUE(rc_clk)) { + pr_aud_err("%s Enable RX master clock Error\n", \ + __func__); + goto error_invalid_freq; + } + } else { + msm_snddev_rx_mclk_request(); + + drv->rx_osrclk = clk_get(0, "i2s_spkr_osr_clk"); + if (IS_ERR(drv->rx_osrclk)) + pr_aud_err("%s master clock Error\n", __func__); + + trc = clk_set_rate(drv->rx_osrclk, + SNDDEV_ICODEC_CLK_RATE(icodec->sample_rate)); + if (IS_ERR_VALUE(trc)) { + pr_aud_err("ERROR setting m clock1\n"); + goto error_invalid_freq; + } + + clk_enable(drv->rx_osrclk); + } + + drv->rx_bitclk = clk_get(0, "i2s_spkr_bit_clk"); + if (IS_ERR(drv->rx_bitclk)) + pr_aud_err("%s clock Error\n", __func__); + + /* *************************************** + * 1. CPU MASTER MODE: + * Master clock = Sample Rate * OSR rate bit clock + * OSR Rate bit clock = bit/sample * channel master + * clock / bit clock = divider value = 8 + * + * 2. CPU SLAVE MODE: + * bitclk = 0 + * *************************************** */ + + if (msm_codec_i2s_slave_mode) { + pr_debug("%s: configuring bit clock for slave mode\n", + __func__); + trc = clk_set_rate(drv->rx_bitclk, 0); + } else + trc = clk_set_rate(drv->rx_bitclk, 8); + + if (IS_ERR_VALUE(trc)) { + pr_aud_err("ERROR setting m clock1\n"); + goto error_adie; + } + clk_enable(drv->rx_bitclk); + + if (icodec->data->voltage_on) + icodec->data->voltage_on(1); + + if (support_aic3254) { + if (aic3254_ops->aic3254_set_mode) { + if (msm_get_call_state() == 1) + aic3254_ops->aic3254_set_mode(AIC3254_CONFIG_RX, + icodec->data->aic3254_voc_id); + else + aic3254_ops->aic3254_set_mode(AIC3254_CONFIG_RX, + icodec->data->aic3254_id); + } + } + if (support_adie) { + /* Configure ADIE */ + trc = adie_codec_open(icodec->data->profile, &icodec->adie_path); + if (IS_ERR_VALUE(trc)) + pr_aud_err("%s: adie codec open failed\n", __func__); + else + adie_codec_setpath(icodec->adie_path, + icodec->sample_rate, 256); + /* OSR default to 256, can be changed for power optimization + * If OSR is to be changed, need clock API for setting the divider + */ + } + + switch (icodec->data->channel_mode) { + case 2: + afe_channel_mode = MSM_AFE_STEREO; + break; + case 1: + default: + afe_channel_mode = MSM_AFE_MONO; + break; + } + afe_config.mi2s.channel = afe_channel_mode; + afe_config.mi2s.bitwidth = 16; + afe_config.mi2s.line = 1; + if (msm_codec_i2s_slave_mode) + afe_config.mi2s.ws = 0; + else + afe_config.mi2s.ws = 1; + + trc = afe_open(icodec->data->copp_id, &afe_config, icodec->sample_rate); + + if (support_adie) { + /* Enable ADIE */ + if (icodec->adie_path) { + adie_codec_proceed_stage(icodec->adie_path, + ADIE_CODEC_DIGITAL_READY); + adie_codec_proceed_stage(icodec->adie_path, + ADIE_CODEC_DIGITAL_ANALOG_READY); + } + + if (msm_codec_i2s_slave_mode) + adie_codec_set_master_mode(icodec->adie_path, 1); + else + adie_codec_set_master_mode(icodec->adie_path, 0); + } + /* Enable power amplifier */ + if (icodec->data->pamp_on) + icodec->data->pamp_on(1); + + icodec->enabled = 1; + + wake_unlock(&drv->rx_idlelock); + return 0; + +error_adie: + clk_disable(drv->rx_bitclk); + clk_disable(drv->rx_osrclk); +error_invalid_freq: + + pr_aud_err("%s: encounter error\n", __func__); + msm_snddev_rx_mclk_free(); + + wake_unlock(&drv->rx_idlelock); + return -ENODEV; +} + +static int snddev_icodec_open_tx(struct snddev_icodec_state *icodec) +{ + int trc; + int rc_clk; + int afe_channel_mode; + union afe_port_config afe_config; + struct snddev_icodec_drv_state *drv = &snddev_icodec_drv;; + + wake_lock(&drv->tx_idlelock); + + if (drv->snddev_vreg) + vreg_mode_vote(drv->snddev_vreg, 1, SNDDEV_HIGH_POWER_MODE); + + if (support_aic3254_use_mclk) { + rc_clk = snddev_icodec_rxclk_enable(icodec, 1); + if (IS_ERR_VALUE(rc_clk)) { + pr_aud_err("%s Enable RX master clock Error\n", \ + __func__); + goto error_invalid_osrclk; + } + } + + msm_snddev_tx_mclk_request(); + + drv->tx_osrclk = clk_get(0, "i2s_mic_osr_clk"); + if (IS_ERR(drv->tx_osrclk)) { + pr_aud_err("%s master clock Error\n", __func__); + goto error_invalid_osrclk; + } + + trc = clk_set_rate(drv->tx_osrclk, + SNDDEV_ICODEC_CLK_RATE(icodec->sample_rate)); + if (IS_ERR_VALUE(trc)) { + pr_aud_err("ERROR setting m clock1\n"); + goto error_invalid_freq; + } + + clk_enable(drv->tx_osrclk); + + drv->tx_bitclk = clk_get(0, "i2s_mic_bit_clk"); + if (IS_ERR(drv->tx_bitclk)) { + pr_aud_err("%s clock Error\n", __func__); + goto error_invalid_bitclk; + } + + /* *************************************** + * 1. CPU MASTER MODE: + * Master clock = Sample Rate * OSR rate bit clock + * OSR Rate bit clock = bit/sample * channel master + * clock / bit clock = divider value = 8 + * + * 2. CPU SLAVE MODE: + * bitclk = 0 + * *************************************** */ + if (msm_codec_i2s_slave_mode) { + pr_debug("%s: configuring bit clock for slave mode\n", + __func__); + trc = clk_set_rate(drv->tx_bitclk, 0); + } else + trc = clk_set_rate(drv->tx_bitclk, 8); + + clk_enable(drv->tx_bitclk); + + if (support_aic3254) { + if (aic3254_ops->aic3254_set_mode) { + if (msm_get_call_state() == 1) + aic3254_ops->aic3254_set_mode(AIC3254_CONFIG_TX, + icodec->data->aic3254_voc_id); + else + aic3254_ops->aic3254_set_mode(AIC3254_CONFIG_TX, + icodec->data->aic3254_id); + } + } + if (support_adie) { + /* Enable ADIE */ + trc = adie_codec_open(icodec->data->profile, &icodec->adie_path); + if (IS_ERR_VALUE(trc)) + pr_aud_err("%s: adie codec open failed\n", __func__); + else + adie_codec_setpath(icodec->adie_path, + icodec->sample_rate, 256); + } + switch (icodec->data->channel_mode) { + case 2: + afe_channel_mode = MSM_AFE_STEREO; + break; + case 1: + default: + afe_channel_mode = MSM_AFE_MONO; + break; + } + afe_config.mi2s.channel = afe_channel_mode; + afe_config.mi2s.bitwidth = 16; + afe_config.mi2s.line = 1; + if (msm_codec_i2s_slave_mode) + afe_config.mi2s.ws = 0; + else + afe_config.mi2s.ws = 1; + + trc = afe_open(icodec->data->copp_id, &afe_config, icodec->sample_rate); + + if (icodec->adie_path && support_adie) { + adie_codec_proceed_stage(icodec->adie_path, + ADIE_CODEC_DIGITAL_READY); + adie_codec_proceed_stage(icodec->adie_path, + ADIE_CODEC_DIGITAL_ANALOG_READY); + + if (msm_codec_i2s_slave_mode) + adie_codec_set_master_mode(icodec->adie_path, 1); + else + adie_codec_set_master_mode(icodec->adie_path, 0); + } + + /* Reuse pamp_on for TX platform-specific setup */ + if (icodec->data->pamp_on) + icodec->data->pamp_on(1); + + icodec->enabled = 1; + + wake_unlock(&drv->tx_idlelock); + return 0; + +error_invalid_bitclk: + clk_disable(drv->tx_osrclk); +error_invalid_freq: +error_invalid_osrclk: + if (icodec->data->pamp_on) + icodec->data->pamp_on(0); + msm_snddev_tx_mclk_free(); + + pr_aud_err("%s: encounter error\n", __func__); + + wake_unlock(&drv->tx_idlelock); + return -ENODEV; +} + +static int snddev_icodec_close_rx(struct snddev_icodec_state *icodec) +{ + struct snddev_icodec_drv_state *drv = &snddev_icodec_drv; + struct snddev_icodec_data *data = icodec->data; + + wake_lock(&drv->rx_idlelock); + + if (drv->snddev_vreg) + vreg_mode_vote(drv->snddev_vreg, 0, SNDDEV_HIGH_POWER_MODE); + + /* Disable power amplifier */ + if (icodec->data->pamp_on) + icodec->data->pamp_on(0); + + if (support_aic3254) { + /* Restore default id for A3254 */ + if (data->aic3254_id != data->default_aic3254_id) + data->aic3254_id = data->default_aic3254_id; + /* Disable External Codec A3254 */ + if (aic3254_ops->aic3254_set_mode) + aic3254_ops->aic3254_set_mode(AIC3254_CONFIG_RX, DOWNLINK_OFF); + } + if (support_adie) { + /* Disable ADIE */ + if (icodec->adie_path) { + adie_codec_proceed_stage(icodec->adie_path, + ADIE_CODEC_DIGITAL_OFF); + adie_codec_close(icodec->adie_path); + icodec->adie_path = NULL; + } + } + + afe_close(icodec->data->copp_id); + + if (icodec->data->voltage_on) + icodec->data->voltage_on(0); + + clk_disable(drv->rx_bitclk); + + if (support_aic3254_use_mclk) + snddev_icodec_rxclk_enable(icodec, 0); + else + clk_disable(drv->rx_osrclk); + + msm_snddev_rx_mclk_free(); + + icodec->enabled = 0; + + wake_unlock(&drv->rx_idlelock); + return 0; +} + +static int snddev_icodec_close_tx(struct snddev_icodec_state *icodec) +{ + struct snddev_icodec_drv_state *drv = &snddev_icodec_drv; + struct snddev_icodec_data *data = icodec->data; + + wake_lock(&drv->tx_idlelock); + + if (drv->snddev_vreg) + vreg_mode_vote(drv->snddev_vreg, 0, SNDDEV_HIGH_POWER_MODE); + + /* Reuse pamp_off for TX platform-specific setup */ + if (icodec->data->pamp_on) + icodec->data->pamp_on(0); + + if (support_aic3254) { + /* Restore default id for A3254 */ + if (data->aic3254_id != data->default_aic3254_id) + data->aic3254_id = data->default_aic3254_id; + /* Disable External Codec A3254 */ + if (aic3254_ops->aic3254_set_mode) + aic3254_ops->aic3254_set_mode(AIC3254_CONFIG_TX, UPLINK_OFF); + } + if (support_adie) { + /* Disable ADIE */ + if (icodec->adie_path) { + adie_codec_proceed_stage(icodec->adie_path, + ADIE_CODEC_DIGITAL_OFF); + adie_codec_close(icodec->adie_path); + icodec->adie_path = NULL; + } + } + afe_close(icodec->data->copp_id); + + clk_disable(drv->tx_bitclk); + clk_disable(drv->tx_osrclk); + + if (support_aic3254_use_mclk) + snddev_icodec_rxclk_enable(icodec, 0); + + msm_snddev_tx_mclk_free(); + + + icodec->enabled = 0; + + wake_unlock(&drv->tx_idlelock); + return 0; +} + +static int snddev_icodec_set_device_volume_impl( + struct msm_snddev_info *dev_info, u32 volume) +{ + struct snddev_icodec_state *icodec; + + int rc = 0; + + icodec = dev_info->private_data; + + if (icodec->data->dev_vol_type & SNDDEV_DEV_VOL_DIGITAL) { + + rc = adie_codec_set_device_digital_volume(icodec->adie_path, + icodec->data->channel_mode, volume); + if (rc < 0) { + pr_aud_err("%s: unable to set_device_digital_volume for" + "%s volume in percentage = %u\n", + __func__, dev_info->name, volume); + return rc; + } + + } else if (icodec->data->dev_vol_type & SNDDEV_DEV_VOL_ANALOG) + rc = adie_codec_set_device_analog_volume(icodec->adie_path, + icodec->data->channel_mode, volume); + if (rc < 0) { + pr_aud_err("%s: unable to set_device_analog_volume for" + "%s volume in percentage = %u\n", + __func__, dev_info->name, volume); + return rc; + } + else { + pr_aud_err("%s: Invalid device volume control\n", __func__); + return -EPERM; + } + return rc; +} + +static int snddev_icodec_open(struct msm_snddev_info *dev_info) +{ + int rc = 0; + struct snddev_icodec_state *icodec; + struct snddev_icodec_drv_state *drv = &snddev_icodec_drv; + + if (!dev_info) { + rc = -EINVAL; + goto error; + } + + icodec = dev_info->private_data; + + if (icodec->data->capability & SNDDEV_CAP_RX) { + mutex_lock(&drv->rx_lock); + if (drv->rx_active) { + mutex_unlock(&drv->rx_lock); + rc = -EBUSY; + goto error; + } + rc = snddev_icodec_open_rx(icodec); + + if (!IS_ERR_VALUE(rc)) { + drv->rx_active = 1; + if (support_adie && (icodec->data->dev_vol_type & ( + SNDDEV_DEV_VOL_DIGITAL | + SNDDEV_DEV_VOL_ANALOG))) + rc = snddev_icodec_set_device_volume_impl( + dev_info, dev_info->dev_volume); + } + mutex_unlock(&drv->rx_lock); + } else { + mutex_lock(&drv->tx_lock); + if (drv->tx_active) { + mutex_unlock(&drv->tx_lock); + rc = -EBUSY; + goto error; + } + rc = snddev_icodec_open_tx(icodec); + + if (!IS_ERR_VALUE(rc)) { + drv->tx_active = 1; + if (support_adie && (icodec->data->dev_vol_type & ( + SNDDEV_DEV_VOL_DIGITAL | + SNDDEV_DEV_VOL_ANALOG))) + rc = snddev_icodec_set_device_volume_impl( + dev_info, dev_info->dev_volume); + } + mutex_unlock(&drv->tx_lock); + } +error: + return rc; +} + +static int snddev_icodec_close(struct msm_snddev_info *dev_info) +{ + int rc = 0; + struct snddev_icodec_state *icodec; + struct snddev_icodec_drv_state *drv = &snddev_icodec_drv; + if (!dev_info) { + rc = -EINVAL; + goto error; + } + + icodec = dev_info->private_data; + + if (icodec->data->capability & SNDDEV_CAP_RX) { + mutex_lock(&drv->rx_lock); + if (!drv->rx_active) { + mutex_unlock(&drv->rx_lock); + rc = -EPERM; + goto error; + } + rc = snddev_icodec_close_rx(icodec); + if (!IS_ERR_VALUE(rc)) + drv->rx_active = 0; + mutex_unlock(&drv->rx_lock); + } else { + mutex_lock(&drv->tx_lock); + if (!drv->tx_active) { + mutex_unlock(&drv->tx_lock); + rc = -EPERM; + goto error; + } + rc = snddev_icodec_close_tx(icodec); + if (!IS_ERR_VALUE(rc)) + drv->tx_active = 0; + mutex_unlock(&drv->tx_lock); + } + +error: + return rc; +} + +static int snddev_icodec_check_freq(u32 req_freq) +{ + int rc = -EINVAL; + + if ((req_freq != 0) && (req_freq >= 8000) && (req_freq <= 48000)) { + if ((req_freq == 8000) || (req_freq == 11025) || + (req_freq == 12000) || (req_freq == 16000) || + (req_freq == 22050) || (req_freq == 24000) || + (req_freq == 32000) || (req_freq == 44100) || + (req_freq == 48000)) { + rc = 0; + } else + pr_aud_info("%s: Unsupported Frequency:%d\n", __func__, + req_freq); + } + return rc; +} + +static int snddev_icodec_set_freq(struct msm_snddev_info *dev_info, u32 rate) +{ + int rc; + struct snddev_icodec_state *icodec; + + if (!dev_info) { + rc = -EINVAL; + goto error; + } + + icodec = dev_info->private_data; + if (support_adie && + adie_codec_freq_supported(icodec->data->profile, rate) != 0) { + rc = -EINVAL; + goto error; + } else { + if (snddev_icodec_check_freq(rate) != 0) { + rc = -EINVAL; + goto error; + } else + icodec->sample_rate = rate; + } + + if (icodec->enabled) { + snddev_icodec_close(dev_info); + snddev_icodec_open(dev_info); + } + + return icodec->sample_rate; + +error: + return rc; +} + +static int snddev_icodec_enable_sidetone(struct msm_snddev_info *dev_info, + u32 enable, uint16_t gain) +{ + int rc = 0; + struct snddev_icodec_state *icodec; + struct snddev_icodec_drv_state *drv = &snddev_icodec_drv; + + /*3254 sidetone will be binded with dsp image.*/ + if (support_aic3254 || !support_adie) + goto error; + + if (!dev_info) { + pr_aud_err("invalid dev_info\n"); + rc = -EINVAL; + goto error; + } + + icodec = dev_info->private_data; + + if (icodec->data->capability & SNDDEV_CAP_RX) { + mutex_lock(&drv->rx_lock); + if (!drv->rx_active || !dev_info->opened) { + pr_aud_err("dev not active\n"); + rc = -EPERM; + mutex_unlock(&drv->rx_lock); + goto error; + } + rc = afe_sidetone(PRIMARY_I2S_TX, PRIMARY_I2S_RX, enable, gain); + if (rc < 0) + pr_aud_err("%s: AFE command sidetone failed\n", __func__); + mutex_unlock(&drv->rx_lock); + } else { + rc = -EINVAL; + pr_aud_err("rx device only\n"); + } + +error: + return rc; + +} +static int snddev_icodec_enable_anc(struct msm_snddev_info *dev_info, + u32 enable) +{ + int rc = 0; + struct adie_codec_anc_data *reg_writes; + struct acdb_cal_block cal_block; + struct snddev_icodec_state *icodec; + struct snddev_icodec_drv_state *drv = &snddev_icodec_drv; + + if (support_aic3254 || !support_adie) + goto error; + + pr_aud_info("%s: enable=%d\n", __func__, enable); + + if (!dev_info) { + pr_aud_err("invalid dev_info\n"); + rc = -EINVAL; + goto error; + } + icodec = dev_info->private_data; + + if ((icodec->data->capability & SNDDEV_CAP_RX) && + (icodec->data->capability & SNDDEV_CAP_ANC)) { + mutex_lock(&drv->rx_lock); + + if (!drv->rx_active || !dev_info->opened) { + pr_aud_err("dev not active\n"); + rc = -EPERM; + mutex_unlock(&drv->rx_lock); + goto error; + } + if (enable) { + get_anc_cal(&cal_block); + reg_writes = (struct adie_codec_anc_data *) + cal_block.cal_kvaddr; + + if (reg_writes == NULL) { + pr_aud_err("error, no calibration data\n"); + rc = -1; + mutex_unlock(&drv->rx_lock); + goto error; + } + + rc = adie_codec_enable_anc(icodec->adie_path, + 1, reg_writes); + } else { + rc = adie_codec_enable_anc(icodec->adie_path, + 0, NULL); + } + mutex_unlock(&drv->rx_lock); + } else { + rc = -EINVAL; + pr_aud_err("rx and ANC device only\n"); + } + +error: + return rc; + +} + +int snddev_icodec_set_device_volume(struct msm_snddev_info *dev_info, + u32 volume) +{ + struct snddev_icodec_state *icodec; + struct mutex *lock; + struct snddev_icodec_drv_state *drv = &snddev_icodec_drv; + int rc = -EPERM; + + if (!dev_info) { + pr_aud_info("%s : device not intilized.\n", __func__); + return -EINVAL; + } + + icodec = dev_info->private_data; + + if (!(icodec->data->dev_vol_type & (SNDDEV_DEV_VOL_DIGITAL + | SNDDEV_DEV_VOL_ANALOG))) { + + pr_aud_info("%s : device %s does not support device volume " + "control.", __func__, dev_info->name); + return -EPERM; + } + dev_info->dev_volume = volume; + + if (icodec->data->capability & SNDDEV_CAP_RX) + lock = &drv->rx_lock; + else + lock = &drv->tx_lock; + + mutex_lock(lock); + + rc = snddev_icodec_set_device_volume_impl(dev_info, + dev_info->dev_volume); + mutex_unlock(lock); + return rc; +} + +void htc_8x60_register_icodec_ops(struct q6v2audio_icodec_ops *ops) +{ + audio_ops = ops; +} + +static int snddev_icodec_probe(struct platform_device *pdev) +{ + int rc = 0; + struct snddev_icodec_data *pdata; + struct msm_snddev_info *dev_info; + struct snddev_icodec_state *icodec; + static int first_time = 1; + + if (!pdev || !pdev->dev.platform_data) { + printk(KERN_ALERT "Invalid caller\n"); + rc = -1; + goto error; + } + pdata = pdev->dev.platform_data; + if ((pdata->capability & SNDDEV_CAP_RX) && + (pdata->capability & SNDDEV_CAP_TX)) { + pr_aud_err("%s: invalid device data either RX or TX\n", __func__); + goto error; + } + icodec = kzalloc(sizeof(struct snddev_icodec_state), GFP_KERNEL); + if (!icodec) { + rc = -ENOMEM; + goto error; + } + dev_info = kmalloc(sizeof(struct msm_snddev_info), GFP_KERNEL); + if (!dev_info) { + kfree(icodec); + rc = -ENOMEM; + goto error; + } + + dev_info->name = pdata->name; + dev_info->copp_id = pdata->copp_id; + dev_info->private_data = (void *) icodec; + dev_info->dev_ops.open = snddev_icodec_open; + dev_info->dev_ops.close = snddev_icodec_close; + dev_info->dev_ops.set_freq = snddev_icodec_set_freq; + dev_info->dev_ops.set_device_volume = snddev_icodec_set_device_volume; + dev_info->capability = pdata->capability; + dev_info->opened = 0; + msm_snddev_register(dev_info); + icodec->data = pdata; + icodec->sample_rate = pdata->default_sample_rate; + dev_info->sample_rate = pdata->default_sample_rate; + dev_info->channel_mode = pdata->channel_mode; + if (pdata->capability & SNDDEV_CAP_RX) + dev_info->dev_ops.enable_sidetone = + snddev_icodec_enable_sidetone; + else + dev_info->dev_ops.enable_sidetone = NULL; + + if (pdata->capability & SNDDEV_CAP_ANC) { + dev_info->dev_ops.enable_anc = + snddev_icodec_enable_anc; + } else { + dev_info->dev_ops.enable_anc = NULL; + } + if (first_time) { + if (audio_ops->support_aic3254) { + support_aic3254 = audio_ops->support_aic3254(); + } else { + support_aic3254 = 0; + } + pr_aud_info("%s: support_aic3254 = %d\n", + __func__, support_aic3254); + + if (audio_ops->support_adie) { + support_adie = audio_ops->support_adie(); + } else { + support_adie = 0; + } + pr_aud_info("%s: support_adie = %d\n", + __func__, support_adie); + + if (audio_ops->is_msm_i2s_slave) { + msm_codec_i2s_slave_mode = audio_ops->is_msm_i2s_slave(); + } else { + msm_codec_i2s_slave_mode = 0; + } + pr_aud_info("%s: msm_codec_i2s_slave_mode = %d\n", + __func__, msm_codec_i2s_slave_mode); + + if (audio_ops->support_aic3254_use_mclk) + support_aic3254_use_mclk = \ + audio_ops->support_aic3254_use_mclk(); + else + support_aic3254_use_mclk = 0; + pr_aud_info("%s: support_aic3254_use_mclk = %d\n", + __func__, support_aic3254_use_mclk); + + first_time = 0; + } + +error: + return rc; +} + +static int snddev_icodec_remove(struct platform_device *pdev) +{ + return 0; +} + +static struct platform_driver snddev_icodec_driver = { + .probe = snddev_icodec_probe, + .remove = snddev_icodec_remove, + .driver = { .name = "snddev_icodec" } +}; + + +void htc_8x60_register_aic3254_ops(struct q6v2audio_aic3254_ops *ops) +{ + aic3254_ops = ops; +} + +int update_aic3254_info(struct aic3254_info *info) +{ + struct msm_snddev_info *dev_info; + int rc = 0; + + dev_info = audio_dev_ctrl_find_dev(info->dev_id); + if (IS_ERR(dev_info)) + rc = -ENODEV; + else { + if ((dev_info->copp_id == PRIMARY_I2S_RX) || + (dev_info->copp_id == PRIMARY_I2S_TX)) { + struct snddev_icodec_state *icodec; + icodec = dev_info->private_data; + icodec->data->aic3254_id = info->path_id; + pr_aud_info("%s: update aic3254 id of device %s as %d\n", + __func__, dev_info->name, icodec->data->aic3254_id); + } + } + + return rc; +} + +module_param(msm_codec_i2s_slave_mode, bool, 0); +MODULE_PARM_DESC(msm_codec_i2s_slave_mode, "Set MSM to I2S slave clock mode"); + +static int __init snddev_icodec_init(void) +{ + s32 rc; + struct snddev_icodec_drv_state *icodec_drv = &snddev_icodec_drv; + + rc = platform_driver_register(&snddev_icodec_driver); + if (IS_ERR_VALUE(rc)) { + pr_aud_err("%s: platform_driver_register for snddev icodec failed\n", + __func__); + goto error_snddev_icodec_driver; + } + + rc = platform_driver_register(&msm_cdcclk_ctl_driver); + if (IS_ERR_VALUE(rc)) { + pr_aud_err("%s: platform_driver_register for msm snddev failed\n", + __func__); + goto error_msm_cdcclk_ctl_driver; + } + + mutex_init(&icodec_drv->rx_lock); + mutex_init(&icodec_drv->tx_lock); + + mutex_init(&icodec_drv->rx_mclk_lock); + + icodec_drv->rx_active = 0; + icodec_drv->tx_active = 0; + icodec_drv->snddev_vreg = vreg_init(); + + wake_lock_init(&icodec_drv->tx_idlelock, WAKE_LOCK_IDLE, + "snddev_tx_idle"); + wake_lock_init(&icodec_drv->rx_idlelock, WAKE_LOCK_IDLE, + "snddev_rx_idle"); + return 0; + +error_msm_cdcclk_ctl_driver: + platform_driver_unregister(&snddev_icodec_driver); +error_snddev_icodec_driver: + return -ENODEV; +} + +static void __exit snddev_icodec_exit(void) +{ + struct snddev_icodec_drv_state *icodec_drv = &snddev_icodec_drv; + + platform_driver_unregister(&snddev_icodec_driver); + platform_driver_unregister(&msm_cdcclk_ctl_driver); + + clk_put(icodec_drv->rx_osrclk); + clk_put(icodec_drv->tx_osrclk); + if (icodec_drv->snddev_vreg) { + vreg_deinit(icodec_drv->snddev_vreg); + icodec_drv->snddev_vreg = NULL; + } + return; +} + +module_init(snddev_icodec_init); +module_exit(snddev_icodec_exit); + +MODULE_DESCRIPTION("ICodec Sound Device driver"); +MODULE_VERSION("1.0"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/qdsp6v3/snddev_mi2s.c b/arch/arm/mach-msm/qdsp6v3/snddev_mi2s.c new file mode 100644 index 00000000000..ddeb8d2a894 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v3/snddev_mi2s.c @@ -0,0 +1,472 @@ +/* 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. + * + * 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 "snddev_mi2s.h" + +#define SNDDEV_MI2S_PCM_SZ 32 /* 16 bit / sample stereo mode */ +#define SNDDEV_MI2S_MUL_FACTOR 3 /* Multi by 8 Shift by 3 */ +#define SNDDEV_MI2S_CLK_RATE(freq) \ + (((freq) * (SNDDEV_MI2S_PCM_SZ)) << (SNDDEV_MI2S_MUL_FACTOR)) + + +/* Global state for the driver */ +struct snddev_mi2s_drv_state { + + struct clk *tx_osrclk; + struct clk *tx_bitclk; + int mi2s_ws; + int mi2s_mclk; + int mi2s_sclk; + int fm_mi2s_sd; +}; + +static struct snddev_mi2s_drv_state snddev_mi2s_drv; + +static struct msm_mi2s_gpio_data *mi2s_gpio; + +static int mi2s_gpios_request(void) +{ + int rc = 0; + + pr_debug("%s\n", __func__); + rc = gpio_request(snddev_mi2s_drv.mi2s_ws, "MI2S_WS"); + if (rc < 0) { + pr_aud_err("%s: GPIO request for MI2S_WS failed\n", __func__); + return rc; + } + + rc = gpio_request(snddev_mi2s_drv.mi2s_sclk, "MI2S_SCLK"); + if (rc < 0) { + pr_aud_err("%s: GPIO request for MI2S_SCLK failed\n", __func__); + gpio_free(snddev_mi2s_drv.mi2s_sclk); + return rc; + } + + rc = gpio_request(snddev_mi2s_drv.mi2s_mclk, "MI2S_MCLK"); + if (rc < 0) { + pr_aud_err("%s: GPIO request for MI2S_MCLK failed\n", + __func__); + gpio_free(snddev_mi2s_drv.mi2s_ws); + gpio_free(snddev_mi2s_drv.mi2s_sclk); + return rc; + } + + rc = gpio_request(snddev_mi2s_drv.fm_mi2s_sd, "FM_MI2S_SD"); + if (rc < 0) { + pr_aud_err("%s: GPIO request for FM_MI2S_SD failed\n", + __func__); + gpio_free(snddev_mi2s_drv.mi2s_ws); + gpio_free(snddev_mi2s_drv.mi2s_sclk); + gpio_free(snddev_mi2s_drv.mi2s_mclk); + return rc; + } + + return rc; +} + +static void mi2s_gpios_free(void) +{ + pr_debug("%s\n", __func__); + gpio_free(snddev_mi2s_drv.mi2s_ws); + gpio_free(snddev_mi2s_drv.mi2s_sclk); + gpio_free(snddev_mi2s_drv.mi2s_mclk); + gpio_free(snddev_mi2s_drv.fm_mi2s_sd); +} + +static int mi2s_get_gpios(struct platform_device *pdev) +{ + int rc = 0; + struct resource *res; + + /* Claim all of the GPIOs. */ + res = platform_get_resource_byname(pdev, IORESOURCE_IO, "mi2s_ws"); + if (!res) { + pr_aud_err("%s: failed to get gpio MI2S_WS\n", __func__); + return -ENODEV; + } + + snddev_mi2s_drv.mi2s_ws = res->start; + + res = platform_get_resource_byname(pdev, IORESOURCE_IO, "mi2s_sclk"); + if (!res) { + pr_aud_err("%s: failed to get gpio MI2S_SCLK\n", __func__); + return -ENODEV; + } + + snddev_mi2s_drv.mi2s_sclk = res->start; + + res = platform_get_resource_byname(pdev, IORESOURCE_IO, + "mi2s_mclk"); + if (!res) { + pr_aud_err("%s: failed to get gpio MI2S_MCLK\n", __func__); + return -ENODEV; + } + + snddev_mi2s_drv.mi2s_mclk = res->start; + + res = platform_get_resource_byname(pdev, IORESOURCE_IO, + "fm_mi2s_sd"); + if (!res) { + pr_aud_err("%s: failed to get gpio FM_MI2S_SD\n", __func__); + return -ENODEV; + } + + snddev_mi2s_drv.fm_mi2s_sd = res->start; + + return rc; +} + +static int mi2s_fm_probe(struct platform_device *pdev) +{ + int rc = 0; + + pr_aud_info("%s:\n", __func__); + + rc = mi2s_get_gpios(pdev); + if (rc < 0) { + pr_aud_err("%s: GPIO configuration failed\n", __func__); + return rc; + } + + mi2s_gpio = (struct msm_mi2s_gpio_data *)(pdev->dev.platform_data); + return rc; +} + +static struct platform_driver mi2s_fm_driver = { + .probe = mi2s_fm_probe, + .driver = { .name = "msm_mi2s"} +}; + +static u8 num_of_bits_set(u8 sd_line_mask) +{ + u8 num_bits_set = 0; + + while (sd_line_mask) { + + if (sd_line_mask & 1) + num_bits_set++; + sd_line_mask = sd_line_mask >> 1; + } + return num_bits_set; +} + +static int snddev_mi2s_open(struct msm_snddev_info *dev_info) +{ + int rc = 0; + union afe_port_config afe_config; + u8 channels; + u8 num_of_sd_lines = 0; + struct snddev_mi2s_drv_state *drv = &snddev_mi2s_drv; + struct snddev_mi2s_data *snddev_mi2s_data = dev_info->private_data; + + if (!dev_info) { + pr_aud_err("%s: msm_snddev_info is null\n", __func__); + return -EINVAL; + } + + /* set up osr clk */ + drv->tx_osrclk = clk_get(0, "mi2s_osr_clk"); + if (IS_ERR(drv->tx_osrclk)) + pr_aud_err("%s master clock Error\n", __func__); + + rc = clk_set_rate(drv->tx_osrclk, + SNDDEV_MI2S_CLK_RATE(dev_info->sample_rate)); + if (IS_ERR_VALUE(rc)) { + pr_aud_err("ERROR setting osr clock\n"); + return -ENODEV; + } + clk_enable(drv->tx_osrclk); + + /* set up bit clk */ + drv->tx_bitclk = clk_get(0, "mi2s_bit_clk"); + if (IS_ERR(drv->tx_bitclk)) + pr_aud_err("%s clock Error\n", __func__); + + rc = clk_set_rate(drv->tx_bitclk, 8); + if (IS_ERR_VALUE(rc)) { + pr_aud_err("ERROR setting bit clock\n"); + clk_disable(drv->tx_osrclk); + return -ENODEV; + } + clk_enable(drv->tx_bitclk); + + afe_config.mi2s.bitwidth = 16; + + if (snddev_mi2s_data->channel_mode == 1) + channels = AFE_MI2S_MONO; + else if (snddev_mi2s_data->channel_mode == 2) + channels = AFE_MI2S_STEREO; + else if (snddev_mi2s_data->channel_mode == 4) + channels = AFE_MI2S_4CHANNELS; + else if (snddev_mi2s_data->channel_mode == 6) + channels = AFE_MI2S_6CHANNELS; + else if (snddev_mi2s_data->channel_mode == 8) + channels = AFE_MI2S_8CHANNELS; + else { + pr_aud_err("ERROR: Invalid MI2S channel mode\n"); + goto error_invalid_data; + } + + num_of_sd_lines = num_of_bits_set(snddev_mi2s_data->sd_lines); + + switch (num_of_sd_lines) { + case 1: + switch (snddev_mi2s_data->sd_lines) { + case MI2S_SD0: + afe_config.mi2s.line = AFE_I2S_SD0; + break; + case MI2S_SD1: + afe_config.mi2s.line = AFE_I2S_SD1; + break; + case MI2S_SD2: + afe_config.mi2s.line = AFE_I2S_SD2; + break; + case MI2S_SD3: + afe_config.mi2s.line = AFE_I2S_SD3; + break; + default: + pr_aud_err("%s: invalid SD line\n", + __func__); + goto error_invalid_data; + } + if (channels != AFE_MI2S_STEREO && + channels != AFE_MI2S_MONO) { + pr_aud_err("%s: for one SD line, channel " + "must be 1 or 2\n", __func__); + goto error_invalid_data; + } + afe_config.mi2s.channel = channels; + break; + case 2: + switch (snddev_mi2s_data->sd_lines) { + case MI2S_SD0 | MI2S_SD1: + afe_config.mi2s.line = AFE_I2S_QUAD01; + break; + case MI2S_SD2 | MI2S_SD3: + afe_config.mi2s.line = AFE_I2S_QUAD23; + break; + default: + pr_aud_err("%s: invalid SD line\n", + __func__); + goto error_invalid_data; + } + if (channels != AFE_MI2S_4CHANNELS) { + pr_aud_err("%s: for two SD lines, channel " + "must be 1 and 2 or 3 and 4\n", __func__); + goto error_invalid_data; + } + break; + case 3: + switch (snddev_mi2s_data->sd_lines) { + case MI2S_SD0 | MI2S_SD1 | MI2S_SD2: + afe_config.mi2s.line = AFE_I2S_6CHS; + break; + default: + pr_aud_err("%s: invalid SD lines\n", + __func__); + goto error_invalid_data; + } + if (channels != AFE_MI2S_6CHANNELS) { + pr_aud_err("%s: for three SD lines, lines " + "must be 1, 2, and 3\n", __func__); + goto error_invalid_data; + } + break; + case 4: + switch (snddev_mi2s_data->sd_lines) { + case MI2S_SD0 | MI2S_SD1 | MI2S_SD2 | MI2S_SD3: + afe_config.mi2s.line = AFE_I2S_8CHS; + break; + default: + pr_aud_err("%s: invalid SD lines\n", + __func__); + goto error_invalid_data; + } + + if (channels != AFE_MI2S_8CHANNELS) { + pr_aud_err("%s: for four SD lines, lines " + "must be 1, 2, 3, and 4\n", __func__); + goto error_invalid_data; + } + break; + default: + pr_aud_err("%s: invalid SD lines\n", __func__); + goto error_invalid_data; + } + afe_config.mi2s.ws = 1; + rc = afe_open(snddev_mi2s_data->copp_id, &afe_config, + dev_info->sample_rate); + + if (rc < 0) { + pr_aud_err("%s: afe_open failed\n", __func__); + goto error_invalid_data; + } + + /*enable fm gpio here*/ + rc = mi2s_gpios_request(); + if (rc < 0) { + pr_aud_err("%s: GPIO request failed\n", __func__); + return rc; + } + + pr_aud_info("%s: afe_open done\n", __func__); + + return rc; + +error_invalid_data: + + clk_disable(drv->tx_bitclk); + clk_disable(drv->tx_osrclk); + return -EINVAL; +} + +static int snddev_mi2s_close(struct msm_snddev_info *dev_info) +{ + + struct snddev_mi2s_drv_state *mi2s_drv = &snddev_mi2s_drv; + struct snddev_mi2s_data *snddev_mi2s_data = dev_info->private_data; + + if (!dev_info) { + pr_aud_err("%s: msm_snddev_info is null\n", __func__); + return -EINVAL; + } + + if (!dev_info->opened) { + pr_aud_err(" %s: calling close device with out opening the" + " device\n", __func__); + return -EIO; + } + afe_close(snddev_mi2s_data->copp_id); + clk_disable(mi2s_drv->tx_bitclk); + clk_disable(mi2s_drv->tx_osrclk); + + mi2s_gpios_free(); + + pr_aud_info("%s:\n", __func__); + + return 0; +} + +static int snddev_mi2s_set_freq(struct msm_snddev_info *dev_info, u32 req_freq) +{ + if (req_freq != 48000) { + pr_aud_info("%s: Unsupported Frequency:%d\n", __func__, req_freq); + return -EINVAL; + } + return 48000; +} + + +static int snddev_mi2s_probe(struct platform_device *pdev) +{ + int rc = 0; + struct snddev_mi2s_data *pdata; + struct msm_snddev_info *dev_info; + + if (!pdev || !pdev->dev.platform_data) { + printk(KERN_ALERT "Invalid caller\n"); + return -ENODEV; + } + + pdata = pdev->dev.platform_data; + + dev_info = kzalloc(sizeof(struct msm_snddev_info), GFP_KERNEL); + if (!dev_info) { + pr_aud_err("%s: uneable to allocate memeory for msm_snddev_info\n", + __func__); + + return -ENOMEM; + } + + dev_info->name = pdata->name; + dev_info->copp_id = pdata->copp_id; + dev_info->dev_ops.open = snddev_mi2s_open; + dev_info->dev_ops.close = snddev_mi2s_close; + dev_info->private_data = (void *)pdata; + dev_info->dev_ops.set_freq = snddev_mi2s_set_freq; + dev_info->capability = pdata->capability; + dev_info->opened = 0; + dev_info->sample_rate = pdata->sample_rate; + msm_snddev_register(dev_info); + + pr_aud_info("%s: probe done for %s\n", __func__, pdata->name); + return rc; +} + +static struct platform_driver snddev_mi2s_driver = { + .probe = snddev_mi2s_probe, + .driver = {.name = "snddev_mi2s"} +}; + +static int __init snddev_mi2s_init(void) +{ + s32 rc = 0; + + rc = platform_driver_register(&mi2s_fm_driver); + if (IS_ERR_VALUE(rc)) { + pr_aud_err("%s: platform_driver_register for mi2s_fm_driver failed\n", + __func__); + goto error_mi2s_fm_platform_driver; + } + + rc = platform_driver_register(&snddev_mi2s_driver); + if (IS_ERR_VALUE(rc)) { + + pr_aud_err("%s: platform_driver_register failed\n", __func__); + goto error_platform_driver; + } + + pr_aud_info("snddev_mi2s_init : done\n"); + + return rc; + +error_platform_driver: + platform_driver_unregister(&mi2s_fm_driver); +error_mi2s_fm_platform_driver: + pr_aud_err("%s: encounter error\n", __func__); + return -ENODEV; +} + +static void __exit snddev_mi2s_exit(void) +{ + struct snddev_mi2s_drv_state *mi2s_drv = &snddev_mi2s_drv; + + platform_driver_unregister(&snddev_mi2s_driver); + clk_put(mi2s_drv->tx_osrclk); + clk_put(mi2s_drv->tx_bitclk); + return; +} + + +module_init(snddev_mi2s_init); +module_exit(snddev_mi2s_exit); + +MODULE_DESCRIPTION("MI2S Sound Device driver"); +MODULE_VERSION("1.0"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/qdsp6v3/snddev_mi2s.h b/arch/arm/mach-msm/qdsp6v3/snddev_mi2s.h new file mode 100644 index 00000000000..fa1c55e2bf8 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v3/snddev_mi2s.h @@ -0,0 +1,46 @@ +/* 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 __MACH_QDSP6_V2_SNDDEV_MI2S_H +#define __MACH_QDSP6_V2_SNDDEV_MI2S_H + +struct snddev_mi2s_data { + u32 capability; /* RX or TX */ + const char *name; + u32 copp_id; /* audpp routing */ + u16 channel_mode; + u16 sd_lines; + u32 sample_rate; +}; + +#define MI2S_SD0 (1 << 0) +#define MI2S_SD1 (1 << 1) +#define MI2S_SD2 (1 << 2) +#define MI2S_SD3 (1 << 3) + +#endif diff --git a/arch/arm/mach-msm/qdsp6v3/snddev_virtual.c b/arch/arm/mach-msm/qdsp6v3/snddev_virtual.c new file mode 100644 index 00000000000..c0f2a9329ae --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v3/snddev_virtual.c @@ -0,0 +1,172 @@ +/* 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 "snddev_virtual.h" + +static DEFINE_MUTEX(snddev_virtual_lock); + +static int snddev_virtual_open(struct msm_snddev_info *dev_info) +{ + int rc = 0; + + pr_debug("%s\n", __func__); + + mutex_lock(&snddev_virtual_lock); + + if (!dev_info) { + pr_aud_err("%s: NULL dev_info\n", __func__); + + rc = -EINVAL; + goto done; + } + + if (!dev_info->opened) { + rc = afe_start_pseudo_port(dev_info->copp_id); + } else { + pr_aud_err("%s: Pseudo port 0x%x is already open\n", + __func__, dev_info->copp_id); + + rc = -EBUSY; + } + +done: + mutex_unlock(&snddev_virtual_lock); + + return rc; +} + +static int snddev_virtual_close(struct msm_snddev_info *dev_info) +{ + int rc = 0; + + pr_debug("%s\n", __func__); + + mutex_lock(&snddev_virtual_lock); + + if (!dev_info) { + pr_aud_err("%s: NULL dev_info\n", __func__); + + rc = -EINVAL; + goto done; + } + + if (dev_info->opened) { + rc = afe_stop_pseudo_port(dev_info->copp_id); + } else { + pr_aud_err("%s: Pseudo port 0x%x is not open\n", + __func__, dev_info->copp_id); + + rc = -EPERM; + } + +done: + mutex_unlock(&snddev_virtual_lock); + + return rc; +} + +static int snddev_virtual_set_freq(struct msm_snddev_info *dev_info, u32 rate) +{ + int rc = 0; + + if (!dev_info) + rc = -EINVAL; + + return rate; +} + +static int snddev_virtual_probe(struct platform_device *pdev) +{ + int rc = 0; + struct snddev_virtual_data *pdata; + struct msm_snddev_info *dev_info; + + pr_debug("%s\n", __func__); + + if (!pdev || !pdev->dev.platform_data) { + pr_aud_err("%s: Invalid caller\n", __func__); + + rc = -EPERM; + goto done; + } + + pdata = pdev->dev.platform_data; + + dev_info = kmalloc(sizeof(struct msm_snddev_info), GFP_KERNEL); + if (!dev_info) { + pr_aud_err("%s: Out of memory\n", __func__); + + rc = -ENOMEM; + goto done; + } + + dev_info->name = pdata->name; + dev_info->copp_id = pdata->copp_id; + dev_info->private_data = (void *) NULL; + dev_info->dev_ops.open = snddev_virtual_open; + dev_info->dev_ops.close = snddev_virtual_close; + dev_info->dev_ops.set_freq = snddev_virtual_set_freq; + dev_info->capability = pdata->capability; + dev_info->sample_rate = 48000; + dev_info->opened = 0; + dev_info->sessions = 0; + + msm_snddev_register(dev_info); + +done: + return rc; +} + +static int snddev_virtual_remove(struct platform_device *pdev) +{ + return 0; +} + +static struct platform_driver snddev_virtual_driver = { + .probe = snddev_virtual_probe, + .remove = snddev_virtual_remove, + .driver = { .name = "snddev_virtual" } +}; + +static int __init snddev_virtual_init(void) +{ + int rc = 0; + + pr_debug("%s\n", __func__); + + rc = platform_driver_register(&snddev_virtual_driver); + if (IS_ERR_VALUE(rc)) { + pr_aud_err("%s: Platform driver register failure\n", __func__); + + return -ENODEV; + } + + return 0; +} + +static void __exit snddev_virtual_exit(void) +{ + platform_driver_unregister(&snddev_virtual_driver); + + return; +} + +module_init(snddev_virtual_init); +module_exit(snddev_virtual_exit); + +MODULE_DESCRIPTION("Virtual Sound Device driver"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/qdsp6v3/snddev_virtual.h b/arch/arm/mach-msm/qdsp6v3/snddev_virtual.h new file mode 100644 index 00000000000..dec4d0739de --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v3/snddev_virtual.h @@ -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. + */ +#ifndef __MACH_QDSP6V2_SNDDEV_VIRTUAL_H +#define __MACH_QDSP6V2_SNDDEV_VIRTUAL_H + +struct snddev_virtual_data { + u32 capability; /* RX or TX */ + const char *name; + u32 copp_id; /* Audpp routing */ +}; +#endif diff --git a/arch/arm/mach-msm/qdsp6v3/timpani_profile_8x60.h b/arch/arm/mach-msm/qdsp6v3/timpani_profile_8x60.h new file mode 100644 index 00000000000..599919988ea --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v3/timpani_profile_8x60.h @@ -0,0 +1,2269 @@ +/* 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 __MACH_QDSP6V2_TIMPANI_PROFILE_H +#define __MACH_QDSP6V2_TIMPANI_PROFILE_H + +/* + * TX Device Profiles + */ + +/* Analog MIC */ +/* AMIC Primary mono */ +#define AMIC_PRI_MONO_8000_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x05)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0xD0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x65)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAB, 0x09, 0x00)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3A98}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0xFF, 0x1E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x93, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x94, 0xFF, 0x1B)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x99, 0x0F, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x9F, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xC0)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x64)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAB, 0x09, 0x09)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + + + +/* AMIC Secondary mono */ +#define AMIC_SEC_MONO_8000_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x05)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0E, 0xFF, 0xA8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x65)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAC, 0x09, 0x00)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3A98 },\ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0xFF, 0x1E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x93, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x94, 0xFF, 0x1B)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x99, 0x0F, 0x05)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x9F, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xC0)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0E, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x64)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAC, 0x09, 0x09)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +/* AMIC dual */ +#define AMIC_DUAL_8000_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x05)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0xFF, 0x1E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x93, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x94, 0xFF, 0x1B)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x99, 0x0F, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x9F, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0xB0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0E, 0xFF, 0xA8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x65)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAB, 0x09, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAC, 0x09, 0x00)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xbb8 }, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xC0)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0E, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x64)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAB, 0x09, 0x09)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAC, 0x09, 0x09)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + + +/* + * Digital MIC + */ +/* DMIC1 Primary (DMIC 1 - TX1) */ +#define DMIC1_PRI_MONO_8000_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x05)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0x1F, 0x1E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x92, 0x3F, 0x21)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x94, 0x3F, 0x24)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x39, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA8, 0x0F, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAB, 0x3F, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xC0)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x92, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x94, 0xFF, 0x1B)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +/* DMIC1 Secondary - (DMIC 2 - TX1) */ +#define DMIC1_SEC_MONO_8000_OSR_64 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x05)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0xFF, 0x12)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x92, 0xFF, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x93, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x94, 0xFF, 0x24)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA8, 0x0F, 0x05)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x9F, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x65)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xbb8 }, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xC0)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x64)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +/* DMIC Dual Primary (DMIC 1/2 - TX1) */ +#define DMIC1_PRI_STEREO_8000_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x05)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0x1F, 0x1E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x92, 0x3F, 0x19)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x94, 0x3F, 0x24)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x39, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA8, 0x0F, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAB, 0x3F, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xC0)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x92, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x94, 0xFF, 0x1B)} } + +/* DMIC2 Dual Primary (DMIC 3/4 - TX2 - Left/Right) */ +#define DMIC2_SEC_DUAL_8000_OSR_64 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xbb8 }, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x30, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x30, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0xC0, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA6, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA7, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0xFF, 0x22)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x92, 0xFF, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x93, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x96, 0xFF, 0x24)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA8, 0xF0, 0xE0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x9F, 0x0C, 0x0C)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x09, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0A, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x65)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xbb8 }, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0xC0, 0xC0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA6, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA7, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0xF0, 0xC0)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0xC0, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x64)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x09, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0A, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define HS_DMIC2_STEREO_8000_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xbb8 }, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x05)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0x1F, 0x1E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x92, 0x3F, 0x19)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x94, 0x3F, 0x24)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x39, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA8, 0x0F, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAB, 0x3F, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xC0)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0xC0, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x92, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x94, 0xFF, 0x1B)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +/* + * LINE IN + */ +#define LINEIN_PRI_MONO_8000_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x05)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0xFF, 0x1E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x93, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x94, 0xFF, 0x1B)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x99, 0x0F, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x9F, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0xA4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x65)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xbb8 }, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xC0)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x64)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define LINEIN_PRI_STEREO_8000_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x05)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0xFF, 0x1E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x93, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x94, 0xFF, 0x1B)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x99, 0x0F, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x9F, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0xA4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0E, 0xFF, 0xA2)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x65)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xbb8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xC0)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0E, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x64)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define LINEIN_SEC_MONO_8000_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x30, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x30, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0xC0, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA6, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA7, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0xFF, 0x2E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x93, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x96, 0xFF, 0x1B)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x99, 0xF0, 0xA0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x9F, 0x0C, 0x0C)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x18, 0xFF, 0xA2)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x09, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0A, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x65)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xbb8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0xC0, 0xC0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA6, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA7, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0xF0, 0xC0)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0xC0, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x18, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x64)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x09, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0A, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define LINEIN_SEC_STEREO_8000_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x30, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x30, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0xC0, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA6, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA7, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0xFF, 0x2E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x93, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x96, 0xFF, 0x1B)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x99, 0xF0, 0xE0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x9F, 0x0C, 0x0C)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x18, 0xFF, 0xA2)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x19, 0xFF, 0xA2)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x09, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0A, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x65)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xbb8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0xC0, 0xC0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA6, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA7, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0xF0, 0xC0)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0xC0, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x18, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x19, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x64)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x09, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0A, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define LINEIN_SEC_STEREO_8000_OSR_64 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x30, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x30, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0xC0, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA6, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA7, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0xFF, 0x22)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x93, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x96, 0xFF, 0x18)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x99, 0xF0, 0xE0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x9F, 0x0C, 0x0C)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x18, 0xFF, 0xA2)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x19, 0xFF, 0xA2)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x09, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0A, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x65)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xbb8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0xC0, 0xC0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA6, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA7, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0xF0, 0xC0)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0xC0, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x18, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x19, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x64)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x09, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0A, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +/* + * AUX IN + */ +#define AUXIN_MONO_8000_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x05)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0xFF, 0x1E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x93, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x94, 0xFF, 0x1B)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x99, 0x0F, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x9F, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0xA1)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x65)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xbb8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xC0)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x64)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +/* Headset MIC */ +#define HEADSET_AMIC2_TX_MONO_PRI_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x05)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0xFF, 0x1E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x93, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x94, 0xFF, 0x1B)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x99, 0x0F, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x9F, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0xC8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x65)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAB, 0x09, 0x00)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xbb8 }, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xC0)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x64)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAB, 0x09, 0x09)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +/* + * RX Device Profiles + */ + +/* RX EAR */ +#define EAR_PRI_MONO_8000_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0F)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x97, 0xFF, 0x01)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0xFF, 0x4C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x39, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xbb8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0E)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x39, 0x01, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define EAR_SEC_8000_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x08, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x08, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x30, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA4, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0x0F, 0x0F)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA1, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x04, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x98, 0xFF, 0x02)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x28, 0xFF, 0xCA)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x39, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xbb8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x30, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA4, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0x0F, 0x0E)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x39, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +/* ANC Headset: Speakers on Primary Rx, Noise Microphones on Secondary Tx */ + +#define ANC_HEADSET_CPLS_AMIC1_AUXL_RX1_48000_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x95, 0xFF, 0x40)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x9F, 0x0C, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x9B, 0x01, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x18, 0xFF, 0xD0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x19, 0xFF, 0xC1)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x09, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0A, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x65)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x6C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xB7, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x55)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0xFF, 0x29)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xBB8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0xF5)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x27, 0x24)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x04)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xC0, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xD0, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x18, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x19, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x64)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x09, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0A, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +/* + * RX HPH PRIMARY + */ + +/* RX HPH CLASS AB CAPLESS */ + +#define HEADSET_AB_CPLS_48000_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x6C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xB7, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x55)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0xFF, 0x29)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xBB8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0xF5)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x27, 0x24)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x04)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define HPH_PRI_AB_CPLS_MONO \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0F)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x97, 0xFF, 0x01)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0xFF, 0x6C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x24, 0x20)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3D, 0x24, 0x20)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFF, 0xA6)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFF, 0xA6)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFD, 0x55)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 100}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFC, 0xF5)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFC, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x24, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3D, 0x24, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define HPH_PRI_AB_CPLS_DIFF \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0F)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x97, 0xFF, 0x01)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0xFF, 0x6C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x24, 0x20)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3D, 0x24, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFF, 0xA6)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFF, 0xA6)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFD, 0x55)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 100}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFC, 0xF5)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFC, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x24, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3D, 0x24, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define HPH_PRI_AB_CPLS_STEREO \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0F)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x97, 0xFF, 0x01)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0xFF, 0x6C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x24, 0x24)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFF, 0xA6)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFF, 0xA6)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFD, 0x55)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 100}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFC, 0xF5)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFC, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x24, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +/* RX HPH CLASS AB LEGACY */ + +#define HPH_PRI_AB_LEG_MONO \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0F)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x97, 0xFF, 0x01)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0xFF, 0x6C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x24, 0x20)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3D, 0x24, 0x20)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFF, 0xA6)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFF, 0xA6)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFD, 0x59)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 300000}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFD, 0xF9)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 100}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFC, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x24, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3D, 0x24, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define HP_PRI_AB_LEG_DIFF \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0F)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x97, 0xFF, 0x01)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0xFF, 0x6C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x24, 0x20)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3D, 0x24, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFF, 0xA6)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFF, 0xA6)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFD, 0x59)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 300000}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFD, 0xF9)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 100}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFC, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x24, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3D, 0x24, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define HPH_PRI_AB_LEG_STEREO \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x6C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xB7, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x09)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x59)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x186A0}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0xF9)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x27, 0x27)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x10)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +/* RX HPH CLASS D LEGACY */ + +#define HPH_PRI_D_LEG_DIFF \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0F)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x97, 0xFF, 0x01)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0xFF, 0x6C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3E, 0x90, 0x90)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFF, 0xA6)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFF, 0xA6)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x0A, 0x0A)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 300000}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x0F, 0x0F)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 100}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x0F, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x24, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3D, 0x24, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define HPH_PRI_D_LEG_STEREO \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x21, 0xFF, 0x60)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x22, 0xFF, 0xE1)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x6C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x26, 0xFF, 0xD0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x2D, 0xFF, 0x6F)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x2E, 0xFF, 0x55)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xB7, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3F, 0xFF, 0x0F)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x40, 0xFF, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x41, 0x08, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x42, 0xFF, 0xBB)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x43, 0xFF, 0xF2)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x44, 0xF7, 0x37)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x45, 0xFF, 0xFF)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x46, 0xFF, 0x77)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x47, 0xFF, 0xF2)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x48, 0xF7, 0x37)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x49, 0xFF, 0xFF)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4A, 0xFF, 0x77)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x05)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3E, 0xFF, 0x8C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x0F, 0x0A)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 300000}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x0F, 0x0F)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x04)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3E, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x0F, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +/* + * RX HPH SECONDARY + */ + +/* RX HPH CLASS AB CAPLESS */ +#define HPH_SEC_AB_CPLS_MONO \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x08, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x08, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x30, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA4, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0x0F, 0x0F)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA1, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x04, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x98, 0xFF, 0x02)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x28, 0xFF, 0xC2)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x48, 0x40)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3D, 0x48, 0x40)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFF, 0xA6)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFF, 0xA6)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFD, 0x55)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 100}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFC, 0xF5)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x30, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA4, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0x0F, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFC, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x48, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3D, 0x48, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x30, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + + +#define HPH_SEC_AB_CPLS_DIFF \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x08, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x08, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x30, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA4, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0x0F, 0x0F)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA1, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x04, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x98, 0xFF, 0x02)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x28, 0xFF, 0xC2)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x48, 0x40)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3D, 0x48, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFF, 0xA6)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFF, 0xA6)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFD, 0x55)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 100}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFC, 0xF5)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x30, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA4, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0x0F, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFC, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x48, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3D, 0x48, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x30, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define HPH_SEC_AB_CPLS_STEREO \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x08, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x08, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x30, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA4, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA5, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0x0F, 0x0F)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA1, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x04, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x98, 0xFF, 0x02)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x28, 0xFF, 0xC2)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x29, 0xFF, 0xC2)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x48, 0x48)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFF, 0xA6)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFF, 0xA6)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFD, 0x55)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 100}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFC, 0xF5)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x30, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA4, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA5, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0x0F, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFC, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x48, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x30, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +/* RX HPH CLASS AB LEGACY */ +#define HPH_SEC_AB_LEG_MONO \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x08, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x08, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x30, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA4, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0x0F, 0x0F)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA1, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x04, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x98, 0xFF, 0x02)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x28, 0xFF, 0xC2)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x48, 0x40)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3D, 0x48, 0x40)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFF, 0xA6)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFF, 0xA6)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFD, 0x59)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 300000}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFD, 0xF9)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 100}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x30, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA4, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0x0F, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFC, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x48, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3D, 0x48, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x30, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define HPH_SEC_AB_LEG_DIFF \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x08, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x08, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x30, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA4, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0x0F, 0x0F)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA1, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x04, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x98, 0xFF, 0x02)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x28, 0xFF, 0xC2)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x48, 0x40)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3D, 0x48, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFF, 0xA6)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFF, 0xA6)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFD, 0x59)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 300000}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFD, 0xF9)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 100}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x30, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA4, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0x0F, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFC, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x48, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3D, 0x48, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x30, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + + +#define HPH_SEC_AB_LEG_STEREO \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x08, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x08, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x30, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA4, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA5, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0x0F, 0x0F)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA1, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x04, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x98, 0xFF, 0x02)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x28, 0xFF, 0xC2)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x29, 0xFF, 0xC2)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x48, 0x48)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFF, 0xA6)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFF, 0xA6)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFD, 0x59)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 300000}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFD, 0xF9)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 100}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x30, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA4, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA5, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0x0F, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFC, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x48, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x30, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +/* RX HPH CLASS D LEGACY */ + +#define HPH_SEC_D_LEG_DIFF \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x08, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x08, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x30, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA4, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0x0F, 0x0F)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA1, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x04, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x98, 0xFF, 0x02)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x28, 0xFF, 0xC2)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3E, 0x50, 0x50)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFF, 0xA6)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFF, 0xA6)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x0A, 0x0A)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 300000},\ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x0F, 0x0F)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 100}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x30, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA4, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0x0F, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x0F, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x48, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3D, 0x48, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x30, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define HPH_SEC_D_LEG_STEREO \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x08, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x08, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x30, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA4, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA5, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0x0F, 0x0F)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA1, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x04, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x98, 0xFF, 0x02)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x28, 0xFF, 0xC2)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x29, 0xFF, 0xC2)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3E, 0x50, 0x40)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFF, 0xA6)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFF, 0xA6)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x0A, 0x0A)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 300000}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x0F, 0x0F)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 100}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x30, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA4, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0x0F, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x0F, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x48, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x30, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +/* RX LINE OUT PRIMARY */ +#define LINEOUT_PRI_MONO \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0F)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x97, 0xFF, 0x01)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0xFF, 0x6C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0x24, 0x20)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0x24, 0x20)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFF, 0xA4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFF, 0xA4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0x58, 0x58)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 100000}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0xF8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFF, 0xA4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFF, 0xA4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0x24, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0x24, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define SPEAKER_HPH_AB_CPL_PRI_STEREO_48000_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x6C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xB7, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x55)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0xFF, 0x29)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x1388}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0xF5)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x48)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x1388}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0xF8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0x24, 0x24)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x27, 0x24)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x04)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0x24, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define LINEOUT_PRI_DIFF \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0F)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x97, 0xFF, 0x01)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0xFF, 0x6C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0x24, 0x20)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0x24, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFF, 0xA4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFF, 0xA4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0x58, 0x58)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 100000}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0xF8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFF, 0xA4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFF, 0xA4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0x24, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0x24, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define LINEOUT_PRI_STEREO \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0F)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x97, 0xFF, 0x01)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0xFF, 0x6C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0x24, 0x24)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFF, 0xA4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFF, 0xA4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0x58, 0x58)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 100000}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0xF8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0c)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFF, 0xA4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFF, 0xA4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0x24, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +/* RX LINE OUT SECONDARY */ +#define LINEOUT_SEC_MONO \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x08, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x08, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x30, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA4, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0x0F, 0x0F)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA1, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x04, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x98, 0xFF, 0x02)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x28, 0xFF, 0xC2)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0x48, 0x40)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0x48, 0x40)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFF, 0xA4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFF, 0xA4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0x58, 0x58)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 100000}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0xF8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x30, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA4, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0x0F, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFF, 0xA4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFF, 0xA4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0x48, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0x48, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x30, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define LINEOUT_SEC_DIFF \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x08, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x08, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x30, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA4, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0x0F, 0x0F)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA1, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x04, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x98, 0xFF, 0x02)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x28, 0xFF, 0xC2)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0x48, 0x40)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0x48, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFF, 0xA4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFF, 0xA4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0x58, 0x58)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 100000}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0xF8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x30, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA4, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0x0F, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFF, 0xA4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFF, 0xA4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0x48, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0x48, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x30, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define LINEOUT_SEC_STEREO \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x08, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x08, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x30, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA4, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA5, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0x0F, 0x0F)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA1, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x04, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x98, 0xFF, 0x02)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x28, 0xFF, 0xC2)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x29, 0xFF, 0xC2)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0x48, 0x48)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFF, 0xA4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFF, 0xA4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0x58, 0x58)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 100000}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0xF8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x30, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA4, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA5, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0x0F, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFF, 0xA4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFF, 0xA4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0x48, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x30, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define SPEAKER_PRI_STEREO_48000_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x6C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xB7, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x08)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x1388}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x48)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x1388}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0xF8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0x24, 0x24)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x10)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x05, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0x24, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +/* RX AUX */ +#define AUXOUT_PRI_MONO_8000_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0F)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x97, 0xFF, 0x01)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0xFF, 0x4C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x39, 0x20, 0x20)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0x07, 0x03)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 50000}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0x07, 0x07)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0E)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0x07, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x39, 0x20, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define AUXOUT_SEC_MONO_8000_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x08, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x08, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x30, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA4, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0x0F, 0x0F)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA1, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x04, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x98, 0xFF, 0x02)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x28, 0xFF, 0xCA)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x39, 0x40, 0x40)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0x07, 0x03)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 50000}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0x07, 0x07)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x30, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA4, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0x0F, 0x0E)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0x07, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x39, 0x40, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + + +/* + * LB Device Profiles + */ + +/* EAR */ +#define LB_EAR_PRI_MONO \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xbb8 }, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x30, 0x20)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x38, 0xFF, 0xA0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x39, 0x04, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xbb8 }, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x30, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x39, 0x04, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +/* HPH CLASS AB CAPLESS */ +#define LB_HPH_AB_CPLS_PRI_MONO \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x80, 0x80)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3D, 0x80, 0x80)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x38, 0xFF, 0xA0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFF, 0xA6)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFF, 0xA6)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x30, 0x20)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFC, 0x55)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 100}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFC, 0xF5)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFC, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x30, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x80, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3D, 0x80, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define LB_HPH_AB_CPLS_PRI_DIFF \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x80, 0x80)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3D, 0x10, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x38, 0xFF, 0xA0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFF, 0xA6)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFF, 0xA6)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x30, 0x20)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFC, 0x55)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 100}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFC, 0xF5)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFC, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x30, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x08, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3D, 0x08, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define SPEAKER_HPH_AB_CPL_PRI_STEREO_48000_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x6C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xB7, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x55)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0xFF, 0x29)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x1388}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0xF5)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x48)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x1388}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0xF8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0x24, 0x24)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x27, 0x24)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x04)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0x24, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define LB_HPH_AB_CPLS_PRI_STEREO \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x90, 0x90)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x38, 0xFF, 0xAA)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFF, 0xA6)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFF, 0xA6)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x30, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFC, 0x55)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 100}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFC, 0xF5)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFC, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x30, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x90, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +/* HPH CLASS AB LEGACY */ +#define LB_HPH_AB_LEG_PRI_MONO \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x80, 0x80)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3D, 0x80, 0x80)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x38, 0xFF, 0xA0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFF, 0xA6)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFF, 0xA6)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x30, 0x20)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFC, 0x59)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 300000}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFC, 0xFC)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 100}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFC, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x30, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x80, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3D, 0x80, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define LB_PHP_AB_LEG_PRI_DIFF \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x80, 0x80)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3D, 0x10, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x38, 0xFF, 0xA0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFF, 0xA6)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFF, 0xA6)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x30, 0x20)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFC, 0x59)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 300000}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFC, 0xFC)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 100}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFC, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x30, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x08, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3D, 0x08, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define LB_HPH_AB_LEG_PRI_STEREO \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x90, 0x90)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x38, 0xFF, 0xAA)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFF, 0xA6)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFF, 0xA6)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x30, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFC, 0x59)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 300000}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFC, 0xFC)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 100}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFC, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x30, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x90, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +/* HPH CLASS D LEGACY */ +#define LB_HPH_D_LEG_PRI_DIFF \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3E, 0x30, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x38, 0xFF, 0xA0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFF, 0xA6)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFF, 0xA6)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x3A, 0x2A)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 300000}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x3F, 0x2F)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 100}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x3F, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3E, 0x30, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define LB_HPH_D_LEG_PRI_STEREO \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3E, 0x30, 0x20)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x38, 0xFF, 0xAA)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFF, 0xA6)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFF, 0xA6)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x3A, 0x3A)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 300000}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x3F, 0x3F)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 100}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x3F, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3E, 0x30, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +/* LINE OUT */ +#define LB_LINEOUT_PRI_MONO \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xbb8}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0x80, 0x80)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0x80, 0x80)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x38, 0xFF, 0xA0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFF, 0xA4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFF, 0xA4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x30, 0x20)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0x58, 0x58)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 100000}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0xF8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFF, 0xA4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFF, 0xA4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x30, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0x80, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0x80, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define LB_LINEOUT_PRI_DIFF \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xbb8}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0x80, 0x80)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0x10, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x38, 0xFF, 0xA0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFF, 0xA4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFF, 0xA4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x30, 0x20)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0x58, 0x58)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 100000}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0xF8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFF, 0xA4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFF, 0xA4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x30, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0x80, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0x10, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define LB_LINEOUT_PRI_STEREO \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xbb8}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0x90, 0x90)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x38, 0xFF, 0xAA)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFF, 0xA4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFF, 0xA4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x30, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0x58, 0x58)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 100000}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0xF8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFF, 0xA4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFF, 0xA4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x30, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0x90, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +/* AUX OUT */ +#define LB_AUXOUT_PRI_MONO \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xbb8}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x39, 0xE0, 0x80)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x30, 0x20)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x38, 0xFF, 0xA0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0x07, 0x03)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 50000}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0x07, 0x07)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0x07, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x30, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x39, 0xE0, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +/* TTY RX */ +#define TTY_HEADSET_MONO_RX_8000_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x97, 0xFF, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x06)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x01)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x4C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xB7, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x45)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0xFF, 0x29)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xBB8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0xC5)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x27, 0x20)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x04)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +/* TTY TX */ +#define TTY_HEADSET_MONO_TX_8000_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x05)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0xFF, 0x1E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x93, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x94, 0xFF, 0x1B)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x99, 0x0F, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x9F, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0xA8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x65)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xBB8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xC0)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x64)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + + +#endif diff --git a/arch/arm/mach-msm/qdsp6v3/timpani_profile_8x60_lead.h b/arch/arm/mach-msm/qdsp6v3/timpani_profile_8x60_lead.h new file mode 100644 index 00000000000..3f747bd99c7 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v3/timpani_profile_8x60_lead.h @@ -0,0 +1,699 @@ +/* arch/arm/mach-msm/qdsp6v3/timpani_profile_8x60_lead.h + * + * Copyright (C) 2010 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 __TIMPANI_PROFILE_LEAD_H +#define __TIMPANI_PROFILE_LEAD_H + +/* RX HPH CLASS AB CAPLESS, STEREO OR MONO OR MONO-DIFF */ +#define HEADSET_STEREO_AB_CPLS_48000_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x6C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xB7, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x55)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0xFE, 0xCA)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xBB8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0xFD)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0xFE, 0xC8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x27, 0x27)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3D, 0x04, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x36, 0xFE, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x37, 0xFE, 0x04)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x27, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3D, 0x04, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + + +/* RX EAR MONO*/ +#define EAR_PRI_MONO_48000_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x6C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xB7, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x55)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0xFE, 0xCA)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xBB8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0xFD)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0xFE, 0xC8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x27, 0x23)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3D, 0x04, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x36, 0xFE, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x37, 0xFE, 0x04)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x27, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3D, 0x04, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +/* RX SPK, STEREO OR MONO OR MONO-DIFF */ +#define SPEAKER_PRI_48000_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x6C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xB7, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x55)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0xFE, 0xCA)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xBB8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0xFD)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0xFE, 0xC8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x27, 0x23)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3D, 0x04, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x36, 0xFE, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x37, 0xFE, 0x04)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x27, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3D, 0x04, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + + +/* ANALOG IMIC Primary MONO */ +#define AMIC_PRI_MONO_48000_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x05)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0xFF, 0x1E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x93, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x94, 0xFF, 0x1B)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x99, 0x0F, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x9F, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0xD0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x65)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xbb8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xC0)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x64)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +/*back mic tx*/ +#define AMIC_SEC_MONO_48000_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x05)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0xD0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x65)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAB, 0x09, 0x00)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3A98}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0xFF, 0x1E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x93, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x94, 0xFF, 0x1B)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x99, 0x0F, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x9F, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xC0)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x64)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAB, 0x09, 0x09)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + + +/* Headset MIC */ +#define HS_AMIC2_MONO_48000_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x05)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0xFF, 0x1E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x93, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x94, 0xFF, 0x1B)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x99, 0x0F, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x9F, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0xC8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x65)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xbb8 }, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xC0)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x64)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + + +/* Headset MIC */ +#define HS_AMIC2_MONO_8000_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x05)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0xFF, 0x1E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x93, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x94, 0xFF, 0x1B)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x99, 0x0F, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x9F, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0xC8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x65)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xbb8 }, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xC0)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x64)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + + +/* ANC Headset: Speakers on Primary Rx, Noise Microphones on Secondary Tx */ + +#define ANC_HEADSET_CPLS_AMIC1_AUXL_RX1_48000_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x95, 0xFF, 0x40)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x9F, 0x0C, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x9B, 0x01, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x18, 0xFF, 0xD0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x19, 0xFF, 0xC1)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x09, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0A, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x65)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x6C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xB7, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x55)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0xFF, 0x29)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xBB8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0xF5)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x27, 0x24)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x04)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xC0, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xD0, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x18, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x19, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x64)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x09, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0A, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +/* TTY RX */ +#define TTY_HEADSET_MONO_RX_48000_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x97, 0xFF, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x06)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x01)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x4C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xB7, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x45)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0xFE, 0xCA)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xBB8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0xC5)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0xFE, 0xC8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x27, 0x20)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3D, 0x20, 0x20)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x04)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3D, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + + +/* TTY TX */ +#define TTY_HEADSET_MONO_TX_48000_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x05)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0xFF, 0x1E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x93, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x94, 0xFF, 0x1B)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x99, 0x0F, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x9F, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0xA8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x65)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xBB8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xC0)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x64)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + + +/* FM HPH, RX HPH CLASS AB CAPLESS, STEREO OR MONO OR MONO-DIFF */ +#define AUXPGA_HEADSET_AB_CPLS_RX_48000 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0xF0, 0xC0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x2F, 0x88, 0x88)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x30, 0x90, 0x90)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x6C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xB7, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x55)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0xFE, 0xCA)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xBB8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0xF0, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0xF5)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0xFE, 0xC8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x38, 0xEE, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x90, 0x90)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3D, 0x90, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x38, 0xEE, 0x55)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x38, 0xEE, 0xAA)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x04)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x38, 0xEE, 0x55)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x38, 0xEE, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x90, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3D, 0x90, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0xF0, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + + +/* FM SPK, RX SPK, STEREO OR MONO OR MONO-DIFF */ +#define AUXPGA_SPEAKER_RX \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0xF0, 0xC0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x2F, 0x88, 0x88)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x30, 0x90, 0x90)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x6C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xB7, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x55)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0xFE, 0xCA)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xBB8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0xF0, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0xF5)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0xFE, 0xC8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x38, 0xEE, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x90, 0x80)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3D, 0x10, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x38, 0xEE, 0x55)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x38, 0xEE, 0xAA)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x04)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x38, 0xEE, 0x55)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x38, 0xEE, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x90, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3D, 0x90, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0xF0, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +/* +#define SPEAKER_HPH_AB_CPL_PRI_48000_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x6C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xB7, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x55)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0xFE, 0xCA)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x1388}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0xF5)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0xFE, 0xC8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x48)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x1388}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0xF8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0x24, 0x24)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0x04, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x27, 0x24)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3D, 0x04, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x04)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x27, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3D, 0x04, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0x24, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0x04, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } +*/ +#define SPEAKER_HPH_AB_CPL_PRI_48000_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x50, 0xFF, 0xEA)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0xF0, 0xC0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x2F, 0x88, 0x88)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x30, 0x90, 0x90)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xB7, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x55)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0xFE, 0x29)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xBB8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x50, 0xFF, 0xEA)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0xF0, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0xF5)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0xFE, 0x29)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x38, 0xEE, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x36, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x37, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x90, 0x90)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3D, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x38, 0xEE, 0x55)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x38, 0xEE, 0xBB)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x36, 0xFE, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x37, 0xFE, 0x04)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x38, 0xEE, 0x55)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x38, 0xEE, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x36, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x37, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x90, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3D, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0xF0, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define DUAL_MIC_STEREO_TX_48000_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x05)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0xFF, 0x1E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x93, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x94, 0xFF, 0x1B)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x99, 0x0F, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x9F, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0xC8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x65)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xbb8 }, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xC0)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x64)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#endif diff --git a/arch/arm/mach-msm/qdsp6v3/timpani_profile_8x60_vigor.h b/arch/arm/mach-msm/qdsp6v3/timpani_profile_8x60_vigor.h new file mode 100644 index 00000000000..e2bcc30e227 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v3/timpani_profile_8x60_vigor.h @@ -0,0 +1,641 @@ +/* arch/arm/mach-msm/qdsp6v2_1x/timpani_profile_8x60_lead.h + * + * Copyright (C) 2010 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 __TIMPANI_PROFILE_VIGOR_H +#define __TIMPANI_PROFILE_VIGOR_H + +/* RX HPH CLASS AB CAPLESS, STEREO OR MONO OR MONO-DIFF */ +#define HEADSET_STEREO_AB_CPLS_48000_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x6C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xB7, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x55)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0xFE, 0xCA)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xBB8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0xFD)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0xFE, 0xC8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x27, 0x27)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3D, 0x04, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x36, 0xFE, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x37, 0xFE, 0x04)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x27, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3D, 0x04, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + + +/* RX EAR MONO*/ +#define EAR_PRI_MONO_48000_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x21, 0xFF, 0x28)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x22, 0xFF, 0xA9)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x26, 0xFF, 0xD1)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x6C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xB7, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x08)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xBB8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0xF8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0x24, 0x20)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0x04, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x04)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x05, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0x24, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0x04, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +/* RX SPK, STEREO OR MONO OR MONO-DIFF */ +#define SPEAKER_PRI_48000_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x21, 0xFF, 0x28)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x22, 0xFF, 0xA9)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x26, 0xFF, 0xD1)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x6C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xB7, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x08)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xBB8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0xF8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0x24, 0x20)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0x04, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x04)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x05, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0x24, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0x04, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + + +/* ANALOG IMIC Primary MONO */ +#define AMIC_PRI_MONO_48000_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x05)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0xFF, 0x1E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x93, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x94, 0xFF, 0x1B)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x99, 0x0F, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x9F, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0xD0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x65)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xbb8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xC0)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x64)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +/*back mic tx*/ +#define AMIC_SEC_MONO_48000_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x05)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xC0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0xFF, 0x1E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x93, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x94, 0xFF, 0x1B)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x99, 0x0F, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x9F, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0xC1)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0xFC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0xFC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x65)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3A98 },\ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x0C)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x64)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + + +/* Headset MIC */ +#define HS_AMIC2_MONO_48000_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x05)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0xFF, 0x1E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x93, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x94, 0xFF, 0x1B)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x99, 0x0F, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x9F, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0xC8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x65)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xbb8 }, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xC0)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x64)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + + +/* Headset MIC */ +#define HS_AMIC2_MONO_8000_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x05)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0xFF, 0x1E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x93, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x94, 0xFF, 0x1B)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x99, 0x0F, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x9F, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0xC8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x65)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xbb8 }, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xC0)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x64)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + + +/* ANC Headset: Speakers on Primary Rx, Noise Microphones on Secondary Tx */ + +#define ANC_HEADSET_CPLS_AMIC1_AUXL_RX1_48000_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x95, 0xFF, 0x40)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x9F, 0x0C, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x9B, 0x01, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x18, 0xFF, 0xD0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x19, 0xFF, 0xC1)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x09, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0A, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x65)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x6C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xB7, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x55)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0xFF, 0x29)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xBB8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0xF5)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x27, 0x24)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x04)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xC0, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xD0, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x18, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x19, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x64)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x09, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0A, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +/* TTY RX */ +#define TTY_HEADSET_MONO_RX_48000_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x97, 0xFF, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x06)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x01)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x4C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xB7, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x45)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0xFE, 0xCA)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xBB8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0xC5)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0xFE, 0xC8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x27, 0x20)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3D, 0x20, 0x20)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x04)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3D, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + + +/* TTY TX */ +#define TTY_HEADSET_MONO_TX_48000_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x05)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0xFF, 0x1E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x93, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x94, 0xFF, 0x1B)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x99, 0x0F, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x9F, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0xA8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x65)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xBB8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xC0)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x64)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + + +/* FM HPH, RX HPH CLASS AB CAPLESS, STEREO OR MONO OR MONO-DIFF */ +#define AUXPGA_HEADSET_AB_CPLS_RX_48000 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0xF0, 0xC0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x2F, 0x88, 0x88)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x30, 0x90, 0x90)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x6C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xB7, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x55)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0xFE, 0xCA)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xBB8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0xF0, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0xF5)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0xFE, 0xC8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x38, 0xEE, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x90, 0x90)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3D, 0x90, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x38, 0xEE, 0x55)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x38, 0xEE, 0xAA)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x04)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x38, 0xEE, 0x55)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x38, 0xEE, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x90, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3D, 0x90, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0xF0, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +/* FM SPK, RX SPK, STEREO OR MONO OR MONO-DIFF */ +#define AUXPGA_SPEAKER_RX \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0xF0, 0xC0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x2F, 0x88, 0x88)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x30, 0x90, 0x90)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x6C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xB7, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x55)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0xFE, 0xCA)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xBB8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0xF0, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0xF5)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0xFE, 0xC8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x38, 0xEE, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x90, 0x80)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3D, 0x10, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x38, 0xEE, 0x55)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x38, 0xEE, 0xAA)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x04)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x38, 0xEE, 0x55)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x38, 0xEE, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x90, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3D, 0x90, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0xF0, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + + +#define SPEAKER_HPH_AB_CPL_PRI_48000_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x6C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xB7, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x55)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0xFE, 0xCA)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x1388}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0xF5)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0xFE, 0xC8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x48)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x1388}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0xF8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0x24, 0x24)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0x04, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x27, 0x24)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3D, 0x04, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x04)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x27, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3D, 0x04, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0x24, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0x04, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define DUAL_MIC_STEREO_TX_48000_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x05)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0xFF, 0x1E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x93, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x94, 0xFF, 0x1B)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x99, 0x0F, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x9F, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0xC8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x65)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xbb8 }, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xC0)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x64)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#endif diff --git a/include/linux/msm_audio_mvs.h b/include/linux/msm_audio_mvs.h new file mode 100644 index 00000000000..2ea486c4f48 --- /dev/null +++ b/include/linux/msm_audio_mvs.h @@ -0,0 +1,113 @@ +#ifndef __MSM_AUDIO_MVS_H +#define __MSM_AUDIO_MVS_H + +#include + +#define AUDIO_GET_MVS_CONFIG _IOW(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM + 0), unsigned) +#define AUDIO_SET_MVS_CONFIG _IOR(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM + 1), unsigned) + +/* MVS modes */ +#define MVS_MODE_IS733 0x1 +#define MVS_MODE_IS127 0x2 +#define MVS_MODE_4GV_NB 0x3 +#define MVS_MODE_4GV_WB 0x4 +#define MVS_MODE_AMR 0x5 +#define MVS_MODE_EFR 0x6 +#define MVS_MODE_FR 0x7 +#define MVS_MODE_HR 0x8 +#define MVS_MODE_LINEAR_PCM 0x9 +#define MVS_MODE_G711 0xA +#define MVS_MODE_PCM 0xC +#define MVS_MODE_AMR_WB 0xD +#define MVS_MODE_G729A 0xE +#define MVS_MODE_G711A 0xF +#define MVS_MODE_G722 0x10 +#define MVS_MODE_PCM_WB 0x80000000 + +enum msm_audio_amr_mode { + MVS_AMR_MODE_0475, /* AMR 4.75 kbps */ + MVS_AMR_MODE_0515, /* AMR 5.15 kbps */ + MVS_AMR_MODE_0590, /* AMR 5.90 kbps */ + MVS_AMR_MODE_0670, /* AMR 6.70 kbps */ + MVS_AMR_MODE_0740, /* AMR 7.40 kbps */ + MVS_AMR_MODE_0795, /* AMR 7.95 kbps */ + MVS_AMR_MODE_1020, /* AMR 10.20 kbps */ + MVS_AMR_MODE_1220, /* AMR 12.20 kbps */ + MVS_AMR_MODE_0660, /* AMR-WB 6.60 kbps */ + MVS_AMR_MODE_0885, /* AMR-WB 8.85 kbps */ + MVS_AMR_MODE_1265, /* AMR-WB 12.65 kbps */ + MVS_AMR_MODE_1425, /* AMR-WB 14.25 kbps */ + MVS_AMR_MODE_1585, /* AMR-WB 15.85 kbps */ + MVS_AMR_MODE_1825, /* AMR-WB 18.25 kbps */ + MVS_AMR_MODE_1985, /* AMR-WB 19.85 kbps */ + MVS_AMR_MODE_2305, /* AMR-WB 23.05 kbps */ + MVS_AMR_MODE_2385, /* AMR-WB 23.85 kbps */ + MVS_AMR_MODE_UNDEF +}; + +enum msm_audio_voc_rate { + MVS_VOC_0_RATE, /* Blank frame */ + MVS_VOC_8_RATE, /* 1/8 rate */ + MVS_VOC_4_RATE, /* 1/4 rate */ + MVS_VOC_2_RATE, /* 1/2 rate */ + MVS_VOC_1_RATE /* Full rate */ +}; + +enum msm_audio_amr_frame_type { + MVS_AMR_SPEECH_GOOD, /* Good speech frame */ + MVS_AMR_SPEECH_DEGRADED, /* Speech degraded */ + MVS_AMR_ONSET, /* Onset */ + MVS_AMR_SPEECH_BAD, /* Corrupt speech frame (bad CRC) */ + MVS_AMR_SID_FIRST, /* First silence descriptor */ + MVS_AMR_SID_UPDATE, /* Comfort noise frame */ + MVS_AMR_SID_BAD, /* Corrupt SID frame (bad CRC) */ + MVS_AMR_NO_DATA, /* Nothing to transmit */ + MVS_AMR_SPEECH_LOST /* Downlink speech lost */ +}; + +enum msm_audio_g711a_mode { + MVS_G711A_MODE_MULAW, + MVS_G711A_MODE_ALAW +}; + +enum mvs_g722_mode_type { + MVS_G722_MODE_01, + MVS_G722_MODE_02, + MVS_G722_MODE_03, + MVS_G722_MODE_MAX, + MVS_G722_MODE_UNDEF +}; + +enum msm_audio_g711a_frame_type { + MVS_G711A_SPEECH_GOOD, + MVS_G711A_SID, + MVS_G711A_NO_DATA, + MVS_G711A_ERASURE +}; + +enum msm_audio_g729a_frame_type { + MVS_G729A_NO_DATA, + MVS_G729A_SPEECH_GOOD, + MVS_G729A_SID, + MVS_G729A_ERASURE +}; + +struct msm_audio_mvs_config { + uint32_t mvs_mode; + uint32_t rate_type; + uint32_t dtx_mode; +}; + +#define MVS_MAX_VOC_PKT_SIZE 640 + +struct msm_audio_mvs_frame { + uint32_t frame_type; + uint32_t frame_rate; + uint32_t len; + uint8_t voc_pkt[MVS_MAX_VOC_PKT_SIZE]; + +}; + +#endif /* __MSM_AUDIO_MVS_H */ From 937dc8c3f5af220c7094220a163674427c58e80e Mon Sep 17 00:00:00 2001 From: Matt Filetto Date: Tue, 16 Apr 2013 13:50:03 -0700 Subject: [PATCH 007/111] GPU: update genlock to jb-chocolote For use with new display and media repos Change-Id: Ibf0eb4f9dbdfa37081af24b9e1fd3accc3ffce4e --- drivers/base/genlock.c | 199 +++++++++++++++++++++++++++++++--------- include/linux/genlock.h | 7 +- 2 files changed, 164 insertions(+), 42 deletions(-) diff --git a/drivers/base/genlock.c b/drivers/base/genlock.c index 9f7a6008f22..1fb606f116d 100644 --- a/drivers/base/genlock.c +++ b/drivers/base/genlock.c @@ -34,7 +34,15 @@ #define GENLOCK_LOG_ERR(fmt, args...) \ pr_err("genlock: %s: " fmt, __func__, ##args) +/* The genlock magic stored in the kernel private data is used to protect + * against the possibility of user space passing a valid fd to a + * non-genlock file for genlock_attach_lock() + */ +#define GENLOCK_MAGIC_OK 0xD2EAD10C +#define GENLOCK_MAGIC_BAD 0xD2EADBAD + struct genlock { + unsigned int magic; /* Magic for attach verification */ 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 */ @@ -56,7 +64,7 @@ struct genlock_handle { * released while another process tries to attach it */ -static DEFINE_SPINLOCK(genlock_file_lock); +static DEFINE_SPINLOCK(genlock_ref_lock); static void genlock_destroy(struct kref *kref) { @@ -68,10 +76,9 @@ static void genlock_destroy(struct kref *kref) * 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); + lock->magic = GENLOCK_MAGIC_BAD; kfree(lock); } @@ -125,6 +132,7 @@ struct genlock *genlock_create_lock(struct genlock_handle *handle) init_waitqueue_head(&lock->queue); spin_lock_init(&lock->lock); + lock->magic = GENLOCK_MAGIC_OK; lock->state = _UNLOCKED; /* @@ -193,21 +201,30 @@ struct genlock *genlock_attach_lock(struct genlock_handle *handle, int fd) * released and then attached */ - spin_lock(&genlock_file_lock); + spin_lock(&genlock_ref_lock); lock = file->private_data; - spin_unlock(&genlock_file_lock); fput(file); if (lock == NULL) { GENLOCK_LOG_ERR("File descriptor is invalid\n"); - return ERR_PTR(-EINVAL); + goto fail_invalid; + } + + if (lock->magic != GENLOCK_MAGIC_OK) { + GENLOCK_LOG_ERR("Magic is invalid - 0x%X\n", lock->magic); + goto fail_invalid; } handle->lock = lock; kref_get(&lock->refcount); + spin_unlock(&genlock_ref_lock); return lock; + +fail_invalid: + spin_unlock(&genlock_ref_lock); + return ERR_PTR(-EINVAL); } EXPORT_SYMBOL(genlock_attach_lock); @@ -278,7 +295,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); @@ -297,12 +314,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; } @@ -311,32 +331,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 */ @@ -346,15 +379,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) { - int elapsed; + 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); @@ -363,7 +407,7 @@ static int _genlock_lock(struct genlock *lock, struct genlock_handle *handle, goto done; } - ticks = elapsed; + ticks = (unsigned long) elapsed; } dolock: @@ -371,7 +415,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); @@ -380,7 +424,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 @@ -391,10 +435,73 @@ 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; + unsigned long irqflags; + + 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: + 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; + default: + GENLOCK_LOG_ERR("Invalid lock operation\n"); + ret = -EINVAL; + break; + } + + return ret; +} +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 = handle->lock; + 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; @@ -416,7 +523,7 @@ int genlock_lock(struct genlock_handle *handle, int op, int flags, return ret; } -EXPORT_SYMBOL(genlock_lock); +EXPORT_SYMBOL(genlock_dreadlock); /** * genlock_wait - Wait for the lock to be released @@ -429,7 +536,7 @@ 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); + unsigned long ticks = msecs_to_jiffies(timeout); if (lock == NULL) { GENLOCK_LOG_ERR("Handle does not have a lock attached\n"); @@ -449,7 +556,7 @@ int genlock_wait(struct genlock_handle *handle, uint32_t timeout) } while (lock->state != _UNLOCKED) { - int elapsed; + signed long elapsed; spin_unlock_irqrestore(&lock->lock, irqflags); @@ -463,7 +570,7 @@ int genlock_wait(struct genlock_handle *handle, uint32_t timeout) break; } - ticks = elapsed; + ticks = (unsigned long) elapsed; } done: @@ -493,7 +600,9 @@ void genlock_release_lock(struct genlock_handle *handle) } spin_unlock_irqrestore(&handle->lock->lock, flags); + spin_lock(&genlock_ref_lock); kref_put(&handle->lock->refcount, genlock_destroy); + spin_unlock(&genlock_ref_lock); handle->lock = NULL; handle->active = 0; } @@ -635,6 +744,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 2e9f9d682a3..128d676cf9b 100644 --- a/include/linux/genlock.h +++ b/include/linux/genlock.h @@ -21,7 +21,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 (1 << 0) +#define GENLOCK_WRITE_TO_READ (1 << 1) struct genlock_lock { int fd; @@ -37,9 +38,13 @@ 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) #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 d30e816db17ffdbe14c27256a7e475fe26d3df9e Mon Sep 17 00:00:00 2001 From: Arne Coucheron Date: Sun, 21 Apr 2013 07:41:53 +0200 Subject: [PATCH 008/111] msm: rotator: Partial update to jb_chocolate Change-Id: I8bf66ce48190490b6cae15ca8a3f021e6ac0d02e --- drivers/char/msm_rotator.c | 132 ++++++++++++++++++++++-------------- include/linux/msm_rotator.h | 3 +- 2 files changed, 81 insertions(+), 54 deletions(-) diff --git a/drivers/char/msm_rotator.c b/drivers/char/msm_rotator.c index 38ed23fb65e..ec0e28c65f1 100644 --- a/drivers/char/msm_rotator.c +++ b/drivers/char/msm_rotator.c @@ -43,6 +43,7 @@ #define MSM_ROTATOR_START (MSM_ROTATOR_BASE+0x0030) #define MSM_ROTATOR_MAX_BURST_SIZE (MSM_ROTATOR_BASE+0x0050) #define MSM_ROTATOR_HW_VERSION (MSM_ROTATOR_BASE+0x0070) +#define MSM_ROTATOR_SW_RESET (MSM_ROTATOR_BASE+0x0074) #define MSM_ROTATOR_SRC_SIZE (MSM_ROTATOR_BASE+0x1108) #define MSM_ROTATOR_SRCP0_ADDR (MSM_ROTATOR_BASE+0x110c) #define MSM_ROTATOR_SRCP1_ADDR (MSM_ROTATOR_BASE+0x1110) @@ -118,12 +119,19 @@ struct msm_rotator_mem_planes { #define checkoffset(offset, size, max_size) \ ((size) > (max_size) || (offset) > ((max_size) - (size))) +struct msm_rotator_fd_info { + int pid; + int ref_cnt; + struct list_head list; +}; + struct msm_rotator_dev { void __iomem *io_base; int irq; struct msm_rotator_img_info *img_info[MAX_SESSIONS]; struct clk *core_clk; - int pid_list[MAX_SESSIONS]; + struct msm_rotator_fd_info *fd_info[MAX_SESSIONS]; + struct list_head fd_list; struct clk *pclk; int rot_clk_state; struct regulator *regulator; @@ -854,11 +862,11 @@ static int msm_rotator_do_rotate(unsigned long arg) break; if (s == MAX_SESSIONS) { - dev_dbg(msm_rotator_dev->device, - "%s() : Attempt to use invalid session_id %d\n", + pr_err("%s() : Attempt to use invalid session_id %d\n", __func__, s); rc = -EINVAL; - goto do_rotate_unlock_mutex; + mutex_unlock(&msm_rotator_dev->rotator_lock); + return rc; } if (msm_rotator_dev->img_info[s]->enable == 0) { @@ -866,7 +874,8 @@ static int msm_rotator_do_rotate(unsigned long arg) "%s() : Session_id %d not enabled \n", __func__, s); rc = -EINVAL; - goto do_rotate_unlock_mutex; + mutex_unlock(&msm_rotator_dev->rotator_lock); + return rc; } img_info = msm_rotator_dev->img_info[s]; @@ -876,7 +885,8 @@ static int msm_rotator_do_rotate(unsigned long arg) &src_planes)) { pr_err("%s: invalid src format\n", __func__); rc = -EINVAL; - goto do_rotate_unlock_mutex; + mutex_unlock(&msm_rotator_dev->rotator_lock); + return rc; } if (msm_rotator_get_plane_sizes(img_info->dst.format, img_info->dst.width, @@ -884,7 +894,8 @@ static int msm_rotator_do_rotate(unsigned long arg) &dst_planes)) { pr_err("%s: invalid dst format\n", __func__); rc = -EINVAL; - goto do_rotate_unlock_mutex; + mutex_unlock(&msm_rotator_dev->rotator_lock); + return rc; } rc = get_img(&info.src, (unsigned long *)&in_paddr, @@ -1073,11 +1084,13 @@ static int msm_rotator_do_rotate(unsigned long arg) break; default: rc = -EINVAL; + pr_err("%s(): Unsupported format %u\n", __func__, format); goto do_rotate_exit; } if (rc != 0) { msm_rotator_dev->last_session_idx = INVALID_SESSION; + pr_err("%s(): Invalid session error\n", __func__); goto do_rotate_exit; } @@ -1089,8 +1102,11 @@ static int msm_rotator_do_rotate(unsigned long arg) wait_event(msm_rotator_dev->wq, (msm_rotator_dev->processing == 0)); status = (unsigned char)ioread32(MSM_ROTATOR_INTR_STATUS); - if ((status & 0x03) != 0x01) + if ((status & 0x03) != 0x01) { + pr_err("%s(): AXI Bus Error, issuing SW_RESET\n", __func__); + iowrite32(0x1, MSM_ROTATOR_SW_RESET); rc = -EFAULT; + } iowrite32(0, MSM_ROTATOR_INTR_ENABLE); iowrite32(3, MSM_ROTATOR_INTR_CLEAR); @@ -1111,31 +1127,12 @@ static int msm_rotator_do_rotate(unsigned long arg) return rc; } -static void msm_rotator_set_perf_level(u32 wh, u32 is_rgb) -{ - u32 perf_level; - - if (is_rgb) - perf_level = 1; - else if (wh <= (640 * 480)) - perf_level = 2; - else if (wh <= (736 * 1280)) - perf_level = 3; - else - perf_level = 4; - -#ifdef CONFIG_MSM_BUS_SCALING - msm_bus_scale_client_update_request(msm_rotator_dev->bus_client_handle, - perf_level); -#endif - -} - -static int msm_rotator_start(unsigned long arg, int pid) +static int msm_rotator_start(unsigned long arg, + struct msm_rotator_fd_info *fd_info) { struct msm_rotator_img_info info; int rc = 0; - int s, is_rgb = 0; + int s; int first_free_index = INVALID_SESSION; unsigned int dst_w, dst_h; @@ -1177,7 +1174,6 @@ static int msm_rotator_start(unsigned long arg, int pid) case MDP_XRGB_8888: case MDP_RGBX_8888: case MDP_BGRA_8888: - is_rgb = 1; info.dst.format = info.src.format; break; case MDP_Y_CBCR_H2V2: @@ -1204,15 +1200,13 @@ static int msm_rotator_start(unsigned long arg, int pid) mutex_lock(&msm_rotator_dev->rotator_lock); - msm_rotator_set_perf_level((info.src.width*info.src.height), is_rgb); - for (s = 0; s < MAX_SESSIONS; s++) { if ((msm_rotator_dev->img_info[s] != NULL) && (info.session_id == (unsigned int)msm_rotator_dev->img_info[s] )) { *(msm_rotator_dev->img_info[s]) = info; - msm_rotator_dev->pid_list[s] = pid; + msm_rotator_dev->fd_info[s] = fd_info; if (msm_rotator_dev->last_session_idx == s) msm_rotator_dev->last_session_idx = @@ -1240,16 +1234,16 @@ static int msm_rotator_start(unsigned long arg, int pid) info.session_id = (unsigned int) msm_rotator_dev->img_info[first_free_index]; *(msm_rotator_dev->img_info[first_free_index]) = info; - msm_rotator_dev->pid_list[first_free_index] = pid; - - if (copy_to_user((void __user *)arg, &info, sizeof(info))) - rc = -EFAULT; + msm_rotator_dev->fd_info[first_free_index] = fd_info; } else if (s == MAX_SESSIONS) { dev_dbg(msm_rotator_dev->device, "%s: all sessions in use\n", __func__); rc = -EBUSY; } + if (rc == 0 && copy_to_user((void __user *)arg, &info, sizeof(info))) + rc = -EFAULT; + rotator_start_exit: mutex_unlock(&msm_rotator_dev->rotator_lock); @@ -1275,7 +1269,7 @@ static int msm_rotator_finish(unsigned long arg) INVALID_SESSION; kfree(msm_rotator_dev->img_info[s]); msm_rotator_dev->img_info[s] = NULL; - msm_rotator_dev->pid_list[s] = 0; + msm_rotator_dev->fd_info[s] = NULL; break; } } @@ -1293,24 +1287,45 @@ static int msm_rotator_finish(unsigned long arg) static int msm_rotator_open(struct inode *inode, struct file *filp) { - int *id; + struct msm_rotator_fd_info *tmp, *fd_info = NULL; int i; if (filp->private_data) return -EBUSY; mutex_lock(&msm_rotator_dev->rotator_lock); - id = &msm_rotator_dev->pid_list[0]; - for (i = 0; i < MAX_SESSIONS; i++, id++) { - if (*id == 0) + for (i = 0; i < MAX_SESSIONS; i++) { + if (msm_rotator_dev->fd_info[i] == NULL) break; } - mutex_unlock(&msm_rotator_dev->rotator_lock); - if (i == MAX_SESSIONS) + if (i == MAX_SESSIONS) { + mutex_unlock(&msm_rotator_dev->rotator_lock); return -EBUSY; + } + + list_for_each_entry(tmp, &msm_rotator_dev->fd_list, list) { + if (tmp->pid == current->pid) { + fd_info = tmp; + break; + } + } - filp->private_data = (void *)current->pid; + if (!fd_info) { + fd_info = kzalloc(sizeof(*fd_info), GFP_KERNEL); + if (!fd_info) { + mutex_unlock(&msm_rotator_dev->rotator_lock); + pr_err("%s: insufficient memory to alloc resources\n", + __func__); + return -ENOMEM; + } + list_add(&fd_info->list, &msm_rotator_dev->fd_list); + fd_info->pid = current->pid; + } + fd_info->ref_cnt++; + mutex_unlock(&msm_rotator_dev->rotator_lock); + + filp->private_data = fd_info; return 0; } @@ -1318,21 +1333,33 @@ msm_rotator_open(struct inode *inode, struct file *filp) static int msm_rotator_close(struct inode *inode, struct file *filp) { + struct msm_rotator_fd_info *fd_info; int s; - int pid; - pid = (int)filp->private_data; + fd_info = (struct msm_rotator_fd_info *)filp->private_data; + mutex_lock(&msm_rotator_dev->rotator_lock); + if (--fd_info->ref_cnt > 0) { + mutex_unlock(&msm_rotator_dev->rotator_lock); + return 0; + } + for (s = 0; s < MAX_SESSIONS; s++) { if (msm_rotator_dev->img_info[s] != NULL && - msm_rotator_dev->pid_list[s] == pid) { + msm_rotator_dev->fd_info[s] == fd_info) { + pr_debug("%s: freeing rotator session %p (pid %d)\n", + __func__, msm_rotator_dev->img_info[s], + fd_info->pid); kfree(msm_rotator_dev->img_info[s]); msm_rotator_dev->img_info[s] = NULL; + msm_rotator_dev->fd_info[s] = NULL; if (msm_rotator_dev->last_session_idx == s) msm_rotator_dev->last_session_idx = INVALID_SESSION; } } + list_del(&fd_info->list); + kfree(fd_info); mutex_unlock(&msm_rotator_dev->rotator_lock); return 0; @@ -1341,16 +1368,16 @@ msm_rotator_close(struct inode *inode, struct file *filp) static long msm_rotator_ioctl(struct file *file, unsigned cmd, unsigned long arg) { - int pid; + struct msm_rotator_fd_info *fd_info; if (_IOC_TYPE(cmd) != MSM_ROTATOR_IOCTL_MAGIC) return -ENOTTY; - pid = (int)file->private_data; + fd_info = (struct msm_rotator_fd_info *)file->private_data; switch (cmd) { case MSM_ROTATOR_IOCTL_START: - return msm_rotator_start(arg, pid); + return msm_rotator_start(arg, fd_info); case MSM_ROTATOR_IOCTL_ROTATE: return msm_rotator_do_rotate(arg); case MSM_ROTATOR_IOCTL_FINISH: @@ -1393,6 +1420,7 @@ static int __devinit msm_rotator_probe(struct platform_device *pdev) msm_rotator_dev->imem_owner = IMEM_NO_OWNER; mutex_init(&msm_rotator_dev->imem_lock); + INIT_LIST_HEAD(&msm_rotator_dev->fd_list); msm_rotator_dev->imem_clk_state = CLK_DIS; INIT_DELAYED_WORK(&msm_rotator_dev->imem_clk_work, msm_rotator_imem_clk_work_f); diff --git a/include/linux/msm_rotator.h b/include/linux/msm_rotator.h index 3910aea982b..a348ab6b651 100644 --- a/include/linux/msm_rotator.h +++ b/include/linux/msm_rotator.h @@ -31,17 +31,16 @@ struct msm_rotator_img_info { unsigned char rotations; int enable; unsigned int downscale_ratio; + unsigned int secure; }; struct msm_rotator_data_info { int session_id; struct msmfb_data src; struct msmfb_data dst; -#ifndef CONFIG_MSM_ROTATOR_LEGACY unsigned int version_key; struct msmfb_data src_chroma; struct msmfb_data dst_chroma; -#endif }; struct msm_rot_clocks { From 9a279fef34ae663ef36661275907775bcdaf247b Mon Sep 17 00:00:00 2001 From: Dorian Snyder Date: Tue, 2 Apr 2013 23:15:17 -0700 Subject: [PATCH 009/111] Update iommu to jb_chocolate Change-Id: I74f5239677eee4c15b389279190488fb206de6df --- arch/arm/mach-msm/subsystem_map.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-msm/subsystem_map.c b/arch/arm/mach-msm/subsystem_map.c index 33e592f7c6a..d37da30685f 100644 --- a/arch/arm/mach-msm/subsystem_map.c +++ b/arch/arm/mach-msm/subsystem_map.c @@ -358,7 +358,8 @@ struct msm_mapped_buffer *msm_subsystem_map_buffer(unsigned long phys, temp_phys += SZ_4K, temp_va += SZ_4K) { ret = iommu_map(d, temp_va, temp_phys, - get_order(SZ_4K), 0); + get_order(SZ_4K), + (IOMMU_READ | IOMMU_WRITE)); if (ret) { pr_err("%s: could not map iommu for" " domain %p, iova %lx," @@ -373,7 +374,7 @@ struct msm_mapped_buffer *msm_subsystem_map_buffer(unsigned long phys, if (flags & MSM_SUBSYSTEM_MAP_IOMMU_2X) msm_iommu_map_extra (d, temp_va, length, SZ_4K, - (IOMMU_READ | IOMMU_WRITE)); + (IOMMU_READ | IOMMU_WRITE)); } } From 44e46073430c46c58ca7badbab1de3923d6bb62a Mon Sep 17 00:00:00 2001 From: David Hays Date: Mon, 6 May 2013 18:44:59 -0500 Subject: [PATCH 010/111] Kbuild: copy msm headers Change-Id: If0139897c19254c3e40f6b8925b9351c1d9514ec --- include/Kbuild | 1 + include/linux/Kbuild | 19 + include/linux/msm_ion.h | 23 + include/linux/msm_mdp.h | 4 + include/linux/spi_aic3254.h | 177 ++++ include/linux/tpa2051d3.h | 77 ++ include/media/Kbuild | 1 + include/media/msm_camera.h | 1457 ++++++++---------------------- include/sound/Kbuild | 2 + include/sound/compress_offload.h | 81 ++ include/sound/compress_params.h | 241 +++++ 11 files changed, 1022 insertions(+), 1061 deletions(-) create mode 100644 include/linux/msm_ion.h create mode 100644 include/linux/spi_aic3254.h create mode 100644 include/linux/tpa2051d3.h create mode 100644 include/media/Kbuild create mode 100644 include/sound/compress_offload.h create mode 100644 include/sound/compress_params.h diff --git a/include/Kbuild b/include/Kbuild index 8d226bfa269..5f65ac2887a 100644 --- a/include/Kbuild +++ b/include/Kbuild @@ -10,3 +10,4 @@ header-y += video/ header-y += drm/ header-y += xen/ header-y += scsi/ +header-y += media/ diff --git a/include/linux/Kbuild b/include/linux/Kbuild index 01f63627505..adfcd107d88 100644 --- a/include/linux/Kbuild +++ b/include/linux/Kbuild @@ -399,3 +399,22 @@ header-y += wireless.h header-y += x25.h header-y += xattr.h header-y += xfrm.h +header-y += msm_audio_aac.h +header-y += msm_audio_acdb.h +header-y += msm_audio_amrnb.h +header-y += msm_audio_mvs.h +header-y += msm_audio.h +header-y += msm_audio_qcp.h +header-y += msm_ion.h +header-y += msm_kgsl.h +header-y += msm_mdp.h +header-y += msm_rotator.h +header-y += msm_vidc_dec.h +header-y += msm_vidc_enc.h +header-y += android_pmem.h +header-y += ashmem.h +header-y += genlock.h +header-y += ion.h +header-y += spi_aic3254.h +header-y += tpa2051d3.h +header-y += videodev2.h diff --git a/include/linux/msm_ion.h b/include/linux/msm_ion.h new file mode 100644 index 00000000000..6ba89e81e99 --- /dev/null +++ b/include/linux/msm_ion.h @@ -0,0 +1,23 @@ +/**************************************************************************** + **************************************************************************** + *** + *** 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 _LINUX_MSM_ION_H +#define _LINUX_MSM_ION_H +#include +#endif + diff --git a/include/linux/msm_mdp.h b/include/linux/msm_mdp.h index f04a1d755de..188d31f5f0c 100644 --- a/include/linux/msm_mdp.h +++ b/include/linux/msm_mdp.h @@ -150,6 +150,10 @@ enum { #define MDP_SOURCE_ROTATED_90 0x00100000 #define MDP_MEMORY_ID_TYPE_FB 0x00001000 #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 diff --git a/include/linux/spi_aic3254.h b/include/linux/spi_aic3254.h new file mode 100644 index 00000000000..1db8df3805a --- /dev/null +++ b/include/linux/spi_aic3254.h @@ -0,0 +1,177 @@ +/**************************************************************************** + **************************************************************************** + *** + *** 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 __SPI_AIC3254_H__ +#define __SPI_AIC3254_H__ +#include +typedef struct _CODEC_SPI_CMD { +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + unsigned char act; + unsigned char reg; + unsigned char data; +} CODEC_SPI_CMD; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +typedef struct _CODEC_SPI_CMD_PARAM { + CODEC_SPI_CMD *data; + unsigned int len; +} CODEC_SPI_CMD_PARAM; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +struct AIC3254_PARAM { + unsigned int row_num; + unsigned int col_num; + void *cmd_data; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +}; +struct CODEC_CFG { + unsigned char tb_idx; + unsigned char index; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +}; +#define AIC3254_IOCTL_MAGIC 's' +#define AIC3254_SET_TX_PARAM _IOW(AIC3254_IOCTL_MAGIC, 0x10, unsigned) +#define AIC3254_SET_RX_PARAM _IOW(AIC3254_IOCTL_MAGIC, 0x11, unsigned) +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define AIC3254_CONFIG_TX _IOW(AIC3254_IOCTL_MAGIC, 0x12, unsigned int) +#define AIC3254_CONFIG_RX _IOW(AIC3254_IOCTL_MAGIC, 0x13, unsigned int) +#define AIC3254_SET_DSP_PARAM _IOW(AIC3254_IOCTL_MAGIC, 0x20, unsigned) +#define AIC3254_CONFIG_MEDIA _IOW(AIC3254_IOCTL_MAGIC, 0x21, unsigned int) +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define AIC3254_CONFIG_VOICE _IOW(AIC3254_IOCTL_MAGIC, 0x22, unsigned int) +#define AIC3254_CONFIG_VOLUME_L _IOW(AIC3254_IOCTL_MAGIC, 0x23, unsigned int) +#define AIC3254_CONFIG_VOLUME_R _IOW(AIC3254_IOCTL_MAGIC, 0x24, unsigned int) +#define AIC3254_POWERDOWN _IOW(AIC3254_IOCTL_MAGIC, 0x25, unsigned int) +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define AIC3254_LOOPBACK _IOW(AIC3254_IOCTL_MAGIC, 0x26, unsigned int) +#define AIC3254_DUMP_PAGES _IOW(AIC3254_IOCTL_MAGIC, 0x30, unsigned int) +#define AIC3254_READ_REG _IOWR(AIC3254_IOCTL_MAGIC, 0x31, unsigned) +#define AIC3254_WRITE_REG _IOW(AIC3254_IOCTL_MAGIC, 0x32, unsigned) +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define AIC3254_RESET _IOW(AIC3254_IOCTL_MAGIC, 0x33, unsigned int) +#define AIC3254_MAX_PAGES 255 +#define AIC3254_MAX_REGS 128 +#define AIC3254_MAX_RETRY 10 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define IO_CTL_ROW_MAX 64 +#define IO_CTL_COL_MAX 1024 +#define MINIDSP_ROW_MAX 32 +#define MINIDSP_COL_MAX 16384 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +enum aic3254_uplink_mode { + INITIAL = 0, + CALL_UPLINK_IMIC_RECEIVER = 1, + CALL_UPLINK_EMIC_HEADSET, +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + CALL_UPLINK_IMIC_HEADSET, + CALL_UPLINK_IMIC_SPEAKER, + CALL_UPLINK_IMIC_RECEIVER_DUALMIC, + CALL_UPLINK_EMIC_HEADSET_DUALMIC, +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + CALL_UPLINK_IMIC_SPEAKER_DUALMIC, + CALL_UPLINK_IMIC_RECIVER_TESTSIM, + CALL_UPLINK_EMIC_HEADSET_TESTSIM, + CALL_UPLINK_IMIC_SPEAKER_TESTSIM, +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + VOICERECORD_IMIC = 15, + VOICERECORD_EMIC, + VIDEORECORD_IMIC, + VIDEORECORD_EMIC, +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + VOICERECOGNITION_IMIC, + VOICERECOGNITION_EMIC, + FM_IN_SPEAKER, + FM_IN_HEADSET, +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + TTY_IN_HCO, + TTY_IN_VCO, + TTY_IN_FULL, + UPLINK_OFF = 29, +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + UPLINK_WAKEUP, + POWER_OFF, + SLEEP_WITH_HP_IN, + VOICERECORD_IMIC_PLAYBACK_SPEAKER, +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + VOICERECORD_EMIC_PLAYBACK_HEADSET, + VOICERECORD_IMIC_PLAYBACK_HEADSET, +}; +enum aic3254_downlink_mode { +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + CALL_DOWNLINK_IMIC_RECEIVER = 1, + CALL_DOWNLINK_EMIC_HEADSET, + CALL_DOWNLINK_IMIC_HEADSET, + CALL_DOWNLINK_IMIC_SPEAKER, +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + CALL_DOWNLINK_IMIC_RECEIVER_DUALMIC, + CALL_DOWNLINK_EMIC_HEADSET_DUALMIC, + CALL_DOWNLINK_IMIC_SPEAKER_DUALMIC, + CALL_DOWNLINK_IMIC_RECIVER_TESTSIM, +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + CALL_DOWNLINK_EMIC_HEADSET_TESTSIM, + CALL_DOWNLINK_IMIC_SPEAKER_TESTSIM, + PLAYBACK_RECEIVER, + PLAYBACK_HEADSET, +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + PLAYBACK_SPEAKER = 13, + RING_HEADSET_SPEAKER, + PLAYBACK_SPEAKER_ALT, + USB_AUDIO, +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + FM_OUT_SPEAKER = 21, + FM_OUT_HEADSET, + TTY_OUT_HCO, + TTY_OUT_VCO, +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + TTY_OUT_FULL, + MUSE, + HAC, + LPM_IMIC_RECEIVER, +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + DOWNLINK_OFF = 29, + DOWNLINK_WAKEUP, +}; +struct aic3254_ctl_ops { +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + void (*tx_amp_enable)(int en); + void (*rx_amp_enable)(int en); + int (*panel_sleep_in)(void); + void (*reset_3254)(void); +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + void (*spibus_enable)(int en); + CODEC_SPI_CMD_PARAM *downlink_off; + CODEC_SPI_CMD_PARAM *uplink_off; + CODEC_SPI_CMD_PARAM *downlink_on; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + CODEC_SPI_CMD_PARAM *uplink_on; + CODEC_SPI_CMD_PARAM *lb_dsp_init; + CODEC_SPI_CMD_PARAM *lb_downlink_receiver; + CODEC_SPI_CMD_PARAM *lb_downlink_speaker; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + CODEC_SPI_CMD_PARAM *lb_downlink_headset; + CODEC_SPI_CMD_PARAM *lb_uplink_imic; + CODEC_SPI_CMD_PARAM *lb_uplink_emic; + CODEC_SPI_CMD_PARAM *lb_receiver_imic; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + CODEC_SPI_CMD_PARAM *lb_speaker_imic; + CODEC_SPI_CMD_PARAM *lb_headset_emic; + CODEC_SPI_CMD_PARAM *lb_receiver_bmic; + CODEC_SPI_CMD_PARAM *lb_speaker_bmic; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + CODEC_SPI_CMD_PARAM *lb_headset_bmic; +}; +#endif + diff --git a/include/linux/tpa2051d3.h b/include/linux/tpa2051d3.h new file mode 100644 index 00000000000..041ddc18ad1 --- /dev/null +++ b/include/linux/tpa2051d3.h @@ -0,0 +1,77 @@ +/**************************************************************************** + **************************************************************************** + *** + *** 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 TPA2051D3_H +#define TPA2051D3_H +#include +#define TPA2051D3_I2C_NAME "tpa2051d3" +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define SPKR_OUTPUT 0 +#define HEADSET_OUTPUT 1 +#define DUAL_OUTPUT 2 +#define HANDSET_OUTPUT 3 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define BEATS_ON_OUTPUT 4 +#define BEATS_OFF_OUTPUT 5 +#define LINEOUT_OUTPUT 6 +#define MODE_CMD_LEM 9 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +struct tpa2051d3_platform_data { + uint32_t gpio_tpa2051_spk_en; + unsigned char spkr_cmd[7]; + unsigned char hsed_cmd[7]; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + unsigned char rece_cmd[7]; + uint32_t gpio_tpa2051_spk_en_cpu; +}; +struct tpa2051_config_data { +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + unsigned int data_len; + unsigned int mode_num; + unsigned char *cmd_data; +}; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +enum TPA2051_Mode { + TPA2051_MODE_OFF, + TPA2051_MODE_PLAYBACK_SPKR, + TPA2051_MODE_PLAYBACK_HEADSET, +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + TPA2051_MODE_RING, + TPA2051_MODE_VOICECALL_SPKR, + TPA2051_MODE_VOICECALL_HEADSET, + TPA2051_MODE_FM_SPKR, +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + TPA2051_MODE_FM_HEADSET, + TPA2051_MODE_PLAYBACK_REC, + TPA2051_MODE_VOICECALL_REC, + TPA2051_MODE_PLAYBACK_HEADSET_BEATS_ON, +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + TPA2051_MODE_PLAYBACK_HEADSET_BEATS_OFF, + TPA2051_MODE_LINEOUT, + TPA2051_MAX_MODE +}; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define TPA2051_IOCTL_MAGIC 'a' +#define TPA2051_SET_CONFIG _IOW(TPA2051_IOCTL_MAGIC, 0x01, unsigned) +#define TPA2051_READ_CONFIG _IOW(TPA2051_IOCTL_MAGIC, 0x02, unsigned) +#define TPA2051_SET_MODE _IOW(TPA2051_IOCTL_MAGIC, 0x03, unsigned) +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define TPA2051_SET_PARAM _IOW(TPA2051_IOCTL_MAGIC, 0x04, unsigned) +#define TPA2051_WRITE_REG _IOW(TPA2051_IOCTL_MAGIC, 0x07, unsigned) +#endif + diff --git a/include/media/Kbuild b/include/media/Kbuild new file mode 100644 index 00000000000..75a11b286a6 --- /dev/null +++ b/include/media/Kbuild @@ -0,0 +1 @@ +header-y += msm_camera.h diff --git a/include/media/msm_camera.h b/include/media/msm_camera.h index a3ba8adc4c8..9dc98ec4cf0 100644 --- a/include/media/msm_camera.h +++ b/include/media/msm_camera.h @@ -1,1135 +1,470 @@ +/**************************************************************************** + **************************************************************************** + *** + *** 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 __LINUX_MSM_CAMERA_H #define __LINUX_MSM_CAMERA_H - #ifdef MSM_CAMERA_BIONIC #include +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #endif #include +#include #include -#include +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #ifdef MSM_CAMERA_GCC #include #else #include +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #endif - +#define MAX_SENSOR_NUM 5 +#define MAX_SENSOR_NAME 32 #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_frame *) - -#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_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 MSM_CAM_IOCTL_AF_CTRL \ - _IOR(MSM_CAM_IOCTL_MAGIC, 25, struct msm_ctrl_cmt_t *) - -#define MSM_CAM_IOCTL_AF_CTRL_DONE \ - _IOW(MSM_CAM_IOCTL_MAGIC, 26, struct msm_ctrl_cmt_t *) - -#define MSM_CAM_IOCTL_CONFIG_VPE \ - _IOW(MSM_CAM_IOCTL_MAGIC, 27, struct msm_camera_vpe_cfg_cmd *) - -#define MSM_CAM_IOCTL_AXI_VPE_CONFIG \ - _IOW(MSM_CAM_IOCTL_MAGIC, 28, struct msm_camera_vpe_cfg_cmd *) - -#define MSM_CAM_IOCTL_STROBE_FLASH_CFG \ - _IOW(MSM_CAM_IOCTL_MAGIC, 29, uint32_t *) - -#define MSM_CAM_IOCTL_STROBE_FLASH_CHARGE \ - _IOW(MSM_CAM_IOCTL_MAGIC, 30, uint32_t *) - -#define MSM_CAM_IOCTL_STROBE_FLASH_RELEASE \ - _IO(MSM_CAM_IOCTL_MAGIC, 31) - -#define MSM_CAM_IOCTL_FLASH_CTRL \ - _IOW(MSM_CAM_IOCTL_MAGIC, 32, struct flash_ctrl_data *) - -#define MSM_CAM_IOCTL_ERROR_CONFIG \ - _IOW(MSM_CAM_IOCTL_MAGIC, 33, uint32_t *) - -#define MSM_CAM_IOCTL_ABORT_CAPTURE \ - _IO(MSM_CAM_IOCTL_MAGIC, 34) - -#define MSM_CAM_IOCTL_SET_FD_ROI \ - _IOW(MSM_CAM_IOCTL_MAGIC, 35, struct fd_roi_info *) - -#define MSM_CAM_IOCTL_GET_CAMERA_INFO \ - _IOR(MSM_CAM_IOCTL_MAGIC, 36, struct msm_camera_info *) - -#define MSM_CAM_IOCTL_UNBLOCK_POLL_PIC_FRAME \ - _IO(MSM_CAM_IOCTL_MAGIC, 37) - -#define MSM_CAM_IOCTL_RELEASE_PIC_BUFFER \ - _IOW(MSM_CAM_IOCTL_MAGIC, 38, struct camera_enable_cmd *) - -#define MSM_CAM_IOCTL_PUT_ST_FRAME \ - _IOW(MSM_CAM_IOCTL_MAGIC, 39, struct msm_camera_st_frame *) - -#define MSM_CAM_IOCTL_GET_CONFIG_INFO \ - _IOR(MSM_CAM_IOCTL_MAGIC, 40, struct msm_cam_config_dev_info *) - -#define MSM_CAM_IOCTL_V4L2_EVT_NOTIFY \ - _IOR(MSM_CAM_IOCTL_MAGIC, 41, struct v4l2_event *) - -#define MSM_CAM_IOCTL_SET_MEM_MAP_INFO \ - _IOR(MSM_CAM_IOCTL_MAGIC, 42, struct msm_mem_map_info *) - -#define MSM_CAM_IOCTL_ACTUATOR_IO_CFG \ - _IOW(MSM_CAM_IOCTL_MAGIC, 43, struct msm_actuator_cfg_data *) - -#define MSM_CAM_IOCTL_MCTL_POST_PROC \ - _IOW(MSM_CAM_IOCTL_MAGIC, 44, struct msm_mctl_post_proc_cmd *) - -#define MSM_CAM_IOCTL_RESERVE_FREE_FRAME \ - _IOW(MSM_CAM_IOCTL_MAGIC, 45, struct msm_cam_evt_divert_frame *) - -#define MSM_CAM_IOCTL_RELEASE_FREE_FRAME \ - _IOR(MSM_CAM_IOCTL_MAGIC, 46, struct msm_cam_evt_divert_frame *) - -struct msm_mctl_pp_cmd { - int32_t id; - uint16_t length; - void *value; -}; - -struct msm_mctl_post_proc_cmd { - int32_t type; - struct msm_mctl_pp_cmd cmd; -}; - -#define MSM_CAMERA_LED_OFF 0 -#define MSM_CAMERA_LED_LOW 1 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#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 *) +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#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 *) +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#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 *) +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#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 *) +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#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 *) +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define MSM_CAM_IOCTL_SENSOR_IO_CFG _IOW(MSM_CAM_IOCTL_MAGIC, 21, struct sensor_cfg_data *) +#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 *) +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define MSM_CAM_IOCTL_AF_CTRL _IOR(MSM_CAM_IOCTL_MAGIC, 25, struct msm_ctrl_cmt_t *) +#define MSM_CAM_IOCTL_AF_CTRL_DONE _IOW(MSM_CAM_IOCTL_MAGIC, 26, struct msm_ctrl_cmt_t *) +#define MSM_CAM_IOCTL_CONFIG_VPE _IOW(MSM_CAM_IOCTL_MAGIC, 27, struct msm_camera_vpe_cfg_cmd *) +#define MSM_CAM_IOCTL_AXI_VPE_CONFIG _IOW(MSM_CAM_IOCTL_MAGIC, 28, struct msm_camera_vpe_cfg_cmd *) +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define MSM_CAM_IOCTL_STROBE_FLASH_CFG _IOW(MSM_CAM_IOCTL_MAGIC, 29, uint32_t *) +#define MSM_CAM_IOCTL_STROBE_FLASH_CHARGE _IOW(MSM_CAM_IOCTL_MAGIC, 30, uint32_t *) +#define MSM_CAM_IOCTL_STROBE_FLASH_RELEASE _IO(MSM_CAM_IOCTL_MAGIC, 31) +#define MSM_CAM_IOCTL_FLASH_CTRL _IOW(MSM_CAM_IOCTL_MAGIC, 32, struct flash_ctrl_data *) +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define MSM_CAM_IOCTL_ERROR_CONFIG _IOW(MSM_CAM_IOCTL_MAGIC, 33, uint32_t *) +#define MSM_CAM_IOCTL_ABORT_CAPTURE _IO(MSM_CAM_IOCTL_MAGIC, 34) +#define MSM_CAM_IOCTL_SET_FD_ROI _IOW(MSM_CAM_IOCTL_MAGIC, 35, struct fd_roi_info *) +#define MSM_CAM_IOCTL_GET_CAMERA_INFO _IOR(MSM_CAM_IOCTL_MAGIC, 36, struct msm_camera_info *) +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define MSM_CAM_IOCTL_PUT_ST_FRAME _IOW(MSM_CAM_IOCTL_MAGIC, 37, struct msm_camera_st_frame *) +#define MSM_CAM_IOCTL_SET_CONFIG_CAMERA_ZSL _IOW(MSM_CAM_IOCTL_MAGIC, 41, bool *) +#define MSM_CAM_IOCTL_EFFECT_STATE_CFG _IOW(MSM_CAM_IOCTL_MAGIC, 43, int32_t *) +#define MSM_CAMERA_LED_OFF 0 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define MSM_CAMERA_LED_LOW 1 #define MSM_CAMERA_LED_HIGH 2 -#define MSM_CAMERA_LED_INIT 3 -#define MSM_CAMERA_LED_RELEASE 4 - #define MSM_CAMERA_STROBE_FLASH_NONE 0 #define MSM_CAMERA_STROBE_FLASH_XENON 1 - -#define MSM_MAX_CAMERA_SENSORS 5 -#define MAX_SENSOR_NAME 32 - -#define MSM_MAX_CAMERA_CONFIGS 2 - -#define PP_SNAP 0x01 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define PP_SNAP 0x01 #define PP_RAW_SNAP ((0x01)<<1) -#define PP_PREV ((0x01)<<2) -#define PP_MASK (PP_SNAP|PP_RAW_SNAP|PP_PREV) - -#define MSM_CAM_CTRL_CMD_DONE 0 +#define PP_PREV ((0x01)<<2) +#define PP_MASK (PP_SNAP|PP_RAW_SNAP|PP_PREV) +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define MSM_CAM_CTRL_CMD_DONE 0 #define MSM_CAM_SENSOR_VFE_CMD 1 - -/* Should be same as VIDEO_MAX_PLANES in videodev2.h */ -#define MAX_PLANES 8 - -/***************************************************** - * 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 */ - int vnode_id; /* video dev id. Can we overload resp_fd? */ - uint32_t stream_type; /* used to pass value to qcamera server */ - int config_ident; /*used as identifier for config node*/ + uint16_t type; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + uint16_t length; + void *value; + uint16_t status; + uint32_t timeout_ms; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + int resp_fd; }; - struct msm_cam_evt_msg { - unsigned short type; /* 1 == event (RPC), 0 == message (adsp) */ - unsigned short msg_id; - unsigned int len; /* size in, number of bytes out */ - uint32_t frame_id; - void *data; -}; - -struct msm_pp_frame_sp { - /* phy addr of the buffer */ - unsigned long phy_addr; - uint32_t y_off; - uint32_t cbcr_off; - /* buffer length */ - uint32_t length; - int32_t fd; - uint32_t addr_offset; - /* mapped addr */ - unsigned long vaddr; -}; - -struct msm_pp_frame_mp { - /* phy addr of the plane */ - unsigned long phy_addr; - /* offset of plane data */ - uint32_t data_offset; - /* plane length */ - uint32_t length; - int32_t fd; - uint32_t addr_offset; - /* mapped addr */ - unsigned long vaddr; -}; - -struct msm_pp_frame { - uint32_t handle; /* stores vb cookie */ - uint32_t frame_id; - int path; - unsigned short image_type; - unsigned short num_planes; /* 1 for sp */ - struct timeval timestamp; - union { - struct msm_pp_frame_sp sp; - struct msm_pp_frame_mp mp[MAX_PLANES]; - }; -}; - -struct msm_cam_evt_divert_frame { - unsigned short image_mode; - unsigned short op_mode; - unsigned short inst_idx; - unsigned short node_idx; - struct msm_pp_frame frame; - int do_pp; -}; - -struct msm_mctl_pp_cmd_ack_event { - uint32_t cmd; /* VPE_CMD_ZOOM? */ - int status; /* 0 done, < 0 err */ - uint32_t cookie; /* daemon's cookie */ -}; - -struct msm_mctl_pp_event_info { - int32_t event; - union { - struct msm_mctl_pp_cmd_ack_event ack; - }; -}; - -struct msm_isp_event_ctrl { - unsigned short resptype; - union { - struct msm_cam_evt_msg isp_msg; - struct msm_ctrl_cmd ctrl; - struct msm_cam_evt_divert_frame div_frame; - struct msm_mctl_pp_event_info pp_event_info; - } isp_data; -}; - -#define MSM_CAM_RESP_CTRL 0 -#define MSM_CAM_RESP_STAT_EVT_MSG 1 -#define MSM_CAM_RESP_STEREO_OP_1 2 -#define MSM_CAM_RESP_STEREO_OP_2 3 -#define MSM_CAM_RESP_V4L2 4 -#define MSM_CAM_RESP_DIV_FRAME_EVT_MSG 5 -#define MSM_CAM_RESP_DONE_EVENT 6 -#define MSM_CAM_RESP_MCTL_PP_EVENT 7 -#define MSM_CAM_RESP_MAX 8 - -#define MSM_CAM_APP_NOTIFY_EVENT 0 - -/* this one is used to send ctrl/status up to config thread */ - + unsigned short type; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + unsigned short msg_id; + unsigned int len; + uint32_t frame_id; + void *data; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +}; +struct msm_isp_evt_msg { + unsigned short type; + unsigned short msg_id; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + unsigned int len; + uint8_t data[48]; +}; +struct msm_vpe_evt_msg { +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + unsigned short type; + unsigned short msg_id; + unsigned int len; + uint32_t frame_id; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + void *data; +}; +struct msm_isp_stats_event_ctrl { + unsigned short resptype; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + union { + struct msm_isp_evt_msg isp_msg; + struct msm_ctrl_cmd ctrl; + } isp_data; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +}; +#define MSM_CAM_RESP_CTRL 0 +#define MSM_CAM_RESP_STAT_EVT_MSG 1 +#define MSM_CAM_RESP_STEREO_OP_1 2 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define MSM_CAM_RESP_STEREO_OP_2 3 +#define MSM_CAM_RESP_V4L2 4 +#define MSM_CAM_RESP_MAX 5 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_cam_evt_msg stats_event; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + int resptype; + int timeout_ms; + struct msm_ctrl_cmd ctrl_cmd; + struct msm_cam_evt_msg stats_event; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ }; - -/* 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_FRAME_BUF_RELEASE 7 -#define CMD_PREV_BUF_CFG 8 -#define CMD_SNAP_BUF_RELEASE 9 -#define CMD_SNAP_BUF_CFG 10 -#define CMD_STATS_DISABLE 11 -#define CMD_STATS_AEC_AWB_ENABLE 12 -#define CMD_STATS_AF_ENABLE 13 -#define CMD_STATS_AEC_ENABLE 14 -#define CMD_STATS_AWB_ENABLE 15 -#define CMD_STATS_ENABLE 16 - -#define CMD_STATS_AXI_CFG 17 -#define CMD_STATS_AEC_AXI_CFG 18 -#define CMD_STATS_AF_AXI_CFG 19 -#define CMD_STATS_AWB_AXI_CFG 20 -#define CMD_STATS_RS_AXI_CFG 21 -#define CMD_STATS_CS_AXI_CFG 22 -#define CMD_STATS_IHIST_AXI_CFG 23 -#define CMD_STATS_SKIN_AXI_CFG 24 - -#define CMD_STATS_BUF_RELEASE 25 -#define CMD_STATS_AEC_BUF_RELEASE 26 -#define CMD_STATS_AF_BUF_RELEASE 27 -#define CMD_STATS_AWB_BUF_RELEASE 28 -#define CMD_STATS_RS_BUF_RELEASE 29 -#define CMD_STATS_CS_BUF_RELEASE 30 -#define CMD_STATS_IHIST_BUF_RELEASE 31 -#define CMD_STATS_SKIN_BUF_RELEASE 32 - -#define UPDATE_STATS_INVALID 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 - + uint16_t cfg_type; + uint16_t cmd_type; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + uint16_t queue; + uint16_t length; + void *value; +}; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#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 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define CMD_PICT_T_AXI_CFG 4 +#define CMD_PICT_M_AXI_CFG 5 +#define CMD_RAW_PICT_AXI_CFG 6 +#define CMD_FRAME_BUF_RELEASE 7 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define CMD_PREV_BUF_CFG 8 +#define CMD_SNAP_BUF_RELEASE 9 +#define CMD_SNAP_BUF_CFG 10 +#define CMD_STATS_DISABLE 11 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define CMD_STATS_AEC_AWB_ENABLE 12 +#define CMD_STATS_AF_ENABLE 13 +#define CMD_STATS_AEC_ENABLE 14 +#define CMD_STATS_AWB_ENABLE 15 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define CMD_STATS_ENABLE 16 +#define CMD_STATS_AXI_CFG 17 +#define CMD_STATS_AEC_AXI_CFG 18 +#define CMD_STATS_AF_AXI_CFG 19 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define CMD_STATS_AWB_AXI_CFG 20 +#define CMD_STATS_RS_AXI_CFG 21 +#define CMD_STATS_CS_AXI_CFG 22 +#define CMD_STATS_IHIST_AXI_CFG 23 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define CMD_STATS_SKIN_AXI_CFG 24 +#define CMD_STATS_BUF_RELEASE 25 +#define CMD_STATS_AEC_BUF_RELEASE 26 +#define CMD_STATS_AF_BUF_RELEASE 27 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define CMD_STATS_AWB_BUF_RELEASE 28 +#define CMD_STATS_RS_BUF_RELEASE 29 +#define CMD_STATS_CS_BUF_RELEASE 30 +#define CMD_STATS_IHIST_BUF_RELEASE 31 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define CMD_STATS_SKIN_BUF_RELEASE 32 +#define UPDATE_STATS_INVALID 33 +#define CMD_AXI_CFG_SNAP_GEMINI 34 +#define CMD_AXI_CFG_SNAP 35 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define CMD_AXI_CFG_PREVIEW 36 +#define CMD_AXI_CFG_VIDEO 37 #define CMD_STATS_IHIST_ENABLE 38 #define CMD_STATS_RS_ENABLE 39 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define CMD_STATS_CS_ENABLE 40 #define CMD_VPE 41 #define CMD_AXI_CFG_VPE 42 -#define CMD_AXI_CFG_ZSL 43 -#define CMD_AXI_CFG_SNAP_VPE 44 -#define CMD_AXI_CFG_SNAP_THUMB_VPE 45 -#define CMD_CONFIG_PING_ADDR 46 -#define CMD_CONFIG_PONG_ADDR 47 -#define CMD_CONFIG_FREE_BUF_ADDR 48 - -/* vfe config command: config command(from config thread)*/ +#define CMD_AXI_CFG_SNAP_VPE 43 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define CMD_AXI_CFG_SNAP_THUMB_VPE 44 struct msm_vfe_cfg_cmd { - int cmd_type; - uint16_t length; - void *value; + int cmd_type; + uint16_t length; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + void *value; }; - struct msm_vpe_cfg_cmd { - int cmd_type; - uint16_t length; - void *value; + int cmd_type; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + uint16_t length; + void *value; }; - #define MAX_CAMERA_ENABLE_NAME_LEN 32 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ 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_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_VIDEO_VPE 16 -#define MSM_PMEM_C2D 17 -#define MSM_PMEM_MAINIMG_VPE 18 -#define MSM_PMEM_THUMBNAIL_VPE 19 -#define MSM_PMEM_MAX 20 - -#define STAT_AEAW 0 -#define STAT_AEC 1 -#define STAT_AF 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 FRAME_PREVIEW_OUTPUT1 0 -#define FRAME_PREVIEW_OUTPUT2 1 -#define FRAME_SNAPSHOT 2 -#define FRAME_THUMBNAIL 3 -#define FRAME_RAW_SNAPSHOT 4 -#define FRAME_MAX 5 - + char name[MAX_CAMERA_ENABLE_NAME_LEN]; +}; +#define MSM_PMEM_OUTPUT1 0 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define MSM_PMEM_OUTPUT2 1 +#define MSM_PMEM_OUTPUT1_OUTPUT2 2 +#define MSM_PMEM_THUMBNAIL 3 +#define MSM_PMEM_MAINIMG 4 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define MSM_PMEM_RAW_MAINIMG 5 +#define MSM_PMEM_AEC_AWB 6 +#define MSM_PMEM_AF 7 +#define MSM_PMEM_AEC 8 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define MSM_PMEM_AWB 9 +#define MSM_PMEM_RS 10 +#define MSM_PMEM_CS 11 +#define MSM_PMEM_IHIST 12 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define MSM_PMEM_SKIN 13 +#define MSM_PMEM_VIDEO 14 +#define MSM_PMEM_PREVIEW 15 +#define MSM_PMEM_VIDEO_VPE 16 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define MSM_PMEM_C2D 17 +#define MSM_PMEM_MAINIMG_VPE 18 +#define MSM_PMEM_THUMBNAIL_VPE 19 +#define MSM_PMEM_MAX 20 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define STAT_AEAW 0 +#define STAT_AEC 1 +#define STAT_AF 2 +#define STAT_AWB 3 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define STAT_RS 4 +#define STAT_CS 5 +#define STAT_IHIST 6 +#define STAT_SKIN 7 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define STAT_MAX 8 +#define FRAME_PREVIEW_OUTPUT1 0 +#define FRAME_PREVIEW_OUTPUT2 1 +#define FRAME_SNAPSHOT 2 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define FRAME_THUMBNAIL 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; - uint32_t cbcr_off; - uint8_t active; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + int type; + int fd; + void *vaddr; + uint32_t offset; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + uint32_t len; + uint32_t y_off; + uint32_t cbcr_off; + uint8_t active; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ }; - 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 /* snapshot only */ -#define OUTPUT_1_AND_3 3 /* video */ + uint32_t height; + uint32_t width; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + uint32_t window_height_firstline; + uint32_t window_height_lastline; +}; +#define OUTPUT_1 0 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define OUTPUT_2 1 +#define OUTPUT_1_AND_2 2 +#define OUTPUT_1_AND_3 3 #define CAMIF_TO_AXI_VIA_OUTPUT_2 4 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define OUTPUT_1_AND_CAMIF_TO_AXI_VIA_OUTPUT_2 5 #define OUTPUT_2_AND_CAMIF_TO_AXI_VIA_OUTPUT_1 6 -#define OUTPUT_1_2_AND_3 7 -#define LAST_AXI_OUTPUT_MODE_ENUM = OUTPUT_1_2_AND_3 7 - -#define MSM_FRAME_PREV_1 0 -#define MSM_FRAME_PREV_2 1 -#define MSM_FRAME_ENC 2 - -#define OUTPUT_TYPE_P (1<<0) -#define OUTPUT_TYPE_T (1<<1) -#define OUTPUT_TYPE_S (1<<2) -#define OUTPUT_TYPE_V (1<<3) -#define OUTPUT_TYPE_L (1<<4) +#define LAST_AXI_OUTPUT_MODE_ENUM = OUTPUT_2_AND_CAMIF_TO_AXI_VIA_OUTPUT_1 7 +#define MSM_FRAME_PREV_1 0 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define MSM_FRAME_PREV_2 1 +#define MSM_FRAME_ENC 2 +#define OUTPUT_TYPE_P (1<<0) +#define OUTPUT_TYPE_T (1<<1) +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define OUTPUT_TYPE_S (1<<2) +#define OUTPUT_TYPE_V (1<<3) +#define OUTPUT_TYPE_L (1<<4) #define OUTPUT_TYPE_ST_L (1<<5) +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define OUTPUT_TYPE_ST_R (1<<6) #define OUTPUT_TYPE_ST_D (1<<7) - struct fd_roi_info { - void *info; - int info_len; + void *info; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + int info_len; }; - -struct msm_mem_map_info { - uint32_t cookie; - uint32_t length; - uint32_t mem_type; -}; - -#define MSM_MEM_MMAP 0 -#define MSM_MEM_USERPTR 1 -#define MSM_PLANE_MAX 8 -#define MSM_PLANE_Y 0 -#define MSM_PLANE_UV 1 - struct msm_frame { - struct timespec ts; - int path; - int type; - unsigned long buffer; - uint32_t phy_offset; - uint32_t y_off; - uint32_t cbcr_off; - int fd; - - void *cropinfo; - int croplen; - uint32_t error_code; - struct fd_roi_info roi_info; - uint32_t frame_id; - int stcam_quality_ind; - uint32_t stcam_conv_value; + struct timespec ts; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + int path; + int type; + unsigned long buffer; + uint32_t phy_offset; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + uint32_t y_off; + uint32_t cbcr_off; + int fd; + void *cropinfo; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + int croplen; + uint32_t error_code; + struct fd_roi_info roi_info; + uint32_t frame_id; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ }; - enum msm_st_frame_packing { - SIDE_BY_SIDE_HALF, - SIDE_BY_SIDE_FULL, - TOP_DOWN_HALF, - TOP_DOWN_FULL, + SIDE_BY_SIDE_HALF, + SIDE_BY_SIDE_FULL, +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + TOP_DOWN_HALF, + TOP_DOWN_FULL, }; - struct msm_st_crop { - uint32_t in_w; - uint32_t in_h; - uint32_t out_w; - uint32_t out_h; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + uint32_t in_w; + uint32_t in_h; + uint32_t out_w; + uint32_t out_h; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ }; - struct msm_st_half { - uint32_t buf_y_off; - uint32_t buf_cbcr_off; - uint32_t buf_y_stride; - uint32_t buf_cbcr_stride; - uint32_t pix_x_off; - uint32_t pix_y_off; - struct msm_st_crop stCropInfo; + uint32_t buf_y_off; + uint32_t buf_cbcr_off; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + uint32_t buf_y_stride; + uint32_t buf_cbcr_stride; + uint32_t pix_x_off; + uint32_t pix_y_off; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + struct msm_st_crop stCropInfo; }; - struct msm_st_frame { - struct msm_frame buf_info; - int type; - enum msm_st_frame_packing packing; - struct msm_st_half L; - struct msm_st_half R; - int frame_id; + struct msm_frame buf_info; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + int type; + enum msm_st_frame_packing packing; + struct msm_st_half L; + struct msm_st_half R; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + int frame_id; }; - #define MSM_CAMERA_ERR_MASK (0xFFFFFFFF & 1) - -struct stats_buff { - unsigned long buff; - int fd; -}; - struct msm_stats_buf { - uint8_t awb_ymin; - struct stats_buff aec; - struct stats_buff awb; - struct stats_buff af; - struct stats_buff ihist; - struct stats_buff rs; - struct stats_buff cs; - struct stats_buff skin; - int type; - uint32_t status_bits; - unsigned long buffer; - int fd; - uint32_t frame_id; -}; -#define MSM_V4L2_EXT_CAPTURE_MODE_DEFAULT 0 -/* video capture mode in VIDIOC_S_PARM */ -#define MSM_V4L2_EXT_CAPTURE_MODE_PREVIEW \ - (MSM_V4L2_EXT_CAPTURE_MODE_DEFAULT+1) -/* extendedmode for video recording in VIDIOC_S_PARM */ -#define MSM_V4L2_EXT_CAPTURE_MODE_VIDEO \ - (MSM_V4L2_EXT_CAPTURE_MODE_DEFAULT+2) -/* extendedmode for the full size main image in VIDIOC_S_PARM */ -#define MSM_V4L2_EXT_CAPTURE_MODE_MAIN (MSM_V4L2_EXT_CAPTURE_MODE_DEFAULT+3) -/* extendedmode for the thumb nail image in VIDIOC_S_PARM */ -#define MSM_V4L2_EXT_CAPTURE_MODE_THUMBNAIL \ - (MSM_V4L2_EXT_CAPTURE_MODE_DEFAULT+4) -#define MSM_V4L2_EXT_CAPTURE_MODE_RAW \ - (MSM_V4L2_EXT_CAPTURE_MODE_DEFAULT+5) -#define MSM_V4L2_EXT_CAPTURE_MODE_MAX (MSM_V4L2_EXT_CAPTURE_MODE_DEFAULT+6) - - -#define MSM_V4L2_PID_MOTION_ISO V4L2_CID_PRIVATE_BASE -#define MSM_V4L2_PID_EFFECT (V4L2_CID_PRIVATE_BASE+1) -#define MSM_V4L2_PID_HJR (V4L2_CID_PRIVATE_BASE+2) -#define MSM_V4L2_PID_LED_MODE (V4L2_CID_PRIVATE_BASE+3) -#define MSM_V4L2_PID_PREP_SNAPSHOT (V4L2_CID_PRIVATE_BASE+4) -#define MSM_V4L2_PID_EXP_METERING (V4L2_CID_PRIVATE_BASE+5) -#define MSM_V4L2_PID_ISO (V4L2_CID_PRIVATE_BASE+6) -#define MSM_V4L2_PID_CAM_MODE (V4L2_CID_PRIVATE_BASE+7) -#define MSM_V4L2_PID_LUMA_ADAPTATION (V4L2_CID_PRIVATE_BASE+8) -#define MSM_V4L2_PID_BEST_SHOT (V4L2_CID_PRIVATE_BASE+9) -#define MSM_V4L2_PID_FOCUS_MODE (V4L2_CID_PRIVATE_BASE+10) -#define MSM_V4L2_PID_BL_DETECTION (V4L2_CID_PRIVATE_BASE+11) -#define MSM_V4L2_PID_SNOW_DETECTION (V4L2_CID_PRIVATE_BASE+12) -#define MSM_V4L2_PID_CTRL_CMD (V4L2_CID_PRIVATE_BASE+13) -#define MSM_V4L2_PID_EVT_SUB_INFO (V4L2_CID_PRIVATE_BASE+14) -#define MSM_V4L2_PID_STROBE_FLASH (V4L2_CID_PRIVATE_BASE+15) -#define MSM_V4L2_PID_MMAP_ENTRY (V4L2_CID_PRIVATE_BASE+16) -#define MSM_V4L2_PID_MMAP_INST (V4L2_CID_PRIVATE_BASE+17) -#define MSM_V4L2_PID_MAX MSM_V4L2_PID_MMAP_INST - -/* camera operation mode for video recording - two frame output queues */ -#define MSM_V4L2_CAM_OP_DEFAULT 0 -/* camera operation mode for video recording - two frame output queues */ -#define MSM_V4L2_CAM_OP_PREVIEW (MSM_V4L2_CAM_OP_DEFAULT+1) -/* camera operation mode for video recording - two frame output queues */ -#define MSM_V4L2_CAM_OP_VIDEO (MSM_V4L2_CAM_OP_DEFAULT+2) -/* camera operation mode for standard shapshot - two frame output queues */ -#define MSM_V4L2_CAM_OP_CAPTURE (MSM_V4L2_CAM_OP_DEFAULT+3) -/* camera operation mode for zsl shapshot - three output queues */ -#define MSM_V4L2_CAM_OP_ZSL (MSM_V4L2_CAM_OP_DEFAULT+4) -/* camera operation mode for raw snapshot - one frame output queue */ -#define MSM_V4L2_CAM_OP_RAW (MSM_V4L2_CAM_OP_DEFAULT+5) - -#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_GET_CROP 8 -#define MSM_V4L2_SET_CROP 9 -#define MSM_V4L2_OPEN 10 -#define MSM_V4L2_CLOSE 11 -#define MSM_V4L2_SET_CTRL_CMD 12 -#define MSM_V4L2_EVT_SUB_MASK 13 -#define MSM_V4L2_MAX 14 -#define V4L2_CAMERA_EXIT 43 - +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + int type; + unsigned long buffer; + int fd; + uint32_t frame_id; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +}; +#define MSM_V4L2_VID_CAP_TYPE 0 +#define MSM_V4L2_STREAM_ON 1 +#define MSM_V4L2_STREAM_OFF 2 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define MSM_V4L2_SNAPSHOT 3 +#define MSM_V4L2_QUERY_CTRL 4 +#define MSM_V4L2_GET_CTRL 5 +#define MSM_V4L2_SET_CTRL 6 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define MSM_V4L2_QUERY 7 +#define MSM_V4L2_GET_CROP 8 +#define MSM_V4L2_SET_CROP 9 +#define MSM_V4L2_MAX 10 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define V4L2_CAMERA_EXIT 43 struct crop_info { - void *info; - int len; + void *info; + int len; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ }; - 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_SEND_WB_INFO 28 -#define CFG_SENSOR_INIT 29 -#define CFG_GET_3D_CALI_DATA 30 -#define CFG_GET_CALIB_DATA 31 -#define CFG_GET_OUTPUT_INFO 32 -#define CFG_GET_EEPROM_DATA 33 -#define CFG_SET_ACTUATOR_INFO 34 -#define CFG_GET_ACTUATOR_INFO 35 -#if 1 /* HTC_START Hayden Huang 20111006 YUV Sensor */ -#define CFG_SET_SHARPNESS 36 -#define CFG_SET_SATURATION 37 -#define CFG_SET_OV_LSC_RAW_CAPTURE 38 -#define CFG_SET_ISO 39 -#define CFG_SET_COORDINATE 40 -#define CFG_RUN_AUTO_FOCUS 41 -#define CFG_CANCEL_AUTO_FOCUS 42 -#define CFG_GET_EXP_FOR_LED 43 -#define CFG_UPDATE_AEC_FOR_LED 44 -#define CFG_SET_FRONT_CAMERA_MODE 45 -#define CFG_SET_QCT_LSC_RAW_CAPTURE 46 -#define CFG_SET_QTR_SIZE_MODE 47 -#define CFG_GET_AF_STATE 48 -#define CFG_SET_DMODE 49 -#define CFG_SET_CALIBRATION 50 -#define CFG_SET_AF_MODE 51 -#define CFG_GET_SP3D_L_FRAME 52 -#define CFG_GET_SP3D_R_FRAME 53 -#define CFG_SET_FLASHLIGHT 54 -#define CFG_SET_FLASHLIGHT_EXP_DIV 55 -#define CFG_GET_ISO 56 -#define CFG_GET_EXP_GAIN 57 -#define CFG_SET_FRAMERATE 58 -#endif /* HTC_END Hayden Huang 20111006 YUV Sensor */ -#define CFG_MAX 59 - -/* HTC_START */ -// Ray add for read fuse id command -#define CFG_I2C_IOCTL_R_OTP 70 -/* HTC_END */ - -#define MOVE_NEAR 0 -#define MOVE_FAR 1 - -/* HTC_START Angie 20111019 - Full Size Preview */ -#define SENSOR_PREVIEW_MODE 0 /* SENSOR_MODE_PREVIEW, SENSOR_MODE_VIDEO, SENSOR_MODE_FULL_SIZE_PREVIEW */ -#define SENSOR_SNAPSHOT_MODE 1 /* SENSOR_MODE_SNAPSHOT */ -#define SENSOR_RAW_SNAPSHOT_MODE 2 /* SENSOR_MODE_RAW_SNAPSHOT */ -/* HTC_END */ -#define SENSOR_HFR_60FPS_MODE 3 -#define SENSOR_HFR_90FPS_MODE 4 -#define SENSOR_HFR_120FPS_MODE 5 - -#define SENSOR_QTR_SIZE 0 -#define SENSOR_FULL_SIZE 1 -#define SENSOR_QVGA_SIZE 2 -#define SENSOR_INVALID_SIZE 3 - -#define CAMERA_EFFECT_OFF 0 -#define CAMERA_EFFECT_MONO 1 -#define CAMERA_EFFECT_NEGATIVE 2 -#define CAMERA_EFFECT_SOLARIZE 3 -#define CAMERA_EFFECT_SEPIA 4 -#define CAMERA_EFFECT_POSTERIZE 5 -#define CAMERA_EFFECT_WHITEBOARD 6 -#define CAMERA_EFFECT_BLACKBOARD 7 -#define CAMERA_EFFECT_AQUA 8 -#define CAMERA_EFFECT_EMBOSS 9 -#define CAMERA_EFFECT_SKETCH 10 -#define CAMERA_EFFECT_NEON 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; + int ftnum; + struct msm_frame fthumnail; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + int fmnum; + struct msm_frame fmain; }; - -struct fps_cfg { - uint16_t f_mult; - uint16_t fps_div; - uint32_t pict_fps_div; -}; -struct wb_info_cfg { - uint16_t red_gain; - uint16_t green_gain; - uint16_t blue_gain; -}; -struct sensor_3d_exp_cfg { - uint16_t gain; - uint32_t line; - uint16_t r_gain; - uint16_t b_gain; - uint16_t gr_gain; - uint16_t gb_gain; - uint16_t gain_adjust; -}; -struct sensor_3d_cali_data_t{ - unsigned char left_p_matrix[3][4][8]; - unsigned char right_p_matrix[3][4][8]; - unsigned char square_len[8]; - unsigned char focal_len[8]; - unsigned char pixel_pitch[8]; - uint16_t left_r; - uint16_t left_b; - uint16_t left_gb; - uint16_t left_af_far; - uint16_t left_af_mid; - uint16_t left_af_short; - uint16_t left_af_5um; - uint16_t left_af_50up; - uint16_t left_af_50down; - uint16_t right_r; - uint16_t right_b; - uint16_t right_gb; - uint16_t right_af_far; - uint16_t right_af_mid; - uint16_t right_af_short; - uint16_t right_af_5um; - uint16_t right_af_50up; - uint16_t right_af_50down; -}; -struct sensor_init_cfg { - uint8_t prev_res; - uint8_t pict_res; -}; - -struct sensor_calib_data { - /* Color Related Measurements */ - uint16_t r_over_g; - uint16_t b_over_g; - uint16_t gr_over_gb; - - /* Lens Related Measurements */ - uint16_t macro_2_inf; - uint16_t inf_2_macro; - uint16_t stroke_amt; - uint16_t af_pos_1m; - uint16_t af_pos_inf; -}; - -enum msm_sensor_resolution_t { - MSM_SENSOR_RES_FULL, - MSM_SENSOR_RES_QTR, - MSM_SENSOR_RES_VIDEO, - MSM_SENSOR_RES_VIDEO_HFR, - MSM_SENSOR_RES_4, - MSM_SENSOR_RES_5, - MSM_SENSOR_RES_6, - MSM_SENSOR_RES_7, - MSM_SENSOR_INVALID_RES, -}; - -struct msm_sensor_output_info_t { - uint16_t x_output; - uint16_t y_output; - uint16_t line_length_pclk; - uint16_t frame_length_lines; - uint32_t vt_pixel_clk; - uint32_t op_pixel_clk; - uint16_t binning_factor; -}; - -struct sensor_output_info_t { - struct msm_sensor_output_info_t *output_info; - uint16_t num_info; - /* HTC_START Angie 20111019 - Fix FPS */ - uint16_t vert_offset; - uint16_t min_vert; - int mirror_flip; -/* HTC_END */ -}; - -struct sensor_eeprom_data_t { - void *eeprom_data; - uint16_t index; -}; - -#if 1 /* HTC_START Hayden Huang 20111006 YUV Sensor */ -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, - CAMERA_PORTRAIT_REVERSE, /* 0916 for 3rd party */ -}; - -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 iso_mode{ - CAMERA_ISO_AUTO = 0, - CAMERA_ISO_DEBLUR, - CAMERA_ISO_100, - CAMERA_ISO_200, - CAMERA_ISO_400, - CAMERA_ISO_800, - CAMERA_ISO_1250, - CAMERA_ISO_1600, - CAMERA_ISO_MAX -}; - -enum sharpness_mode{ - CAMERA_SHARPNESS_X0, - CAMERA_SHARPNESS_X1, - CAMERA_SHARPNESS_X2, - CAMERA_SHARPNESS_X3, - CAMERA_SHARPNESS_X4, - CAMERA_SHARPNESS_X5, - CAMERA_SHARPNESS_X6, -}; - -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, -}; - -enum qtr_size_mode{ - NORMAL_QTR_SIZE_MODE, - LARGER_QTR_SIZE_MODE, -}; - -enum sensor_af_mode{ - SENSOR_AF_MODE_AUTO, - SENSOR_AF_MODE_NORMAL, - SENSOR_AF_MODE_MACRO, -}; -#endif /* HTC_END Hayden Huang 20111006 YUV Sensor */ - -/* HTC_START */ -// Ray add fuse id structure -struct fuse_id{ - uint32_t fuse_id_word1; - uint32_t fuse_id_word2; - uint32_t fuse_id_word3; - uint32_t fuse_id_word4; -}; -/* HTC_END */ -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_init_cfg init_info; - struct sensor_pict_fps gfps; - struct exp_gain_cfg exp_gain; - struct focus_cfg focus; - struct fps_cfg fps; - struct wb_info_cfg wb_info; - struct sensor_3d_exp_cfg sensor_3d_exp; - struct sensor_calib_data calib_info; - struct sensor_output_info_t output_info; - struct sensor_eeprom_data_t eeprom_data; - /* HTC_START */ - /* Ray add fuse to member */ - struct fuse_id fuse; - /* HTC_END */ -#if 1 /* HTC_START Hayden Huang 20111006 YUV Sensor */ - enum antibanding_mode antibanding_value; - enum brightness_t brightness_value; - enum frontcam_t frontcam_value; - enum wb_mode wb_value; - enum iso_mode iso_value; - enum sharpness_mode sharpness_value; - enum saturation_mode saturation_value; - enum contrast_mode contrast_value; - enum qtr_size_mode qtr_size_mode_value; - enum sensor_af_mode af_mode_value; -#endif /* HTC_END Hayden Huang 20111006 YUV Sensor */ - } cfg; -}; - -struct msm_actuator_move_params_t { - int8_t dir; - int32_t num_steps; -}; - -struct msm_actuator_set_info_t { - uint32_t total_steps; - uint16_t gross_steps; - uint16_t fine_steps; -}; - -struct msm_actuator_get_info_t { - uint32_t focal_length_num; - uint32_t focal_length_den; - uint32_t f_number_num; - uint32_t f_number_den; - uint32_t f_pix_num; - uint32_t f_pix_den; - uint32_t total_f_dist_num; - uint32_t total_f_dist_den; -}; - -struct msm_actuator_cfg_data { - int cfgtype; - uint8_t is_af_supported; - union { - struct msm_actuator_move_params_t move; - struct msm_actuator_set_info_t set_info; - struct msm_actuator_get_info_t get_info; - } cfg; -}; - -struct sensor_large_data { - int cfgtype; - union { - struct sensor_3d_cali_data_t sensor_3d_cali_data; - } data; -}; - -enum sensor_type_t { - BAYER, - YUV, - JPEG_SOC, -}; - enum flash_type { - LED_FLASH, - STROBE_FLASH, +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + LED_FLASH, + STROBE_FLASH, }; - enum strobe_flash_ctrl_type { - STROBE_FLASH_CTRL_INIT, - STROBE_FLASH_CTRL_CHARGE, - STROBE_FLASH_CTRL_RELEASE +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + STROBE_FLASH_CTRL_INIT, + STROBE_FLASH_CTRL_CHARGE, + STROBE_FLASH_CTRL_RELEASE }; - +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ struct strobe_flash_ctrl_data { - enum strobe_flash_ctrl_type type; - int charge_en; -}; - -struct msm_camera_info { - int num_cameras; - uint8_t has_3d_support[MSM_MAX_CAMERA_SENSORS]; - uint8_t is_internal_cam[MSM_MAX_CAMERA_SENSORS]; - uint32_t s_mount_angle[MSM_MAX_CAMERA_SENSORS]; - const char *video_dev_name[MSM_MAX_CAMERA_SENSORS]; - enum sensor_type_t sensor_type[MSM_MAX_CAMERA_SENSORS]; - + enum strobe_flash_ctrl_type type; + int charge_en; }; - -struct msm_cam_config_dev_info { - int num_config_nodes; - const char *config_dev_name[MSM_MAX_CAMERA_CONFIGS]; - int config_dev_id[MSM_MAX_CAMERA_CONFIGS]; -}; - +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ struct flash_ctrl_data { - int flashtype; - union { - int led_state; - struct strobe_flash_ctrl_data strobe_ctrl; - } ctrl_data; + int flashtype; + union { + int led_state; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + struct strobe_flash_ctrl_data strobe_ctrl; + } ctrl_data; }; - -#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; - int8_t total_steps; - uint8_t support_3d; +struct msm_snapshot_pp_status { +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + void *status; }; - -#define V4L2_SINGLE_PLANE 0 -#define V4L2_MULTI_PLANE_Y 0 -#define V4L2_MULTI_PLANE_CBCR 1 -#define V4L2_MULTI_PLANE_CB 1 -#define V4L2_MULTI_PLANE_CR 2 - -struct plane_data { - int plane_id; - uint32_t offset; - unsigned long size; +struct msm_camsensor_info { + char name[MAX_SENSOR_NAME]; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + uint8_t flash_enabled; + int8_t total_steps; }; - -struct img_plane_info { - uint32_t width; - uint32_t height; - uint32_t pixelformat; - uint8_t buffer_type; /*Single/Multi planar*/ - uint8_t output_port; - uint32_t ext_mode; - uint8_t num_planes; - struct plane_data plane[MAX_PLANES]; - uint8_t vpe_can_use; +struct msm_camera_info { +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + int num_cameras; + uint8_t has_3d_support[MAX_SENSOR_NUM]; + uint8_t is_internal_cam[MAX_SENSOR_NUM]; }; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#endif -#endif /* __LINUX_MSM_CAMERA_H */ diff --git a/include/sound/Kbuild b/include/sound/Kbuild index 802947f6091..a98e10d29ba 100644 --- a/include/sound/Kbuild +++ b/include/sound/Kbuild @@ -6,3 +6,5 @@ header-y += hdsp.h header-y += hdspm.h header-y += sb16_csp.h header-y += sfnt_info.h +header-y += compress_offload.h +header-y += compress_params.h diff --git a/include/sound/compress_offload.h b/include/sound/compress_offload.h new file mode 100644 index 00000000000..c43ff12ab60 --- /dev/null +++ b/include/sound/compress_offload.h @@ -0,0 +1,81 @@ +/**************************************************************************** + **************************************************************************** + *** + *** 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 __COMPRESS_OFFLOAD_H +#define __COMPRESS_OFFLOAD_H +#include +struct snd_compressed_buffer { +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + size_t fragment_size; + int fragments; +}; +struct snd_compr_params { +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + struct snd_compressed_buffer buffer; + struct snd_codec codec; +}; +struct snd_compr_tstamp { +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + size_t copied_bytes; + size_t copied_total; + size_t decoded; + size_t rendered; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + __u32 sampling_rate; + uint64_t timestamp; +}; +struct snd_compr_avail { +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + size_t avail; + struct snd_compr_tstamp tstamp; +}; +struct snd_compr_caps { +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + __u32 num_codecs; + __u32 min_fragment_size; + __u32 max_fragment_size; + __u32 min_fragments; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + __u32 max_fragments; + __u32 codecs[MAX_NUM_CODECS]; + __u32 reserved[11]; +}; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +struct snd_compr_codec_caps { + __u32 codec; + __u32 num_descriptors; + struct snd_codec_desc descriptor[MAX_NUM_CODEC_DESCRIPTORS]; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +}; +#define SNDRV_COMPRESS_GET_CAPS _IOWR('C', 0x00, struct snd_compr_caps *) +#define SNDRV_COMPRESS_GET_CODEC_CAPS _IOWR('C', 0x01, struct snd_compr_codec_caps *) +#define SNDRV_COMPRESS_SET_PARAMS _IOW('C', 0x02, struct snd_compr_params *) +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define SNDRV_COMPRESS_GET_PARAMS _IOR('C', 0x03, struct snd_compr_params *) +#define SNDRV_COMPRESS_TSTAMP _IOR('C', 0x10, struct snd_compr_tstamp *) +#define SNDRV_COMPRESS_AVAIL _IOR('C', 0x11, struct snd_compr_avail *) +#define SNDRV_COMPRESS_PAUSE _IO('C', 0x20) +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define SNDRV_COMPRESS_RESUME _IO('C', 0x21) +#define SNDRV_COMPRESS_START _IO('C', 0x22) +#define SNDRV_COMPRESS_STOP _IO('C', 0x23) +#define SNDRV_COMPRESS_DRAIN _IO('C', 0x24) +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define SND_COMPR_TRIGGER_DRAIN 7 +#endif + diff --git a/include/sound/compress_params.h b/include/sound/compress_params.h new file mode 100644 index 00000000000..a86b94d3c95 --- /dev/null +++ b/include/sound/compress_params.h @@ -0,0 +1,241 @@ +/**************************************************************************** + **************************************************************************** + *** + *** 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 __SND_COMPRESS_PARAMS_H +#define __SND_COMPRESS_PARAMS_H +#define MAX_NUM_CODECS 32 +#define MAX_NUM_CODEC_DESCRIPTORS 32 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define MAX_NUM_BITRATES 32 +#define SND_AUDIOCODEC_PCM ((__u32) 0x00000001) +#define SND_AUDIOCODEC_MP3 ((__u32) 0x00000002) +#define SND_AUDIOCODEC_AMR ((__u32) 0x00000003) +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define SND_AUDIOCODEC_AMRWB ((__u32) 0x00000004) +#define SND_AUDIOCODEC_AMRWBPLUS ((__u32) 0x00000005) +#define SND_AUDIOCODEC_AAC ((__u32) 0x00000006) +#define SND_AUDIOCODEC_WMA ((__u32) 0x00000007) +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define SND_AUDIOCODEC_REAL ((__u32) 0x00000008) +#define SND_AUDIOCODEC_VORBIS ((__u32) 0x00000009) +#define SND_AUDIOCODEC_FLAC ((__u32) 0x0000000A) +#define SND_AUDIOCODEC_IEC61937 ((__u32) 0x0000000B) +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define SND_AUDIOCODEC_G723_1 ((__u32) 0x0000000C) +#define SND_AUDIOCODEC_G729 ((__u32) 0x0000000D) +#define SND_AUDIOPROFILE_PCM ((__u32) 0x00000001) +#define SND_AUDIOCHANMODE_MP3_MONO ((__u32) 0x00000001) +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define SND_AUDIOCHANMODE_MP3_STEREO ((__u32) 0x00000002) +#define SND_AUDIOCHANMODE_MP3_JOINTSTEREO ((__u32) 0x00000004) +#define SND_AUDIOCHANMODE_MP3_DUAL ((__u32) 0x00000008) +#define SND_AUDIOPROFILE_AMR ((__u32) 0x00000001) +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define SND_AUDIOMODE_AMR_DTX_OFF ((__u32) 0x00000001) +#define SND_AUDIOMODE_AMR_VAD1 ((__u32) 0x00000002) +#define SND_AUDIOMODE_AMR_VAD2 ((__u32) 0x00000004) +#define SND_AUDIOSTREAMFORMAT_UNDEFINED ((__u32) 0x00000000) +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define SND_AUDIOSTREAMFORMAT_CONFORMANCE ((__u32) 0x00000001) +#define SND_AUDIOSTREAMFORMAT_IF1 ((__u32) 0x00000002) +#define SND_AUDIOSTREAMFORMAT_IF2 ((__u32) 0x00000004) +#define SND_AUDIOSTREAMFORMAT_FSF ((__u32) 0x00000008) +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define SND_AUDIOSTREAMFORMAT_RTPPAYLOAD ((__u32) 0x00000010) +#define SND_AUDIOSTREAMFORMAT_ITU ((__u32) 0x00000020) +#define SND_AUDIOPROFILE_AMRWB ((__u32) 0x00000001) +#define SND_AUDIOMODE_AMRWB_DTX_OFF ((__u32) 0x00000001) +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define SND_AUDIOMODE_AMRWB_VAD1 ((__u32) 0x00000002) +#define SND_AUDIOMODE_AMRWB_VAD2 ((__u32) 0x00000004) +#define SND_AUDIOPROFILE_AMRWBPLUS ((__u32) 0x00000001) +#define SND_AUDIOPROFILE_AAC ((__u32) 0x00000001) +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define SND_AUDIOMODE_AAC_MAIN ((__u32) 0x00000001) +#define SND_AUDIOMODE_AAC_LC ((__u32) 0x00000002) +#define SND_AUDIOMODE_AAC_SSR ((__u32) 0x00000004) +#define SND_AUDIOMODE_AAC_LTP ((__u32) 0x00000008) +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define SND_AUDIOMODE_AAC_HE ((__u32) 0x00000010) +#define SND_AUDIOMODE_AAC_SCALABLE ((__u32) 0x00000020) +#define SND_AUDIOMODE_AAC_ERLC ((__u32) 0x00000040) +#define SND_AUDIOMODE_AAC_LD ((__u32) 0x00000080) +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define SND_AUDIOMODE_AAC_HE_PS ((__u32) 0x00000100) +#define SND_AUDIOMODE_AAC_HE_MPS ((__u32) 0x00000200) +#define SND_AUDIOSTREAMFORMAT_MP2ADTS ((__u32) 0x00000001) +#define SND_AUDIOSTREAMFORMAT_MP4ADTS ((__u32) 0x00000002) +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define SND_AUDIOSTREAMFORMAT_MP4LOAS ((__u32) 0x00000004) +#define SND_AUDIOSTREAMFORMAT_MP4LATM ((__u32) 0x00000008) +#define SND_AUDIOSTREAMFORMAT_ADIF ((__u32) 0x00000010) +#define SND_AUDIOSTREAMFORMAT_MP4FF ((__u32) 0x00000020) +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define SND_AUDIOSTREAMFORMAT_RAW ((__u32) 0x00000040) +#define SND_AUDIOPROFILE_WMA7 ((__u32) 0x00000001) +#define SND_AUDIOPROFILE_WMA8 ((__u32) 0x00000002) +#define SND_AUDIOPROFILE_WMA9 ((__u32) 0x00000004) +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define SND_AUDIOPROFILE_WMA10 ((__u32) 0x00000008) +#define SND_AUDIOMODE_WMA_LEVEL1 ((__u32) 0x00000001) +#define SND_AUDIOMODE_WMA_LEVEL2 ((__u32) 0x00000002) +#define SND_AUDIOMODE_WMA_LEVEL3 ((__u32) 0x00000004) +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define SND_AUDIOMODE_WMA_LEVEL4 ((__u32) 0x00000008) +#define SND_AUDIOMODE_WMAPRO_LEVELM0 ((__u32) 0x00000010) +#define SND_AUDIOMODE_WMAPRO_LEVELM1 ((__u32) 0x00000020) +#define SND_AUDIOMODE_WMAPRO_LEVELM2 ((__u32) 0x00000040) +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define SND_AUDIOMODE_WMAPRO_LEVELM3 ((__u32) 0x00000080) +#define SND_AUDIOSTREAMFORMAT_WMA_ASF ((__u32) 0x00000001) +#define SND_AUDIOSTREAMFORMAT_WMA_NOASF_HDR ((__u32) 0x00000002) +#define SND_AUDIOPROFILE_REALAUDIO ((__u32) 0x00000001) +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define SND_AUDIOMODE_REALAUDIO_G2 ((__u32) 0x00000001) +#define SND_AUDIOMODE_REALAUDIO_8 ((__u32) 0x00000002) +#define SND_AUDIOMODE_REALAUDIO_10 ((__u32) 0x00000004) +#define SND_AUDIOMODE_REALAUDIO_SURROUND ((__u32) 0x00000008) +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define SND_AUDIOPROFILE_VORBIS ((__u32) 0x00000001) +#define SND_AUDIOMODE_VORBIS ((__u32) 0x00000001) +#define SND_AUDIOPROFILE_FLAC ((__u32) 0x00000001) +#define SND_AUDIOMODE_FLAC_LEVEL0 ((__u32) 0x00000001) +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define SND_AUDIOMODE_FLAC_LEVEL1 ((__u32) 0x00000002) +#define SND_AUDIOMODE_FLAC_LEVEL2 ((__u32) 0x00000004) +#define SND_AUDIOMODE_FLAC_LEVEL3 ((__u32) 0x00000008) +#define SND_AUDIOMODE_FLAC_LEVEL4 ((__u32) 0x00000010) +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define SND_AUDIOMODE_FLAC_LEVEL5 ((__u32) 0x00000020) +#define SND_AUDIOMODE_FLAC_LEVEL6 ((__u32) 0x00000040) +#define SND_AUDIOMODE_FLAC_LEVEL7 ((__u32) 0x00000080) +#define SND_AUDIOMODE_FLAC_LEVEL8 ((__u32) 0x00000100) +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define SND_AUDIOSTREAMFORMAT_FLAC ((__u32) 0x00000001) +#define SND_AUDIOSTREAMFORMAT_FLAC_OGG ((__u32) 0x00000002) +#define SND_AUDIOPROFILE_IEC61937 ((__u32) 0x00000001) +#define SND_AUDIOPROFILE_IEC61937_SPDIF ((__u32) 0x00000002) +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define SND_AUDIOMODE_IEC_REF_STREAM_HEADER ((__u32) 0x00000000) +#define SND_AUDIOMODE_IEC_LPCM ((__u32) 0x00000001) +#define SND_AUDIOMODE_IEC_AC3 ((__u32) 0x00000002) +#define SND_AUDIOMODE_IEC_MPEG1 ((__u32) 0x00000004) +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define SND_AUDIOMODE_IEC_MP3 ((__u32) 0x00000008) +#define SND_AUDIOMODE_IEC_MPEG2 ((__u32) 0x00000010) +#define SND_AUDIOMODE_IEC_AACLC ((__u32) 0x00000020) +#define SND_AUDIOMODE_IEC_DTS ((__u32) 0x00000040) +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define SND_AUDIOMODE_IEC_ATRAC ((__u32) 0x00000080) +#define SND_AUDIOMODE_IEC_SACD ((__u32) 0x00000100) +#define SND_AUDIOMODE_IEC_EAC3 ((__u32) 0x00000200) +#define SND_AUDIOMODE_IEC_DTS_HD ((__u32) 0x00000400) +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define SND_AUDIOMODE_IEC_MLP ((__u32) 0x00000800) +#define SND_AUDIOMODE_IEC_DST ((__u32) 0x00001000) +#define SND_AUDIOMODE_IEC_WMAPRO ((__u32) 0x00002000) +#define SND_AUDIOMODE_IEC_REF_CXT ((__u32) 0x00004000) +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define SND_AUDIOMODE_IEC_HE_AAC ((__u32) 0x00008000) +#define SND_AUDIOMODE_IEC_HE_AAC2 ((__u32) 0x00010000) +#define SND_AUDIOMODE_IEC_MPEG_SURROUND ((__u32) 0x00020000) +#define SND_AUDIOPROFILE_G723_1 ((__u32) 0x00000001) +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define SND_AUDIOMODE_G723_1_ANNEX_A ((__u32) 0x00000001) +#define SND_AUDIOMODE_G723_1_ANNEX_B ((__u32) 0x00000002) +#define SND_AUDIOMODE_G723_1_ANNEX_C ((__u32) 0x00000004) +#define SND_AUDIOPROFILE_G729 ((__u32) 0x00000001) +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define SND_AUDIOMODE_G729_ANNEX_A ((__u32) 0x00000001) +#define SND_AUDIOMODE_G729_ANNEX_B ((__u32) 0x00000002) +#define SND_RATECONTROLMODE_CONSTANTBITRATE ((__u32) 0x00000001) +#define SND_RATECONTROLMODE_VARIABLEBITRATE ((__u32) 0x00000002) +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +struct snd_enc_wma { + __u32 super_block_align; +}; +struct snd_enc_vorbis { +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + __s32 quality; + __u32 managed; + __u32 max_bit_rate; + __u32 min_bit_rate; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + __u32 downmix; +}; +struct snd_enc_real { + __u32 quant_bits; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + __u32 start_region; + __u32 num_regions; +}; +struct snd_enc_flac { +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + __u32 num; + __u32 gain; +}; +struct snd_enc_generic { +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + __u32 bw; + __s32 reserved[15]; +}; +union snd_codec_options { +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + struct snd_enc_wma wma; + struct snd_enc_vorbis vorbis; + struct snd_enc_real real; + struct snd_enc_flac flac; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + struct snd_enc_generic generic; +}; +struct snd_codec_desc { + __u32 max_ch; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + __u32 sample_rates; + __u32 bit_rate[MAX_NUM_BITRATES]; + __u32 num_bitrates; + __u32 rate_control; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + __u32 profiles; + __u32 modes; + __u32 formats; + __u32 min_buffer; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + __u32 reserved[15]; +}; +struct snd_codec { + __u32 id; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + __u32 ch_in; + __u32 ch_out; + __u32 sample_rate; + __u32 bit_rate; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + __u32 rate_control; + __u32 profile; + __u32 level; + __u32 ch_mode; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + __u32 format; + __u32 align; + union snd_codec_options options; + __u32 reserved[3]; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +}; +#endif + From 7edc5342483ce6041990148e0a9f6908b7566518 Mon Sep 17 00:00:00 2001 From: David Hays Date: Sat, 25 May 2013 00:37:10 -0400 Subject: [PATCH 011/111] vigor: audio changes/enable back mic --- arch/arm/mach-msm/board-vigor-audio.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-msm/board-vigor-audio.c b/arch/arm/mach-msm/board-vigor-audio.c index 15fc1c36dbc..b96a4d904b6 100644 --- a/arch/arm/mach-msm/board-vigor-audio.c +++ b/arch/arm/mach-msm/board-vigor-audio.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -326,7 +327,7 @@ int vigor_is_msm_i2s_slave(void) int vigor_support_aic3254(void) { - return 0; + return 1; } int vigor_support_adie(void) @@ -336,7 +337,7 @@ int vigor_support_adie(void) int vigor_support_back_mic(void) { - return 0; + return 1; } int vigor_is_msm_i2s_master(void) From 2fba0aae993b063f17b3eae85783ff471e88e34f Mon Sep 17 00:00:00 2001 From: Hemant Kumar Date: Thu, 3 Nov 2011 16:40:32 -0700 Subject: [PATCH 012/111] usb: Add network bridge host driver for dun and rmnet This host driver will be used to communicate with modem devices with dial up network and RMNET interfaces. This driver works as a bridge to pass control and data packets between the modem and peripheral usb gadget driver. Driver currently supports modem devices (vendor ID 0x05c6) with PIDs 0x9001 Change-Id: Id85b552b39d061528a1c3c90a354d73580c9b631 Signed-off-by: Hemant Kumar Signed-off-by: Jack Pham --- arch/arm/mach-msm/include/mach/usb_bridge.h | 122 +++ drivers/usb/misc/mdm_ctrl_bridge.c | 729 ++++++++++++++++ drivers/usb/misc/mdm_data_bridge.c | 923 ++++++++++++++++++++ 3 files changed, 1774 insertions(+) create mode 100644 arch/arm/mach-msm/include/mach/usb_bridge.h create mode 100644 drivers/usb/misc/mdm_ctrl_bridge.c create mode 100644 drivers/usb/misc/mdm_data_bridge.c diff --git a/arch/arm/mach-msm/include/mach/usb_bridge.h b/arch/arm/mach-msm/include/mach/usb_bridge.h new file mode 100644 index 00000000000..2b7e754b35c --- /dev/null +++ b/arch/arm/mach-msm/include/mach/usb_bridge.h @@ -0,0 +1,122 @@ +/* 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_USB_BRIDGE_H__ +#define __LINUX_USB_BRIDGE_H__ + +#include +#include + +/* bridge device 0: DUN + * bridge device 1 : Tethered RMNET + */ +#define MAX_BRIDGE_DEVICES 2 + +/*PID 9001*/ +#define DUN_IFACE_NUM 2 +#define TETHERED_RMNET_IFACE_NUM 3 + +struct bridge_ops { + int (*send_pkt)(void *, void *, size_t actual); + void (*send_cbits)(void *, unsigned int); + + /* flow control */ + void (*unthrottle_tx)(void *); +}; + +#define TX_THROTTLED BIT(0) +#define RX_THROTTLED BIT(1) + +struct bridge { + /* context of the gadget port using bridge driver */ + void *ctx; + + /* bridge device array index mapped to the gadget port array index. + * data bridge[ch_id] <-- bridge --> gadget port[ch_id] + */ + unsigned int ch_id; + + /* flow control bits */ + unsigned long flags; + + /* data/ctrl bridge callbacks */ + struct bridge_ops ops; +}; + +#if defined(CONFIG_USB_QCOM_MDM_BRIDGE) || \ + defined(CONFIG_USB_QCOM_MDM_BRIDGE_MODULE) + +/* Bridge APIs called by gadget driver */ +int ctrl_bridge_open(struct bridge *); +void ctrl_bridge_close(unsigned int); +int ctrl_bridge_write(unsigned int, char *, size_t); +int ctrl_bridge_set_cbits(unsigned int, unsigned int); +unsigned int ctrl_bridge_get_cbits_tohost(unsigned int); +int data_bridge_open(struct bridge *brdg); +void data_bridge_close(unsigned int); +int data_bridge_write(unsigned int , struct sk_buff *); +int data_bridge_unthrottle_rx(unsigned int); + +/* defined in control bridge */ +int ctrl_bridge_probe(struct usb_interface *, struct usb_host_endpoint *, int); +void ctrl_bridge_disconnect(unsigned int); +int ctrl_bridge_resume(unsigned int); +int ctrl_bridge_suspend(unsigned int); + +#else + +static inline int __maybe_unused ctrl_bridge_open(struct bridge *brdg) +{ + return -ENODEV; +} + +static inline void __maybe_unused ctrl_bridge_close(unsigned int id) { } + +static inline int __maybe_unused ctrl_bridge_write(unsigned int id, + char *data, size_t size) +{ + return -ENODEV; +} + +static inline int __maybe_unused ctrl_bridge_set_cbits(unsigned int id, + unsigned int cbits) +{ + return -ENODEV; +} + +static inline unsigned int __maybe_unused +ctrl_bridge_get_cbits_tohost(unsigned int id) +{ + return -ENODEV; +} + +static inline int __maybe_unused data_bridge_open(struct bridge *brdg) +{ + return -ENODEV; +} + +static inline void __maybe_unused data_bridge_close(unsigned int id) { } + +static inline int __maybe_unused data_bridge_write(unsigned int id, + struct sk_buff *skb) +{ + return -ENODEV; +} + +static inline int __maybe_unused data_bridge_unthrottle_rx(unsigned int id) +{ + return -ENODEV; +} + +#endif +#endif diff --git a/drivers/usb/misc/mdm_ctrl_bridge.c b/drivers/usb/misc/mdm_ctrl_bridge.c new file mode 100644 index 00000000000..87adf2e92cb --- /dev/null +++ b/drivers/usb/misc/mdm_ctrl_bridge.c @@ -0,0 +1,729 @@ +/* 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 +#include +#include + +static const char *ctrl_bridge_names[] = { + "dun_ctrl_hsic0", + "rmnet_ctrl_hsic0" +}; + +/* polling interval for Interrupt ep */ +#define HS_INTERVAL 7 +#define FS_LS_INTERVAL 3 + +#define ACM_CTRL_DTR (1 << 0) +#define DEFAULT_READ_URB_LENGTH 4096 + +struct ctrl_bridge { + + struct usb_device *udev; + struct usb_interface *intf; + + unsigned int int_pipe; + struct urb *inturb; + void *intbuf; + + struct urb *readurb; + void *readbuf; + + struct usb_anchor tx_submitted; + struct usb_ctrlrequest *in_ctlreq; + + struct bridge *brdg; + struct platform_device *pdev; + + /* input control lines (DSR, CTS, CD, RI) */ + unsigned int cbits_tohost; + + /* output control lines (DTR, RTS) */ + unsigned int cbits_tomdm; + + /* counters */ + unsigned int snd_encap_cmd; + unsigned int get_encap_res; + unsigned int resp_avail; + unsigned int set_ctrl_line_sts; + unsigned int notify_ser_state; + +}; + +static struct ctrl_bridge *__dev[MAX_BRIDGE_DEVICES]; + +/* counter used for indexing ctrl bridge devices */ +static int ch_id; + +unsigned int ctrl_bridge_get_cbits_tohost(unsigned int id) +{ + struct ctrl_bridge *dev; + + if (id >= MAX_BRIDGE_DEVICES) + return -EINVAL; + + dev = __dev[id]; + if (!dev) + return -ENODEV; + + return dev->cbits_tohost; +} +EXPORT_SYMBOL(ctrl_bridge_get_cbits_tohost); + +int ctrl_bridge_set_cbits(unsigned int id, unsigned int cbits) +{ + struct ctrl_bridge *dev; + struct bridge *brdg; + int retval; + + if (id >= MAX_BRIDGE_DEVICES) + return -EINVAL; + + dev = __dev[id]; + if (!dev) + return -ENODEV; + + pr_debug("%s: dev[id] =%u cbits : %u\n", __func__, id, cbits); + + brdg = dev->brdg; + if (!brdg) + return -ENODEV; + + dev->cbits_tomdm = cbits; + + retval = ctrl_bridge_write(id, NULL, 0); + + /* if DTR is high, update latest modem info to host */ + if (brdg && (cbits & ACM_CTRL_DTR) && brdg->ops.send_cbits) + brdg->ops.send_cbits(brdg->ctx, dev->cbits_tohost); + + return retval; +} +EXPORT_SYMBOL(ctrl_bridge_set_cbits); + +static void resp_avail_cb(struct urb *urb) +{ + struct ctrl_bridge *dev = urb->context; + struct usb_device *udev; + int status = 0; + int resubmit_urb = 1; + struct bridge *brdg = dev->brdg; + + udev = interface_to_usbdev(dev->intf); + switch (urb->status) { + case 0: + /*success*/ + dev->get_encap_res++; + if (brdg && brdg->ops.send_pkt) + brdg->ops.send_pkt(brdg->ctx, urb->transfer_buffer, + urb->actual_length); + break; + + /*do not resubmit*/ + case -ESHUTDOWN: + case -ENOENT: + case -ECONNRESET: + /* unplug */ + case -EPROTO: + /*babble error*/ + resubmit_urb = 0; + /*resubmit*/ + case -EOVERFLOW: + default: + dev_dbg(&udev->dev, "%s: non zero urb status = %d\n", + __func__, urb->status); + } + + if (resubmit_urb) { + /*re- submit int urb to check response available*/ + status = usb_submit_urb(dev->inturb, GFP_ATOMIC); + if (status) + dev_err(&udev->dev, + "%s: Error re-submitting Int URB %d\n", + __func__, status); + } +} + +static void notification_available_cb(struct urb *urb) +{ + int status; + struct usb_cdc_notification *ctrl; + struct usb_device *udev; + struct ctrl_bridge *dev = urb->context; + struct bridge *brdg = dev->brdg; + unsigned int ctrl_bits; + unsigned char *data; + + udev = interface_to_usbdev(dev->intf); + + switch (urb->status) { + case 0: + /*success*/ + break; + case -ESHUTDOWN: + case -ENOENT: + case -ECONNRESET: + case -EPROTO: + /* unplug */ + return; + case -EPIPE: + dev_err(&udev->dev, "%s: stall on int endpoint\n", __func__); + /* TBD : halt to be cleared in work */ + case -EOVERFLOW: + default: + pr_debug_ratelimited("%s: non zero urb status = %d\n", + __func__, urb->status); + goto resubmit_int_urb; + } + + ctrl = (struct usb_cdc_notification *)urb->transfer_buffer; + data = (unsigned char *)(ctrl + 1); + + switch (ctrl->bNotificationType) { + case USB_CDC_NOTIFY_RESPONSE_AVAILABLE: + dev->resp_avail++; + usb_fill_control_urb(dev->readurb, udev, + usb_rcvctrlpipe(udev, 0), + (unsigned char *)dev->in_ctlreq, + dev->readbuf, + DEFAULT_READ_URB_LENGTH, + resp_avail_cb, dev); + + status = usb_submit_urb(dev->readurb, GFP_ATOMIC); + if (status) { + dev_err(&udev->dev, + "%s: Error submitting Read URB %d\n", + __func__, status); + goto resubmit_int_urb; + } + return; + case USB_CDC_NOTIFY_NETWORK_CONNECTION: + dev_dbg(&udev->dev, "%s network\n", ctrl->wValue ? + "connected to" : "disconnected from"); + break; + case USB_CDC_NOTIFY_SERIAL_STATE: + dev->notify_ser_state++; + ctrl_bits = get_unaligned_le16(data); + dev_dbg(&udev->dev, "serial state: %d\n", ctrl_bits); + dev->cbits_tohost = ctrl_bits; + if (brdg && brdg->ops.send_cbits) + brdg->ops.send_cbits(brdg->ctx, ctrl_bits); + break; + default: + dev_err(&udev->dev, "%s: unknown notification %d received:" + "index %d len %d data0 %d data1 %d", + __func__, ctrl->bNotificationType, ctrl->wIndex, + ctrl->wLength, data[0], data[1]); + } + +resubmit_int_urb: + status = usb_submit_urb(urb, GFP_ATOMIC); + if (status) + dev_err(&udev->dev, "%s: Error re-submitting Int URB %d\n", + __func__, status); +} + +int ctrl_bridge_start_read(struct ctrl_bridge *dev) +{ + int retval = 0; + struct usb_device *udev; + + udev = interface_to_usbdev(dev->intf); + + retval = usb_autopm_get_interface_async(dev->intf); + if (retval < 0) { + dev_err(&udev->dev, "%s resumption fail\n", __func__); + goto done_nopm; + } + + retval = usb_submit_urb(dev->inturb, GFP_KERNEL); + if (retval < 0) + dev_err(&udev->dev, "%s intr submit %d\n", __func__, retval); + + usb_autopm_put_interface_async(dev->intf); +done_nopm: + return retval; +} + +static int ctrl_bridge_stop_read(struct ctrl_bridge *dev) +{ + if (dev->readurb) { + dev_dbg(&dev->udev->dev, "killing rcv urb\n"); + usb_unlink_urb(dev->readurb); + } + + if (dev->inturb) { + dev_dbg(&dev->udev->dev, "killing int urb\n"); + usb_unlink_urb(dev->inturb); + } + + return 0; +} + +int ctrl_bridge_open(struct bridge *brdg) +{ + struct ctrl_bridge *dev; + + if (!brdg) { + err("bridge is null\n"); + return -EINVAL; + } + + if (brdg->ch_id >= MAX_BRIDGE_DEVICES) + return -EINVAL; + + dev = __dev[brdg->ch_id]; + if (!dev) { + err("dev is null\n"); + return -ENODEV; + } + + dev->brdg = brdg; + dev->snd_encap_cmd = 0; + dev->get_encap_res = 0; + dev->resp_avail = 0; + dev->set_ctrl_line_sts = 0; + dev->notify_ser_state = 0; + + return ctrl_bridge_start_read(dev); +} +EXPORT_SYMBOL(ctrl_bridge_open); + +void ctrl_bridge_close(unsigned int id) +{ + struct ctrl_bridge *dev; + + if (id >= MAX_BRIDGE_DEVICES) + return; + + dev = __dev[id]; + if (!dev && !dev->brdg) + return; + + dev_dbg(&dev->udev->dev, "%s:\n", __func__); + + ctrl_bridge_set_cbits(dev->brdg->ch_id, 0); + usb_unlink_anchored_urbs(&dev->tx_submitted); + ctrl_bridge_stop_read(dev); + + dev->brdg = NULL; +} +EXPORT_SYMBOL(ctrl_bridge_close); + +static void ctrl_write_callback(struct urb *urb) +{ + + if (urb->status) { + pr_debug("Write status/size %d/%d\n", + urb->status, urb->actual_length); + } + + kfree(urb->transfer_buffer); + kfree(urb->setup_packet); + usb_free_urb(urb); +} + +int ctrl_bridge_write(unsigned int id, char *data, size_t size) +{ + int result; + struct urb *writeurb; + struct usb_ctrlrequest *out_ctlreq; + struct usb_device *udev; + struct ctrl_bridge *dev; + + if (id >= MAX_BRIDGE_DEVICES) { + result = -EINVAL; + goto free_data; + } + + dev = __dev[id]; + + if (!dev) { + result = -ENODEV; + goto free_data; + } + + udev = interface_to_usbdev(dev->intf); + + dev_dbg(&udev->dev, "%s:[id]:%u: write (%d bytes)\n", + __func__, id, size); + + writeurb = usb_alloc_urb(0, GFP_ATOMIC); + if (!writeurb) { + dev_err(&udev->dev, "%s: error allocating read urb\n", + __func__); + result = -ENOMEM; + goto free_data; + } + + out_ctlreq = kmalloc(sizeof(*out_ctlreq), GFP_ATOMIC); + if (!out_ctlreq) { + dev_err(&udev->dev, + "%s: error allocating setup packet buffer\n", + __func__); + result = -ENOMEM; + goto free_urb; + } + + /* CDC Send Encapsulated Request packet */ + out_ctlreq->bRequestType = (USB_DIR_OUT | USB_TYPE_CLASS | + USB_RECIP_INTERFACE); + if (!data && !size) { + out_ctlreq->bRequest = USB_CDC_REQ_SET_CONTROL_LINE_STATE; + out_ctlreq->wValue = dev->cbits_tomdm; + dev->set_ctrl_line_sts++; + } else { + out_ctlreq->bRequest = USB_CDC_SEND_ENCAPSULATED_COMMAND; + out_ctlreq->wValue = 0; + dev->snd_encap_cmd++; + } + out_ctlreq->wIndex = + dev->intf->cur_altsetting->desc.bInterfaceNumber; + out_ctlreq->wLength = cpu_to_le16(size); + + usb_fill_control_urb(writeurb, udev, + usb_sndctrlpipe(udev, 0), + (unsigned char *)out_ctlreq, + (void *)data, size, + ctrl_write_callback, NULL); + + result = usb_autopm_get_interface_async(dev->intf); + if (result < 0) { + dev_err(&udev->dev, "%s: unable to resume interface: %d\n", + __func__, result); + + /* + * Revisit: if (result == -EPERM) + * bridge_suspend(dev->intf, PMSG_SUSPEND); + */ + + goto free_ctrlreq; + } + + usb_anchor_urb(writeurb, &dev->tx_submitted); + result = usb_submit_urb(writeurb, GFP_ATOMIC); + if (result < 0) { + dev_err(&udev->dev, "%s: submit URB error %d\n", + __func__, result); + usb_autopm_put_interface_async(dev->intf); + goto unanchor_urb; + } + + return size; + +unanchor_urb: + usb_unanchor_urb(writeurb); +free_ctrlreq: + kfree(out_ctlreq); +free_urb: + usb_free_urb(writeurb); +free_data: + kfree(data); + + return result; +} +EXPORT_SYMBOL(ctrl_bridge_write); + +int ctrl_bridge_suspend(unsigned int id) +{ + struct ctrl_bridge *dev; + + if (id >= MAX_BRIDGE_DEVICES) + return -EINVAL; + + dev = __dev[id]; + if (!dev) + return -ENODEV; + + usb_kill_anchored_urbs(&dev->tx_submitted); + + return ctrl_bridge_stop_read(dev); +} + +int ctrl_bridge_resume(unsigned int id) +{ + struct ctrl_bridge *dev; + + if (id >= MAX_BRIDGE_DEVICES) + return -EINVAL; + + dev = __dev[id]; + if (!dev) + return -ENODEV; + + return ctrl_bridge_start_read(dev); +} + +#if defined(CONFIG_DEBUG_FS) +#define DEBUG_BUF_SIZE 1024 +static ssize_t ctrl_bridge_read_stats(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct ctrl_bridge *dev; + char *buf; + int ret; + int i; + int temp = 0; + + buf = kzalloc(sizeof(char) * DEBUG_BUF_SIZE, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + for (i = 0; i < ch_id; i++) { + dev = __dev[i]; + if (!dev) + continue; + + temp += scnprintf(buf + temp, DEBUG_BUF_SIZE - temp, + "\nName#%s dev %p\n" + "snd encap cmd cnt: %u\n" + "get encap res cnt: %u\n" + "res available cnt: %u\n" + "set ctrlline sts cnt: %u\n" + "notify ser state cnt: %u\n" + "cbits_tomdm: %d\n" + "cbits_tohost: %d\n", + dev->pdev->name, dev, + dev->snd_encap_cmd, + dev->get_encap_res, + dev->resp_avail, + dev->set_ctrl_line_sts, + dev->notify_ser_state, + dev->cbits_tomdm, + dev->cbits_tohost); + + } + + ret = simple_read_from_buffer(ubuf, count, ppos, buf, temp); + + kfree(buf); + + return ret; +} + +static ssize_t ctrl_bridge_reset_stats(struct file *file, + const char __user *buf, size_t count, loff_t *ppos) +{ + struct ctrl_bridge *dev; + int i; + + for (i = 0; i < ch_id; i++) { + dev = __dev[i]; + if (!dev) + continue; + + dev->snd_encap_cmd = 0; + dev->get_encap_res = 0; + dev->resp_avail = 0; + dev->set_ctrl_line_sts = 0; + dev->notify_ser_state = 0; + } + return count; +} + +const struct file_operations ctrl_stats_ops = { + .read = ctrl_bridge_read_stats, + .write = ctrl_bridge_reset_stats, +}; + +struct dentry *ctrl_dent; +struct dentry *ctrl_dfile; +static void ctrl_bridge_debugfs_init(void) +{ + ctrl_dent = debugfs_create_dir("ctrl_hsic_bridge", 0); + if (IS_ERR(ctrl_dent)) + return; + + ctrl_dfile = + debugfs_create_file("status", 0644, ctrl_dent, 0, + &ctrl_stats_ops); + if (!ctrl_dfile || IS_ERR(ctrl_dfile)) + debugfs_remove(ctrl_dent); +} + +static void ctrl_bridge_debugfs_exit(void) +{ + debugfs_remove(ctrl_dfile); + debugfs_remove(ctrl_dent); +} + +#else +static void ctrl_bridge_debugfs_init(void) { } +static void ctrl_bridge_debugfs_exit(void) { } +#endif + +int +ctrl_bridge_probe(struct usb_interface *ifc, struct usb_host_endpoint *int_in, + int id) +{ + struct ctrl_bridge *dev; + struct usb_device *udev; + struct usb_endpoint_descriptor *ep; + u16 wMaxPacketSize; + int retval = 0; + int interval; + + udev = interface_to_usbdev(ifc); + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) { + dev_err(&udev->dev, "%s: unable to allocate dev\n", + __func__); + return -ENOMEM; + } + dev->pdev = platform_device_alloc(ctrl_bridge_names[id], id); + if (!dev->pdev) { + dev_err(&dev->udev->dev, + "%s: unable to allocate platform device\n", __func__); + retval = -ENOMEM; + goto nomem; + } + + dev->udev = udev; + dev->int_pipe = usb_rcvintpipe(udev, + int_in->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK); + dev->intf = ifc; + + init_usb_anchor(&dev->tx_submitted); + + /*use max pkt size from ep desc*/ + ep = &dev->intf->cur_altsetting->endpoint[0].desc; + + dev->inturb = usb_alloc_urb(0, GFP_KERNEL); + if (!dev->inturb) { + dev_err(&udev->dev, "%s: error allocating int urb\n", __func__); + retval = -ENOMEM; + goto pdev_del; + } + + wMaxPacketSize = le16_to_cpu(ep->wMaxPacketSize); + + dev->intbuf = kmalloc(wMaxPacketSize, GFP_KERNEL); + if (!dev->intbuf) { + dev_err(&udev->dev, "%s: error allocating int buffer\n", + __func__); + retval = -ENOMEM; + goto free_inturb; + } + + interval = + (udev->speed == USB_SPEED_HIGH) ? HS_INTERVAL : FS_LS_INTERVAL; + + usb_fill_int_urb(dev->inturb, udev, dev->int_pipe, + dev->intbuf, wMaxPacketSize, + notification_available_cb, dev, interval); + + dev->readurb = usb_alloc_urb(0, GFP_KERNEL); + if (!dev->readurb) { + dev_err(&udev->dev, "%s: error allocating read urb\n", + __func__); + retval = -ENOMEM; + goto free_intbuf; + } + + dev->readbuf = kmalloc(DEFAULT_READ_URB_LENGTH, GFP_KERNEL); + if (!dev->readbuf) { + dev_err(&udev->dev, "%s: error allocating read buffer\n", + __func__); + retval = -ENOMEM; + goto free_rurb; + } + + dev->in_ctlreq = kmalloc(sizeof(*dev->in_ctlreq), GFP_KERNEL); + if (!dev->in_ctlreq) { + dev_err(&udev->dev, + "%s:error allocating setup packet buffer\n", + __func__); + retval = -ENOMEM; + goto free_rbuf; + } + + dev->in_ctlreq->bRequestType = + (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE); + dev->in_ctlreq->bRequest = USB_CDC_GET_ENCAPSULATED_RESPONSE; + dev->in_ctlreq->wValue = 0; + dev->in_ctlreq->wIndex = + dev->intf->cur_altsetting->desc.bInterfaceNumber; + dev->in_ctlreq->wLength = cpu_to_le16(DEFAULT_READ_URB_LENGTH); + + __dev[id] = dev; + + platform_device_add(dev->pdev); + + ch_id++; + + return retval; + +free_rbuf: + kfree(dev->readbuf); +free_rurb: + usb_free_urb(dev->readurb); +free_intbuf: + kfree(dev->intbuf); +free_inturb: + usb_free_urb(dev->inturb); +pdev_del: + platform_device_del(dev->pdev); +nomem: + kfree(dev); + + return retval; +} + +void ctrl_bridge_disconnect(unsigned int id) +{ + struct ctrl_bridge *dev = __dev[id]; + + dev_dbg(&dev->udev->dev, "%s:\n", __func__); + + kfree(dev->in_ctlreq); + kfree(dev->readbuf); + kfree(dev->intbuf); + + usb_free_urb(dev->readurb); + usb_free_urb(dev->inturb); + + platform_device_del(dev->pdev); + __dev[id] = NULL; + ch_id--; + + kfree(dev); +} + +static int __init ctrl_bridge_init(void) +{ + ctrl_bridge_debugfs_init(); + + return 0; +} +module_init(ctrl_bridge_init); + +static void __exit ctrl_bridge_exit(void) +{ + ctrl_bridge_debugfs_exit(); +} +module_exit(ctrl_bridge_exit); + +MODULE_DESCRIPTION("Qualcomm modem control bridge driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/usb/misc/mdm_data_bridge.c b/drivers/usb/misc/mdm_data_bridge.c new file mode 100644 index 00000000000..c41fcfb0b67 --- /dev/null +++ b/drivers/usb/misc/mdm_data_bridge.c @@ -0,0 +1,923 @@ +/* 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 + +#define MAX_RX_URBS 50 +#define RMNET_RX_BUFSIZE 2048 + +#define STOP_SUBMIT_URB_LIMIT 400 +#define FLOW_CTRL_EN_THRESHOLD 500 +#define FLOW_CTRL_DISABLE 300 +#define FLOW_CTRL_SUPPORT 1 + +static const char *data_bridge_names[] = { + "dun_data_hsic0", + "rmnet_data_hsic0" +}; + +static struct workqueue_struct *bridge_wq; + +static unsigned int fctrl_support = FLOW_CTRL_SUPPORT; +module_param(fctrl_support, uint, S_IRUGO | S_IWUSR); + +static unsigned int fctrl_en_thld = FLOW_CTRL_EN_THRESHOLD; +module_param(fctrl_en_thld, uint, S_IRUGO | S_IWUSR); + +static unsigned int fctrl_dis_thld = FLOW_CTRL_DISABLE; +module_param(fctrl_dis_thld, uint, S_IRUGO | S_IWUSR); + +unsigned int max_rx_urbs = MAX_RX_URBS; +module_param(max_rx_urbs, uint, S_IRUGO | S_IWUSR); + +unsigned int stop_submit_urb_limit = STOP_SUBMIT_URB_LIMIT; +module_param(stop_submit_urb_limit, uint, S_IRUGO | S_IWUSR); + +#define TX_HALT BIT(0) +#define RX_HALT BIT(1) +#define SUSPENDED BIT(2) + +struct data_bridge { + struct usb_interface *intf; + struct usb_device *udev; + unsigned int bulk_in; + unsigned int bulk_out; + + /* keep track of in-flight URBs */ + struct usb_anchor tx_active; + struct usb_anchor rx_active; + + /* keep track of outgoing URBs during suspend */ + struct usb_anchor delayed; + + struct list_head rx_idle; + struct sk_buff_head rx_done; + + struct workqueue_struct *wq; + struct work_struct process_rx_w; + + struct bridge *brdg; + + /* work queue function for handling halt conditions */ + struct work_struct kevent; + + unsigned long flags; + + struct platform_device *pdev; + + /* counters */ + atomic_t pending_txurbs; + unsigned int txurb_drp_cnt; + unsigned long to_host; + unsigned long to_modem; + unsigned int tx_throttled_cnt; + unsigned int tx_unthrottled_cnt; + unsigned int rx_throttled_cnt; + unsigned int rx_unthrottled_cnt; +}; + +static struct data_bridge *__dev[MAX_BRIDGE_DEVICES]; + +/* counter used for indexing data bridge devices */ +static int ch_id; + +static int submit_rx_urb(struct data_bridge *dev, struct urb *urb, + gfp_t flags); + +static inline bool rx_halted(struct data_bridge *dev) +{ + return test_bit(RX_HALT, &dev->flags); +} + +static inline bool rx_throttled(struct bridge *brdg) +{ + return test_bit(RX_THROTTLED, &brdg->flags); +} + +int data_bridge_unthrottle_rx(unsigned int id) +{ + struct data_bridge *dev; + + if (id >= MAX_BRIDGE_DEVICES) + return -EINVAL; + + dev = __dev[id]; + if (!dev && !dev->brdg) + return -ENODEV; + + dev->rx_unthrottled_cnt++; + queue_work(dev->wq, &dev->process_rx_w); + + return 0; +} +EXPORT_SYMBOL(data_bridge_unthrottle_rx); + +static void data_bridge_process_rx(struct work_struct *work) +{ + int retval; + unsigned long flags; + struct urb *rx_idle; + struct sk_buff *skb; + struct data_bridge *dev = + container_of(work, struct data_bridge, process_rx_w); + + struct bridge *brdg = dev->brdg; + + if (!brdg || !brdg->ops.send_pkt || rx_halted(dev)) + return; + + while (!rx_throttled(brdg) && (skb = skb_dequeue(&dev->rx_done))) { + dev->to_host++; + /* hand off sk_buff to client,they'll need to free it */ + retval = brdg->ops.send_pkt(brdg->ctx, skb, skb->len); + if (retval == -ENOTCONN || retval == -EINVAL) { + return; + } else if (retval == -EBUSY) { + dev->rx_throttled_cnt++; + break; + } + } + + spin_lock_irqsave(&dev->rx_done.lock, flags); + if (dev->rx_done.qlen > stop_submit_urb_limit && rx_throttled(brdg)) { + spin_unlock_irqrestore(&dev->rx_done.lock, flags); + return; + } + + while (!list_empty(&dev->rx_idle)) { + + rx_idle = list_first_entry(&dev->rx_idle, struct urb, urb_list); + list_del(&rx_idle->urb_list); + spin_unlock_irqrestore(&dev->rx_done.lock, flags); + retval = submit_rx_urb(dev, rx_idle, GFP_KERNEL); + spin_lock_irqsave(&dev->rx_done.lock, flags); + if (retval) + break; + } + spin_unlock_irqrestore(&dev->rx_done.lock, flags); +} + +static void data_bridge_read_cb(struct urb *urb) +{ + struct bridge *brdg; + struct sk_buff *skb = urb->context; + struct data_bridge *dev = *(struct data_bridge **)skb->cb; + bool queue = 0; + + brdg = dev->brdg; + + skb_put(skb, urb->actual_length); + + switch (urb->status) { + case 0: /* success */ + queue = 1; + spin_lock(&dev->rx_done.lock); + __skb_queue_tail(&dev->rx_done, skb); + spin_unlock(&dev->rx_done.lock); + break; + + /*do not resubmit*/ + case -EPIPE: + set_bit(RX_HALT, &dev->flags); + dev_err(&dev->udev->dev, "%s: epout halted\n", __func__); + schedule_work(&dev->kevent); + /* FALLTHROUGH */ + case -ESHUTDOWN: + case -ENOENT: /* suspended */ + case -ECONNRESET: /* unplug */ + case -EPROTO: + dev_kfree_skb_any(skb); + break; + + /*resubmit */ + case -EOVERFLOW: /*babble error*/ + default: + queue = 1; + dev_kfree_skb_any(skb); + pr_debug_ratelimited("%s: non zero urb status = %d\n", + __func__, urb->status); + break; + } + + spin_lock(&dev->rx_done.lock); + list_add_tail(&urb->urb_list, &dev->rx_idle); + spin_unlock(&dev->rx_done.lock); + + if (queue) + queue_work(dev->wq, &dev->process_rx_w); +} + +static int submit_rx_urb(struct data_bridge *dev, struct urb *rx_urb, + gfp_t flags) +{ + struct sk_buff *skb; + int retval = -EINVAL; + + skb = alloc_skb(RMNET_RX_BUFSIZE, flags); + if (!skb) { + usb_free_urb(rx_urb); + return -ENOMEM; + } + + *((struct data_bridge **)skb->cb) = dev; + + usb_fill_bulk_urb(rx_urb, dev->udev, dev->bulk_in, + skb->data, RMNET_RX_BUFSIZE, + data_bridge_read_cb, skb); + + if (test_bit(SUSPENDED, &dev->flags)) + goto suspended; + + usb_anchor_urb(rx_urb, &dev->rx_active); + retval = usb_submit_urb(rx_urb, flags); + if (retval) + goto fail; + + return 0; +fail: + usb_unanchor_urb(rx_urb); +suspended: + dev_kfree_skb_any(skb); + usb_free_urb(rx_urb); + return retval; +} + +static int data_bridge_prepare_rx(struct data_bridge *dev) +{ + int i; + struct urb *rx_urb; + + for (i = 0; i < max_rx_urbs; i++) { + rx_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!rx_urb) + return -ENOMEM; + + list_add_tail(&rx_urb->urb_list, &dev->rx_idle); + } + return 0; +} + +int data_bridge_open(struct bridge *brdg) +{ + struct data_bridge *dev; + + if (!brdg) { + err("bridge is null\n"); + return -EINVAL; + } + + if (brdg->ch_id >= MAX_BRIDGE_DEVICES) + return -EINVAL; + + dev = __dev[brdg->ch_id]; + if (!dev) { + err("dev is null\n"); + return -ENODEV; + } + + dev_dbg(&dev->udev->dev, "%s: dev:%p\n", __func__, dev); + + dev->brdg = brdg; + atomic_set(&dev->pending_txurbs, 0); + dev->to_host = 0; + dev->to_modem = 0; + dev->txurb_drp_cnt = 0; + dev->tx_throttled_cnt = 0; + dev->tx_unthrottled_cnt = 0; + dev->rx_throttled_cnt = 0; + dev->rx_unthrottled_cnt = 0; + + queue_work(dev->wq, &dev->process_rx_w); + + return 0; +} +EXPORT_SYMBOL(data_bridge_open); + +void data_bridge_close(unsigned int id) +{ + struct data_bridge *dev; + struct sk_buff *skb; + unsigned long flags; + + if (id >= MAX_BRIDGE_DEVICES) + return; + + dev = __dev[id]; + if (!dev && !dev->brdg) + return; + + dev_dbg(&dev->udev->dev, "%s:\n", __func__); + + usb_unlink_anchored_urbs(&dev->tx_active); + usb_unlink_anchored_urbs(&dev->rx_active); + usb_unlink_anchored_urbs(&dev->delayed); + + spin_lock_irqsave(&dev->rx_done.lock, flags); + while ((skb = __skb_dequeue(&dev->rx_done))) + dev_kfree_skb_any(skb); + spin_unlock_irqrestore(&dev->rx_done.lock, flags); + + dev->brdg = NULL; +} +EXPORT_SYMBOL(data_bridge_close); + +static void defer_kevent(struct work_struct *work) +{ + int status; + struct data_bridge *dev = + container_of(work, struct data_bridge, kevent); + + if (!dev) + return; + + if (test_bit(TX_HALT, &dev->flags)) { + usb_unlink_anchored_urbs(&dev->tx_active); + + status = usb_autopm_get_interface(dev->intf); + if (status < 0) { + dev_err(&dev->udev->dev, + "can't acquire interface, status %d\n", status); + return; + } + + status = usb_clear_halt(dev->udev, dev->bulk_out); + usb_autopm_put_interface(dev->intf); + if (status < 0 && status != -EPIPE && status != -ESHUTDOWN) + dev_err(&dev->udev->dev, + "can't clear tx halt, status %d\n", status); + else + clear_bit(TX_HALT, &dev->flags); + } + + if (test_bit(RX_HALT, &dev->flags)) { + usb_unlink_anchored_urbs(&dev->rx_active); + + status = usb_autopm_get_interface(dev->intf); + if (status < 0) { + dev_err(&dev->udev->dev, + "can't acquire interface, status %d\n", status); + return; + } + + status = usb_clear_halt(dev->udev, dev->bulk_in); + usb_autopm_put_interface(dev->intf); + if (status < 0 && status != -EPIPE && status != -ESHUTDOWN) + dev_err(&dev->udev->dev, + "can't clear rx halt, status %d\n", status); + else { + clear_bit(RX_HALT, &dev->flags); + if (dev->brdg) + queue_work(dev->wq, &dev->process_rx_w); + } + } +} + +static void data_bridge_write_cb(struct urb *urb) +{ + struct sk_buff *skb = urb->context; + struct data_bridge *dev = *(struct data_bridge **)skb->cb; + struct bridge *brdg = dev->brdg; + int pending; + + pr_debug("%s: dev:%p\n", __func__, dev); + + switch (urb->status) { + case 0: /*success*/ + break; + case -EPIPE: + set_bit(TX_HALT, &dev->flags); + dev_err(&dev->udev->dev, "%s: epout halted\n", __func__); + schedule_work(&dev->kevent); + /* FALLTHROUGH */ + case -ESHUTDOWN: + case -ENOENT: /* suspended */ + case -ECONNRESET: /* unplug */ + case -EOVERFLOW: /*babble error*/ + /* FALLTHROUGH */ + default: + pr_debug_ratelimited("%s: non zero urb status = %d\n", + __func__, urb->status); + } + + usb_free_urb(urb); + dev_kfree_skb_any(skb); + + pending = atomic_dec_return(&dev->pending_txurbs); + + /*flow ctrl*/ + if (brdg && fctrl_support && pending <= fctrl_dis_thld && + test_and_clear_bit(TX_THROTTLED, &brdg->flags)) { + pr_debug_ratelimited("%s: disable flow ctrl: pend urbs:%u\n", + __func__, pending); + dev->tx_unthrottled_cnt++; + if (brdg->ops.unthrottle_tx) + brdg->ops.unthrottle_tx(brdg->ctx); + } + + usb_autopm_put_interface_async(dev->intf); +} + +int data_bridge_write(unsigned int id, struct sk_buff *skb) +{ + int result; + int size = skb->len; + int pending; + struct urb *txurb; + struct data_bridge *dev = __dev[id]; + struct bridge *brdg; + + if (!dev || !dev->brdg || !usb_get_intfdata(dev->intf)) + return -ENODEV; + + brdg = dev->brdg; + + dev_dbg(&dev->udev->dev, "%s: write (%d bytes)\n", __func__, skb->len); + + result = usb_autopm_get_interface(dev->intf); + if (result < 0) { + dev_err(&dev->udev->dev, "%s: resume failure\n", __func__); + goto error; + } + + txurb = usb_alloc_urb(0, GFP_KERNEL); + if (!txurb) { + dev_err(&dev->udev->dev, "%s: error allocating read urb\n", + __func__); + result = -ENOMEM; + goto error; + } + + /* store dev pointer in skb */ + *((struct data_bridge **)skb->cb) = dev; + + usb_fill_bulk_urb(txurb, dev->udev, dev->bulk_out, + skb->data, skb->len, data_bridge_write_cb, skb); + + if (test_bit(SUSPENDED, &dev->flags)) { + usb_anchor_urb(txurb, &dev->delayed); + goto free_urb; + } + + pending = atomic_inc_return(&dev->pending_txurbs); + usb_anchor_urb(txurb, &dev->tx_active); + + result = usb_submit_urb(txurb, GFP_KERNEL); + if (result < 0) { + usb_unanchor_urb(txurb); + atomic_dec(&dev->pending_txurbs); + dev_err(&dev->udev->dev, "%s: submit URB error %d\n", + __func__, result); + goto free_urb; + } + + dev->to_modem++; + dev_dbg(&dev->udev->dev, "%s: pending_txurbs: %u\n", __func__, pending); + + /* flow control: last urb submitted but return -EBUSY */ + if (fctrl_support && pending > fctrl_en_thld) { + set_bit(TX_THROTTLED, &brdg->flags); + dev->tx_throttled_cnt++; + pr_debug_ratelimited("%s: enable flow ctrl pend txurbs:%u\n", + __func__, pending); + return -EBUSY; + } + + return size; + +free_urb: + usb_free_urb(txurb); +error: + dev->txurb_drp_cnt++; + usb_autopm_put_interface(dev->intf); + + return result; +} +EXPORT_SYMBOL(data_bridge_write); + +static int data_bridge_resume(struct data_bridge *dev) +{ + struct urb *urb; + int retval; + + while ((urb = usb_get_from_anchor(&dev->delayed))) { + usb_anchor_urb(urb, &dev->tx_active); + atomic_inc(&dev->pending_txurbs); + retval = usb_submit_urb(urb, GFP_ATOMIC); + if (retval < 0) { + atomic_dec(&dev->pending_txurbs); + usb_unanchor_urb(urb); + + /* TODO: need to free urb data */ + usb_scuttle_anchored_urbs(&dev->delayed); + break; + } + dev->to_modem++; + dev->txurb_drp_cnt--; + } + + clear_bit(SUSPENDED, &dev->flags); + + if (dev->brdg) + queue_work(dev->wq, &dev->process_rx_w); + + return 0; +} + +static int bridge_resume(struct usb_interface *iface) +{ + int retval = 0; + int oldstate; + struct data_bridge *dev = usb_get_intfdata(iface); + struct bridge *brdg = dev->brdg; + + oldstate = iface->dev.power.power_state.event; + iface->dev.power.power_state.event = PM_EVENT_ON; + + retval = data_bridge_resume(dev); + if (!retval) { + if (oldstate & PM_EVENT_SUSPEND && brdg) + retval = ctrl_bridge_resume(brdg->ch_id); + } + return retval; +} + +static int data_bridge_suspend(struct data_bridge *dev, pm_message_t message) +{ + if (atomic_read(&dev->pending_txurbs) && + (message.event & PM_EVENT_AUTO)) + return -EBUSY; + + set_bit(SUSPENDED, &dev->flags); + + usb_kill_anchored_urbs(&dev->tx_active); + usb_kill_anchored_urbs(&dev->rx_active); + + return 0; +} + +static int bridge_suspend(struct usb_interface *intf, pm_message_t message) +{ + int retval; + struct data_bridge *dev = usb_get_intfdata(intf); + struct bridge *brdg = dev->brdg; + + retval = data_bridge_suspend(dev, message); + if (!retval) { + if (message.event & PM_EVENT_SUSPEND) { + if (brdg) + retval = ctrl_bridge_suspend(brdg->ch_id); + intf->dev.power.power_state.event = message.event; + } + } else { + dev_dbg(&dev->udev->dev, "%s: device is busy,cannot suspend\n", + __func__); + } + return retval; +} + +static int data_bridge_probe(struct usb_interface *iface, + struct usb_host_endpoint *bulk_in, + struct usb_host_endpoint *bulk_out, int id) +{ + struct data_bridge *dev; + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) { + err("%s: unable to allocate dev\n", __func__); + return -ENOMEM; + } + + dev->pdev = platform_device_alloc(data_bridge_names[id], id); + if (!dev->pdev) { + err("%s: unable to allocate platform device\n", __func__); + kfree(dev); + return -ENOMEM; + } + + init_usb_anchor(&dev->tx_active); + init_usb_anchor(&dev->rx_active); + init_usb_anchor(&dev->delayed); + + INIT_LIST_HEAD(&dev->rx_idle); + skb_queue_head_init(&dev->rx_done); + + dev->wq = bridge_wq; + + dev->udev = interface_to_usbdev(iface); + dev->intf = iface; + + dev->bulk_in = usb_rcvbulkpipe(dev->udev, + bulk_in->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK); + + dev->bulk_out = usb_sndbulkpipe(dev->udev, + bulk_out->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK); + + usb_set_intfdata(iface, dev); + + INIT_WORK(&dev->kevent, defer_kevent); + INIT_WORK(&dev->process_rx_w, data_bridge_process_rx); + + __dev[id] = dev; + + /*allocate list of rx urbs*/ + data_bridge_prepare_rx(dev); + + platform_device_add(dev->pdev); + + return 0; +} + +#if defined(CONFIG_DEBUG_FS) +#define DEBUG_BUF_SIZE 1024 +static ssize_t data_bridge_read_stats(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct data_bridge *dev; + char *buf; + int ret; + int i; + int temp = 0; + + buf = kzalloc(sizeof(char) * DEBUG_BUF_SIZE, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + for (i = 0; i < ch_id; i++) { + dev = __dev[i]; + if (!dev) + continue; + + temp += scnprintf(buf + temp, DEBUG_BUF_SIZE - temp, + "\nName#%s dev %p\n" + "pending tx urbs: %u\n" + "tx urb drp cnt: %u\n" + "to host: %lu\n" + "to mdm: %lu\n" + "tx throttled cnt: %u\n" + "tx unthrottled cnt: %u\n" + "rx throttled cnt: %u\n" + "rx unthrottled cnt: %u\n" + "rx done skb qlen: %u\n" + "suspended: %d\n" + "TX_HALT: %d\n" + "RX_HALT: %d\n", + dev->pdev->name, dev, + atomic_read(&dev->pending_txurbs), + dev->txurb_drp_cnt, + dev->to_host, + dev->to_modem, + dev->tx_throttled_cnt, + dev->tx_unthrottled_cnt, + dev->rx_throttled_cnt, + dev->rx_unthrottled_cnt, + dev->rx_done.qlen, + test_bit(SUSPENDED, &dev->flags), + test_bit(TX_HALT, &dev->flags), + test_bit(RX_HALT, &dev->flags)); + + } + + ret = simple_read_from_buffer(ubuf, count, ppos, buf, temp); + + kfree(buf); + + return ret; +} + +static ssize_t data_bridge_reset_stats(struct file *file, + const char __user *buf, size_t count, loff_t *ppos) +{ + struct data_bridge *dev; + int i; + + for (i = 0; i < ch_id; i++) { + dev = __dev[i]; + if (!dev) + continue; + + dev->to_host = 0; + dev->to_modem = 0; + dev->txurb_drp_cnt = 0; + dev->tx_throttled_cnt = 0; + dev->tx_unthrottled_cnt = 0; + dev->rx_throttled_cnt = 0; + dev->rx_unthrottled_cnt = 0; + } + return count; +} + +const struct file_operations data_stats_ops = { + .read = data_bridge_read_stats, + .write = data_bridge_reset_stats, +}; + +struct dentry *data_dent; +struct dentry *data_dfile; +static void data_bridge_debugfs_init(void) +{ + data_dent = debugfs_create_dir("data_hsic_bridge", 0); + if (IS_ERR(data_dent)) + return; + + data_dfile = debugfs_create_file("status", 0644, data_dent, 0, + &data_stats_ops); + if (!data_dfile || IS_ERR(data_dfile)) + debugfs_remove(data_dent); +} + +static void data_bridge_debugfs_exit(void) +{ + debugfs_remove(data_dfile); + debugfs_remove(data_dent); +} + +#else +static void data_bridge_debugfs_init(void) { } +static void data_bridge_debugfs_exit(void) { } +#endif + +static int __devinit +bridge_probe(struct usb_interface *iface, const struct usb_device_id *id) +{ + struct usb_host_endpoint *endpoint = NULL; + struct usb_host_endpoint *bulk_in = NULL; + struct usb_host_endpoint *bulk_out = NULL; + struct usb_host_endpoint *int_in = NULL; + struct usb_device *udev; + int i; + int status = 0; + int numends; + int iface_num; + + iface_num = iface->cur_altsetting->desc.bInterfaceNumber; + + if (iface->num_altsetting != 1) { + err("%s invalid num_altsetting %u\n", + __func__, iface->num_altsetting); + return -EINVAL; + } + + udev = interface_to_usbdev(iface); + usb_get_dev(udev); + + if (iface_num != DUN_IFACE_NUM && iface_num != TETHERED_RMNET_IFACE_NUM) + return 0; + + numends = iface->cur_altsetting->desc.bNumEndpoints; + for (i = 0; i < numends; i++) { + endpoint = iface->cur_altsetting->endpoint + i; + if (!endpoint) { + dev_err(&udev->dev, "%s: invalid endpoint %u\n", + __func__, i); + status = -EINVAL; + goto out; + } + + if (usb_endpoint_is_bulk_in(&endpoint->desc)) + bulk_in = endpoint; + else if (usb_endpoint_is_bulk_out(&endpoint->desc)) + bulk_out = endpoint; + else if (usb_endpoint_is_int_in(&endpoint->desc)) + int_in = endpoint; + } + + if (!bulk_in || !bulk_out || !int_in) { + dev_err(&udev->dev, "%s: invalid endpoints\n", __func__); + status = -EINVAL; + goto out; + } + + status = data_bridge_probe(iface, bulk_in, bulk_out, ch_id); + if (status < 0) { + dev_err(&udev->dev, "data_bridge_probe failed %d\n", status); + goto out; + } + + status = ctrl_bridge_probe(iface, int_in, ch_id); + if (status < 0) { + dev_err(&udev->dev, "ctrl_bridge_probe failed %d\n", status); + goto free_data_bridge; + } + ch_id++; + + return 0; + +free_data_bridge: + platform_device_del(__dev[ch_id]->pdev); + usb_set_intfdata(iface, NULL); + kfree(__dev[ch_id]); + __dev[ch_id] = NULL; +out: + usb_put_dev(udev); + + return status; +} + +static void bridge_disconnect(struct usb_interface *intf) +{ + struct data_bridge *dev = usb_get_intfdata(intf); + struct list_head *head; + struct urb *rx_urb; + unsigned long flags; + int iface_num; + + if (!dev) { + err("%s: data device not found\n", __func__); + return; + } + + iface_num = intf->cur_altsetting->desc.bInterfaceNumber; + if (iface_num != DUN_IFACE_NUM && iface_num != TETHERED_RMNET_IFACE_NUM) + return; + + ch_id--; + ctrl_bridge_disconnect(ch_id); + platform_device_del(dev->pdev); + usb_set_intfdata(intf, NULL); + __dev[ch_id] = NULL; + + cancel_work_sync(&dev->process_rx_w); + cancel_work_sync(&dev->kevent); + + /*free rx urbs*/ + head = &dev->rx_idle; + spin_lock_irqsave(&dev->rx_done.lock, flags); + while (!list_empty(head)) { + rx_urb = list_entry(head->next, struct urb, urb_list); + list_del(&rx_urb->urb_list); + usb_free_urb(rx_urb); + } + spin_unlock_irqrestore(&dev->rx_done.lock, flags); + + usb_put_dev(dev->udev); + kfree(dev); +} + +static const struct usb_device_id bridge_ids[] = { + { USB_DEVICE(0x5c6, 0x9001) }, +}; + +MODULE_DEVICE_TABLE(usb, bridge_ids); + +static struct usb_driver bridge_driver = { + .name = "mdm_bridge", + .probe = bridge_probe, + .disconnect = bridge_disconnect, + .id_table = bridge_ids, + .suspend = bridge_suspend, + .resume = bridge_resume, + .supports_autosuspend = 1, +}; + +static int __init bridge_init(void) +{ + int ret; + + ret = usb_register(&bridge_driver); + if (ret) { + err("%s: unable to register mdm_bridge driver", __func__); + return ret; + } + + bridge_wq = create_singlethread_workqueue("mdm_bridge"); + if (!bridge_wq) { + usb_deregister(&bridge_driver); + pr_err("%s: Unable to create workqueue:bridge\n", __func__); + return -ENOMEM; + } + + data_bridge_debugfs_init(); + + return 0; +} + +static void __exit bridge_exit(void) +{ + data_bridge_debugfs_exit(); + destroy_workqueue(bridge_wq); + usb_deregister(&bridge_driver); +} + +module_init(bridge_init); +module_exit(bridge_exit); + +MODULE_DESCRIPTION("Qualcomm modem data bridge driver"); +MODULE_LICENSE("GPL v2"); From cfaebd1bfc050a192d7feb64903f34c7d1de9738 Mon Sep 17 00:00:00 2001 From: Shane Passmore Date: Mon, 26 Nov 2012 19:20:27 -0600 Subject: [PATCH 013/111] USB: OTG: Take wakelock when VBUS present Enabled by default, can disable with: echo N > /sys/module/otg_wakelock/parameters/enabled Change-Id: I34974624c52ae23490852b44c270d2f326cf6116 Signed-off-by: Todd Poynor usb: otg: Temporarily grab wakelock on charger and disconnect events Change-Id: If995d4af4adcb08e8369009483f2956ad9627267 Signed-off-by: Todd Poynor add for OTG support by faux123 --- drivers/usb/otg/otg-wakelock.c | 170 +++++++++++++++++++++++++++++++++ 1 file changed, 170 insertions(+) create mode 100644 drivers/usb/otg/otg-wakelock.c diff --git a/drivers/usb/otg/otg-wakelock.c b/drivers/usb/otg/otg-wakelock.c new file mode 100644 index 00000000000..f4429b06e14 --- /dev/null +++ b/drivers/usb/otg/otg-wakelock.c @@ -0,0 +1,170 @@ +/* + * otg-wakelock.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 + +#define TEMPORARY_HOLD_TIME 2000 + +static bool enabled = true; +static struct otg_transceiver *otgwl_xceiv; +static struct notifier_block otgwl_nb; + +/* + * otgwl_spinlock is held while the VBUS lock is grabbed or dropped and the + * held field is updated to match. + */ + +static DEFINE_SPINLOCK(otgwl_spinlock); + +/* + * Only one lock, but since these 3 fields are associated with each other... + */ + +struct otgwl_lock { + char name[40]; + struct wake_lock wakelock; + bool held; +}; + +/* + * VBUS present lock. Also used as a timed lock on charger + * connect/disconnect and USB host disconnect, to allow the system + * to react to the change in power. + */ + +static struct otgwl_lock vbus_lock; + +static void otgwl_hold(struct otgwl_lock *lock) +{ + if (!lock->held) { + wake_lock(&lock->wakelock); + lock->held = true; + } +} + +static void otgwl_temporary_hold(struct otgwl_lock *lock) +{ + wake_lock_timeout(&lock->wakelock, + msecs_to_jiffies(TEMPORARY_HOLD_TIME)); + lock->held = false; +} + +static void otgwl_drop(struct otgwl_lock *lock) +{ + if (lock->held) { + wake_unlock(&lock->wakelock); + lock->held = false; + } +} + +static void otgwl_handle_event(unsigned long event) +{ + unsigned long irqflags; + + spin_lock_irqsave(&otgwl_spinlock, irqflags); + + if (!enabled) { + otgwl_drop(&vbus_lock); + spin_unlock_irqrestore(&otgwl_spinlock, irqflags); + return; + } + + switch (event) { + case USB_EVENT_VBUS: + case USB_EVENT_ENUMERATED: + otgwl_hold(&vbus_lock); + break; + + case USB_EVENT_NONE: + case USB_EVENT_ID: + case USB_EVENT_CHARGER: + otgwl_temporary_hold(&vbus_lock); + break; + + default: + break; + } + + spin_unlock_irqrestore(&otgwl_spinlock, irqflags); +} + +static int otgwl_otg_notifications(struct notifier_block *nb, + unsigned long event, void *unused) +{ + otgwl_handle_event(event); + return NOTIFY_OK; +} + +static int set_enabled(const char *val, const struct kernel_param *kp) +{ + int rv = param_set_bool(val, kp); + + if (rv) + return rv; + + if (otgwl_xceiv) + otgwl_handle_event(otgwl_xceiv->last_event); + + return 0; +} + +static struct kernel_param_ops enabled_param_ops = { + .set = set_enabled, + .get = param_get_bool, +}; + +module_param_cb(enabled, &enabled_param_ops, &enabled, 0644); +MODULE_PARM_DESC(enabled, "enable wakelock when VBUS present"); + +static int __init otg_wakelock_init(void) +{ + int ret; + + otgwl_xceiv = otg_get_transceiver(); + + if (!otgwl_xceiv) { + pr_err("%s: No OTG transceiver found\n", __func__); + return -ENODEV; + } + + snprintf(vbus_lock.name, sizeof(vbus_lock.name), "vbus-%s", + dev_name(otgwl_xceiv->dev)); + wake_lock_init(&vbus_lock.wakelock, WAKE_LOCK_SUSPEND, + vbus_lock.name); + + otgwl_nb.notifier_call = otgwl_otg_notifications; + ret = otg_register_notifier(otgwl_xceiv, &otgwl_nb); + + if (ret) { + pr_err("%s: otg_register_notifier on transceiver %s" + " failed\n", __func__, + dev_name(otgwl_xceiv->dev)); + otgwl_xceiv = NULL; + wake_lock_destroy(&vbus_lock.wakelock); + return ret; + } + + otgwl_handle_event(otgwl_xceiv->last_event); + return ret; +} + +late_initcall(otg_wakelock_init); + From 3b3b746f41ec48925c95497de2073e38f3ff966c Mon Sep 17 00:00:00 2001 From: David Hays Date: Sat, 25 May 2013 10:15:32 -0500 Subject: [PATCH 014/111] vigor: update defconfig Change-Id: Ie693c3feee0f8e19fc406c12e1e91af6a52ec731 --- arch/arm/configs/vigor_aosp_defconfig | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/arch/arm/configs/vigor_aosp_defconfig b/arch/arm/configs/vigor_aosp_defconfig index 410a6ad202d..bb85202f838 100644 --- a/arch/arm/configs/vigor_aosp_defconfig +++ b/arch/arm/configs/vigor_aosp_defconfig @@ -44,7 +44,7 @@ CONFIG_KERNEL_GZIP=y # CONFIG_KERNEL_LZMA is not set # CONFIG_KERNEL_LZO is not set CONFIG_DEFAULT_HOSTNAME="(none)" -# 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 @@ -1021,8 +1021,8 @@ 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_BNEP_MC_FILTER=y +CONFIG_BT_BNEP_PROTO_FILTER=y CONFIG_BT_HIDP=y # @@ -2138,7 +2138,7 @@ CONFIG_USB_ANNOUNCE_NEW_DEVICES=y CONFIG_USB_DEVICE_CLASS=y # CONFIG_USB_DYNAMIC_MINORS is not set CONFIG_USB_SUSPEND=y -# CONFIG_USB_OTG is not set +CONFIG_USB_OTG=y # CONFIG_USB_OTG_WHITELIST is not set # CONFIG_USB_OTG_BLACKLIST_HUB is not set # CONFIG_USB_MON is not set @@ -2284,7 +2284,7 @@ CONFIG_USB_GADGET_VERIZON_PRODUCT_ID=y # OTG and related infrastructure # CONFIG_USB_OTG_UTILS=y -# CONFIG_USB_OTG_WAKELOCK is not set +CONFIG_USB_OTG_WAKELOCK=y # CONFIG_USB_GPIO_VBUS is not set # CONFIG_USB_ULPI is not set # CONFIG_USB_MSM_OTG_72K is not set From 569a7aaf50e56ac86f70919e228cb96527761bc4 Mon Sep 17 00:00:00 2001 From: Ethan Chen Date: Thu, 22 Nov 2012 02:07:00 -0800 Subject: [PATCH 015/111] USB: gadget: use CAF android gadget at M8260AAABQNLZA30170 Conflicts: drivers/char/diag/diagchar_core.c drivers/char/diag/diagfwd.c drivers/usb/gadget/Kconfig drivers/usb/gadget/android.c drivers/usb/gadget/ci13xxx_udc.c drivers/usb/gadget/composite.c drivers/usb/gadget/f_adb.c drivers/usb/gadget/f_mass_storage.c drivers/usb/gadget/f_rmnet_sdio.c drivers/usb/gadget/f_rmnet_smd_sdio.c drivers/usb/gadget/f_serial.c drivers/usb/gadget/u_ether.c Change-Id: I67b3945aa74ba09fd8a99962c718de5713b44f5d --- arch/arm/mach-msm/include/mach/usb_bam.h | 40 + .../mach-msm/include/mach/usb_gadget_xport.h | 89 + drivers/char/diag/Kconfig | 19 +- drivers/char/diag/Makefile | 1 + drivers/char/diag/diagchar.h | 94 +- drivers/char/diag/diagchar_core.c | 744 +---- drivers/char/diag/diagfwd.c | 1017 ++++-- drivers/char/diag/diagfwd.h | 25 +- drivers/char/diag/diagfwd_cntl.c | 152 +- drivers/char/diag/diagfwd_cntl.h | 55 +- drivers/char/diag/diagfwd_hsic.c | 530 ++++ drivers/char/diag/diagfwd_hsic.h | 23 + drivers/char/diag/diagfwd_sdio.c | 210 +- drivers/char/diag/diagfwd_sdio.h | 2 +- drivers/usb/gadget/Kconfig | 252 +- drivers/usb/gadget/android.c | 1247 ++------ drivers/usb/gadget/ci13xxx_msm.c | 34 +- drivers/usb/gadget/ci13xxx_udc.c | 186 +- drivers/usb/gadget/ci13xxx_udc.h | 15 + drivers/usb/gadget/composite.c | 80 - drivers/usb/gadget/f_accessory.c | 4 +- drivers/usb/gadget/f_acm.c | 191 +- drivers/usb/gadget/f_adb.c | 69 +- drivers/usb/gadget/f_ccid.c | 1014 ++++++ drivers/usb/gadget/f_ccid.h | 83 + drivers/usb/gadget/f_diag.c | 271 +- drivers/usb/gadget/f_diag.h | 24 + drivers/usb/gadget/f_mass_storage.c | 216 +- drivers/usb/gadget/f_mtp.c | 30 +- drivers/usb/gadget/f_rmnet.c | 1057 +++++++ drivers/usb/gadget/f_rmnet.h | 19 + drivers/usb/gadget/f_rmnet_sdio.c | 1535 +++++++++ drivers/usb/gadget/f_rmnet_smd.c | 1368 ++++++++ drivers/usb/gadget/f_rmnet_smd_sdio.c | 87 +- drivers/usb/gadget/f_rndis.c | 50 +- drivers/usb/gadget/f_serial.c | 242 +- drivers/usb/gadget/msm72k_udc.c | 2793 +++++++++++++++++ drivers/usb/gadget/printer.c | 2 +- drivers/usb/gadget/qcom_maemo.c | 304 ++ drivers/usb/gadget/rndis.c | 19 +- drivers/usb/gadget/storage_common.c | 7 +- drivers/usb/gadget/u_bam.c | 488 ++- drivers/usb/gadget/u_ctrl_hsic.c | 617 ++++ drivers/usb/gadget/u_data_hsic.c | 962 ++++++ drivers/usb/gadget/u_ether.c | 179 +- drivers/usb/gadget/u_ether.h | 3 + drivers/usb/gadget/u_rmnet.h | 19 +- drivers/usb/gadget/u_rmnet_ctrl_smd.c | 114 +- drivers/usb/gadget/u_sdio.c | 23 +- drivers/usb/gadget/u_serial.c | 35 +- drivers/usb/gadget/u_serial.h | 2 +- drivers/usb/gadget/u_smd.c | 80 +- include/linux/diagchar.h | 142 +- include/linux/usb/android.h | 24 + include/linux/usb/ccid_desc.h | 112 + include/linux/usb/composite.h | 8 - include/linux/usb/gadget.h | 4 +- 57 files changed, 13265 insertions(+), 3747 deletions(-) create mode 100644 arch/arm/mach-msm/include/mach/usb_bam.h create mode 100644 arch/arm/mach-msm/include/mach/usb_gadget_xport.h create mode 100644 drivers/char/diag/diagfwd_hsic.c create mode 100644 drivers/char/diag/diagfwd_hsic.h create mode 100644 drivers/usb/gadget/f_ccid.c create mode 100644 drivers/usb/gadget/f_ccid.h create mode 100644 drivers/usb/gadget/f_diag.h create mode 100644 drivers/usb/gadget/f_rmnet.c create mode 100644 drivers/usb/gadget/f_rmnet.h create mode 100644 drivers/usb/gadget/f_rmnet_sdio.c create mode 100644 drivers/usb/gadget/f_rmnet_smd.c create mode 100644 drivers/usb/gadget/msm72k_udc.c create mode 100644 drivers/usb/gadget/qcom_maemo.c create mode 100644 drivers/usb/gadget/u_ctrl_hsic.c create mode 100644 drivers/usb/gadget/u_data_hsic.c create mode 100644 include/linux/usb/android.h create mode 100644 include/linux/usb/ccid_desc.h diff --git a/arch/arm/mach-msm/include/mach/usb_bam.h b/arch/arm/mach-msm/include/mach/usb_bam.h new file mode 100644 index 00000000000..4caa71bb3de --- /dev/null +++ b/arch/arm/mach-msm/include/mach/usb_bam.h @@ -0,0 +1,40 @@ +/* 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 _USB_BAM_H_ +#define _USB_BAM_H_ + +/** + * Connect USB-to-Periperal SPS connection. + * + * This function returns the allocated pipes number. + * + * @idx - Connection index. + * + * @src_pipe_idx - allocated pipe index - USB as a + * source (output) + * + * @dst_pipe_idx - allocated pipe index - USB as a + * destination (output) + * + * @return 0 on success, negative value on error + * + */ +#ifdef CONFIG_USB_BAM +int usb_bam_connect(u8 idx, u8 *src_pipe_idx, u8 *dst_pipe_idx); +#else +int usb_bam_connect(u8 idx, u8 *src_pipe_idx, u8 *dst_pipe_idx) +{ + return -ENODEV; +} +#endif +#endif /* _USB_BAM_H_ */ diff --git a/arch/arm/mach-msm/include/mach/usb_gadget_xport.h b/arch/arm/mach-msm/include/mach/usb_gadget_xport.h new file mode 100644 index 00000000000..d8a3e60f63d --- /dev/null +++ b/arch/arm/mach-msm/include/mach/usb_gadget_xport.h @@ -0,0 +1,89 @@ +/* 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_USB_GADGET_XPORT_H__ +#define __LINUX_USB_GADGET_XPORT_H__ + +enum transport_type { + USB_GADGET_XPORT_UNDEF, + USB_GADGET_XPORT_TTY, + USB_GADGET_XPORT_SDIO, + USB_GADGET_XPORT_SMD, + USB_GADGET_XPORT_BAM, + USB_GADGET_XPORT_BAM2BAM, + USB_GADGET_XPORT_HSIC, + USB_GADGET_XPORT_NONE, +}; + +#define XPORT_STR_LEN 10 + +static char *xport_to_str(enum transport_type t) +{ + switch (t) { + case USB_GADGET_XPORT_TTY: + return "TTY"; + case USB_GADGET_XPORT_SDIO: + return "SDIO"; + case USB_GADGET_XPORT_SMD: + return "SMD"; + case USB_GADGET_XPORT_BAM: + return "BAM"; + case USB_GADGET_XPORT_BAM2BAM: + return "BAM2BAM"; + case USB_GADGET_XPORT_HSIC: + return "HSIC"; + case USB_GADGET_XPORT_NONE: + return "NONE"; + default: + return "UNDEFINED"; + } +} + +static enum transport_type str_to_xport(const char *name) +{ + if (!strncasecmp("TTY", name, XPORT_STR_LEN)) + return USB_GADGET_XPORT_TTY; + if (!strncasecmp("SDIO", name, XPORT_STR_LEN)) + return USB_GADGET_XPORT_SDIO; + if (!strncasecmp("SMD", name, XPORT_STR_LEN)) + return USB_GADGET_XPORT_SMD; + if (!strncasecmp("BAM", name, XPORT_STR_LEN)) + return USB_GADGET_XPORT_BAM; + if (!strncasecmp("BAM2BAM", name, XPORT_STR_LEN)) + return USB_GADGET_XPORT_BAM2BAM; + if (!strncasecmp("HSIC", name, XPORT_STR_LEN)) + return USB_GADGET_XPORT_HSIC; + if (!strncasecmp("", name, XPORT_STR_LEN)) + return USB_GADGET_XPORT_NONE; + + return USB_GADGET_XPORT_UNDEF; +} + +enum gadget_type { + USB_GADGET_SERIAL, + USB_GADGET_RMNET, +}; + +#define NUM_RMNET_HSIC_PORTS 1 +#define NUM_DUN_HSIC_PORTS 1 +#define NUM_PORTS (NUM_RMNET_HSIC_PORTS \ + + NUM_DUN_HSIC_PORTS) + +int ghsic_ctrl_connect(void *, int); +void ghsic_ctrl_disconnect(void *, int); +int ghsic_ctrl_setup(unsigned int, enum gadget_type); +int ghsic_data_connect(void *, int); +void ghsic_data_disconnect(void *, int); +int ghsic_data_setup(unsigned int, enum gadget_type); + +#endif diff --git a/drivers/char/diag/Kconfig b/drivers/char/diag/Kconfig index bb8ee2e3a82..53df29b7611 100644 --- a/drivers/char/diag/Kconfig +++ b/drivers/char/diag/Kconfig @@ -18,15 +18,6 @@ config DIAG_OVER_USB default y help This feature helps segregate code required for DIAG traffic to go over USB. - -config MODEM_DIAG_MASTER - bool "Set if Modem is to be the master on DIAG" - depends on ARCH_MSM - default n - help - Diag master: Android just forwards the Diag packet to modem. Modem will remove HDLC by itself. - Diag slave: Android kernel should remove HDLC from Diag packet before send to modem. - In the latest modem codebase, it was n by default. endmenu menu "SDIO support for DIAG" @@ -38,3 +29,13 @@ config DIAG_SDIO_PIPE help SDIO Transport Layer for DIAG Router endmenu + +menu "HSIC support for DIAG" + +config DIAG_HSIC_PIPE + depends on USB_QCOM_DIAG_BRIDGE + default y + bool "Enable 9K DIAG traffic over HSIC" + help + HSIC Transport Layer for DIAG Router +endmenu diff --git a/drivers/char/diag/Makefile b/drivers/char/diag/Makefile index 52ab2b94e1d..c62b7fdd30a 100644 --- a/drivers/char/diag/Makefile +++ b/drivers/char/diag/Makefile @@ -1,3 +1,4 @@ obj-$(CONFIG_DIAG_CHAR) := diagchar.o obj-$(CONFIG_DIAG_SDIO_PIPE) += diagfwd_sdio.o +obj-$(CONFIG_DIAG_HSIC_PIPE) += diagfwd_hsic.o diagchar-objs := diagchar_core.o diagchar_hdlc.o diagfwd.o diagmem.o diagfwd_cntl.o diff --git a/drivers/char/diag/diagchar.h b/drivers/char/diag/diagchar.h index f96c724bf3d..7025ea8ebca 100644 --- a/drivers/char/diag/diagchar.h +++ b/drivers/char/diag/diagchar.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 @@ -17,14 +17,13 @@ #include #include #include -#include #include #include #include -#include #include /* Size of the USB buffers used for read and write*/ #define USB_MAX_OUT_BUF 4096 +#define APPS_BUF_SIZE 2000 #define IN_BUF_SIZE 16384 #define MAX_IN_BUF_SIZE 32768 #define MAX_SYNC_OBJ_NAME_SIZE 32 @@ -41,20 +40,24 @@ #define APPS_DATA 3 #define SDIO_DATA 4 #define WCNSS_DATA 5 +#define HSIC_DATA 6 #define MODEM_PROC 0 #define APPS_PROC 1 #define QDSP_PROC 2 #define WCNSS_PROC 3 -#define MSG_MASK_SIZE 8000 +#define MSG_MASK_SIZE 9500 #define LOG_MASK_SIZE 8000 #define EVENT_MASK_SIZE 1000 #define USER_SPACE_DATA 8000 #define PKT_SIZE 4096 -#define MAX_EQUIP_ID 12 +#define MAX_EQUIP_ID 15 +#define DIAG_CTRL_MSG_LOG_MASK 9 +#define DIAG_CTRL_MSG_EVENT_MASK 10 +#define DIAG_CTRL_MSG_F3_MASK 11 /* Maximum number of pkt reg supported at initialization*/ -extern unsigned int diag_max_registration; -extern unsigned int diag_threshold_registration; +extern unsigned int diag_max_reg; +extern unsigned int diag_threshold_reg; #define APPEND_DEBUG(ch) \ do { \ @@ -130,11 +133,7 @@ struct diagchar_dev { int *data_ready; int num_clients; struct diag_write_device *buf_tbl; - spinlock_t diagchar_lock; -#ifdef CONFIG_DIAG_SDIO_PIPE - struct cdev *cdev_mdm; - int num_mdmclients; -#endif + /* Memory pool parameters */ unsigned int itemsize; unsigned int poolsize; @@ -152,7 +151,11 @@ struct diagchar_dev { int count_hdlc_pool; int count_write_struct_pool; int used; - + /* Buffers for masks */ + struct mutex diag_cntl_mutex; + struct diag_ctrl_event_mask *event_mask; + struct diag_ctrl_log_mask *log_mask; + struct diag_ctrl_msg_mask *msg_mask; /* State for diag forwarding */ unsigned char *buf_in_1; unsigned char *buf_in_2; @@ -162,13 +165,13 @@ struct diagchar_dev { unsigned char *buf_in_qdsp_cntl; unsigned char *buf_in_wcnss; unsigned char *buf_in_wcnss_cntl; - struct mutex diagcharmdm_mutex; - wait_queue_head_t mdmwait_q; - struct diag_client_map *mdmclient_map; - int *mdmdata_ready; unsigned char *usb_buf_out; unsigned char *apps_rsp_buf; unsigned char *user_space_data; + /* buffer for updating mask to peripherals */ + unsigned char *buf_msg_mask_update; + unsigned char *buf_log_mask_update; + unsigned char *buf_event_mask_update; smd_channel_t *ch; smd_channel_t *ch_cntl; smd_channel_t *chqdsp; @@ -191,7 +194,6 @@ struct diagchar_dev { struct work_struct diag_read_work; #endif struct workqueue_struct *diag_wq; - struct wake_lock wake_lock; struct work_struct diag_drain_work; struct work_struct diag_read_smd_work; struct work_struct diag_read_smd_cntl_work; @@ -199,6 +201,10 @@ struct diagchar_dev { struct work_struct diag_read_smd_qdsp_cntl_work; struct work_struct diag_read_smd_wcnss_work; struct work_struct diag_read_smd_wcnss_cntl_work; + struct workqueue_struct *diag_cntl_wq; + struct work_struct diag_modem_mask_update_work; + struct work_struct diag_qdsp_mask_update_work; + struct work_struct diag_wcnss_mask_update_work; uint8_t *msg_masks; uint8_t *log_masks; int log_masks_length; @@ -214,45 +220,43 @@ struct diagchar_dev { struct diag_request *write_ptr_qdsp_2; struct diag_request *write_ptr_wcnss; int logging_mode; + int mask_check; int logging_process_id; -#if DIAG_XPST - unsigned char nohdlc; - unsigned char in_busy_dmrounter; - struct mutex smd_lock; - unsigned char init_done; - unsigned char is2ARM11; -#endif #ifdef CONFIG_DIAG_SDIO_PIPE - unsigned char *buf_in_sdio_1; - unsigned char *buf_in_sdio_2; + unsigned char *buf_in_sdio; unsigned char *usb_buf_mdm_out; struct sdio_channel *sdio_ch; int read_len_mdm; - int in_busy_sdio_1; - int in_busy_sdio_2; + int in_busy_sdio; struct usb_diag_ch *mdm_ch; struct work_struct diag_read_mdm_work; struct workqueue_struct *diag_sdio_wq; struct work_struct diag_read_sdio_work; - struct work_struct diag_remove_sdio_work; + struct work_struct diag_close_sdio_work; struct diag_request *usb_read_mdm_ptr; - struct diag_request *write_ptr_mdm_1; - struct diag_request *write_ptr_mdm_2; + struct diag_request *write_ptr_mdm; +#endif +#ifdef CONFIG_DIAG_HSIC_PIPE + unsigned char *buf_in_hsic; + unsigned char *usb_buf_mdm_out; + int hsic_initialized; + int hsic_ch; + int hsic_device_enabled; + int hsic_device_opened; + int read_len_mdm; + int in_busy_hsic_read_on_mdm; + int in_busy_hsic_write_on_mdm; + int in_busy_hsic_write; + int in_busy_hsic_read; + int usb_mdm_connected; + struct usb_diag_ch *mdm_ch; + struct workqueue_struct *diag_hsic_wq; + struct work_struct diag_read_mdm_work; + struct work_struct diag_read_hsic_work; + struct diag_request *usb_read_mdm_ptr; + struct diag_request *write_ptr_mdm; #endif - u64 diag_smd_count; /* from smd */ - u64 diag_qdsp_count; /* from qdsp */ - void (*enable_sd_log)(unsigned int enable); - int qxdm2sd_drop; }; -#define EPST_FUN 1 -#define HPST_FUN 0 - -#if defined(CONFIG_ARCH_MSM8X60) || defined(CONFIG_ARCH_MSM8960) || defined(CONFIG_ARCH_MSM7X27A) -#define SMDDIAG_NAME "DIAG" -#else -#define SMDDIAG_NAME "SMD_DIAG" -#endif extern struct diagchar_dev *driver; -extern int is_wcnss_used; #endif diff --git a/drivers/char/diag/diagchar_core.c b/drivers/char/diag/diagchar_core.c index 0126f28dc08..bbed3299cba 100644 --- a/drivers/char/diag/diagchar_core.c +++ b/drivers/char/diag/diagchar_core.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 @@ -30,15 +30,11 @@ #include "diagfwd_cntl.h" #ifdef CONFIG_DIAG_SDIO_PIPE #include "diagfwd_sdio.h" -static unsigned char *buf_9k; #endif -#include - -#ifdef CONFIG_USB_ANDROID_MDM9K_DIAG -int diag_support_mdm9k = 1; -#else -int diag_support_mdm9k = 0; +#ifdef CONFIG_DIAG_HSIC_PIPE +#include "diagfwd_hsic.h" #endif +#include MODULE_DESCRIPTION("Diag Char Driver"); MODULE_LICENSE("GPL v2"); @@ -52,7 +48,7 @@ struct diagchar_priv { }; /* The following variables can be specified by module options */ /* for copy buffer */ -static unsigned int itemsize = 2048; /*Size of item in the mempool */ +static unsigned int itemsize = 4096; /*Size of item in the mempool */ static unsigned int poolsize = 10; /*Number of items in the mempool */ /* for hdlc buffer */ static unsigned int itemsize_hdlc = 8192; /*Size of item in the mempool */ @@ -64,8 +60,8 @@ static unsigned int poolsize_write_struct = 8; /* Num of items in the mempool */ static unsigned int max_clients = 15; static unsigned int threshold_client_limit = 30; /* This is the maximum number of pkt registrations supported at initialization*/ -unsigned int diag_max_registration = 500; -unsigned int diag_threshold_registration = 650; +unsigned int diag_max_reg = 600; +unsigned int diag_threshold_reg = 750; /* Timer variables */ static struct timer_list drain_timer; @@ -75,96 +71,6 @@ module_param(itemsize, uint, 0); module_param(poolsize, uint, 0); module_param(max_clients, uint, 0); -static unsigned s, entries_once = 50; -static ssize_t show_diag_registration(struct device *dev, - struct device_attribute *attr, char *buf) -{ - uint16_t i, p = 0, e; - e = s + entries_once; - e = (e > diag_max_registration)?diag_max_registration:e; - - p += sprintf(buf+p, "Registration(%d) #%d -> #%d\n", - diag_max_registration, s, e - 1); - - for (i = s; i < e ; i++) { - p += sprintf(buf+p, "#%03d cmd_code: 0x%02x, subsys_id: 0x%02x ", i, - driver->table[i].cmd_code, driver->table[i].subsys_id); - if (driver->table[i].client_id == APPS_PROC) - p += sprintf(buf+p, "APPS_PROC(%d)\n", - driver->table[i].process_id); - else if (driver->table[i].client_id == MODEM_PROC) - p += sprintf(buf+p, "MODEM_PROC\n"); - else if (driver->table[i].client_id == QDSP_PROC) - p += sprintf(buf+p, "QDSP_PROC\n"); - else if (driver->table[i].client_id == WCNSS_PROC) - p += sprintf(buf+p, "WCNSS_PROC\n"); - else - p += sprintf(buf+p, "UNKNOWN SOURCE\n"); - } - - return p; - -} - -static ssize_t store_registration_index(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) -{ - int ret; - unsigned long u; - ret = strict_strtoul(buf, 10, (unsigned long *)&u); - if (ret < 0) - return ret; - if (u > diag_max_registration) - return 0; - s = u; - return count; -} - -unsigned diag7k_debug_mask = DIAGLOG_MODE_NONE; -unsigned diag9k_debug_mask = DIAGLOG_MODE_NONE; -static ssize_t show_diag_debug_mask(struct device *dev, - struct device_attribute *attr, char *buf) -{ - uint16_t p = 0; - - p += sprintf(buf+p, "diag7k_debug_mask: %d\n" - "diag9k_debug_mask: %d\n", - diag7k_debug_mask, diag9k_debug_mask); - - return p; -} - -static ssize_t store_diag7k_debug_mask(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) -{ - int ret; - unsigned long u; - ret = strict_strtoul(buf, 10, (unsigned long *)&u); - if (ret < 0) - return ret; - diag7k_debug_mask = u; - return count; -} - -static ssize_t store_diag9k_debug_mask(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) -{ - int ret; - unsigned long u; - ret = strict_strtoul(buf, 10, (unsigned long *)&u); - if (ret < 0) - return ret; - diag9k_debug_mask = u; - return count; -} - -static DEVICE_ATTR(diag_reg_table, 0664, - show_diag_registration, store_registration_index); -static DEVICE_ATTR(diag7k_debug_mask, 0664, - show_diag_debug_mask, store_diag7k_debug_mask); -static DEVICE_ATTR(diag9k_debug_mask, 0664, - show_diag_debug_mask, store_diag9k_debug_mask); - /* delayed_rsp_id 0 represents no delay in the response. Any other number means that the diag packet has a delayed response. */ static uint16_t delayed_rsp_id = 1; @@ -177,9 +83,8 @@ static uint16_t delayed_rsp_id = 1; #define COPY_USER_SPACE_OR_EXIT(buf, data, length) \ do { \ - if (count < ret+length) \ - goto exit; \ - if (copy_to_user(buf, (void *)&data, length)) { \ + if ((count < ret+length) || (copy_to_user(buf, \ + (void *)&data, length))) { \ ret = -EFAULT; \ goto exit; \ } \ @@ -248,8 +153,6 @@ static int diagchar_open(struct inode *inode, struct file *file) { int i = 0; void *temp; - DIAG_INFO("%s:%s(parent:%s): tgid=%d\n", __func__, - current->comm, current->parent->comm, current->tgid); if (driver) { mutex_lock(&driver->diagchar_mutex); @@ -281,15 +184,16 @@ static int diagchar_open(struct inode *inode, struct file *file) } else { mutex_unlock(&driver->diagchar_mutex); pr_alert("Max client limit for DIAG reached\n"); - DIAG_INFO("Cannot open handle %s" + pr_info("Cannot open handle %s" " %d", current->comm, current->tgid); for (i = 0; i < driver->num_clients; i++) - DIAG_WARNING("%d) %s PID=%d", i, driver-> + pr_debug("%d) %s PID=%d", i, driver-> client_map[i].name, driver->client_map[i].pid); return -ENOMEM; } } + driver->data_ready[i] = 0x0; driver->data_ready[i] |= MSG_MASKS_TYPE; driver->data_ready[i] |= EVENT_MASKS_TYPE; driver->data_ready[i] |= LOG_MASKS_TYPE; @@ -319,19 +223,19 @@ static int diagchar_close(struct inode *inode, struct file *file) return -ENOMEM; } - if (driver) { #ifdef CONFIG_DIAG_OVER_USB - /* If the SD logging process exits, change logging to USB mode */ - if (driver->logging_process_id == current->tgid) { - driver->logging_mode = USB_MODE; - diagfwd_connect(); - } + /* If the SD logging process exits, change logging to USB mode */ + if (driver->logging_process_id == current->tgid) { + driver->logging_mode = USB_MODE; + diagfwd_connect(); + } #endif /* DIAG over USB */ - /* Delete the pkt response table entry for the exiting process */ - for (i = 0; i < diag_max_registration; i++) - if (driver->table[i].process_id == current->tgid) - driver->table[i].process_id = 0; + /* Delete the pkt response table entry for the exiting process */ + for (i = 0; i < diag_max_reg; i++) + if (driver->table[i].process_id == current->tgid) + driver->table[i].process_id = 0; + if (driver) { mutex_lock(&driver->diagchar_mutex); driver->ref_count--; /* On Client exit, try to destroy all 3 pools */ @@ -357,7 +261,7 @@ void diag_clear_reg(int proc_num) { int i; - for (i = 0; i < diag_max_registration; i++) { + for (i = 0; i < diag_max_reg; i++) { if (driver->table[i].client_id == proc_num) { driver->table[i].process_id = 0; } @@ -389,14 +293,11 @@ long diagchar_ioctl(struct file *filp, int success = -1; void *temp_buf; - DIAG_INFO("%s:%s(parent:%s): tgid=%d\n", __func__, - current->comm, current->parent->comm, current->tgid); - if (iocmd == DIAG_IOCTL_COMMAND_REG) { struct bindpkt_params_per_process *pkt_params = (struct bindpkt_params_per_process *) ioarg; mutex_lock(&driver->diagchar_mutex); - for (i = 0; i < diag_max_registration; i++) { + for (i = 0; i < diag_max_reg; i++) { if (driver->table[i].process_id == 0) { diag_add_reg(i, pkt_params->params, &success, &count_entries); @@ -408,19 +309,20 @@ long diagchar_ioctl(struct file *filp, } } } - if (i < diag_threshold_registration) { + if (i < diag_threshold_reg) { /* Increase table size by amount required */ - diag_max_registration += pkt_params->count - + diag_max_reg += pkt_params->count - count_entries; /* Make sure size doesnt go beyond threshold */ - if (diag_max_registration > diag_threshold_registration) - diag_max_registration = - diag_threshold_registration; + if (diag_max_reg > diag_threshold_reg) { + diag_max_reg = diag_threshold_reg; + pr_info("diag: best case memory allocation\n"); + } temp_buf = krealloc(driver->table, - diag_max_registration*sizeof(struct + diag_max_reg*sizeof(struct diag_master_table), GFP_KERNEL); if (!temp_buf) { - diag_max_registration -= pkt_params->count - + diag_max_reg -= pkt_params->count - count_entries; pr_alert("diag: Insufficient memory for reg."); mutex_unlock(&driver->diagchar_mutex); @@ -428,7 +330,7 @@ long diagchar_ioctl(struct file *filp, } else { driver->table = temp_buf; } - for (j = i; j < diag_max_registration; j++) { + for (j = i; j < diag_max_reg; j++) { diag_add_reg(j, pkt_params->params, &success, &count_entries); if (pkt_params->count > count_entries) { @@ -438,6 +340,7 @@ long diagchar_ioctl(struct file *filp, return success; } } + mutex_unlock(&driver->diagchar_mutex); } else { mutex_unlock(&driver->diagchar_mutex); pr_err("Max size reached, Pkt Registration failed for" @@ -469,8 +372,12 @@ long diagchar_ioctl(struct file *filp, mutex_lock(&driver->diagchar_mutex); temp = driver->logging_mode; driver->logging_mode = (int)ioarg; - if (driver->logging_mode == UART_MODE) + if (driver->logging_mode == MEMORY_DEVICE_MODE) + driver->mask_check = 1; + if (driver->logging_mode == UART_MODE) { + driver->mask_check = 0; driver->logging_mode = MEMORY_DEVICE_MODE; + } driver->logging_process_id = current->tgid; mutex_unlock(&driver->diagchar_mutex); if (temp == MEMORY_DEVICE_MODE && driver->logging_mode @@ -480,6 +387,9 @@ long diagchar_ioctl(struct file *filp, driver->in_busy_qdsp_1 = 1; driver->in_busy_qdsp_2 = 1; driver->in_busy_wcnss = 1; +#ifdef CONFIG_DIAG_SDIO_PIPE + driver->in_busy_sdio = 1; +#endif } else if (temp == NO_LOGGING_MODE && driver->logging_mode == MEMORY_DEVICE_MODE) { driver->in_busy_1 = 0; @@ -497,6 +407,13 @@ long diagchar_ioctl(struct file *filp, if (driver->ch_wcnss) queue_work(driver->diag_wq, &(driver->diag_read_smd_wcnss_work)); +#ifdef CONFIG_DIAG_SDIO_PIPE + driver->in_busy_sdio = 0; + /* Poll SDIO channel to check for data */ + if (driver->sdio_ch) + queue_work(driver->diag_sdio_wq, + &(driver->diag_read_sdio_work)); +#endif } #ifdef CONFIG_DIAG_OVER_USB else if (temp == USB_MODE && driver->logging_mode @@ -507,9 +424,7 @@ long diagchar_ioctl(struct file *filp, diagfwd_connect(); else if (temp == USB_MODE && driver->logging_mode == MEMORY_DEVICE_MODE) { - DIAG_INFO("diag: USB disconnected\n"); diagfwd_disconnect(); - DIAG_INFO("sdlogging enable\n"); driver->in_busy_1 = 0; driver->in_busy_2 = 0; driver->in_busy_qdsp_2 = 0; @@ -525,11 +440,16 @@ long diagchar_ioctl(struct file *filp, if (driver->ch_wcnss) queue_work(driver->diag_wq, &(driver->diag_read_smd_wcnss_work)); - } else if (temp == MEMORY_DEVICE_MODE && driver->logging_mode - == USB_MODE) { - DIAG_INFO("sdlogging disable\n"); - diagfwd_connect(); - } +#ifdef CONFIG_DIAG_SDIO_PIPE + driver->in_busy_sdio = 0; + /* Poll SDIO channel to check for data */ + if (driver->sdio_ch) + queue_work(driver->diag_sdio_wq, + &(driver->diag_read_sdio_work)); +#endif + } else if (temp == MEMORY_DEVICE_MODE && + driver->logging_mode == USB_MODE) + diagfwd_connect(); #endif /* DIAG over USB */ success = 1; } @@ -542,29 +462,17 @@ static int diagchar_read(struct file *file, char __user *buf, size_t count, { int index = -1, i = 0, ret = 0; int num_data = 0, data_type; - -#ifdef SDQXDM_DEBUG - struct timeval t; -#endif - for (i = 0; i < driver->num_clients; i++) if (driver->client_map[i].pid == current->tgid) index = i; if (index == -1) { - DIAG_ERR("%s:%s(parent:%s): tgid=%d " - "Client PID not found in table\n", __func__, - current->comm, current->parent->comm, current->tgid); - for (i = 0; i < driver->num_clients; i++) - DIAG_ERR("\t#%d: %d\n", i, driver->client_map[i].pid); + pr_err("diag: Client PID not found in table"); return -EINVAL; } wait_event_interruptible(driver->wait_q, driver->data_ready[index]); - if (diag7k_debug_mask) - DIAG_INFO("%s:%s(parent:%s): tgid=%d\n", __func__, - current->comm, current->parent->comm, current->tgid); mutex_lock(&driver->diagchar_mutex); if ((driver->data_ready[index] & USER_SPACE_LOG_TYPE) && (driver-> @@ -672,6 +580,20 @@ static int diagchar_read(struct file *file, char __user *buf, size_t count, driver->write_ptr_wcnss->length); driver->in_busy_wcnss = 0; } +#ifdef CONFIG_DIAG_SDIO_PIPE + /* copy 9K data over SDIO */ + if (driver->in_busy_sdio == 1) { + num_data++; + /*Copy the length of data being passed*/ + COPY_USER_SPACE_OR_EXIT(buf+ret, + (driver->write_ptr_mdm->length), 4); + /*Copy the actual data being passed*/ + COPY_USER_SPACE_OR_EXIT(buf+ret, + *(driver->buf_in_sdio), + driver->write_ptr_mdm->length); + driver->in_busy_sdio = 0; + } +#endif /* copy number of data fields */ COPY_USER_SPACE_OR_EXIT(buf+4, num_data, 4); ret -= 4; @@ -685,90 +607,17 @@ static int diagchar_read(struct file *file, char __user *buf, size_t count, if (driver->ch_wcnss) queue_work(driver->diag_wq, &(driver->diag_read_smd_wcnss_work)); +#ifdef CONFIG_DIAG_SDIO_PIPE + if (driver->sdio_ch) + queue_work(driver->diag_sdio_wq, + &(driver->diag_read_sdio_work)); +#endif APPEND_DEBUG('n'); goto exit; } else if (driver->data_ready[index] & USER_SPACE_LOG_TYPE) { /* In case, the thread wakes up and the logging mode is not memory device any more, the condition needs to be cleared */ driver->data_ready[index] ^= USER_SPACE_LOG_TYPE; - } else if (driver->data_ready[index] & USERMODE_DIAGFWD) { - data_type = USERMODE_DIAGFWD; - driver->data_ready[index] ^= USERMODE_DIAGFWD; - COPY_USER_SPACE_OR_EXIT(buf, data_type, 4); - -#ifdef SDQXDM_DEBUG - do_gettimeofday(&t); - - if (driver->in_busy_1 && t.tv_sec > driver->write_ptr_1->second + 2) - pr_info("[diag-dbg] late pkt now: %ld.%04ld pkt: %d\n", - t.tv_sec, t.tv_usec/1000, driver->write_ptr_1->second); - if (driver->in_busy_2 && t.tv_sec > driver->write_ptr_2->second + 2) - pr_info("[diag-dbg] late pkt now: %ld.%04ld pkt: %d\n", - t.tv_sec, t.tv_usec/1000, driver->write_ptr_2->second); -#endif - for (i = 0; i < driver->poolsize_write_struct; i++) { - if (driver->buf_tbl[i].length > 0) { -#ifdef SDQXDM_DEBUG - if (diag7k_debug_mask) - printk(KERN_INFO "\n WRITING the buf address " - "and length is %x , %d\n", (unsigned int) - (driver->buf_tbl[i].buf), - driver->buf_tbl[i].length); -#endif - if (copy_to_user(buf+ret, (void *)driver-> - buf_tbl[i].buf, driver->buf_tbl[i].length)) - break; - - ret += driver->buf_tbl[i].length; - - diagmem_free(driver, (unsigned char *) - (driver->buf_tbl[i].buf), POOL_TYPE_HDLC); - driver->buf_tbl[i].length = 0; - driver->buf_tbl[i].buf = 0; - } - } - - /* copy modem data */ - if (driver->in_busy_1 == 1) { - /*Copy the actual data being passed*/ - COPY_USER_SPACE_OR_EXIT(buf+ret, - *(driver->buf_in_1), - driver->write_ptr_1->length); - driver->in_busy_1 = 0; - } - if (driver->in_busy_2 == 1) { - /*Copy the actual data being passed*/ - COPY_USER_SPACE_OR_EXIT(buf+ret, - *(driver->buf_in_2), - driver->write_ptr_2->length); - driver->in_busy_2 = 0; - } - - /* copy q6 data */ - if (driver->in_busy_qdsp_1 == 1) { - /*Copy the actual data being passed*/ - COPY_USER_SPACE_OR_EXIT(buf+ret, *(driver-> - buf_in_qdsp_1), - driver->write_ptr_qdsp_1->length); - driver->in_busy_qdsp_1 = 0; - } - if (driver->in_busy_qdsp_2 == 1) { - /*Copy the actual data being passed*/ - COPY_USER_SPACE_OR_EXIT(buf+ret, *(driver-> - buf_in_qdsp_2), driver-> - write_ptr_qdsp_2->length); - driver->in_busy_qdsp_2 = 0; - } - if (driver->ch) - queue_work(driver->diag_wq, - &(driver->diag_read_smd_work)); - if (driver->chqdsp) - queue_work(driver->diag_wq, - &(driver->diag_read_smd_qdsp_work)); - if (diag7k_debug_mask) - pr_info("%s() return %d byte\n", __func__, ret); - - goto exit; } if (driver->data_ready[index] & DEINIT_TYPE) { @@ -820,9 +669,6 @@ static int diagchar_read(struct file *file, char __user *buf, size_t count, } exit: - if (ret) - wake_lock_timeout(&driver->wake_lock, HZ / 2); - mutex_unlock(&driver->diagchar_mutex); return ret; } @@ -854,8 +700,8 @@ static int diagchar_write(struct file *file, const char __user *buf, err = copy_from_user(driver->user_space_data, buf + 4, payload_size); /* Check masks for On-Device logging */ - if (pkt_type == USER_SPACE_LOG_TYPE) { - if (!mask_request_validate((unsigned char *)buf)) { + if (driver->mask_check) { + if (!mask_request_validate(driver->user_space_data)) { pr_alert("diag: mask request Invalid\n"); return -EFAULT; } @@ -864,17 +710,32 @@ static int diagchar_write(struct file *file, const char __user *buf, #ifdef DIAG_DEBUG pr_debug("diag: user space data %d\n", payload_size); for (i = 0; i < payload_size; i++) - printk(KERN_DEBUG "\t %x", *(((unsigned char *)buf)+i)); + pr_debug("\t %x", *((driver->user_space_data)+i)); +#endif +#ifdef CONFIG_DIAG_SDIO_PIPE + /* send masks to 9k too */ + if (driver->sdio_ch) { + wait_event_interruptible(driver->wait_q, + (sdio_write_avail(driver->sdio_ch) >= + payload_size)); + if (driver->sdio_ch && (payload_size > 0)) { + sdio_write(driver->sdio_ch, (void *) + (driver->user_space_data), payload_size); + } + } #endif + /* send masks to modem now */ diag_process_hdlc((void *)(driver->user_space_data), payload_size); return 0; - } else if (pkt_type == USERMODE_DIAGFWD) { - if (diag7k_debug_mask) - pr_info("%s#%d recv %d bytes\n", __func__, __LINE__, payload_size); - buf += 4; - diag_process_hdlc((void *)buf, payload_size); - return count; + } + + if (payload_size > itemsize) { + pr_err("diag: Dropping packet, packet payload size crosses" + "4KB limit. Current payload size %d\n", + payload_size); + driver->dropped_count++; + return -EBADMSG; } buf_copy = diagmem_alloc(driver, payload_size, POOL_TYPE_COPY); @@ -927,8 +788,6 @@ static int diagchar_write(struct file *file, const char __user *buf, goto fail_free_hdlc; } buf_hdlc = NULL; - if (diag7k_debug_mask) - printk(KERN_INFO "\n size written is %d\n", driver->used); driver->used = 0; buf_hdlc = diagmem_alloc(driver, HDLC_OUT_BUF_SIZE, POOL_TYPE_HDLC); @@ -957,8 +816,6 @@ static int diagchar_write(struct file *file, const char __user *buf, goto fail_free_hdlc; } buf_hdlc = NULL; - if (diag7k_debug_mask) - printk(KERN_INFO "\n size written is %d\n", driver->used); driver->used = 0; buf_hdlc = diagmem_alloc(driver, HDLC_OUT_BUF_SIZE, POOL_TYPE_HDLC); @@ -984,8 +841,6 @@ static int diagchar_write(struct file *file, const char __user *buf, goto fail_free_hdlc; } buf_hdlc = NULL; - if (diag7k_debug_mask) - printk(KERN_INFO "\n size written is %d\n", driver->used); driver->used = 0; } @@ -1015,11 +870,11 @@ int mask_request_validate(unsigned char mask_buf[]) uint8_t subsys_id; uint16_t ss_cmd; - packet_id = mask_buf[4]; + packet_id = mask_buf[0]; if (packet_id == 0x4B) { - subsys_id = mask_buf[5]; - ss_cmd = *(uint16_t *)(mask_buf + 6); + subsys_id = mask_buf[1]; + ss_cmd = *(uint16_t *)(mask_buf + 2); /* Packets with SSID which are allowed */ switch (subsys_id) { case 0x04: /* DIAG_SUBSYS_WCDMA */ @@ -1083,315 +938,10 @@ static const struct file_operations diagcharfops = { .release = diagchar_close }; -#ifdef CONFIG_DIAG_SDIO_PIPE -static int diagcharmdm_open(struct inode *inode, struct file *file) -{ - int i = 0; - - DIAG_INFO("%s:%s(parent:%s): tgid=%d\n", __func__, - current->comm, current->parent->comm, current->tgid); - - if (driver) { - mutex_lock(&driver->diagcharmdm_mutex); - - for (i = 0; i < driver->num_mdmclients; i++) - if (driver->mdmclient_map[i].pid == 0) - break; - - if (i < driver->num_mdmclients) { - driver->mdmclient_map[i].pid = current->tgid; - strncpy(driver->mdmclient_map[i].name, current->comm, 20); - driver->mdmclient_map[i].name[19] = '\0'; - } else { - mutex_unlock(&driver->diagcharmdm_mutex); - DIAG_INFO("%s:reach max client count\n", __func__); - for (i = 0; i < driver->num_clients; i++) - DIAG_WARNING("%d) %s PID=%d", i, driver-> - mdmclient_map[i].name, - driver->mdmclient_map[i].pid); - return -ENOMEM; - } - - driver->mdmdata_ready[i] |= MSG_MASKS_TYPE; - driver->mdmdata_ready[i] |= EVENT_MASKS_TYPE; - driver->mdmdata_ready[i] |= LOG_MASKS_TYPE; - - if (driver->ref_count == 0) - diagmem_init(driver); - driver->ref_count++; - - mutex_unlock(&driver->diagcharmdm_mutex); - return 0; - } - - return -ENOMEM; - -} - -static int diagcharmdm_close(struct inode *inode, struct file *file) -{ - - int i = 0; - - DIAG_INFO("%s:%s(parent:%s): tgid=%d\n", __func__, - current->comm, current->parent->comm, current->tgid); - - if (driver) { - mutex_lock(&driver->diagcharmdm_mutex); - - driver->ref_count--; - /* On Client exit, try to destroy all 3 pools */ - diagmem_exit(driver, POOL_TYPE_COPY); - diagmem_exit(driver, POOL_TYPE_HDLC); - diagmem_exit(driver, POOL_TYPE_WRITE_STRUCT); - - for (i = 0; i < driver->num_mdmclients; i++) - if (driver->mdmclient_map[i].pid == current->tgid) { - driver->mdmclient_map[i].pid = 0; - break; - } - - if (i < driver->num_mdmclients) - DIAG_INFO("%s:#%d(%d) %s close\n", __func__, - i, current->tgid, current->comm); - else - DIAG_WARNING("%s: nothing close\n", __func__); - mutex_unlock(&driver->diagcharmdm_mutex); - return 0; - } - - return -ENOMEM; -} - -static long diagcharmdm_ioctl(struct file *filp, - unsigned int iocmd, unsigned long ioarg) -{ - int success = -1; - - if (iocmd == DIAG_IOCTL_SWITCH_LOGGING) { - mutex_lock(&driver->diagcharmdm_mutex); - driver->logging_mode = (int)ioarg; - driver->logging_process_id = current->tgid; - mutex_unlock(&driver->diagcharmdm_mutex); - if (driver->logging_mode == MEMORY_DEVICE_MODE) { - DIAG_INFO("diagcharmdm_ioctl enable\n"); - diagfwd_disconnect(); - driver->qxdm2sd_drop = 0; - driver->in_busy_sdio_1 = 0; - driver->in_busy_sdio_2 = 0; - buf_9k = kzalloc(USB_MAX_OUT_BUF, GFP_KERNEL); - if (driver->sdio_ch) - queue_work(driver->diag_sdio_wq, &(driver->diag_read_sdio_work)); - - } else if (driver->logging_mode == USB_MODE) { - DIAG_INFO("diagcharmdm_ioctl disable\n"); - diagfwd_connect(); - driver->qxdm2sd_drop = 1; - - kfree(buf_9k); - } - success = 1; - } - return success; -} - -static int diagcharmdm_read(struct file *file, char __user *buf, size_t count, - loff_t *ppos) -{ - - int index = -1, i = 0, ret = 0; - int num_data = 0, data_type; - - if (diag9k_debug_mask) - DIAG_INFO("%s:%s(parent:%s): tgid=%d\n", __func__, - current->comm, current->parent->comm, current->tgid); - - for (i = 0; i < driver->num_mdmclients; i++) - if (driver->mdmclient_map[i].pid == current->tgid) - index = i; - - if (index == -1) { - DIAG_ERR("%s:%s(parent:%s): tgid=%d " - "Client PID not found in table\n", __func__, - current->comm, current->parent->comm, current->tgid); - for (i = 0; i < driver->num_mdmclients; i++) - DIAG_ERR("\t#%d: %d\n", i, driver->mdmclient_map[i].pid); - return -EINVAL; - } - - wait_event_interruptible(driver->mdmwait_q, - driver->mdmdata_ready[index]); - - mutex_lock(&driver->diagcharmdm_mutex); - - if ((driver->mdmdata_ready[index] & USER_SPACE_LOG_TYPE) && (driver-> - logging_mode == MEMORY_DEVICE_MODE)) { - /*Copy the type of data being passed*/ - data_type = driver->data_ready[index] & USER_SPACE_LOG_TYPE; - COPY_USER_SPACE_OR_EXIT(buf, data_type, 4); - /* place holder for number of data field */ - ret += 4; - - if (driver->in_busy_sdio_1 == 1) { - - num_data++; - /*Copy the length of data being passed*/ - COPY_USER_SPACE_OR_EXIT(buf+ret, - (driver->write_ptr_mdm_1->length), 4); - /*Copy the actual data being passed*/ - COPY_USER_SPACE_OR_EXIT(buf+ret, *(driver-> - buf_in_sdio_1), driver-> - write_ptr_mdm_1->length); - driver->in_busy_sdio_1 = 0; - } - if (driver->in_busy_sdio_2 == 1) { - - num_data++; - /*Copy the length of data being passed*/ - COPY_USER_SPACE_OR_EXIT(buf+ret, - (driver->write_ptr_mdm_2->length), 4); - /*Copy the actual data being passed*/ - COPY_USER_SPACE_OR_EXIT(buf+ret, *(driver-> - buf_in_sdio_2), driver-> - write_ptr_mdm_2->length); - driver->in_busy_sdio_2 = 0; - } - - /* copy number of data fields */ - COPY_USER_SPACE_OR_EXIT(buf+4, num_data, 4); - ret -= 4; - - driver->mdmdata_ready[index] ^= USER_SPACE_LOG_TYPE; - - if (driver->sdio_ch) - queue_work(driver->diag_sdio_wq, &(driver->diag_read_sdio_work)); - - goto exit; - } else if (driver->mdmdata_ready[index] & USER_SPACE_LOG_TYPE) { - /* In case, the thread wakes up and the logging mode is - not memory device any more, the condition needs to be cleared */ - driver->mdmdata_ready[index] ^= USER_SPACE_LOG_TYPE; - } else if (driver->mdmdata_ready[index] & USERMODE_DIAGFWD) { - data_type = USERMODE_DIAGFWD; - driver->mdmdata_ready[index] ^= USERMODE_DIAGFWD; - COPY_USER_SPACE_OR_EXIT(buf, data_type, 4); - - if (driver->in_busy_sdio_1 == 1) { - /*Copy the actual data being passed*/ - COPY_USER_SPACE_OR_EXIT(buf+ret, *(driver-> - buf_in_sdio_1), driver-> - write_ptr_mdm_1->length); - driver->in_busy_sdio_1 = 0; - } - if (driver->in_busy_sdio_2 == 1) { - /*Copy the actual data being passed*/ - COPY_USER_SPACE_OR_EXIT(buf+ret, *(driver-> - buf_in_sdio_2), driver-> - write_ptr_mdm_2->length); - driver->in_busy_sdio_2 = 0; - } - if (driver->sdio_ch) - queue_work(driver->diag_sdio_wq, &(driver->diag_read_sdio_work)); - goto exit; - } - - if (driver->mdmdata_ready[index] & DEINIT_TYPE) { - - driver->mdmdata_ready[index] ^= DEINIT_TYPE; - goto exit; - } - - if (driver->mdmdata_ready[index] & MSG_MASKS_TYPE) { - - driver->mdmdata_ready[index] ^= MSG_MASKS_TYPE; - goto exit; - } - - if (driver->mdmdata_ready[index] & EVENT_MASKS_TYPE) { - - driver->mdmdata_ready[index] ^= EVENT_MASKS_TYPE; - goto exit; - } - - if (driver->mdmdata_ready[index] & LOG_MASKS_TYPE) { - - driver->mdmdata_ready[index] ^= LOG_MASKS_TYPE; - goto exit; - } - - if (driver->mdmdata_ready[index] & PKT_TYPE) { - - driver->mdmdata_ready[index] ^= PKT_TYPE; - goto exit; - } -exit: - mutex_unlock(&driver->diagcharmdm_mutex); - - return ret; -} - -static int diagcharmdm_write(struct file *file, const char __user *buf, - size_t count, loff_t *ppos) -{ - - int err, pkt_type; - int payload_size; - - if (diag9k_debug_mask) - DIAG_INFO("%s:%s(parent:%s): tgid=%d\n", __func__, - current->comm, current->parent->comm, current->tgid); - - -#ifdef CONFIG_DIAG_OVER_USB - if (((driver->logging_mode == USB_MODE) && (!driver->usb_connected)) || - (driver->logging_mode == NO_LOGGING_MODE)) { - /*Drop the diag payload */ - return -EIO; - } -#endif /* DIAG over USB */ - - /* Get the packet type F3/log/event/Pkt response */ - err = copy_from_user((&pkt_type), buf, 4); - /*First 4 bytes indicate the type of payload - ignore these */ - payload_size = count - 4; - if (pkt_type == USER_SPACE_LOG_TYPE) { - if (diag9k_debug_mask) - DIAGFWD_INFO("writing mask file\n"); - if (!mask_request_validate((unsigned char *)buf)) { - DIAG_ERR("mask request Invalid ..cannot send to modem \n"); - return -EFAULT; - } - buf = buf + 4; - if (driver->sdio_ch) { - memcpy(buf_9k, buf, payload_size); - sdio_write(driver->sdio_ch, buf_9k, payload_size); - } - return count; - } else if (pkt_type == USERMODE_DIAGFWD) { - buf += 4; - if (driver->sdio_ch) { - memcpy(buf_9k, buf, payload_size); - sdio_write(driver->sdio_ch, buf_9k, payload_size); - } - return count; - } - return 0; -} - -static const struct file_operations diagcharmdmfops = { - .owner = THIS_MODULE, - .read = diagcharmdm_read, - .write = diagcharmdm_write, - .unlocked_ioctl = diagcharmdm_ioctl, - .open = diagcharmdm_open, - .release = diagcharmdm_close -}; -#endif - static int diagchar_setup_cdev(dev_t devno) { + int err; - struct device *diagdev; cdev_init(driver->cdev, &diagcharfops); @@ -1412,43 +962,15 @@ static int diagchar_setup_cdev(dev_t devno) return -1; } - diagdev = device_create(driver->diagchar_class, NULL, devno, + device_create(driver->diagchar_class, NULL, devno, (void *)driver, "diag"); - - err = device_create_file(diagdev, &dev_attr_diag_reg_table); - if (err) - DIAG_INFO("dev_attr_diag_reg_table registration failed !\n\n"); - err = device_create_file(diagdev, &dev_attr_diag7k_debug_mask); - if (err) - DIAG_INFO("dev_attr_diag7k_debug_mask registration failed !\n\n"); - err = device_create_file(diagdev, &dev_attr_diag9k_debug_mask); - if (err) - DIAG_INFO("dev_attr_diag9k_debug_mask registration failed !\n\n"); - -#ifdef CONFIG_DIAG_SDIO_PIPE - cdev_init(driver->cdev_mdm, &diagcharmdmfops); - - driver->cdev_mdm->owner = THIS_MODULE; - driver->cdev_mdm->ops = &diagcharmdmfops; - - err = cdev_add(driver->cdev_mdm, devno+1, 1); - - if (err) { - DIAG_ERR("diagchar cdev mdm registration failed !\n\n"); - return -1; - } - - device_create(driver->diagchar_class, NULL, devno+1, (void *)driver, "diag_mdm"); -#endif return 0; } static int diagchar_cleanup(void) { - DIAG_INFO("%s:%s(parent:%s): tgid=%d\n", __func__, - current->comm, current->parent->comm, current->tgid); if (driver) { if (driver->cdev) { /* TODO - Check if device exists before deleting */ @@ -1459,7 +981,6 @@ static int diagchar_cleanup(void) } if (!IS_ERR(driver->diagchar_class)) class_destroy(driver->diagchar_class); - wake_lock_destroy(&driver->wake_lock); kfree(driver); } return 0; @@ -1468,7 +989,7 @@ static int diagchar_cleanup(void) #ifdef CONFIG_DIAG_SDIO_PIPE void diag_sdio_fn(int type) { - if (diag_support_mdm9k) { + if (machine_is_msm8x60_fusion() || machine_is_msm8x60_fusn_ffa()) { if (type == INIT) diagfwd_sdio_init(); else if (type == EXIT) @@ -1479,12 +1000,24 @@ void diag_sdio_fn(int type) inline void diag_sdio_fn(int type) {} #endif +#ifdef CONFIG_DIAG_HSIC_PIPE +void diag_hsic_fn(int type) +{ + if (type == INIT) + diagfwd_hsic_init(); + else if (type == EXIT) + diagfwd_hsic_exit(); +} +#else +inline void diag_hsic_fn(int type) {} +#endif + static int __init diagchar_init(void) { dev_t dev; int error; - DIAG_INFO("diagfwd initializing ..\n"); + pr_debug("diagfwd initializing ..\n"); driver = kzalloc(sizeof(struct diagchar_dev) + 5, GFP_KERNEL); if (driver) { @@ -1500,10 +1033,9 @@ static int __init diagchar_init(void) driver->poolsize_write_struct = poolsize_write_struct; driver->num_clients = max_clients; driver->logging_mode = USB_MODE; + driver->mask_check = 0; mutex_init(&driver->diagchar_mutex); init_waitqueue_head(&driver->wait_q); - wake_lock_init(&driver->wake_lock, WAKE_LOCK_SUSPEND, "diagchar"); - INIT_WORK(&(driver->diag_drain_work), diag_drain_work_fn); INIT_WORK(&(driver->diag_read_smd_work), diag_read_smd_work_fn); INIT_WORK(&(driver->diag_read_smd_cntl_work), @@ -1516,25 +1048,12 @@ static int __init diagchar_init(void) diag_read_smd_wcnss_work_fn); INIT_WORK(&(driver->diag_read_smd_wcnss_cntl_work), diag_read_smd_wcnss_cntl_work_fn); -#ifdef CONFIG_DIAG_SDIO_PIPE - driver->num_mdmclients = 1; - init_waitqueue_head(&driver->mdmwait_q); - spin_lock_init(&driver->diagchar_lock); - mutex_init(&driver->diagcharmdm_mutex); - - driver->num = 2; -#else - driver->num = 1; -#endif diagfwd_init(); - if (chk_config_get_id() == AO8960_TOOLS_ID) { - diagfwd_cntl_init(); - DIAGFWD_INFO("CNTL channel was enabled in the platform\n"); - } else - DIAGFWD_INFO("CNTL channel was not enabled in the platform\n"); - + diagfwd_cntl_init(); diag_sdio_fn(INIT); + diag_hsic_fn(INIT); pr_debug("diagchar initializing ..\n"); + driver->num = 1; driver->name = ((void *)driver) + sizeof(struct diagchar_dev); strlcpy(driver->name, "diag", 4); @@ -1549,10 +1068,6 @@ static int __init diagchar_init(void) goto fail; } driver->cdev = cdev_alloc(); - -#ifdef CONFIG_DIAG_SDIO_PIPE - driver->cdev_mdm = cdev_alloc(); -#endif error = diagchar_setup_cdev(dev); if (error) goto fail; @@ -1561,7 +1076,7 @@ static int __init diagchar_init(void) goto fail; } - DIAG_INFO("diagchar initialized\n"); + pr_info("diagchar initialized now"); return 0; fail: @@ -1569,6 +1084,7 @@ static int __init diagchar_init(void) diagfwd_exit(); diagfwd_cntl_exit(); diag_sdio_fn(EXIT); + diag_hsic_fn(EXIT); return -1; } @@ -1579,9 +1095,9 @@ static void __exit diagchar_exit(void) ensure no memory leaks */ diagmem_exit(driver, POOL_TYPE_ALL); diagfwd_exit(); - if (chk_config_get_id() == AO8960_TOOLS_ID) - diagfwd_cntl_exit(); + diagfwd_cntl_exit(); diag_sdio_fn(EXIT); + diag_hsic_fn(EXIT); diagchar_cleanup(); printk(KERN_INFO "done diagchar exit\n"); } diff --git a/drivers/char/diag/diagfwd.c b/drivers/char/diag/diagfwd.c index dd0f0314349..e11afa8d00e 100644 --- a/drivers/char/diag/diagfwd.c +++ b/drivers/char/diag/diagfwd.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 @@ -9,7 +9,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ - #include #include #include @@ -22,6 +21,7 @@ #include #include #include +#include #ifdef CONFIG_DIAG_OVER_USB #include #endif @@ -36,22 +36,36 @@ #ifdef CONFIG_DIAG_SDIO_PIPE #include "diagfwd_sdio.h" #endif -#define MODE_CMD 41 -#define RESET_ID 2 +#define MODE_CMD 41 +#define RESET_ID 2 +#define ALL_EQUIP_ID 100 +#define ALL_SSID -1 +#define MAX_SSID_PER_RANGE 100 -int is_wcnss_used; int diag_debug_buf_idx; unsigned char diag_debug_buf[1024]; static unsigned int buf_tbl_size = 8; /*Number of entries in table of buffers */ -int sdio_diag_initialized; -int smd_diag_initialized; -#if DIAG_XPST -static int diag_smd_function_mode; -#endif struct diag_master_table entry; -smd_channel_t *ch_temp; +smd_channel_t *ch_temp, *chqdsp_temp, *ch_wcnss_temp; +int diag_event_num_bytes; +int diag_event_config; struct diag_send_desc_type send = { NULL, NULL, DIAG_STATE_START, 0 }; struct diag_hdlc_dest_type enc = { NULL, NULL, 0 }; +struct mask_info { + int equip_id; + int num_items; + int index; +}; + +#define CREATE_MSG_MASK_TBL_ROW(XX) \ +do { \ + *(int *)(msg_mask_tbl_ptr) = MSG_SSID_ ## XX; \ + msg_mask_tbl_ptr += 4; \ + *(int *)(msg_mask_tbl_ptr) = MSG_SSID_ ## XX ## _LAST; \ + msg_mask_tbl_ptr += 4; \ + /* increment by MAX_SSID_PER_RANGE cells */ \ + msg_mask_tbl_ptr += MAX_SSID_PER_RANGE * sizeof(int); \ +} while (0) #define ENCODE_RSP_AND_SEND(buf_length) \ do { \ @@ -61,7 +75,7 @@ do { \ send.terminate = 1; \ if (!driver->in_busy_1) { \ enc.dest = driver->buf_in_1; \ - enc.dest_last = (void *)(driver->buf_in_1 + 499); \ + enc.dest_last = (void *)(driver->buf_in_1 + APPS_BUF_SIZE - 1);\ diag_hdlc_encode(&send, &enc); \ driver->write_ptr_1->buf = driver->buf_in_1; \ driver->write_ptr_1->length = (int)(enc.dest - \ @@ -69,40 +83,79 @@ do { \ driver->in_busy_1 = 1; \ diag_device_write(driver->buf_in_1, MODEM_DATA, \ driver->write_ptr_1); \ - memset(driver->apps_rsp_buf, '\0', 500); \ + memset(driver->apps_rsp_buf, '\0', APPS_BUF_SIZE); \ } \ } while (0) #define CHK_OVERFLOW(bufStart, start, end, length) \ ((bufStart <= start) && (end - start >= length)) ? 1 : 0 -int chk_config_get_id() +int chk_config_get_id(void) { + /* For all Fusion targets, Modem will always be present */ + if (machine_is_msm8x60_fusion() || machine_is_msm8x60_fusn_ffa()) + return 0; + switch (socinfo_get_id()) { case APQ8060_MACHINE_ID: case MSM8660_MACHINE_ID: return APQ8060_TOOLS_ID; case AO8960_MACHINE_ID: + case MSM8260A_MACHINE_ID: return AO8960_TOOLS_ID; + case APQ8064_MACHINE_ID: + return APQ8064_TOOLS_ID; + case MSM8930_MACHINE_ID: + return MSM8930_TOOLS_ID; + case MSM8974_MACHINE_ID: + return MSM8974_TOOLS_ID; default: return 0; } } +/* + * This will return TRUE for targets which support apps only mode and hence SSR. + * This applies to 8960 and newer targets. + */ +int chk_apps_only(void) +{ + switch (socinfo_get_id()) { + case AO8960_MACHINE_ID: + case APQ8064_MACHINE_ID: + case MSM8930_MACHINE_ID: + case MSM8630_MACHINE_ID: + case MSM8230_MACHINE_ID: + case APQ8030_MACHINE_ID: + case MSM8627_MACHINE_ID: + case MSM8227_MACHINE_ID: + case MSM8974_MACHINE_ID: + case MDM9615_MACHINE_ID: + case MSM8260A_MACHINE_ID: + return 1; + default: + return 0; + } +} + +/* + * This will return TRUE for targets which support apps as master. + * Thus, SW DLOAD and Mode Reset are supported on apps processor. + * This applies to 8960 and newer targets. + */ +int chk_apps_master(void) +{ + if (cpu_is_msm8960() || cpu_is_msm8930() || cpu_is_msm9615()) + return 1; + else + return 0; +} + void __diag_smd_send_req(void) { void *buf = NULL; int *in_busy_ptr = NULL; struct diag_request *write_ptr_modem = NULL; -#if DIAG_XPST - int type; -#endif - -#ifdef SDQXDM_DEBUG - static struct timeval t0 = {0, 0}, t1; - static int full, empty; - long diff; -#endif if (!driver->in_busy_1) { buf = driver->buf_in_1; @@ -135,68 +188,12 @@ void __diag_smd_send_req(void) APPEND_DEBUG('i'); smd_read(driver->ch, buf, r); APPEND_DEBUG('j'); - if (diag7k_debug_mask) { - switch (diag7k_debug_mask) { - case DIAGLOG_MODE_HEAD: - print_hex_dump(KERN_DEBUG, "Read Packet Data" - " from modem(first 16 bytes)", 16, 1, DUMP_PREFIX_ADDRESS, buf, 16, 1); - break; - case DIAGLOG_MODE_FULL: - print_hex_dump(KERN_DEBUG, "Read Packet Data" - " from modem(first 16 bytes)", 16, 1, DUMP_PREFIX_ADDRESS, buf, 16, 1); - print_hex_dump(KERN_DEBUG, "Read Packet Data" - " from modem(last 16 bytes) ", 16, 1, DUMP_PREFIX_ADDRESS, buf+r-16, 16, 1); - break; - default: - #if 0 - print_hex_dump(KERN_DEBUG, "Read Packet Data" - " from modem ", 16, 1, DUMP_PREFIX_ADDRESS, buf, r, 1); - #endif - break; - } - } - -#if DIAG_XPST - type = checkcmd_modem_epst(buf); - if (type) { - modem_to_userspace(buf, r, type, 0); - return; - } -#endif - -#ifdef SDQXDM_DEBUG - if (full) { - pr_err("[diag-dbg] buffer become available %d %d, read %d\n", - driver->in_busy_1, driver->in_busy_2, r); - full = 0; - } - do_gettimeofday(&t1); - diff = (t1.tv_sec-t0.tv_sec)*1000 + (t1.tv_usec-t0.tv_usec)/1000; - if (diff > 1000) { - pr_err("[diag-dbg] Over time (%ld) %ld.%04ld -> %ld.%04ld empty = %d\n", - diff, (long)t0.tv_sec, t0.tv_usec/1000, - (long)t1.tv_sec, t1.tv_usec/1000, empty); - } - write_ptr_modem->second = t1.tv_sec; - t0 = t1; - empty = 0; -#endif write_ptr_modem->length = r; *in_busy_ptr = 1; diag_device_write(buf, MODEM_DATA, write_ptr_modem); } } -#ifdef SDQXDM_DEBUG - else - empty++; -#endif - } else { -#ifdef SDQXDM_DEBUG - if (!full && driver->ch) - pr_info("[diag-dbg] Buffer full, %d bytes pending.\n", smd_read_avail(driver->ch)); - full = 1; -#endif } } @@ -214,35 +211,18 @@ int diag_device_write(void *buf, int proc_num, struct diag_request *write_ptr) #ifdef DIAG_DEBUG pr_debug("diag: ENQUEUE buf ptr" " and length is %x , %d\n", - (unsigned int)(driver->buf_tbl[i].buf), driver->buf_tbl[i].length); + (unsigned int)(driver->buf_ + tbl[i].buf), driver->buf_tbl[i].length); #endif break; } } -#ifdef CONFIG_DIAG_SDIO_PIPE - if (proc_num == SDIO_DATA) { - - for (i = 0; i < driver->num_mdmclients; i++) - if (driver->mdmclient_map[i].pid == - driver->logging_process_id) - break; - - if (i < driver->num_mdmclients) { - driver->mdmdata_ready[i] |= USERMODE_DIAGFWD; - wake_up_interruptible(&driver->mdmwait_q); - - return err; - } else - return -EINVAL; - } -#endif for (i = 0; i < driver->num_clients; i++) if (driver->client_map[i].pid == driver->logging_process_id) break; if (i < driver->num_clients) { - wake_lock_timeout(&driver->wake_lock, HZ / 2); - driver->data_ready[i] |= USERMODE_DIAGFWD; + driver->data_ready[i] |= USER_SPACE_LOG_TYPE; wake_up_interruptible(&driver->wait_q); } else return -EINVAL; @@ -262,6 +242,13 @@ int diag_device_write(void *buf, int proc_num, struct diag_request *write_ptr) queue_work(driver->diag_wq, &(driver-> diag_read_smd_wcnss_work)); } +#ifdef CONFIG_DIAG_SDIO_PIPE + else if (proc_num == SDIO_DATA) { + driver->in_busy_sdio = 0; + queue_work(driver->diag_sdio_wq, + &(driver->diag_read_sdio_work)); + } +#endif err = -1; } #ifdef CONFIG_DIAG_OVER_USB @@ -296,11 +283,23 @@ int diag_device_write(void *buf, int proc_num, struct diag_request *write_ptr) } #ifdef CONFIG_DIAG_SDIO_PIPE else if (proc_num == SDIO_DATA) { - if (diag_support_mdm9k) { + if (machine_is_msm8x60_fusion() || + machine_is_msm8x60_fusn_ffa()) { + write_ptr->buf = buf; + err = usb_diag_write(driver->mdm_ch, write_ptr); + } else + pr_err("diag: Incorrect sdio data " + "while USB write\n"); + } +#endif +#ifdef CONFIG_DIAG_HSIC_PIPE + else if (proc_num == HSIC_DATA) { + if (driver->hsic_device_enabled) { write_ptr->buf = buf; err = usb_diag_write(driver->mdm_ch, write_ptr); } else - pr_err("diag: Incorrect data while USB write"); + pr_err("diag: Incorrect hsic data " + "while USB write\n"); } #endif APPEND_DEBUG('d'); @@ -314,9 +313,6 @@ void __diag_smd_wcnss_send_req(void) void *buf = driver->buf_in_wcnss; int *in_busy_wcnss_ptr = &(driver->in_busy_wcnss); struct diag_request *write_ptr_wcnss = driver->write_ptr_wcnss; -#if DIAG_XPST - int type; -#endif if ((!driver->in_busy_wcnss) && driver->ch_wcnss && buf) { int r = smd_read_avail(driver->ch_wcnss); @@ -336,13 +332,6 @@ void __diag_smd_wcnss_send_req(void) APPEND_DEBUG('i'); smd_read(driver->ch_wcnss, buf, r); APPEND_DEBUG('j'); -#if DIAG_XPST - type = checkcmd_modem_epst(buf); - if (type) { - modem_to_userspace(buf, r, type, 0); - return; - } -#endif write_ptr_wcnss->length = r; *in_busy_wcnss_ptr = 1; diag_device_write(buf, WCNSS_DATA, @@ -357,9 +346,7 @@ void __diag_smd_qdsp_send_req(void) void *buf = NULL; int *in_busy_qdsp_ptr = NULL; struct diag_request *write_ptr_qdsp = NULL; -#if DIAG_XPST - int type; -#endif + if (!driver->in_busy_qdsp_1) { buf = driver->buf_in_qdsp_1; write_ptr_qdsp = driver->write_ptr_qdsp_1; @@ -391,13 +378,6 @@ void __diag_smd_qdsp_send_req(void) APPEND_DEBUG('i'); smd_read(driver->chqdsp, buf, r); APPEND_DEBUG('j'); -#if DIAG_XPST - type = checkcmd_modem_epst(buf); - if (type) { - modem_to_userspace(buf, r, type, 0); - return; - } -#endif write_ptr_qdsp->length = r; *in_busy_qdsp_ptr = 1; diag_device_write(buf, QDSP_DATA, @@ -415,7 +395,7 @@ static void diag_print_mask_table(void) int last; uint8_t *ptr = driver->msg_masks; int i = 0; - + pr_info("diag: F3 message mask table\n"); while (*(uint32_t *)(ptr + 4)) { first = *(uint32_t *)ptr; ptr += 4; @@ -424,12 +404,63 @@ static void diag_print_mask_table(void) printk(KERN_INFO "SSID %d - %d\n", first, last); for (i = 0 ; i <= last - first ; i++) printk(KERN_INFO "MASK:%x\n", *((uint32_t *)ptr + i)); - ptr += ((last - first) + 1)*4; + ptr += MAX_SSID_PER_RANGE*4; } #endif } +void diag_create_msg_mask_table(void) +{ + uint8_t *msg_mask_tbl_ptr = driver->msg_masks; + + CREATE_MSG_MASK_TBL_ROW(0); + CREATE_MSG_MASK_TBL_ROW(1); + CREATE_MSG_MASK_TBL_ROW(2); + CREATE_MSG_MASK_TBL_ROW(3); + CREATE_MSG_MASK_TBL_ROW(4); + CREATE_MSG_MASK_TBL_ROW(5); + CREATE_MSG_MASK_TBL_ROW(6); + CREATE_MSG_MASK_TBL_ROW(7); + CREATE_MSG_MASK_TBL_ROW(8); + CREATE_MSG_MASK_TBL_ROW(9); + CREATE_MSG_MASK_TBL_ROW(10); + CREATE_MSG_MASK_TBL_ROW(11); + CREATE_MSG_MASK_TBL_ROW(12); + CREATE_MSG_MASK_TBL_ROW(13); + CREATE_MSG_MASK_TBL_ROW(14); + CREATE_MSG_MASK_TBL_ROW(15); + CREATE_MSG_MASK_TBL_ROW(16); + CREATE_MSG_MASK_TBL_ROW(17); + CREATE_MSG_MASK_TBL_ROW(18); + CREATE_MSG_MASK_TBL_ROW(19); + CREATE_MSG_MASK_TBL_ROW(20); + CREATE_MSG_MASK_TBL_ROW(21); + CREATE_MSG_MASK_TBL_ROW(22); +} + +static void diag_set_msg_mask(int rt_mask) +{ + int first_ssid, last_ssid, i; + uint8_t *parse_ptr, *ptr = driver->msg_masks; + + mutex_lock(&driver->diagchar_mutex); + while (*(uint32_t *)(ptr + 4)) { + first_ssid = *(uint32_t *)ptr; + ptr += 4; + last_ssid = *(uint32_t *)ptr; + ptr += 4; + parse_ptr = ptr; + pr_debug("diag: updating range %d %d\n", first_ssid, last_ssid); + for (i = 0; i < last_ssid - first_ssid + 1; i++) { + *(int *)parse_ptr = rt_mask; + parse_ptr += 4; + } + ptr += MAX_SSID_PER_RANGE * 4; + } + mutex_unlock(&driver->diagchar_mutex); +} + static void diag_update_msg_mask(int start, int end , uint8_t *buf) { int found = 0; @@ -440,8 +471,8 @@ static void diag_update_msg_mask(int start, int end , uint8_t *buf) uint8_t *ptr_buffer_end = &(*(driver->msg_masks)) + MSG_MASK_SIZE; mutex_lock(&driver->diagchar_mutex); - /* First SSID can be zero : So check that last is non-zero */ + /* First SSID can be zero : So check that last is non-zero */ while (*(uint32_t *)(ptr + 4)) { first = *(uint32_t *)ptr; ptr += 4; @@ -452,9 +483,11 @@ static void diag_update_msg_mask(int start, int end , uint8_t *buf) if (end <= last) if (CHK_OVERFLOW(ptr_buffer_start, ptr, ptr_buffer_end, - (((end - start)+1)*4))) + (((end - start)+1)*4))) { + pr_debug("diag: update ssid start %d," + " end %d\n", start, end); memcpy(ptr, buf , ((end - start)+1)*4); - else + } else printk(KERN_CRIT "Not enough" " buffer space for" " MSG_MASK\n"); @@ -465,7 +498,7 @@ static void diag_update_msg_mask(int start, int end , uint8_t *buf) found = 1; break; } else { - ptr += ((last - first) + 1)*4; + ptr += MAX_SSID_PER_RANGE*4; } } /* Entry was not found - add new table */ @@ -476,6 +509,8 @@ static void diag_update_msg_mask(int start, int end , uint8_t *buf) ptr += 4; memcpy(ptr, &(end), 4); ptr += 4; + pr_debug("diag: adding NEW ssid start %d, end %d\n", + start, end); memcpy(ptr, buf , ((end - start) + 1)*4); } else printk(KERN_CRIT " Not enough buffer" @@ -486,7 +521,19 @@ static void diag_update_msg_mask(int start, int end , uint8_t *buf) } -static void diag_update_event_mask(uint8_t *buf, int toggle, int num_bits) +void diag_toggle_event_mask(int toggle) +{ + uint8_t *ptr = driver->event_masks; + + mutex_lock(&driver->diagchar_mutex); + if (toggle) + memset(ptr, 0xFF, EVENT_MASK_SIZE); + else + memset(ptr, 0, EVENT_MASK_SIZE); + mutex_unlock(&driver->diagchar_mutex); +} + +static void diag_update_event_mask(uint8_t *buf, int toggle, int num_bytes) { uint8_t *ptr = driver->event_masks; uint8_t *temp = buf + 2; @@ -496,27 +543,41 @@ static void diag_update_event_mask(uint8_t *buf, int toggle, int num_bits) memset(ptr, 0 , EVENT_MASK_SIZE); else if (CHK_OVERFLOW(ptr, ptr, - ptr+EVENT_MASK_SIZE, - num_bits/8 + 1)) - memcpy(ptr, temp , num_bits/8 + 1); + ptr+EVENT_MASK_SIZE, num_bytes)) + memcpy(ptr, temp , num_bytes); else printk(KERN_CRIT "Not enough buffer space " "for EVENT_MASK\n"); mutex_unlock(&driver->diagchar_mutex); } +static void diag_disable_log_mask(void) +{ + int i = 0; + struct mask_info *parse_ptr = (struct mask_info *)(driver->log_masks); + + pr_debug("diag: disable log masks\n"); + mutex_lock(&driver->diagchar_mutex); + for (i = 0; i < MAX_EQUIP_ID; i++) { + pr_debug("diag: equip id %d\n", parse_ptr->equip_id); + if (!(parse_ptr->equip_id)) /* Reached a null entry */ + break; + memset(driver->log_masks + parse_ptr->index, 0, + (parse_ptr->num_items + 7)/8); + parse_ptr++; + } + mutex_unlock(&driver->diagchar_mutex); +} + static void diag_update_log_mask(int equip_id, uint8_t *buf, int num_items) { uint8_t *temp = buf; - struct mask_info { - int equip_id; - int index; - }; int i = 0; unsigned char *ptr_data; - int offset = 8*MAX_EQUIP_ID; - struct mask_info *ptr = (struct mask_info *)driver->log_masks; + int offset = (sizeof(struct mask_info))*MAX_EQUIP_ID; + struct mask_info *ptr = (struct mask_info *)(driver->log_masks); + pr_debug("diag: received equip id = %d\n", equip_id); mutex_lock(&driver->diagchar_mutex); /* Check if we already know index of this equipment ID */ for (i = 0; i < MAX_EQUIP_ID; i++) { @@ -525,8 +586,9 @@ static void diag_update_log_mask(int equip_id, uint8_t *buf, int num_items) break; } if ((ptr->equip_id == 0) && (ptr->index == 0)) { - /*Reached a null entry */ + /* Reached a null entry */ ptr->equip_id = equip_id; + ptr->num_items = num_items; ptr->index = driver->log_masks_length; offset = driver->log_masks_length; driver->log_masks_length += ((num_items+7)/8); @@ -539,7 +601,7 @@ static void diag_update_log_mask(int equip_id, uint8_t *buf, int num_items) + LOG_MASK_SIZE, (num_items+7)/8)) memcpy(ptr_data, temp , (num_items+7)/8); else - printk(KERN_CRIT " Not enough buffer space for LOG_MASK\n"); + pr_err("diag: Not enough buffer space for LOG_MASK\n"); mutex_unlock(&driver->diagchar_mutex); } @@ -592,9 +654,10 @@ void diag_send_data(struct diag_master_table entry, unsigned char *buf, } else { if (len > 0) { if (entry.client_id == MODEM_PROC && driver->ch) { - if (cpu_is_msm8960() && + if (chk_apps_master() && (int)(*(char *)buf) == MODE_CMD) - if ((int)(*(char *)(buf+1)) == RESET_ID) + if ((int)(*(char *)(buf+1)) == + RESET_ID) return; smd_write(driver->ch, buf, len); } else if (entry.client_id == QDSP_PROC && @@ -610,11 +673,186 @@ void diag_send_data(struct diag_master_table entry, unsigned char *buf, } } +void diag_modem_mask_update_fn(struct work_struct *work) +{ + diag_send_msg_mask_update(driver->ch_cntl, ALL_SSID, + ALL_SSID, MODEM_PROC); + diag_send_log_mask_update(driver->ch_cntl, ALL_EQUIP_ID); + diag_send_event_mask_update(driver->ch_cntl, diag_event_num_bytes); +} + +void diag_qdsp_mask_update_fn(struct work_struct *work) +{ + diag_send_msg_mask_update(driver->chqdsp_cntl, ALL_SSID, + ALL_SSID, QDSP_PROC); + diag_send_log_mask_update(driver->chqdsp_cntl, ALL_EQUIP_ID); + diag_send_event_mask_update(driver->chqdsp_cntl, diag_event_num_bytes); +} + +void diag_wcnss_mask_update_fn(struct work_struct *work) +{ + diag_send_msg_mask_update(driver->ch_wcnss_cntl, ALL_SSID, + ALL_SSID, WCNSS_PROC); + diag_send_log_mask_update(driver->ch_wcnss_cntl, ALL_EQUIP_ID); + diag_send_event_mask_update(driver->ch_wcnss_cntl, + diag_event_num_bytes); +} + +void diag_send_log_mask_update(smd_channel_t *ch, int equip_id) +{ + void *buf = driver->buf_log_mask_update; + int header_size = sizeof(struct diag_ctrl_log_mask); + struct mask_info *ptr = (struct mask_info *)driver->log_masks; + int i, size, wr_size = -ENOMEM, retry_count = 0, timer; + + mutex_lock(&driver->diag_cntl_mutex); + for (i = 0; i < MAX_EQUIP_ID; i++) { + size = (ptr->num_items+7)/8; + /* reached null entry */ + if ((ptr->equip_id == 0) && (ptr->index == 0)) + break; + driver->log_mask->cmd_type = DIAG_CTRL_MSG_LOG_MASK; + driver->log_mask->num_items = ptr->num_items; + driver->log_mask->data_len = 11 + size; + driver->log_mask->stream_id = 1; /* 2, if dual stream */ + driver->log_mask->status = 3; /* status for valid mask */ + driver->log_mask->equip_id = ptr->equip_id; + driver->log_mask->log_mask_size = size; + /* send only desired update, NOT ALL */ + if (equip_id == ALL_EQUIP_ID || equip_id == + driver->log_mask->equip_id) { + memcpy(buf, driver->log_mask, header_size); + memcpy(buf+header_size, driver->log_masks+ptr->index, + size); + if (ch) { + while (retry_count < 3) { + wr_size = smd_write(ch, buf, + header_size + size); + if (wr_size == -ENOMEM) { + retry_count++; + for (timer = 0; timer < 5; + timer++) + udelay(2000); + } else + break; + } + if (wr_size != header_size + size) + pr_err("diag: log mask update failed" + " %d, tried %d", wr_size, header_size + size); + else + pr_debug("diag: updated log equip ID %d" + ",len %d\n", driver->log_mask->equip_id, + driver->log_mask->log_mask_size); + } else + pr_err("diag: ch not valid for log update\n"); + } + ptr++; + } + mutex_unlock(&driver->diag_cntl_mutex); +} + +void diag_send_event_mask_update(smd_channel_t *ch, int num_bytes) +{ + void *buf = driver->buf_event_mask_update; + int header_size = sizeof(struct diag_ctrl_event_mask); + int wr_size = -ENOMEM, retry_count = 0, timer; + + mutex_lock(&driver->diag_cntl_mutex); + if (num_bytes == 0) { + pr_debug("diag: event mask not set yet, so no update\n"); + mutex_unlock(&driver->diag_cntl_mutex); + return; + } + /* send event mask update */ + driver->event_mask->cmd_type = DIAG_CTRL_MSG_EVENT_MASK; + driver->event_mask->data_len = 7 + num_bytes; + driver->event_mask->stream_id = 1; /* 2, if dual stream */ + driver->event_mask->status = 3; /* status for valid mask */ + driver->event_mask->event_config = diag_event_config; /* event config */ + driver->event_mask->event_mask_size = num_bytes; + memcpy(buf, driver->event_mask, header_size); + memcpy(buf+header_size, driver->event_masks, num_bytes); + if (ch) { + while (retry_count < 3) { + wr_size = smd_write(ch, buf, header_size + num_bytes); + if (wr_size == -ENOMEM) { + retry_count++; + for (timer = 0; timer < 5; timer++) + udelay(2000); + } else + break; + } + if (wr_size != header_size + num_bytes) + pr_err("diag: error writing event mask %d, tried %d\n", + wr_size, header_size + num_bytes); + } else + pr_err("diag: ch not valid for event update\n"); + mutex_unlock(&driver->diag_cntl_mutex); +} + +void diag_send_msg_mask_update(smd_channel_t *ch, int updated_ssid_first, + int updated_ssid_last, int proc) +{ + void *buf = driver->buf_msg_mask_update; + int first, last, size = -ENOMEM, retry_count = 0, timer; + int header_size = sizeof(struct diag_ctrl_msg_mask); + uint8_t *ptr = driver->msg_masks; + + mutex_lock(&driver->diag_cntl_mutex); + while (*(uint32_t *)(ptr + 4)) { + first = *(uint32_t *)ptr; + ptr += 4; + last = *(uint32_t *)ptr; + ptr += 4; + if ((updated_ssid_first >= first && updated_ssid_last <= last) + || (updated_ssid_first == ALL_SSID)) { + /* send f3 mask update */ + driver->msg_mask->cmd_type = DIAG_CTRL_MSG_F3_MASK; + driver->msg_mask->msg_mask_size = last - first + 1; + driver->msg_mask->data_len = 11 + + 4 * (driver->msg_mask->msg_mask_size); + driver->msg_mask->stream_id = 1; /* 2, if dual stream */ + driver->msg_mask->status = 3; /* status valid mask */ + driver->msg_mask->msg_mode = 0; /* Legcay mode */ + driver->msg_mask->ssid_first = first; + driver->msg_mask->ssid_last = last; + memcpy(buf, driver->msg_mask, header_size); + memcpy(buf+header_size, ptr, + 4 * (driver->msg_mask->msg_mask_size)); + if (ch) { + while (retry_count < 3) { + size = smd_write(ch, buf, header_size + + 4*(driver->msg_mask->msg_mask_size)); + if (size == -ENOMEM) { + retry_count++; + for (timer = 0; timer < 5; + timer++) + udelay(2000); + } else + break; + } + if (size != header_size + + 4*(driver->msg_mask->msg_mask_size)) + pr_err("diag: proc %d, msg mask update " + "fail %d, tried %d\n", proc, size, + header_size + 4*(driver->msg_mask->msg_mask_size)); + else + pr_debug("diag: sending mask update for" + "ssid first %d, last %d on PROC %d\n", first, last, proc); + } else + pr_err("diag: proc %d, ch invalid msg mask" + "update\n", proc); + } + ptr += MAX_SSID_PER_RANGE*4; + } + mutex_unlock(&driver->diag_cntl_mutex); +} + static int diag_process_apps_pkt(unsigned char *buf, int len) { uint16_t subsys_cmd_code; int subsys_id, ssid_first, ssid_last, ssid_range; - int packet_type = 1, i, cmd_code; + int packet_type = 1, i, cmd_code, rt_mask; unsigned char *temp = buf; int data_type; #if defined(CONFIG_DIAG_OVER_USB) @@ -622,6 +860,167 @@ static int diag_process_apps_pkt(unsigned char *buf, int len) unsigned char *ptr; #endif + /* Set log masks */ + if (*buf == 0x73 && *(int *)(buf+4) == 3) { + buf += 8; + /* Read Equip ID and pass as first param below*/ + diag_update_log_mask(*(int *)buf, buf+8, *(int *)(buf+4)); + diag_update_userspace_clients(LOG_MASKS_TYPE); +#if defined(CONFIG_DIAG_OVER_USB) + if (chk_apps_only()) { + driver->apps_rsp_buf[0] = 0x73; + *(int *)(driver->apps_rsp_buf + 4) = 0x3; /* op. ID */ + *(int *)(driver->apps_rsp_buf + 8) = 0x0; /* success */ + payload_length = 8 + ((*(int *)(buf + 4)) + 7)/8; + for (i = 0; i < payload_length; i++) + *(int *)(driver->apps_rsp_buf+12+i) = *(buf+i); + if (driver->ch_cntl) + diag_send_log_mask_update(driver->ch_cntl, + *(int *)buf); + if (driver->chqdsp_cntl) + diag_send_log_mask_update(driver->chqdsp_cntl, + *(int *)buf); + if (driver->ch_wcnss_cntl) + diag_send_log_mask_update(driver->ch_wcnss_cntl, + *(int *)buf); + ENCODE_RSP_AND_SEND(12 + payload_length - 1); + return 0; + } else + buf = temp; +#endif + } /* Disable log masks */ + else if (*buf == 0x73 && *(int *)(buf+4) == 0) { + buf += 8; + /* Disable mask for each log code */ + diag_disable_log_mask(); + diag_update_userspace_clients(LOG_MASKS_TYPE); +#if defined(CONFIG_DIAG_OVER_USB) + if (chk_apps_only()) { + driver->apps_rsp_buf[0] = 0x73; + driver->apps_rsp_buf[1] = 0x0; + driver->apps_rsp_buf[2] = 0x0; + driver->apps_rsp_buf[3] = 0x0; + *(int *)(driver->apps_rsp_buf + 4) = 0x0; + if (driver->ch_cntl) + diag_send_log_mask_update(driver->ch_cntl, + ALL_EQUIP_ID); + if (driver->chqdsp_cntl) + diag_send_log_mask_update(driver->chqdsp_cntl, + ALL_EQUIP_ID); + if (driver->ch_wcnss_cntl) + diag_send_log_mask_update(driver->ch_wcnss_cntl, + ALL_EQUIP_ID); + ENCODE_RSP_AND_SEND(7); + return 0; + } else + buf = temp; +#endif + } /* Set runtime message mask */ + else if ((*buf == 0x7d) && (*(buf+1) == 0x4)) { + ssid_first = *(uint16_t *)(buf + 2); + ssid_last = *(uint16_t *)(buf + 4); + ssid_range = 4 * (ssid_last - ssid_first + 1); + pr_debug("diag: received mask update for ssid_first = %d," + " ssid_last = %d", ssid_first, ssid_last); + diag_update_msg_mask(ssid_first, ssid_last , buf + 8); + diag_update_userspace_clients(MSG_MASKS_TYPE); +#if defined(CONFIG_DIAG_OVER_USB) + if (chk_apps_only()) { + for (i = 0; i < 8 + ssid_range; i++) + *(driver->apps_rsp_buf + i) = *(buf+i); + *(driver->apps_rsp_buf + 6) = 0x1; + if (driver->ch_cntl) + diag_send_msg_mask_update(driver->ch_cntl, + ssid_first, ssid_last, MODEM_PROC); + if (driver->chqdsp_cntl) + diag_send_msg_mask_update(driver->chqdsp_cntl, + ssid_first, ssid_last, QDSP_PROC); + if (driver->ch_wcnss_cntl) + diag_send_msg_mask_update(driver->ch_wcnss_cntl, + ssid_first, ssid_last, WCNSS_PROC); + ENCODE_RSP_AND_SEND(8 + ssid_range - 1); + return 0; + } else + buf = temp; +#endif + } /* Set ALL runtime message mask */ + else if ((*buf == 0x7d) && (*(buf+1) == 0x5)) { + rt_mask = *(int *)(buf + 4); + diag_set_msg_mask(rt_mask); + diag_update_userspace_clients(MSG_MASKS_TYPE); +#if defined(CONFIG_DIAG_OVER_USB) + if (chk_apps_only()) { + driver->apps_rsp_buf[0] = 0x7d; /* cmd_code */ + driver->apps_rsp_buf[1] = 0x5; /* set subcommand */ + driver->apps_rsp_buf[2] = 1; /* success */ + driver->apps_rsp_buf[3] = 0; /* rsvd */ + *(int *)(driver->apps_rsp_buf + 4) = rt_mask; + /* send msg mask update to peripheral */ + if (driver->ch_cntl) + diag_send_msg_mask_update(driver->ch_cntl, + ALL_SSID, ALL_SSID, MODEM_PROC); + if (driver->chqdsp_cntl) + diag_send_msg_mask_update(driver->chqdsp_cntl, + ALL_SSID, ALL_SSID, QDSP_PROC); + if (driver->ch_wcnss_cntl) + diag_send_msg_mask_update(driver->ch_wcnss_cntl, + ALL_SSID, ALL_SSID, WCNSS_PROC); + ENCODE_RSP_AND_SEND(7); + return 0; + } else + buf = temp; +#endif + } else if (*buf == 0x82) { /* event mask change */ + buf += 4; + diag_event_num_bytes = (*(uint16_t *)buf)/8+1; + diag_update_event_mask(buf, 1, (*(uint16_t *)buf)/8+1); + diag_update_userspace_clients(EVENT_MASKS_TYPE); +#if defined(CONFIG_DIAG_OVER_USB) + if (chk_apps_only()) { + driver->apps_rsp_buf[0] = 0x82; + driver->apps_rsp_buf[1] = 0x0; + *(uint16_t *)(driver->apps_rsp_buf + 2) = 0x0; + *(uint16_t *)(driver->apps_rsp_buf + 4) = + EVENT_LAST_ID + 1; + memcpy(driver->apps_rsp_buf+6, driver->event_masks, + EVENT_LAST_ID/8+1); + if (driver->ch_cntl) + diag_send_event_mask_update(driver->ch_cntl, + diag_event_num_bytes); + if (driver->chqdsp_cntl) + diag_send_event_mask_update(driver->chqdsp_cntl, + diag_event_num_bytes); + if (driver->ch_wcnss_cntl) + diag_send_event_mask_update( + driver->ch_wcnss_cntl, diag_event_num_bytes); + ENCODE_RSP_AND_SEND(6 + EVENT_LAST_ID/8); + return 0; + } else + buf = temp; +#endif + } else if (*buf == 0x60) { + diag_event_config = *(buf+1); + diag_toggle_event_mask(*(buf+1)); + diag_update_userspace_clients(EVENT_MASKS_TYPE); +#if defined(CONFIG_DIAG_OVER_USB) + if (chk_apps_only()) { + driver->apps_rsp_buf[0] = 0x60; + driver->apps_rsp_buf[1] = 0x0; + driver->apps_rsp_buf[2] = 0x0; + if (driver->ch_cntl) + diag_send_event_mask_update(driver->ch_cntl, + diag_event_num_bytes); + if (driver->chqdsp_cntl) + diag_send_event_mask_update(driver->chqdsp_cntl, + diag_event_num_bytes); + if (driver->ch_wcnss_cntl) + diag_send_event_mask_update( + driver->ch_wcnss_cntl, diag_event_num_bytes); + ENCODE_RSP_AND_SEND(2); + return 0; + } +#endif + } /* Check for registered clients and forward packet to apropriate proc */ cmd_code = (int)(*(char *)buf); temp++; @@ -631,13 +1030,13 @@ static int diag_process_apps_pkt(unsigned char *buf, int len) temp += 2; data_type = APPS_DATA; /* Dont send any command other than mode reset */ - if (cpu_is_msm8960() && cmd_code == MODE_CMD) { + if (chk_apps_master() && cmd_code == MODE_CMD) { if (subsys_id != RESET_ID) data_type = MODEM_DATA; } pr_debug("diag: %d %d %d", cmd_code, subsys_id, subsys_cmd_code); - for (i = 0; i < diag_max_registration; i++) { + for (i = 0; i < diag_max_reg; i++) { entry = driver->table[i]; if (entry.process_id != NO_PROCESS) { if (entry.cmd_code == cmd_code && entry.subsys_id == @@ -671,71 +1070,9 @@ static int diag_process_apps_pkt(unsigned char *buf, int len) } } } - /* set event mask */ - if (*buf == 0x82) { - buf += 4; - diag_update_event_mask(buf, 1, *(uint16_t *)buf); - diag_update_userspace_clients(EVENT_MASKS_TYPE); - } - /* event mask change */ - else if ((*buf == 0x60) && (*(buf+1) == 0x0)) { - diag_update_event_mask(buf+1, 0, 0); - diag_update_userspace_clients(EVENT_MASKS_TYPE); -#if defined(CONFIG_DIAG_OVER_USB) - /* Check for Apps Only 8960 */ - if (!(driver->ch) && (chk_config_get_id() == AO8960_TOOLS_ID)) { - /* echo response back for apps only DIAG */ - driver->apps_rsp_buf[0] = 0x60; - driver->apps_rsp_buf[1] = 0x0; - driver->apps_rsp_buf[2] = 0x0; - ENCODE_RSP_AND_SEND(2); - return 0; - } -#endif - } - /* Set log masks */ - else if (*buf == 0x73 && *(int *)(buf+4) == 3) { - buf += 8; - /* Read Equip ID and pass as first param below*/ - diag_update_log_mask(*(int *)buf, buf+8, *(int *)(buf+4)); - diag_update_userspace_clients(LOG_MASKS_TYPE); #if defined(CONFIG_DIAG_OVER_USB) - /* Check for Apps Only 8960 */ - if (!(driver->ch) && (chk_config_get_id() == AO8960_TOOLS_ID)) { - /* echo response back for Apps only DIAG */ - driver->apps_rsp_buf[0] = 0x73; - *(int *)(driver->apps_rsp_buf + 4) = 0x3; /* op. ID */ - *(int *)(driver->apps_rsp_buf + 8) = 0x0; /* success */ - payload_length = 8 + ((*(int *)(buf + 4)) + 7)/8; - for (i = 0; i < payload_length; i++) - *(int *)(driver->apps_rsp_buf+12+i) = - *(buf+8+i); - ENCODE_RSP_AND_SEND(12 + payload_length - 1); - return 0; - } -#endif - } - /* Check for set message mask */ - else if ((*buf == 0x7d) && (*(buf+1) == 0x4)) { - ssid_first = *(uint16_t *)(buf + 2); - ssid_last = *(uint16_t *)(buf + 4); - ssid_range = 4 * (ssid_last - ssid_first + 1); - diag_update_msg_mask(ssid_first, ssid_last , buf + 8); - diag_update_userspace_clients(MSG_MASKS_TYPE); -#if defined(CONFIG_DIAG_OVER_USB) - if (!(driver->ch) && (chk_config_get_id() == AO8960_TOOLS_ID)) { - /* echo response back for apps only DIAG */ - for (i = 0; i < 8 + ssid_range; i++) - *(driver->apps_rsp_buf + i) = *(buf+i); - ENCODE_RSP_AND_SEND(8 + ssid_range - 1); - return 0; - } -#endif - } -#if defined(CONFIG_DIAG_OVER_USB) - /* Check for Apps Only 8960 & get event mask request */ - else if (!(driver->ch) && (chk_config_get_id() == AO8960_TOOLS_ID) - && *buf == 0x81) { + /* Check for Apps Only & get event mask request */ + if (!(driver->ch) && chk_apps_only() && *buf == 0x81) { driver->apps_rsp_buf[0] = 0x81; driver->apps_rsp_buf[1] = 0x0; *(uint16_t *)(driver->apps_rsp_buf + 2) = 0x0; @@ -745,8 +1082,8 @@ static int diag_process_apps_pkt(unsigned char *buf, int len) ENCODE_RSP_AND_SEND(6 + EVENT_LAST_ID/8); return 0; } - /* Get log ID range & Check for Apps Only 8960 */ - else if (!(driver->ch) && (chk_config_get_id() == AO8960_TOOLS_ID) + /* Get log ID range & Check for Apps Only */ + else if (!(driver->ch) && chk_apps_only() && (*buf == 0x73) && *(int *)(buf+4) == 1) { driver->apps_rsp_buf[0] = 0x73; *(int *)(driver->apps_rsp_buf + 4) = 0x1; /* operation ID */ @@ -771,7 +1108,7 @@ static int diag_process_apps_pkt(unsigned char *buf, int len) return 0; } /* Respond to Get SSID Range request message */ - else if (!(driver->ch) && (chk_config_get_id() == AO8960_TOOLS_ID) + else if (!(driver->ch) && chk_apps_only() && (*buf == 0x7d) && (*(buf+1) == 0x1)) { driver->apps_rsp_buf[0] = 0x7d; driver->apps_rsp_buf[1] = 0x1; @@ -816,11 +1153,19 @@ static int diag_process_apps_pkt(unsigned char *buf, int len) *(uint16_t *)(driver->apps_rsp_buf + 78) = MSG_SSID_17_LAST; *(uint16_t *)(driver->apps_rsp_buf + 80) = MSG_SSID_18; *(uint16_t *)(driver->apps_rsp_buf + 82) = MSG_SSID_18_LAST; - ENCODE_RSP_AND_SEND(83); + *(uint16_t *)(driver->apps_rsp_buf + 84) = MSG_SSID_19; + *(uint16_t *)(driver->apps_rsp_buf + 86) = MSG_SSID_19_LAST; + *(uint16_t *)(driver->apps_rsp_buf + 88) = MSG_SSID_20; + *(uint16_t *)(driver->apps_rsp_buf + 90) = MSG_SSID_20_LAST; + *(uint16_t *)(driver->apps_rsp_buf + 92) = MSG_SSID_21; + *(uint16_t *)(driver->apps_rsp_buf + 94) = MSG_SSID_21_LAST; + *(uint16_t *)(driver->apps_rsp_buf + 96) = MSG_SSID_22; + *(uint16_t *)(driver->apps_rsp_buf + 98) = MSG_SSID_22_LAST; + ENCODE_RSP_AND_SEND(99); return 0; } - /* Check for AO8960 Respond to Get Subsys Build mask */ - else if (!(driver->ch) && (chk_config_get_id() == AO8960_TOOLS_ID) + /* Check for Apps Only Respond to Get Subsys Build mask */ + else if (!(driver->ch) && chk_apps_only() && (*buf == 0x7d) && (*(buf+1) == 0x2)) { ssid_first = *(uint16_t *)(buf + 2); ssid_last = *(uint16_t *)(buf + 4); @@ -911,12 +1256,28 @@ static int diag_process_apps_pkt(unsigned char *buf, int len) for (i = 0; i < ssid_range; i += 4) *(int *)(ptr + i) = msg_bld_masks_18[i/4]; break; + case MSG_SSID_19: + for (i = 0; i < ssid_range; i += 4) + *(int *)(ptr + i) = msg_bld_masks_19[i/4]; + break; + case MSG_SSID_20: + for (i = 0; i < ssid_range; i += 4) + *(int *)(ptr + i) = msg_bld_masks_20[i/4]; + break; + case MSG_SSID_21: + for (i = 0; i < ssid_range; i += 4) + *(int *)(ptr + i) = msg_bld_masks_21[i/4]; + break; + case MSG_SSID_22: + for (i = 0; i < ssid_range; i += 4) + *(int *)(ptr + i) = msg_bld_masks_22[i/4]; + break; } ENCODE_RSP_AND_SEND(8 + ssid_range - 1); return 0; } /* Check for download command */ - else if ((cpu_is_msm8x60() || cpu_is_msm8960()) && (*buf == 0x3A)) { + else if ((cpu_is_msm8x60() || chk_apps_master()) && (*buf == 0x3A)) { /* send response back */ driver->apps_rsp_buf[0] = *buf; ENCODE_RSP_AND_SEND(0); @@ -971,6 +1332,11 @@ static int diag_process_apps_pkt(unsigned char *buf, int len) void diag_send_error_rsp(int index) { int i; + + if (index > 490) { + pr_err("diag: error response too huge, aborting\n"); + return; + } driver->apps_rsp_buf[0] = 0x13; /* error code 13 */ for (i = 0; i < index; i++) driver->apps_rsp_buf[i+1] = *(driver->hdlc_buf+i); @@ -984,10 +1350,6 @@ void diag_process_hdlc(void *data, unsigned len) { struct diag_hdlc_decode_type hdlc; int ret, type = 0; -#ifdef DIAG_DEBUG - int i; -#endif - pr_debug("diag: HDLC decode fn, len of data %d\n", len); hdlc.dest_ptr = driver->hdlc_buf; hdlc.dest_size = USB_MAX_OUT_BUF; @@ -1011,13 +1373,13 @@ void diag_process_hdlc(void *data, unsigned len) driver->debug_flag = 0; } /* send error responses from APPS for Central Routing */ - if (type == 1 && chk_config_get_id() == AO8960_TOOLS_ID) { + if (type == 1 && chk_apps_only()) { diag_send_error_rsp(hdlc.dest_idx); type = 0; } /* implies this packet is NOT meant for apps */ if (!(driver->ch) && type == 1) { - if (chk_config_get_id() == AO8960_TOOLS_ID) { + if (chk_apps_only()) { diag_send_error_rsp(hdlc.dest_idx); } else { /* APQ 8060, Let Q6 respond */ if (driver->chqdsp) @@ -1036,11 +1398,7 @@ void diag_process_hdlc(void *data, unsigned len) /* ignore 2 bytes for CRC, one for 7E and send */ if ((driver->ch) && (ret) && (type) && (hdlc.dest_idx > 3)) { APPEND_DEBUG('g'); -#ifdef CONFIG_MODEM_DIAG_MASTER - smd_write(driver->ch, data, len); -#else smd_write(driver->ch, driver->hdlc_buf, hdlc.dest_idx - 3); -#endif APPEND_DEBUG('h'); #ifdef DIAG_DEBUG printk(KERN_INFO "writing data to SMD, pkt length %d\n", len); @@ -1076,17 +1434,14 @@ int diagfwd_connect(void) queue_work(driver->diag_wq, &(driver->diag_read_smd_work)); queue_work(driver->diag_wq, &(driver->diag_read_smd_qdsp_work)); queue_work(driver->diag_wq, &(driver->diag_read_smd_wcnss_work)); - - if (chk_config_get_id() == AO8960_TOOLS_ID) { - /* Poll SMD CNTL channels to check for data */ - queue_work(driver->diag_wq, &(driver->diag_read_smd_cntl_work)); - queue_work(driver->diag_wq, &(driver->diag_read_smd_qdsp_cntl_work)); - queue_work(driver->diag_wq, &(driver->diag_read_smd_wcnss_cntl_work)); - } + /* Poll SMD CNTL channels to check for data */ + diag_smd_cntl_notify(NULL, SMD_EVENT_DATA); + diag_smd_qdsp_cntl_notify(NULL, SMD_EVENT_DATA); + diag_smd_wcnss_cntl_notify(NULL, SMD_EVENT_DATA); /* Poll USB channel to check for data*/ queue_work(driver->diag_wq, &(driver->diag_read_work)); #ifdef CONFIG_DIAG_SDIO_PIPE - if (diag_support_mdm9k) { + if (machine_is_msm8x60_fusion() || machine_is_msm8x60_fusn_ffa()) { if (driver->mdm_ch && !IS_ERR(driver->mdm_ch)) diagfwd_connect_sdio(); else @@ -1101,8 +1456,7 @@ int diagfwd_disconnect(void) printk(KERN_DEBUG "diag: USB disconnected\n"); driver->usb_connected = 0; driver->debug_flag = 1; - if (driver->usb_connected) - usb_diag_free_req(driver->legacy_ch); + usb_diag_free_req(driver->legacy_ch); if (driver->logging_mode == USB_MODE) { driver->in_busy_1 = 1; driver->in_busy_2 = 1; @@ -1111,7 +1465,7 @@ int diagfwd_disconnect(void) driver->in_busy_wcnss = 1; } #ifdef CONFIG_DIAG_SDIO_PIPE - if (diag_support_mdm9k) + if (machine_is_msm8x60_fusion() || machine_is_msm8x60_fusn_ffa()) if (driver->mdm_ch && !IS_ERR(driver->mdm_ch)) diagfwd_disconnect_sdio(); #endif @@ -1140,26 +1494,19 @@ int diagfwd_write_complete(struct diag_request *diag_write_ptr) driver->in_busy_qdsp_2 = 0; APPEND_DEBUG('P'); queue_work(driver->diag_wq, &(driver->diag_read_smd_qdsp_work)); - } else if (is_wcnss_used && buf == (void *)driver->buf_in_wcnss) { + } else if (buf == (void *)driver->buf_in_wcnss) { driver->in_busy_wcnss = 0; APPEND_DEBUG('R'); queue_work(driver->diag_wq, &(driver->diag_read_smd_wcnss_work)); -#if DIAG_XPST - } else if (driver->in_busy_dmrounter == 1) { - driver->in_busy_dmrounter = 0; -#endif } #ifdef CONFIG_DIAG_SDIO_PIPE - else if (buf == (void *)driver->buf_in_sdio_1) { - driver->in_busy_sdio_1 = 0; - APPEND_DEBUG('q'); - queue_work(driver->diag_sdio_wq, &(driver->diag_read_sdio_work)); - } else if (buf == (void *)driver->buf_in_sdio_2) { - driver->in_busy_sdio_2 = 0; - APPEND_DEBUG('Q'); - queue_work(driver->diag_sdio_wq, &(driver->diag_read_sdio_work)); - } + else if (buf == (void *)driver->buf_in_sdio) + if (machine_is_msm8x60_fusion() || + machine_is_msm8x60_fusn_ffa()) + diagfwd_write_complete_sdio(); + else + pr_err("diag: Incorrect buffer pointer while WRITE"); #endif else { diagmem_free(driver, (unsigned char *)buf, POOL_TYPE_HDLC); @@ -1186,14 +1533,6 @@ int diagfwd_read_complete(struct diag_request *diag_read_ptr) DUMP_PREFIX_ADDRESS, diag_read_ptr->buf, diag_read_ptr->actual, 1); #endif /* DIAG DEBUG */ -#if DIAG_XPST - if (driver->nohdlc) { - driver->usb_read_ptr->buf = driver->usb_buf_out; - driver->usb_read_ptr->length = USB_MAX_OUT_BUF; - usb_diag_read(driver->legacy_ch, driver->usb_read_ptr); - return 0; - } -#endif if (driver->logging_mode == USB_MODE) { if (status != -ECONNRESET && status != -ESHUTDOWN) queue_work(driver->diag_wq, @@ -1205,7 +1544,8 @@ int diagfwd_read_complete(struct diag_request *diag_read_ptr) } #ifdef CONFIG_DIAG_SDIO_PIPE else if (buf == (void *)driver->usb_buf_mdm_out) { - if (diag_support_mdm9k) { + if (machine_is_msm8x60_fusion() || + machine_is_msm8x60_fusn_ffa()) { driver->read_len_mdm = diag_read_ptr->actual; diagfwd_read_complete_sdio(); } else @@ -1267,11 +1607,7 @@ static void diag_smd_notify(void *ctxt, unsigned event) driver->ch = 0; return; } else if (event == SMD_EVENT_OPEN) { - if (ch_temp) - driver->ch = ch_temp; - else - DIAGFWD_INFO("%s: smd_open(%s):, ch_temp:%p, driver->ch:%p, &driver->ch:%p\n", - __func__, SMDDIAG_NAME, ch_temp, driver->ch, &driver->ch); + driver->ch = ch_temp; } queue_work(driver->diag_wq, &(driver->diag_read_smd_work)); } @@ -1279,74 +1615,54 @@ static void diag_smd_notify(void *ctxt, unsigned event) #if defined(CONFIG_MSM_N_WAY_SMD) static void diag_smd_qdsp_notify(void *ctxt, unsigned event) { + if (event == SMD_EVENT_CLOSE) { + pr_info("diag: clean lpass registration\n"); + diag_clear_reg(QDSP_PROC); + driver->chqdsp = 0; + return; + } else if (event == SMD_EVENT_OPEN) { + driver->chqdsp = chqdsp_temp; + } queue_work(driver->diag_wq, &(driver->diag_read_smd_qdsp_work)); } #endif static void diag_smd_wcnss_notify(void *ctxt, unsigned event) { - queue_work(driver->diag_wq, &(driver->diag_read_smd_wcnss_work)); -} - -#if DIAG_XPST -void diag_smd_enable(smd_channel_t *ch, char *src, int mode) -{ - int r = 0; - static smd_channel_t *_ch; - DIAGFWD_INFO("smd_try_open(%s): mode=%d\n", src, mode); - - mutex_lock(&driver->smd_lock); - diag_smd_function_mode = mode; - if (mode) { - if (!driver->ch) { - r = smd_open(SMDDIAG_NAME, &driver->ch, driver, diag_smd_notify); - if (!r) - _ch = driver->ch; - } else - _ch = driver->ch; - } else { - if (driver->ch) { - r = smd_close(driver->ch); - driver->ch = NULL; - if (!r) - _ch = driver->ch; - } + if (event == SMD_EVENT_CLOSE) { + pr_info("diag: clean wcnss registration\n"); + diag_clear_reg(WCNSS_PROC); + driver->ch_wcnss = 0; + return; + } else if (event == SMD_EVENT_OPEN) { + driver->ch_wcnss = ch_wcnss_temp; } - ch = _ch; - mutex_unlock(&driver->smd_lock); - DIAGFWD_INFO("smd_try_open(%s): r=%d _ch=%x\n", src, r, (unsigned int)ch); + queue_work(driver->diag_wq, &(driver->diag_read_smd_wcnss_work)); } -#endif static int diag_smd_probe(struct platform_device *pdev) { int r = 0; if (pdev->id == SMD_APPS_MODEM) { - r = smd_open(SMDDIAG_NAME, &driver->ch, driver, diag_smd_notify); - wmb(); + r = smd_open("DIAG", &driver->ch, driver, diag_smd_notify); ch_temp = driver->ch; - DIAGFWD_INFO("%s: smd_open(%s):%d, ch_temp:%p, driver->ch:%p, &driver->ch:%p\n", - __func__, SMDDIAG_NAME, r, ch_temp, driver->ch, &driver->ch); } #if defined(CONFIG_MSM_N_WAY_SMD) if (pdev->id == SMD_APPS_QDSP) { -#if defined(CONFIG_MACH_MECHA) || defined(CONFIG_ARCH_MSM8X60_LTE) \ - || defined(CONFIG_ARCH_MSM8X60) || defined(CONFIG_ARCH_MSM8960) r = smd_named_open_on_edge("DIAG", SMD_APPS_QDSP , &driver->chqdsp, driver, diag_smd_qdsp_notify); -#else - r = smd_open("DSP_DIAG", &driver->chqdsp, driver, diag_smd_qdsp_notify); -#endif + chqdsp_temp = driver->chqdsp; } #endif - if (pdev->id == SMD_APPS_WCNSS) + if (pdev->id == SMD_APPS_WCNSS) { r = smd_named_open_on_edge("APPS_RIVA_DATA", SMD_APPS_WCNSS , &driver->ch_wcnss, driver, diag_smd_wcnss_notify); + ch_wcnss_temp = driver->ch_wcnss; + } pm_runtime_set_active(&pdev->dev); pm_runtime_enable(&pdev->dev); pr_debug("diag: open SMD port, Id = %d, r = %d\n", pdev->id, r); - smd_diag_initialized = 1; return 0; } @@ -1372,7 +1688,7 @@ static struct platform_driver msm_smd_ch1_driver = { .probe = diag_smd_probe, .driver = { - .name = SMDDIAG_NAME, + .name = "DIAG", .owner = THIS_MODULE, .pm = &diagfwd_dev_pm_ops, }, @@ -1392,16 +1708,26 @@ void diagfwd_init(void) { diag_debug_buf_idx = 0; driver->read_len_legacy = 0; + mutex_init(&driver->diag_cntl_mutex); - /* FIXME: there should be a better way to know if wcnss enabled */ - if (chk_config_get_id() == AO8960_TOOLS_ID) { - is_wcnss_used = 1; - DIAGFWD_INFO("wcnss channel was enabled in the platform\n"); - } else { - is_wcnss_used = 0; - DIAGFWD_INFO("wcnss channel was not enabled in the platform\n"); + if (driver->event_mask == NULL) { + driver->event_mask = kzalloc(sizeof( + struct diag_ctrl_event_mask), GFP_KERNEL); + if (driver->event_mask == NULL) + goto err; + } + if (driver->msg_mask == NULL) { + driver->msg_mask = kzalloc(sizeof( + struct diag_ctrl_msg_mask), GFP_KERNEL); + if (driver->msg_mask == NULL) + goto err; + } + if (driver->log_mask == NULL) { + driver->log_mask = kzalloc(sizeof( + struct diag_ctrl_log_mask), GFP_KERNEL); + if (driver->log_mask == NULL) + goto err; } - if (driver->buf_in_1 == NULL) { driver->buf_in_1 = kzalloc(IN_BUF_SIZE, GFP_KERNEL); if (driver->buf_in_1 == NULL) @@ -1422,11 +1748,29 @@ void diagfwd_init(void) if (driver->buf_in_qdsp_2 == NULL) goto err; } - if (is_wcnss_used && driver->buf_in_wcnss == NULL) { + if (driver->buf_in_wcnss == NULL) { driver->buf_in_wcnss = kzalloc(IN_BUF_SIZE, GFP_KERNEL); if (driver->buf_in_wcnss == NULL) goto err; } + if (driver->buf_msg_mask_update == NULL) { + driver->buf_msg_mask_update = kzalloc(APPS_BUF_SIZE, + GFP_KERNEL); + if (driver->buf_msg_mask_update == NULL) + goto err; + } + if (driver->buf_log_mask_update == NULL) { + driver->buf_log_mask_update = kzalloc(APPS_BUF_SIZE, + GFP_KERNEL); + if (driver->buf_log_mask_update == NULL) + goto err; + } + if (driver->buf_event_mask_update == NULL) { + driver->buf_event_mask_update = kzalloc(APPS_BUF_SIZE, + GFP_KERNEL); + if (driver->buf_event_mask_update == NULL) + goto err; + } if (driver->usb_buf_out == NULL && (driver->usb_buf_out = kzalloc(USB_MAX_OUT_BUF, GFP_KERNEL)) == NULL) @@ -1442,10 +1786,12 @@ void diagfwd_init(void) && (driver->msg_masks = kzalloc(MSG_MASK_SIZE, GFP_KERNEL)) == NULL) goto err; + diag_create_msg_mask_table(); + diag_event_num_bytes = 0; if (driver->log_masks == NULL && (driver->log_masks = kzalloc(LOG_MASK_SIZE, GFP_KERNEL)) == NULL) goto err; - driver->log_masks_length = 8*MAX_EQUIP_ID; + driver->log_masks_length = (sizeof(struct mask_info))*MAX_EQUIP_ID; if (driver->event_masks == NULL && (driver->event_masks = kzalloc(EVENT_MASK_SIZE, GFP_KERNEL)) == NULL) @@ -1455,13 +1801,6 @@ void diagfwd_init(void) ((driver->num_clients) * sizeof(struct diag_client_map), GFP_KERNEL)) == NULL) goto err; -#ifdef CONFIG_DIAG_SDIO_PIPE - if (driver->mdmclient_map == NULL && - (driver->mdmclient_map = kzalloc - ((driver->num_mdmclients) * sizeof(struct diag_client_map), - GFP_KERNEL)) == NULL) - goto err; -#endif if (driver->buf_tbl == NULL) driver->buf_tbl = kzalloc(buf_tbl_size * sizeof(struct diag_write_device), GFP_KERNEL); @@ -1471,14 +1810,8 @@ void diagfwd_init(void) (driver->data_ready = kzalloc(driver->num_clients * sizeof(int) , GFP_KERNEL)) == NULL) goto err; -#ifdef CONFIG_DIAG_SDIO_PIPE - if (driver->mdmdata_ready == NULL && - (driver->mdmdata_ready = kzalloc(driver->num_mdmclients * sizeof(struct - diag_client_map), GFP_KERNEL)) == NULL) - goto err; -#endif if (driver->table == NULL && - (driver->table = kzalloc(diag_max_registration* + (driver->table = kzalloc(diag_max_reg* sizeof(struct diag_master_table), GFP_KERNEL)) == NULL) goto err; @@ -1523,7 +1856,7 @@ void diagfwd_init(void) GFP_KERNEL)) == NULL) goto err; if (driver->apps_rsp_buf == NULL) { - driver->apps_rsp_buf = kzalloc(500, GFP_KERNEL); + driver->apps_rsp_buf = kzalloc(APPS_BUF_SIZE, GFP_KERNEL); if (driver->apps_rsp_buf == NULL) goto err; } @@ -1531,15 +1864,18 @@ void diagfwd_init(void) #ifdef CONFIG_DIAG_OVER_USB INIT_WORK(&(driver->diag_proc_hdlc_work), diag_process_hdlc_fn); INIT_WORK(&(driver->diag_read_work), diag_read_work_fn); + INIT_WORK(&(driver->diag_modem_mask_update_work), + diag_modem_mask_update_fn); + INIT_WORK(&(driver->diag_qdsp_mask_update_work), + diag_qdsp_mask_update_fn); + INIT_WORK(&(driver->diag_wcnss_mask_update_work), + diag_wcnss_mask_update_fn); driver->legacy_ch = usb_diag_open(DIAG_LEGACY, driver, diag_usb_legacy_notifier); if (IS_ERR(driver->legacy_ch)) { printk(KERN_ERR "Unable to open USB diag legacy channel\n"); goto err; } -#endif -#if DIAG_XPST - mutex_init(&driver->smd_lock); #endif platform_driver_register(&msm_smd_ch1_driver); platform_driver_register(&diag_smd_lite_driver); @@ -1547,11 +1883,17 @@ void diagfwd_init(void) return; err: pr_err("diag: Could not initialize diag buffers"); + kfree(driver->event_mask); + kfree(driver->log_mask); + kfree(driver->msg_mask); kfree(driver->buf_in_1); kfree(driver->buf_in_2); kfree(driver->buf_in_qdsp_1); kfree(driver->buf_in_qdsp_2); kfree(driver->buf_in_wcnss); + kfree(driver->buf_msg_mask_update); + kfree(driver->buf_log_mask_update); + kfree(driver->buf_event_mask_update); kfree(driver->usb_buf_out); kfree(driver->hdlc_buf); kfree(driver->msg_masks); @@ -1582,7 +1924,6 @@ void diagfwd_exit(void) driver->ch = 0; /* SMD can make this NULL */ driver->chqdsp = 0; driver->ch_wcnss = 0; - smd_diag_initialized = 0; #ifdef CONFIG_DIAG_OVER_USB if (driver->usb_connected) usb_diag_free_req(driver->legacy_ch); @@ -1590,11 +1931,17 @@ void diagfwd_exit(void) #endif platform_driver_unregister(&msm_smd_ch1_driver); platform_driver_unregister(&diag_smd_lite_driver); + kfree(driver->event_mask); + kfree(driver->log_mask); + kfree(driver->msg_mask); kfree(driver->buf_in_1); kfree(driver->buf_in_2); kfree(driver->buf_in_qdsp_1); kfree(driver->buf_in_qdsp_2); kfree(driver->buf_in_wcnss); + kfree(driver->buf_msg_mask_update); + kfree(driver->buf_log_mask_update); + kfree(driver->buf_event_mask_update); kfree(driver->usb_buf_out); kfree(driver->hdlc_buf); kfree(driver->msg_masks); diff --git a/drivers/char/diag/diagfwd.h b/drivers/char/diag/diagfwd.h index 0843bb399a3..574445915dc 100644 --- a/drivers/char/diag/diagfwd.h +++ b/drivers/char/diag/diagfwd.h @@ -1,5 +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 @@ -17,11 +16,6 @@ #define NO_PROCESS 0 #define NON_APPS_PROC -1 -#define DIAGLOG_MODE_NONE 0 -#define DIAGLOG_MODE_HEAD 1 -#define DIAGLOG_MODE_FULL 2 -#define DIAGLOG_MODE_PING 3 - void diagfwd_init(void); void diagfwd_exit(void); void diag_process_hdlc(void *data, unsigned len); @@ -32,23 +26,18 @@ void diag_usb_legacy_notifier(void *, unsigned, struct diag_request *); long diagchar_ioctl(struct file *, unsigned int, unsigned long); int diag_device_write(void *, int, struct diag_request *); int mask_request_validate(unsigned char mask_buf[]); -int chk_config_get_id(void); void diag_clear_reg(int); - +int chk_apps_only(void); +void diag_send_event_mask_update(smd_channel_t *, int num_bytes); +void diag_send_msg_mask_update(smd_channel_t *, int ssid_first, + int ssid_last, int proc); +void diag_send_log_mask_update(smd_channel_t *, int); /* State for diag forwarding */ #ifdef CONFIG_DIAG_OVER_USB int diagfwd_connect(void); int diagfwd_disconnect(void); #endif -extern int diag_support_mdm9k; extern int diag_debug_buf_idx; extern unsigned char diag_debug_buf[1024]; -extern unsigned diag7k_debug_mask; -extern unsigned diag9k_debug_mask; - -#define SMD_FUNC_CLOSE 0 -#define SMD_FUNC_OPEN_DIAG 1 -#define SMD_FUNC_OPEN_BT 2 -void diag_smd_enable(smd_channel_t *ch, char *src, int mode); - +extern int diag_event_num_bytes; #endif diff --git a/drivers/char/diag/diagfwd_cntl.c b/drivers/char/diag/diagfwd_cntl.c index 13c3c478a93..fcf16d05838 100644 --- a/drivers/char/diag/diagfwd_cntl.c +++ b/drivers/char/diag/diagfwd_cntl.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 @@ -16,12 +16,81 @@ #include "diagchar.h" #include "diagfwd.h" #include "diagfwd_cntl.h" -#ifdef CONFIG_DIAG_OVER_USB -#include -#endif #define HDR_SIZ 8 +void diag_smd_cntl_notify(void *ctxt, unsigned event) +{ + int r1, r2; + + if (!(driver->ch_cntl)) + return; + + switch (event) { + case SMD_EVENT_DATA: + r1 = smd_read_avail(driver->ch_cntl); + r2 = smd_cur_packet_size(driver->ch_cntl); + if (r1 > 0 && r1 == r2) + queue_work(driver->diag_wq, + &(driver->diag_read_smd_cntl_work)); + else + pr_debug("diag: incomplete pkt on Modem CNTL ch\n"); + break; + case SMD_EVENT_OPEN: + queue_work(driver->diag_cntl_wq, + &(driver->diag_modem_mask_update_work)); + break; + } +} + +void diag_smd_qdsp_cntl_notify(void *ctxt, unsigned event) +{ + int r1, r2; + + if (!(driver->chqdsp_cntl)) + return; + + switch (event) { + case SMD_EVENT_DATA: + r1 = smd_read_avail(driver->chqdsp_cntl); + r2 = smd_cur_packet_size(driver->chqdsp_cntl); + if (r1 > 0 && r1 == r2) + queue_work(driver->diag_wq, + &(driver->diag_read_smd_qdsp_cntl_work)); + else + pr_debug("diag: incomplete pkt on LPASS CNTL ch\n"); + break; + case SMD_EVENT_OPEN: + queue_work(driver->diag_cntl_wq, + &(driver->diag_qdsp_mask_update_work)); + break; + } +} + +void diag_smd_wcnss_cntl_notify(void *ctxt, unsigned event) +{ + int r1, r2; + + if (!(driver->ch_wcnss_cntl)) + return; + + switch (event) { + case SMD_EVENT_DATA: + r1 = smd_read_avail(driver->ch_wcnss_cntl); + r2 = smd_cur_packet_size(driver->ch_wcnss_cntl); + if (r1 > 0 && r1 == r2) + queue_work(driver->diag_wq, + &(driver->diag_read_smd_wcnss_cntl_work)); + else + pr_debug("diag: incomplete pkt on WCNSS CNTL ch\n"); + break; + case SMD_EVENT_OPEN: + queue_work(driver->diag_cntl_wq, + &(driver->diag_wcnss_mask_update_work)); + break; + } +} + static void diag_smd_cntl_send_req(int proc_num) { int data_len = 0, type = -1, count_bytes = 0, j, r, flag = 0; @@ -30,12 +99,13 @@ static void diag_smd_cntl_send_req(int proc_num) struct diag_ctrl_msg *msg; struct cmd_code_range *range; struct bindpkt_params *temp; - void *buf = NULL, *dump_buf = NULL; + void *buf = NULL; smd_channel_t *smd_ch = NULL; - DIAG_INFO("%s: %s\n", __func__, - (proc_num == MODEM_PROC)?"MODEM_PROC": - (proc_num == QDSP_PROC)?"QDSP_PROC":"WCNSS_PROC"); + if (pkt_params == NULL) { + pr_alert("diag: Memory allocation failure\n"); + return; + } if (proc_num == MODEM_PROC) { buf = driver->buf_in_cntl; @@ -69,24 +139,29 @@ static void diag_smd_cntl_send_req(int proc_num) while (count_bytes + HDR_SIZ <= r) { type = *(uint32_t *)(buf); data_len = *(uint32_t *)(buf + 4); + if (type < DIAG_CTRL_MSG_REG || + type > DIAG_CTRL_MSG_F3_MASK_V2) { + pr_alert("diag: Invalid Msg type %d proc %d", + type, proc_num); + break; + } + if (data_len < 0 || data_len > r) { + pr_alert("diag: Invalid data len %d proc %d", + data_len, proc_num); + break; + } count_bytes = count_bytes+HDR_SIZ+data_len; if (type == DIAG_CTRL_MSG_REG && r >= count_bytes) { msg = buf+HDR_SIZ; - if (!msg->count_entries) { - DIAG_ERR("version: %d, cmd_code: %d," - " subsysid: %d, count_entries: %d," - " port:%d\n", msg->version, - msg->cmd_code, msg->subsysid, - msg->count_entries, msg->port); - dump_buf = kmalloc(r, GFP_KERNEL); - memcpy(dump_buf, buf, r); - continue; - } range = buf+HDR_SIZ+ sizeof(struct diag_ctrl_msg); pkt_params->count = msg->count_entries; temp = kzalloc(pkt_params->count * sizeof(struct bindpkt_params), GFP_KERNEL); + if (temp == NULL) { + pr_alert("diag: Memory alloc fail\n"); + return; + } for (j = 0; j < pkt_params->count; j++) { temp->cmd_code = msg->cmd_code; temp->subsys_id = msg->subsysid; @@ -103,23 +178,19 @@ static void diag_smd_cntl_send_req(int proc_num) diagchar_ioctl(NULL, DIAG_IOCTL_COMMAND_REG, (unsigned long)pkt_params); kfree(temp); - buf = buf + HDR_SIZ + data_len; } + buf = buf + HDR_SIZ + data_len; } } - if (dump_buf) { - print_hex_dump(KERN_DEBUG, "diag_debug_buf:", - 16, 1, DUMP_PREFIX_ADDRESS, dump_buf, r, 1); - kfree(dump_buf); - } kfree(pkt_params); if (flag) { /* Poll SMD CNTL channels to check for data */ - queue_work(driver->diag_wq, &(driver->diag_read_smd_cntl_work)); - queue_work(driver->diag_wq, - &(driver->diag_read_smd_qdsp_cntl_work)); - queue_work(driver->diag_wq, - &(driver->diag_read_smd_wcnss_cntl_work)); + if (proc_num == MODEM_PROC) + diag_smd_cntl_notify(NULL, SMD_EVENT_DATA); + else if (proc_num == QDSP_PROC) + diag_smd_qdsp_cntl_notify(NULL, SMD_EVENT_DATA); + else if (proc_num == WCNSS_PROC) + diag_smd_wcnss_cntl_notify(NULL, SMD_EVENT_DATA); } } @@ -138,27 +209,12 @@ void diag_read_smd_wcnss_cntl_work_fn(struct work_struct *work) diag_smd_cntl_send_req(WCNSS_PROC); } -static void diag_smd_cntl_notify(void *ctxt, unsigned event) -{ - queue_work(driver->diag_wq, &(driver->diag_read_smd_cntl_work)); -} - -static void diag_smd_qdsp_cntl_notify(void *ctxt, unsigned event) -{ - queue_work(driver->diag_wq, &(driver->diag_read_smd_qdsp_cntl_work)); -} - -static void diag_smd_wcnss_cntl_notify(void *ctxt, unsigned event) -{ - queue_work(driver->diag_wq, &(driver->diag_read_smd_wcnss_cntl_work)); -} - static int diag_smd_cntl_probe(struct platform_device *pdev) { int r = 0; - /* open control ports only on 8960 */ - if (chk_config_get_id() == AO8960_TOOLS_ID) { + /* open control ports only on 8960 & newer targets */ + if (chk_apps_only()) { if (pdev->id == SMD_APPS_MODEM) r = smd_open("DIAG_CNTL", &driver->ch_cntl, driver, diag_smd_cntl_notify); @@ -214,6 +270,7 @@ static struct platform_driver diag_smd_lite_cntl_driver = { void diagfwd_cntl_init(void) { + driver->diag_cntl_wq = create_singlethread_workqueue("diag_cntl_wq"); if (driver->buf_in_cntl == NULL) { driver->buf_in_cntl = kzalloc(IN_BUF_SIZE, GFP_KERNEL); if (driver->buf_in_cntl == NULL) @@ -238,6 +295,8 @@ void diagfwd_cntl_init(void) kfree(driver->buf_in_cntl); kfree(driver->buf_in_qdsp_cntl); kfree(driver->buf_in_wcnss_cntl); + if (driver->diag_cntl_wq) + destroy_workqueue(driver->diag_cntl_wq); } void diagfwd_cntl_exit(void) @@ -248,6 +307,7 @@ void diagfwd_cntl_exit(void) driver->ch_cntl = 0; driver->chqdsp_cntl = 0; driver->ch_wcnss_cntl = 0; + destroy_workqueue(driver->diag_cntl_wq); platform_driver_unregister(&msm_smd_ch1_cntl_driver); platform_driver_unregister(&diag_smd_lite_cntl_driver); diff --git a/drivers/char/diag/diagfwd_cntl.h b/drivers/char/diag/diagfwd_cntl.h index 542138df1f6..ad1fec967b2 100644 --- a/drivers/char/diag/diagfwd_cntl.h +++ b/drivers/char/diag/diagfwd_cntl.h @@ -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 @@ -13,7 +13,22 @@ #ifndef DIAGFWD_CNTL_H #define DIAGFWD_CNTL_H -#define DIAG_CTRL_MSG_REG 1 /* Message registration commands */ +/* Message registration commands */ +#define DIAG_CTRL_MSG_REG 1 +/* Message passing for DTR events */ +#define DIAG_CTRL_MSG_DTR 2 +/* Control Diag sleep vote, buffering etc */ +#define DIAG_CTRL_MSG_DIAGMODE 3 +/* Diag data based on "light" diag mask */ +#define DIAG_CTRL_MSG_DIAGDATA 4 +/* Send diag internal feature mask 'diag_int_feature_mask' */ +#define DIAG_CTRL_MSG_FEATURE 8 +/* Send Diag log mask for a particular equip id */ +#define DIAG_CTRL_MSG_EQUIP_LOG_MASK 9 +/* Send Diag event mask */ +#define DIAG_CTRL_MSG_EVENT_MASK_V2 10 +/* Send Diag F3 mask */ +#define DIAG_CTRL_MSG_F3_MASK_V2 11 struct cmd_code_range { uint16_t cmd_code_lo; @@ -29,10 +44,46 @@ struct diag_ctrl_msg { uint16_t port; }; +struct diag_ctrl_event_mask { + uint32_t cmd_type; + uint32_t data_len; + uint8_t stream_id; + uint8_t status; + uint8_t event_config; + uint32_t event_mask_size; + /* Copy event mask here */ +} __packed; + +struct diag_ctrl_log_mask { + uint32_t cmd_type; + uint32_t data_len; + uint8_t stream_id; + uint8_t status; + uint8_t equip_id; + uint32_t num_items; /* Last log code for this equip_id */ + uint32_t log_mask_size; /* Size of log mask stored in log_mask[] */ + /* Copy log mask here */ +} __packed; + +struct diag_ctrl_msg_mask { + uint32_t cmd_type; + uint32_t data_len; + uint8_t stream_id; + uint8_t status; + uint8_t msg_mode; + uint16_t ssid_first; /* Start of range of supported SSIDs */ + uint16_t ssid_last; /* Last SSID in range */ + uint32_t msg_mask_size; /* ssid_last - ssid_first + 1 */ + /* Copy msg mask here */ +} __packed; + void diagfwd_cntl_init(void); void diagfwd_cntl_exit(void); void diag_read_smd_cntl_work_fn(struct work_struct *); void diag_read_smd_qdsp_cntl_work_fn(struct work_struct *); void diag_read_smd_wcnss_cntl_work_fn(struct work_struct *); +void diag_smd_cntl_notify(void *ctxt, unsigned event); +void diag_smd_qdsp_cntl_notify(void *ctxt, unsigned event); +void diag_smd_wcnss_cntl_notify(void *ctxt, unsigned event); #endif diff --git a/drivers/char/diag/diagfwd_hsic.c b/drivers/char/diag/diagfwd_hsic.c new file mode 100644 index 00000000000..ac5722f3b8b --- /dev/null +++ b/drivers/char/diag/diagfwd_hsic.c @@ -0,0 +1,530 @@ +/* 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 +#include +#include +#include +#include +#ifdef CONFIG_DIAG_OVER_USB +#include +#endif +#include "diagchar_hdlc.h" +#include "diagmem.h" +#include "diagchar.h" +#include "diagfwd.h" +#include "diagfwd_hsic.h" + +static void diag_read_hsic_work_fn(struct work_struct *work) +{ + if (!driver->hsic_ch) { + pr_err("DIAG in %s: driver->hsic_ch == 0\n", __func__); + return; + } + + /* + * If there is no hsic data being read from the hsic and there + * is no hsic data being written to the usb mdm channel + */ + if (!driver->in_busy_hsic_read && !driver->in_busy_hsic_write_on_mdm) { + /* + * Initiate the read from the hsic. The hsic read is + * asynchronous. Once the read is complete the read + * callback function will be called. + */ + int err; + driver->in_busy_hsic_read = 1; + APPEND_DEBUG('i'); + err = diag_bridge_read((char *)driver->buf_in_hsic, + IN_BUF_SIZE); + if (err) { + pr_err("DIAG: Error initiating HSIC read, err: %d\n", + err); + /* + * If the error is recoverable, then clear + * the read flag, so we will resubmit a + * read on the next frame. Otherwise, don't + * resubmit a read on the next frame. + */ + if ((-ESHUTDOWN) != err) + driver->in_busy_hsic_read = 0; + } + } + + /* + * If for some reason there was no hsic data, set up + * the next read + */ + if (!driver->in_busy_hsic_read) + queue_work(driver->diag_hsic_wq, &driver->diag_read_hsic_work); +} + +static void diag_hsic_read_complete_callback(void *ctxt, char *buf, + int buf_size, int actual_size) +{ + /* The read of the data from the HSIC bridge is complete */ + driver->in_busy_hsic_read = 0; + + if (!driver->hsic_ch) { + pr_err("DIAG in %s: driver->hsic_ch == 0\n", __func__); + return; + } + + APPEND_DEBUG('j'); + if (actual_size > 0) { + if (!buf) { + pr_err("Out of diagmem for HSIC\n"); + } else { + driver->write_ptr_mdm->length = actual_size; + /* + * Set flag to denote hsic data is currently + * being written to the usb mdm channel. + * driver->buf_in_hsic was given to + * diag_bridge_read(), so buf here should be + * driver->buf_in_hsic + */ + driver->in_busy_hsic_write_on_mdm = 1; + diag_device_write((void *)buf, HSIC_DATA, + driver->write_ptr_mdm); + } + } else { + pr_err("DIAG in %s: actual_size: %d\n", __func__, actual_size); + } + + /* + * If for some reason there was no hsic data to write to the + * mdm channel, set up another read + */ + if (!driver->in_busy_hsic_write_on_mdm) + queue_work(driver->diag_hsic_wq, &driver->diag_read_hsic_work); +} + +static void diag_hsic_write_complete_callback(void *ctxt, char *buf, + int buf_size, int actual_size) +{ + /* The write of the data to the HSIC bridge is complete */ + driver->in_busy_hsic_write = 0; + + if (!driver->hsic_ch) { + pr_err("DIAG in %s: driver->hsic_ch == 0\n", __func__); + return; + } + + if (actual_size < 0) + pr_err("DIAG in %s: actual_size: %d\n", __func__, actual_size); + + queue_work(driver->diag_hsic_wq, &driver->diag_read_mdm_work); +} + +static struct diag_bridge_ops hsic_diag_bridge_ops = { + .ctxt = NULL, + .read_complete_cb = diag_hsic_read_complete_callback, + .write_complete_cb = diag_hsic_write_complete_callback, +}; + +static int diag_hsic_close(void) +{ + if (driver->hsic_device_enabled) { + driver->hsic_ch = 0; + if (driver->hsic_device_opened) { + driver->hsic_device_opened = 0; + diag_bridge_close(); + } + pr_debug("DIAG in %s: closed successfully\n", __func__); + } else { + pr_debug("DIAG in %s: already closed\n", __func__); + } + + return 0; +} + +/* diagfwd_connect_hsic is called when the USB mdm channel is connected */ +static int diagfwd_connect_hsic(void) +{ + int err; + + pr_debug("DIAG in %s\n", __func__); + + err = usb_diag_alloc_req(driver->mdm_ch, N_MDM_WRITE, N_MDM_READ); + if (err) + pr_err("DIAG: unable to alloc USB req on mdm ch err:%d\n", err); + + driver->usb_mdm_connected = 1; + driver->in_busy_hsic_write_on_mdm = 0; + driver->in_busy_hsic_read_on_mdm = 0; + driver->in_busy_hsic_write = 0; + driver->in_busy_hsic_read = 0; + + /* If the hsic (diag_bridge) platform device is not open */ + if (driver->hsic_device_enabled) { + if (!driver->hsic_device_opened) { + err = diag_bridge_open(&hsic_diag_bridge_ops); + if (err) { + pr_err("DIAG: HSIC channel open error: %d\n", + err); + } else { + pr_info("DIAG: opened HSIC channel\n"); + driver->hsic_device_opened = 1; + } + } else { + pr_info("DIAG: HSIC channel already open\n"); + } + + /* + * Turn on communication over usb mdm and hsic, if the hsic + * device driver is enabled and opened + */ + if (driver->hsic_device_opened) + driver->hsic_ch = 1; + + /* Poll USB mdm channel to check for data */ + queue_work(driver->diag_hsic_wq, &driver->diag_read_mdm_work); + + /* Poll HSIC channel to check for data */ + queue_work(driver->diag_hsic_wq, &driver->diag_read_hsic_work); + } else { + /* The hsic device driver has not yet been enabled */ + pr_info("DIAG: HSIC channel not yet enabled\n"); + } + + return 0; +} + +/* + * diagfwd_disconnect_hsic is called when the USB mdm channel + * is disconnected + */ +static int diagfwd_disconnect_hsic(void) +{ + pr_debug("DIAG in %s\n", __func__); + + driver->usb_mdm_connected = 0; + usb_diag_free_req(driver->mdm_ch); + driver->in_busy_hsic_write_on_mdm = 1; + driver->in_busy_hsic_read_on_mdm = 1; + driver->in_busy_hsic_write = 1; + driver->in_busy_hsic_read = 1; + + /* Turn off communication over usb mdm and hsic */ + driver->hsic_ch = 0; + + return 0; +} + +/* + * diagfwd_write_complete_hsic is called after the asynchronous + * usb_diag_write() on mdm channel is complete + */ +static int diagfwd_write_complete_hsic(void) +{ + /* + * Clear flag to denote that the write of the hsic data on the + * usb mdm channel is complete + */ + driver->in_busy_hsic_write_on_mdm = 0; + + if (!driver->hsic_ch) { + pr_err("DIAG in %s: driver->hsic_ch == 0\n", __func__); + return 0; + } + + APPEND_DEBUG('q'); + + /* Read data from the hsic */ + queue_work(driver->diag_hsic_wq, &driver->diag_read_hsic_work); + + return 0; +} + +/* Called after the asychronous usb_diag_read() on mdm channel is complete */ +static int diagfwd_read_complete_hsic(struct diag_request *diag_read_ptr) +{ + /* The read of the usb driver on the mdm (not hsic) has completed */ + driver->in_busy_hsic_read_on_mdm = 0; + driver->read_len_mdm = diag_read_ptr->actual; + + if (!driver->hsic_ch) { + pr_err("DIAG in %s: driver->hsic_ch == 0\n", __func__); + return 0; + } + + /* + * The read of the usb driver on the mdm channel has completed. + * If there is no write on the hsic in progress, check if the + * read has data to pass on to the hsic. If so, pass the usb + * mdm data on to the hsic. + */ + if (!driver->in_busy_hsic_write && driver->usb_buf_mdm_out && + (driver->read_len_mdm > 0)) { + + /* + * Initiate the hsic write. The hsic write is + * asynchronous. When complete the write + * complete callback function will be called + */ + int err; + driver->in_busy_hsic_write = 1; + err = diag_bridge_write(driver->usb_buf_mdm_out, + driver->read_len_mdm); + if (err) { + pr_err("DIAG: mdm data on hsic write err: %d\n", err); + /* + * If the error is recoverable, then clear + * the write flag, so we will resubmit a + * write on the next frame. Otherwise, don't + * resubmit a write on the next frame. + */ + if ((-ESHUTDOWN) != err) + driver->in_busy_hsic_write = 0; + } + } + + /* + * If there is no write of the usb mdm data on the + * hsic channel + */ + if (!driver->in_busy_hsic_write) + queue_work(driver->diag_hsic_wq, &driver->diag_read_mdm_work); + + return 0; +} + +static void diagfwd_hsic_notifier(void *priv, unsigned event, + struct diag_request *d_req) +{ + switch (event) { + case USB_DIAG_CONNECT: + diagfwd_connect_hsic(); + break; + case USB_DIAG_DISCONNECT: + diagfwd_disconnect_hsic(); + break; + case USB_DIAG_READ_DONE: + diagfwd_read_complete_hsic(d_req); + break; + case USB_DIAG_WRITE_DONE: + diagfwd_write_complete_hsic(); + break; + default: + pr_err("DIAG in %s: Unknown event from USB diag:%u\n", + __func__, event); + break; + } +} + +static void diag_read_mdm_work_fn(struct work_struct *work) +{ + if (!driver->hsic_ch) { + pr_err("DIAG in %s: driver->hsic_ch == 0\n", __func__); + return; + } + + /* + * If there is no data being read from the usb mdm channel + * and there is no mdm channel data currently being written + * to the hsic + */ + if (!driver->in_busy_hsic_read_on_mdm && !driver->in_busy_hsic_write) { + APPEND_DEBUG('x'); + + /* Setup the next read from usb mdm channel */ + driver->in_busy_hsic_read_on_mdm = 1; + driver->usb_read_mdm_ptr->buf = driver->usb_buf_mdm_out; + driver->usb_read_mdm_ptr->length = USB_MAX_OUT_BUF; + usb_diag_read(driver->mdm_ch, driver->usb_read_mdm_ptr); + APPEND_DEBUG('y'); + } + + /* + * If for some reason there was no mdm channel read initiated, + * queue up the reading of data from the mdm channel + */ + if (!driver->in_busy_hsic_read_on_mdm) + queue_work(driver->diag_hsic_wq, &driver->diag_read_mdm_work); +} + +int diag_hsic_enable(void) +{ + pr_debug("DIAG in %s\n", __func__); + + driver->read_len_mdm = 0; + if (driver->buf_in_hsic == NULL) + driver->buf_in_hsic = kzalloc(IN_BUF_SIZE, GFP_KERNEL); + if (driver->buf_in_hsic == NULL) + goto err; + if (driver->usb_buf_mdm_out == NULL) + driver->usb_buf_mdm_out = kzalloc(USB_MAX_OUT_BUF, GFP_KERNEL); + if (driver->usb_buf_mdm_out == NULL) + goto err; + if (driver->write_ptr_mdm == NULL) + driver->write_ptr_mdm = kzalloc( + sizeof(struct diag_request), GFP_KERNEL); + if (driver->write_ptr_mdm == NULL) + goto err; + if (driver->usb_read_mdm_ptr == NULL) + driver->usb_read_mdm_ptr = kzalloc( + sizeof(struct diag_request), GFP_KERNEL); + if (driver->usb_read_mdm_ptr == NULL) + goto err; + driver->diag_hsic_wq = create_singlethread_workqueue("diag_hsic_wq"); +#ifdef CONFIG_DIAG_OVER_USB + INIT_WORK(&(driver->diag_read_mdm_work), diag_read_mdm_work_fn); +#endif + INIT_WORK(&(driver->diag_read_hsic_work), diag_read_hsic_work_fn); + + driver->hsic_device_enabled = 1; + + return 0; +err: + pr_err("DIAG could not initialize buf for HSIC\n"); + kfree(driver->buf_in_hsic); + kfree(driver->usb_buf_mdm_out); + kfree(driver->write_ptr_mdm); + kfree(driver->usb_read_mdm_ptr); + if (driver->diag_hsic_wq) + destroy_workqueue(driver->diag_hsic_wq); + + return -ENOMEM; +} + +static int diag_hsic_probe(struct platform_device *pdev) +{ + int err; + + if (!driver->hsic_device_enabled) { + err = diag_hsic_enable(); + if (err) { + pr_err("DIAG could not enable HSIC, err: %d\n", err); + return err; + } + } + + /* The hsic (diag_bridge) platform device driver is enabled */ + err = diag_bridge_open(&hsic_diag_bridge_ops); + if (err) { + pr_err("DIAG could not open HSIC channel, err: %d\n", err); + driver->hsic_device_opened = 0; + return err; + } + + pr_info("DIAG opened HSIC channel\n"); + driver->hsic_device_opened = 1; + + /* + * The probe function was called after the usb was connected + * on the legacy channel. Communication over usb mdm and hsic + * needs to be turned on. + */ + if (driver->usb_connected) { + driver->hsic_ch = 1; + driver->in_busy_hsic_write_on_mdm = 0; + driver->in_busy_hsic_read_on_mdm = 0; + driver->in_busy_hsic_write = 0; + driver->in_busy_hsic_read = 0; + + /* Poll USB mdm channel to check for data */ + queue_work(driver->diag_hsic_wq, &driver->diag_read_mdm_work); + + /* Poll HSIC channel to check for data */ + queue_work(driver->diag_hsic_wq, &driver->diag_read_hsic_work); + } + + return err; +} + +static int diag_hsic_remove(struct platform_device *pdev) +{ + pr_info("DIAG: %s called\n", __func__); + diag_hsic_close(); + return 0; +} + +static int diagfwd_hsic_runtime_suspend(struct device *dev) +{ + dev_dbg(dev, "pm_runtime: suspending...\n"); + return 0; +} + +static int diagfwd_hsic_runtime_resume(struct device *dev) +{ + dev_dbg(dev, "pm_runtime: resuming...\n"); + return 0; +} + +static const struct dev_pm_ops diagfwd_hsic_dev_pm_ops = { + .runtime_suspend = diagfwd_hsic_runtime_suspend, + .runtime_resume = diagfwd_hsic_runtime_resume, +}; + +static struct platform_driver msm_hsic_ch_driver = { + .probe = diag_hsic_probe, + .remove = diag_hsic_remove, + .driver = { + .name = "diag_bridge", + .owner = THIS_MODULE, + .pm = &diagfwd_hsic_dev_pm_ops, + }, +}; + + +void __init diagfwd_hsic_init(void) +{ + int ret; + + pr_debug("DIAG in %s\n", __func__); + +#ifdef CONFIG_DIAG_OVER_USB + driver->mdm_ch = usb_diag_open(DIAG_MDM, driver, diagfwd_hsic_notifier); + if (IS_ERR(driver->mdm_ch)) { + pr_err("DIAG Unable to open USB diag MDM channel\n"); + goto err; + } +#endif + ret = platform_driver_register(&msm_hsic_ch_driver); + if (ret) + pr_err("DIAG could not register HSIC device, ret: %d\n", ret); + else + driver->hsic_initialized = 1; + + return; +err: + pr_err("DIAG could not initialize for HSIC execution\n"); +} + +void __exit diagfwd_hsic_exit(void) +{ + pr_debug("DIAG in %s\n", __func__); + + if (driver->hsic_initialized) + diag_hsic_close(); + +#ifdef CONFIG_DIAG_OVER_USB + if (driver->usb_mdm_connected) + usb_diag_free_req(driver->mdm_ch); +#endif + platform_driver_unregister(&msm_hsic_ch_driver); +#ifdef CONFIG_DIAG_OVER_USB + usb_diag_close(driver->mdm_ch); +#endif + kfree(driver->buf_in_hsic); + kfree(driver->usb_buf_mdm_out); + kfree(driver->write_ptr_mdm); + kfree(driver->usb_read_mdm_ptr); + destroy_workqueue(driver->diag_hsic_wq); + + driver->hsic_device_enabled = 0; +} diff --git a/drivers/char/diag/diagfwd_hsic.h b/drivers/char/diag/diagfwd_hsic.h new file mode 100644 index 00000000000..6769052590b --- /dev/null +++ b/drivers/char/diag/diagfwd_hsic.h @@ -0,0 +1,23 @@ +/* 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 DIAGFWD_HSIC_H +#define DIAGFWD_HSIC_H + +#include +#define N_MDM_WRITE 1 /* Upgrade to 2 with ping pong buffer */ +#define N_MDM_READ 1 + +void __init diagfwd_hsic_init(void); +void __exit diagfwd_hsic_exit(void); + +#endif diff --git a/drivers/char/diag/diagfwd_sdio.c b/drivers/char/diag/diagfwd_sdio.c index 84b662d9986..a145c0665e7 100644 --- a/drivers/char/diag/diagfwd_sdio.c +++ b/drivers/char/diag/diagfwd_sdio.c @@ -32,32 +32,19 @@ void __diag_sdio_send_req(void) { int r = 0; - void *buf = NULL; - int *in_busy_ptr = NULL; - struct diag_request *write_ptr_modem = NULL; - int retry = 0, type; + void *buf = driver->buf_in_sdio; - if (!driver->in_busy_sdio_1) { - buf = driver->buf_in_sdio_1; - write_ptr_modem = driver->write_ptr_mdm_1; - in_busy_ptr = &(driver->in_busy_sdio_1); - } else if (!driver->in_busy_sdio_2) { - buf = driver->buf_in_sdio_2; - write_ptr_modem = driver->write_ptr_mdm_2; - in_busy_ptr = &(driver->in_busy_sdio_2); - } - - if (driver->sdio_ch && buf) { + if (driver->sdio_ch && (!driver->in_busy_sdio)) { r = sdio_read_avail(driver->sdio_ch); if (r > IN_BUF_SIZE) { if (r < MAX_IN_BUF_SIZE) { pr_err("diag: SDIO sending" - " in packets more than %d bytes", r); + " packets more than %d bytes\n", r); buf = krealloc(buf, r, GFP_KERNEL); } else { pr_err("diag: SDIO sending" - " in packets more than %d bytes", MAX_IN_BUF_SIZE); + " in packets more than %d bytes\n", MAX_IN_BUF_SIZE); return; } } @@ -65,60 +52,20 @@ void __diag_sdio_send_req(void) if (!buf) printk(KERN_INFO "Out of diagmem for SDIO\n"); else { -drop: APPEND_DEBUG('i'); sdio_read(driver->sdio_ch, buf, r); - if ((driver->qxdm2sd_drop) && (driver->logging_mode == USB_MODE)) { - /*Drop the diag payload */ - DIAG_INFO("%s:Drop the diag payload :%d\n", __func__, retry); - print_hex_dump(KERN_DEBUG, "Drop Packet Data" - " from 9K(first 16 bytes)", DUMP_PREFIX_ADDRESS, 16, 1, buf, 16, 1); - driver->in_busy_sdio_1 = 0; - driver->in_busy_sdio_2 = 0; - r = sdio_read_avail(driver->sdio_ch); - if (++retry > 20) { - driver->qxdm2sd_drop = 0; - return; - } - if (r) - goto drop; - else { - driver->qxdm2sd_drop = 0; - return; - } - } - APPEND_DEBUG('j'); - - if (diag9k_debug_mask) { - switch (diag9k_debug_mask) { - case DIAGLOG_MODE_HEAD: - print_hex_dump(KERN_DEBUG, "Read Packet Data" - " from 9K(first 16 bytes)", DUMP_PREFIX_ADDRESS, 16, 1, buf, 16, 1); - break; - case DIAGLOG_MODE_FULL: - print_hex_dump(KERN_DEBUG, "Read Packet Data" - " from 9K(first 16 bytes)", DUMP_PREFIX_ADDRESS, 16, 1, buf, 16, 1); - print_hex_dump(KERN_DEBUG, "Read Packet Data" - " from 9K(last 16 bytes) ", 16, 1, DUMP_PREFIX_ADDRESS, buf+r-16, 16, 1); - break; - default: - print_hex_dump(KERN_DEBUG, "Read Packet Data" - " from 9K ", DUMP_PREFIX_ADDRESS, 16, 1, buf, r, 1); - - } - } - - type = checkcmd_modem_epst(buf); - if (type) { - modem_to_userspace(buf, r, type, 1); + if (((!driver->usb_connected) && (driver-> + logging_mode == USB_MODE)) || (driver-> + logging_mode == NO_LOGGING_MODE)) { + /* Drop the diag payload */ + driver->in_busy_sdio = 0; return; } - - write_ptr_modem->length = r; - *in_busy_ptr = 1; + APPEND_DEBUG('j'); + driver->write_ptr_mdm->length = r; + driver->in_busy_sdio = 1; diag_device_write(buf, SDIO_DATA, - write_ptr_modem); - + driver->write_ptr_mdm); } } } @@ -129,6 +76,31 @@ static void diag_read_sdio_work_fn(struct work_struct *work) __diag_sdio_send_req(); } +static void diag_sdio_notify(void *ctxt, unsigned event) +{ + if (event == SDIO_EVENT_DATA_READ_AVAIL) + queue_work(driver->diag_sdio_wq, + &(driver->diag_read_sdio_work)); + + if (event == SDIO_EVENT_DATA_WRITE_AVAIL) + wake_up_interruptible(&driver->wait_q); +} + +static int diag_sdio_close(void) +{ + queue_work(driver->diag_sdio_wq, &(driver->diag_close_sdio_work)); + return 0; +} + +static void diag_close_sdio_work_fn(struct work_struct *work) +{ + pr_debug("diag: sdio close called\n"); + if (sdio_close(driver->sdio_ch)) + pr_err("diag: could not close SDIO channel\n"); + else + driver->sdio_ch = NULL; /* channel successfully closed */ +} + int diagfwd_connect_sdio(void) { int err; @@ -136,10 +108,20 @@ int diagfwd_connect_sdio(void) err = usb_diag_alloc_req(driver->mdm_ch, N_MDM_WRITE, N_MDM_READ); if (err) - printk(KERN_ERR "diag: unable to alloc USB req on mdm ch"); + pr_err("diag: unable to alloc USB req on mdm ch\n"); + + driver->in_busy_sdio = 0; + if (!driver->sdio_ch) { + err = sdio_open("SDIO_DIAG", &driver->sdio_ch, driver, + diag_sdio_notify); + if (err) + pr_info("diag: could not open SDIO channel\n"); + else + pr_info("diag: opened SDIO channel\n"); + } else { + pr_info("diag: SDIO channel already open\n"); + } - driver->in_busy_sdio_1 = 0; - driver->in_busy_sdio_2 = 0; /* Poll USB channel to check for data*/ queue_work(driver->diag_sdio_wq, &(driver->diag_read_mdm_work)); /* Poll SDIO channel to check for data*/ @@ -149,18 +131,17 @@ int diagfwd_connect_sdio(void) int diagfwd_disconnect_sdio(void) { - /* driver->in_busy_sdio = 1; */ - /* Clear variable to Flush remaining data from SDIO channel */ - driver->in_busy_sdio_1 = 0; - driver->in_busy_sdio_2 = 0; usb_diag_free_req(driver->mdm_ch); + if (driver->sdio_ch && (driver->logging_mode == USB_MODE)) { + driver->in_busy_sdio = 1; + diag_sdio_close(); + } return 0; } int diagfwd_write_complete_sdio(void) { - driver->in_busy_sdio_1 = 0; - driver->in_busy_sdio_2 = 0; + driver->in_busy_sdio = 0; APPEND_DEBUG('q'); queue_work(driver->diag_sdio_wq, &(driver->diag_read_sdio_work)); return 0; @@ -174,12 +155,14 @@ int diagfwd_read_complete_sdio(void) void diag_read_mdm_work_fn(struct work_struct *work) { - if (diag9k_debug_mask) - DIAG_INFO("%s \n", __func__); - if (driver->sdio_ch) { - wait_event_interruptible(driver->wait_q, (sdio_write_avail - (driver->sdio_ch) >= driver->read_len_mdm)); + wait_event_interruptible(driver->wait_q, ((sdio_write_avail + (driver->sdio_ch) >= driver->read_len_mdm) || + !(driver->sdio_ch))); + if (!(driver->sdio_ch)) { + pr_alert("diag: sdio channel not valid"); + return; + } if (driver->sdio_ch && driver->usb_buf_mdm_out && (driver->read_len_mdm > 0)) sdio_write(driver->sdio_ch, driver->usb_buf_mdm_out, @@ -192,21 +175,9 @@ void diag_read_mdm_work_fn(struct work_struct *work) } } -static void diag_sdio_notify(void *ctxt, unsigned event) -{ - if (event == SDIO_EVENT_DATA_READ_AVAIL) - queue_work(driver->diag_sdio_wq, - &(driver->diag_read_sdio_work)); - - if (event == SDIO_EVENT_DATA_WRITE_AVAIL) - wake_up_interruptible(&driver->wait_q); -} - static int diag_sdio_probe(struct platform_device *pdev) { int err; - if (diag9k_debug_mask) - DIAG_INFO("%s\n", __func__); err = sdio_open("SDIO_DIAG", &driver->sdio_ch, driver, diag_sdio_notify); @@ -217,28 +188,15 @@ static int diag_sdio_probe(struct platform_device *pdev) queue_work(driver->diag_sdio_wq, &(driver->diag_read_mdm_work)); } - driver->in_busy_sdio_1 = 0; - driver->in_busy_sdio_2 = 0; - driver->qxdm2sd_drop = 0; - sdio_diag_initialized = 1; - return err; } static int diag_sdio_remove(struct platform_device *pdev) -{ - queue_work(driver->diag_sdio_wq, &(driver->diag_remove_sdio_work)); - return 0; -} - -static void diag_remove_sdio_work_fn(struct work_struct *work) { pr_debug("\n diag: sdio remove called"); - /*Disable SDIO channel to prevent further read/write */ + /* Disable SDIO channel to prevent further read/write */ driver->sdio_ch = NULL; - sdio_diag_initialized = 0; - driver->in_busy_sdio_1 = 1; - driver->in_busy_sdio_2 = 1; + return 0; } static int diagfwd_sdio_runtime_suspend(struct device *dev) @@ -272,31 +230,19 @@ void diagfwd_sdio_init(void) { int ret; - if (diag9k_debug_mask) - DIAG_INFO("%s\n", __func__); - driver->read_len_mdm = 0; - if (driver->buf_in_sdio_1 == NULL) - driver->buf_in_sdio_1 = kzalloc(IN_BUF_SIZE, GFP_KERNEL); - if (driver->buf_in_sdio_1 == NULL) - goto err; - if (driver->buf_in_sdio_2 == NULL) - driver->buf_in_sdio_2 = kzalloc(IN_BUF_SIZE, GFP_KERNEL); - if (driver->buf_in_sdio_2 == NULL) + if (driver->buf_in_sdio == NULL) + driver->buf_in_sdio = kzalloc(IN_BUF_SIZE, GFP_KERNEL); + if (driver->buf_in_sdio == NULL) goto err; if (driver->usb_buf_mdm_out == NULL) driver->usb_buf_mdm_out = kzalloc(USB_MAX_OUT_BUF, GFP_KERNEL); if (driver->usb_buf_mdm_out == NULL) goto err; - if (driver->write_ptr_mdm_1 == NULL) - driver->write_ptr_mdm_1 = kzalloc( - sizeof(struct diag_request), GFP_KERNEL); - if (driver->write_ptr_mdm_1 == NULL) - goto err; - if (driver->write_ptr_mdm_2 == NULL) - driver->write_ptr_mdm_2 = kzalloc( + if (driver->write_ptr_mdm == NULL) + driver->write_ptr_mdm = kzalloc( sizeof(struct diag_request), GFP_KERNEL); - if (driver->write_ptr_mdm_2 == NULL) + if (driver->write_ptr_mdm == NULL) goto err; if (driver->usb_read_mdm_ptr == NULL) driver->usb_read_mdm_ptr = kzalloc( @@ -314,7 +260,7 @@ void diagfwd_sdio_init(void) INIT_WORK(&(driver->diag_read_mdm_work), diag_read_mdm_work_fn); #endif INIT_WORK(&(driver->diag_read_sdio_work), diag_read_sdio_work_fn); - INIT_WORK(&(driver->diag_remove_sdio_work), diag_remove_sdio_work_fn); + INIT_WORK(&(driver->diag_close_sdio_work), diag_close_sdio_work_fn); ret = platform_driver_register(&msm_sdio_ch_driver); if (ret) printk(KERN_INFO "DIAG could not register SDIO device"); @@ -324,11 +270,9 @@ void diagfwd_sdio_init(void) return; err: printk(KERN_INFO "\n Could not initialize diag buf for SDIO"); - kfree(driver->buf_in_sdio_1); - kfree(driver->buf_in_sdio_2); + kfree(driver->buf_in_sdio); kfree(driver->usb_buf_mdm_out); - kfree(driver->write_ptr_mdm_1); - kfree(driver->write_ptr_mdm_2); + kfree(driver->write_ptr_mdm); kfree(driver->usb_read_mdm_ptr); if (driver->diag_sdio_wq) destroy_workqueue(driver->diag_sdio_wq); @@ -344,11 +288,9 @@ void diagfwd_sdio_exit(void) #ifdef CONFIG_DIAG_OVER_USB usb_diag_close(driver->mdm_ch); #endif - kfree(driver->buf_in_sdio_1); - kfree(driver->buf_in_sdio_2); + kfree(driver->buf_in_sdio); kfree(driver->usb_buf_mdm_out); - kfree(driver->write_ptr_mdm_1); - kfree(driver->write_ptr_mdm_2); + kfree(driver->write_ptr_mdm); kfree(driver->usb_read_mdm_ptr); destroy_workqueue(driver->diag_sdio_wq); } diff --git a/drivers/char/diag/diagfwd_sdio.h b/drivers/char/diag/diagfwd_sdio.h index 57a35d29b95..40982c33783 100644 --- a/drivers/char/diag/diagfwd_sdio.h +++ b/drivers/char/diag/diagfwd_sdio.h @@ -14,7 +14,7 @@ #define DIAGFWD_SDIO_H #include -#define N_MDM_WRITE 2 /* Upgrade to 2 with ping pong buffer */ +#define N_MDM_WRITE 1 /* Upgrade to 2 with ping pong buffer */ #define N_MDM_READ 1 void diagfwd_sdio_init(void); diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index da0819171b8..117d3bf9b69 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -626,11 +626,11 @@ config USB_GADGET_DUMMY_HCD side is the master; the gadget side is the slave. Gadget drivers can be high, full, or low speed; and they have access to endpoints like those from NET2280, PXA2xx, or SA1100 hardware. - + This may help in some stages of creating a driver to embed in a Linux device, since it lets you debug several parts of the gadget driver without its hardware or drivers being involved. - + Since such a gadget side driver needs to interoperate with a host side Linux-USB device driver, this may help to debug both sides of a USB protocol stack. @@ -736,7 +736,7 @@ config USB_ETH help This driver implements Ethernet style communication, in one of several ways: - + - The "Communication Device Class" (CDC) Ethernet Control Model. That protocol is often avoided with pure Ethernet adapters, in favor of simpler vendor-specific hardware, but is widely @@ -776,7 +776,7 @@ config USB_ETH_RNDIS If you say "y" here, the Ethernet gadget driver will try to provide a second device configuration, supporting RNDIS to talk to such Microsoft USB hosts. - + To make MS-Windows work with this, use Documentation/usb/linux.inf as the "driver info file". For versions of MS-Windows older than XP, you'll need to download drivers from Microsoft's website; a URL @@ -961,241 +961,6 @@ config USB_G_ANDROID The functions can be configured via a board file and may be enabled and disabled dynamically. -config USB_ANDROID_ACM - boolean "Android gadget ACM serial function" - depends on USB_G_ANDROID - help - Provides ACM serial function for android gadget driver. - -config PASCAL_DETECT - boolean "PASCAL DETECT" - depends on USB_ANDROID_ACM - default n - help - Provides ACM serial function for pascal mode. - -config LISMO - boolean "LISMO" - depends on PASCAL_DETECT - default n - help - Provides LISMO service. - -config USB_ANDROID_ADB - boolean "Android gadget adb function" - depends on USB_G_ANDROID - default y - help - Provides adb function for android gadget driver. - -config USB_ANDROID_DIAG - boolean "USB MSM7K Diag Function" - depends on USB_G_ANDROID - default y - help - Qualcomm diagnostics interface support. - -config USB_ANDROID_MDM9K_DIAG - boolean "USB MDM 9k Diag Function" - depends on USB_ANDROID_DIAG - help - Qualcomm diagnostics interface support for 9K. - -config USB_ANDROID_MDM9K_MODEM - boolean "USB MDM 9k Modem Function" - depends on USB_G_ANDROID - help - Qualcomm Modem interface support for 9K. - -config USB_ANDROID_MASS_STORAGE - boolean "Android gadget mass storage function" - depends on USB_G_ANDROID && SWITCH - default y - help - Provides USB mass storage function for android gadget driver. - -config USB_ANDROID_MTP - boolean "Android MTP function" - depends on USB_G_ANDROID - help - Provides Media Transfer Protocol (MTP) support for android gadget driver. - -config USB_ANDROID_RNDIS - boolean "Android gadget RNDIS ethernet function" - depends on USB_G_ANDROID - default n - help - Provides RNDIS ethernet function for android gadget driver. - -config USB_ANDROID_RMNET - boolean "RmNet function driver" - depends on USB_G_ANDROID - default n - help - Enabling this option adds rmnet support to the - android gadget. Rmnet is an alternative to CDC-ECM - and Windows RNDIS. It uses QUALCOMM MSM Interface - for control transfers. It acts like a bridge between - Host and modem found in MSM chipsets. - -config RMNET_SMD_CTL_CHANNEL - string "control SMD channel name" - depends on USB_ANDROID_RMNET - default "" - help - Control SMD channel for transferring QMI messages - -config RMNET_SMD_DATA_CHANNEL - string "Data SMD channel name" - depends on USB_ANDROID_RMNET - default "" - help - Data SMD channel for transferring network data - -config USB_ANDROID_RMNET_SDIO - boolean "RmNet over SDIO function driver" - depends on USB_G_ANDROID && MSM_SDIO_CMUX && MSM_SDIO_DMUX - default n - help - Enabling this option adds rmnet over sdio support to the - android gadget. Rmnet is an alternative to CDC-ECM - and Windows RNDIS. It uses QUALCOMM MSM Interface - for control transfers. It acts like a bridge between - Host and modem found in MSM chipsets. - -config RMNET_SDIO_CTL_CHANNEL - int "control SDIO channel id" - depends on USB_ANDROID_RMNET_SDIO - help - Control SDIO channel for transferring QMI messages - -config RMNET_SDIO_DATA_CHANNEL - int "Data SDIO channel id" - depends on USB_ANDROID_RMNET_SDIO - help - Data SDIO channel for transferring network data - -config USB_ANDROID_RMNET_SMD_SDIO - boolean "RmNet over SMD/SDIO function driver" - depends on USB_G_ANDROID && MSM_SDIO_CMUX && MSM_SDIO_DMUX - default n - help - Enabling this option adds rmnet over sdio support to the - android gadget. Rmnet is an alternative to CDC-ECM - and Windows RNDIS. It uses QUALCOMM MSM Interface - for control transfers. It acts like a bridge between - Host and modem found in MSM chipsets. - -config RMNET_SMD_SDIO_CTL_CHANNEL - int "control SDIO channel id" - depends on USB_ANDROID_RMNET_SMD_SDIO - default 8 - help - Control SDIO channel for transferring QMI messages - -config RMNET_SMD_SDIO_DATA_CHANNEL - int "Data SDIO channel id" - default 8 - depends on USB_ANDROID_RMNET_SMD_SDIO - help - Data SDIO channel for transferring network data - -config RMNET_SDIO_SMD_DATA_CHANNEL - string "Data SMD channel name" - depends on USB_ANDROID_RMNET_SMD_SDIO - default "DATA40" - help - Data SMD channel for transferring network data - -config USB_ANDROID_RMNET_BAM - boolean "RmNet over BAM driver" - depends on USB_G_ANDROID && MSM_BAM_DMUX - help - Enabling this option adds rmnet over BAM support to the - android gadget. Rmnet is an alternative to CDC-ECM - and Windows RNDIS. It uses QUALCOMM MSM Interface - for control transfers. It acts like a bridge between - Host and Modem processor using bam-dmux interface. - This option enables only DATA interface. Control - interface has to be enabled separately - -config USB_ANDROID_RMNET_CTRL_SMD - boolean "RmNet control over SMD driver" - depends on USB_G_ANDROID && MSM_SMD - help - Enabling this option adds rmnet control over SMD - support to the android gadget. Rmnet is an - alternative to CDC-ECM and Windows RNDIS. - It uses QUALCOMM MSM Interface for control - transfers. This option enables only control interface. - Data interface has to be enabled separately - -config USB_F_SERIAL - boolean "generic serial function driver" - depends on USB_G_ANDROID - default n - help - Say "y" to link the driver statically, or "m" to build - as a part of "g_android" - -config MODEM_SUPPORT - boolean "modem support in generic serial function driver" - depends on USB_F_SERIAL - default y - help - This feature enables the modem functionality in the - generic serial. - adds interrupt endpoint support to send modem notifications - to host. - adds CDC descriptors to enumerate the generic serial as MODEM. - adds CDC class requests to configure MODEM line settings. - Say "y" to enable MODEM support in the generic serial driver. - -config USB_ANDROID_SERIAL - boolean "Android gadget Serial function" - depends on USB_G_ANDROID - help - -config USB_ANDROID_PROJECTOR - boolean "Android gadget Projector function" - depends on USB_G_ANDROID - help - -config USB_ANDROID_ECM - boolean "Android gadget ECM function" - depends on USB_G_ANDROID - help - -config USB_F_SERIAL_SDIO - boolean "generic serial function driver over SDIO" - depends on USB_G_ANDROID && MSM_SDIO_CMUX && MSM_SDIO_AL - default n - help - Gadget serial driver is one way to communicate with modem - device. This option enables serial driver to communicate - to modem device using sdio. It makes very minimal changes - to Gadget usb serial interface(f_serial.c) and adds - separate transport layer u_sdio(very similar to u_serial.c) - Say "y" to link the driver staticall - -config USB_F_SERIAL_SMD - boolean "generic serial function driver over SMD" - depends on USB_G_ANDROID && MSM_SMD - default n - help - Gadget serial driver is one way to communicate with modem - device. This option enables serial driver to communicate - to modem device using smd. It makes very minimal changes - to Gadget usb serial interface(f_serial.c) and adds - separate transport layer u_smd(very similar to u_serial.c) - Say "y" to link the driver statically - -config USB_ANDROID_USBNET - boolean "Android gadget USBNET function for VoIP" - depends on USB_G_ANDROID - default n - help - config USB_CDC_COMPOSITE tristate "CDC Composite Device (Ethernet and ACM)" depends on NET @@ -1317,10 +1082,6 @@ config USB_G_WEBCAM endchoice -config USB_ACCESSORY_DETECT_BY_ADC - boolean "DETECT USB ACCESSORY BY PMIC ADC" - default n - config USB_CSW_HACK boolean "USB Mass storage csw hack Feature" default y @@ -1401,9 +1162,4 @@ config USB_ANDROID_RMNET_CTRL_SMD transfers. This option enables only control interface. Data interface used is BAM. -config USB_GADGET_VERIZON_PRODUCT_ID - boolean "Verizon Product ID Mapping" - depends on USB_G_ANDROID - default n - endif # USB_GADGET diff --git a/drivers/usb/gadget/android.c b/drivers/usb/gadget/android.c index c9db83207d5..403c373714b 100644 --- a/drivers/usb/gadget/android.c +++ b/drivers/usb/gadget/android.c @@ -30,13 +30,9 @@ #include #include #include -#include +#include #include "gadget_chips.h" -#include -#ifdef CONFIG_PERFLOCK -#include -#endif /* * Kbuild is not very cooperative with respect to linking separately @@ -51,73 +47,28 @@ #include "composite.c" #include "f_diag.c" -#if defined(CONFIG_USB_ANDROID_RMNET_SMD) #include "f_rmnet_smd.c" -#elif defined(CONFIG_USB_ANDROID_RMNET_SDIO) #include "f_rmnet_sdio.c" -#elif defined(CONFIG_USB_ANDROID_RMNET_SMD_SDIO) #include "f_rmnet_smd_sdio.c" -#elif defined(CONFIG_USB_ANDROID_RMNET_BAM) #include "f_rmnet.c" -#endif #include "f_mass_storage.c" #include "u_serial.c" #include "u_sdio.c" #include "u_smd.c" #include "u_bam.c" #include "u_rmnet_ctrl_smd.c" +#include "u_ctrl_hsic.c" +#include "u_data_hsic.c" #include "f_serial.c" -#ifdef CONFIG_USB_ANDROID_ACM #include "f_acm.c" -#endif #include "f_adb.c" -#if 0 #include "f_ccid.c" -#endif -#ifdef CONFIG_USB_ANDROID_MTP #include "f_mtp.c" -#endif #include "f_accessory.c" #define USB_ETH_RNDIS y -#ifdef CONFIG_USB_ANDROID_RNDIS #include "f_rndis.c" #include "rndis.c" -#endif -#ifdef CONFIG_USB_ANDROID_ECM -#include "f_ecm.c" -#endif #include "u_ether.c" -#ifdef CONFIG_USB_ANDROID_PROJECTOR -#include "f_projector.c" -#endif -#ifdef CONFIG_USB_ANDROID_USBNET -#include "f_usbnet.c" -#endif -#include - -#ifdef pr_debug -#undef pr_debug -#endif -#define pr_debug(fmt, args...) \ - printk(KERN_DEBUG "[USB] " pr_fmt(fmt), ## args) - -#ifdef pr_err -#undef pr_err -#endif -#define pr_err(fmt, args...) \ - printk(KERN_ERR "[USB] " pr_fmt(fmt), ## args) - -#ifdef pr_warning -#undef pr_warning -#endif -#define pr_warning(fmt, args...) \ - printk(KERN_WARNING "[USB] " pr_fmt(fmt), ## args) - -#ifdef pr_info -#undef pr_info -#endif -#define pr_info(fmt, args...) \ - printk(KERN_INFO "[USB] " pr_fmt(fmt), ## args) MODULE_AUTHOR("Mike Lockwood"); MODULE_DESCRIPTION("Android Composite USB Driver"); @@ -130,8 +81,6 @@ static const char longname[] = "Gadget Android"; #define VENDOR_ID 0x18D1 #define PRODUCT_ID 0x0001 -static bool connect2pc; - struct android_usb_function { char *name; void *config; @@ -152,14 +101,10 @@ struct android_usb_function { /* Optional: called when the configuration is removed */ void (*unbind_config)(struct android_usb_function *, struct usb_configuration *); - /* Optional: handle ctrl requests before the device is configured - * and/or before the function is enabled */ + /* Optional: handle ctrl requests before the device is configured */ int (*ctrlrequest)(struct android_usb_function *, struct usb_composite_dev *, const struct usb_ctrlrequest *); - - /* for performance requirement */ - int performance_lock; }; struct android_dev { @@ -173,34 +118,13 @@ struct android_dev { bool connected; bool sw_connected; struct work_struct work; - struct delayed_work init_work; - /* waiting for enabling functions */ - struct list_head function_list; - - int num_products; - struct android_usb_product *products; - int num_functions; - char **in_house_functions; - - int product_id; - void (*enable_fast_charge)(bool enable); - bool RndisDisableMPDecision; - int (*match)(int product_id, int intrsharing); }; static struct class *android_class; static struct android_dev *_android_dev; - -static struct wake_lock android_usb_idle_wake_lock; -#ifdef CONFIG_PERFLOCK -static struct perf_lock android_usb_perf_lock; -#endif - - static int android_bind_config(struct usb_configuration *c); static void android_unbind_config(struct usb_configuration *c); - /* string IDs are assigned dynamically */ #define STRING_MANUFACTURER_IDX 0 #define STRING_PRODUCT_IDX 1 @@ -247,6 +171,12 @@ static struct usb_configuration android_config_driver = { .bMaxPower = 0xFA, /* 500ma */ }; +enum android_device_state { + USB_DISCONNECTED, + USB_CONNECTED, + USB_CONFIGURED, +}; + static void android_work(struct work_struct *data) { struct android_dev *dev = container_of(data, struct android_dev, work); @@ -254,119 +184,57 @@ static void android_work(struct work_struct *data) char *disconnected[2] = { "USB_STATE=DISCONNECTED", NULL }; char *connected[2] = { "USB_STATE=CONNECTED", NULL }; char *configured[2] = { "USB_STATE=CONFIGURED", NULL }; + char **uevent_envp = NULL; + static enum android_device_state last_uevent, next_state; unsigned long flags; - struct android_usb_function *f; - int count = 0; - - /* release performance related locks first */ - if (wake_lock_active(&android_usb_idle_wake_lock)) - wake_unlock(&android_usb_idle_wake_lock); -#ifdef CONFIG_PERFLOCK - if (is_perf_lock_active(&android_usb_perf_lock)) - perf_unlock(&android_usb_perf_lock); -#endif - spin_lock_irqsave(&cdev->lock, flags); if (cdev->config) { - spin_unlock_irqrestore(&cdev->lock, flags); - kobject_uevent_env(&dev->dev->kobj, KOBJ_CHANGE, - configured); - pr_info("USB_STATE=CONFIGURED"); - - /* hold perflock, wakelock for performance consideration */ - list_for_each_entry(f, &dev->enabled_functions, enabled_list) { - if (f->performance_lock) { - pr_info("Performance lock for '%s'\n", f->name); - count++; - } - } - if (count) { - if (!wake_lock_active(&android_usb_idle_wake_lock)) - wake_lock(&android_usb_idle_wake_lock); -#ifdef CONFIG_PERFLOCK - if (!is_perf_lock_active(&android_usb_perf_lock)) - perf_lock(&android_usb_perf_lock); -#endif - } - - if (!connect2pc && dev->connected) { - connect2pc = true; - switch_set_state(&cdev->sw_connect2pc, 1); - pr_info("set usb_connect2pc = 1\n"); - } - return; + uevent_envp = configured; + next_state = USB_CONFIGURED; + } else if (dev->connected != dev->sw_connected) { + uevent_envp = dev->connected ? connected : disconnected; + next_state = dev->connected ? USB_CONNECTED : USB_DISCONNECTED; } + dev->sw_connected = dev->connected; + spin_unlock_irqrestore(&cdev->lock, flags); - if (dev->connected != dev->sw_connected) { - dev->sw_connected = dev->connected; - spin_unlock_irqrestore(&cdev->lock, flags); - kobject_uevent_env(&dev->dev->kobj, KOBJ_CHANGE, - dev->sw_connected ? connected : disconnected); + if (uevent_envp) { + /* + * Some userspace modules, e.g. MTP, work correctly only if + * CONFIGURED uevent is preceded by DISCONNECT uevent. + * Check if we missed sending out a DISCONNECT uevent. This can + * happen if host PC resets and configures device really quick. + */ + if (((uevent_envp == connected) && + (last_uevent != USB_DISCONNECTED)) || + ((uevent_envp == configured) && + (last_uevent == USB_CONFIGURED))) { + pr_info("%s: sent missed DISCONNECT event\n", __func__); + kobject_uevent_env(&dev->dev->kobj, KOBJ_CHANGE, + disconnected); + msleep(20); + } + /* + * Before sending out CONFIGURED uevent give function drivers + * a chance to wakeup userspace threads and notify disconnect + */ + if (uevent_envp == configured) + msleep(50); - pr_info("%s\n", dev->connected ? connected[0] : disconnected[0]); + kobject_uevent_env(&dev->dev->kobj, KOBJ_CHANGE, uevent_envp); + last_uevent = next_state; + pr_info("%s: sent uevent %s\n", __func__, uevent_envp[0]); } else { - spin_unlock_irqrestore(&cdev->lock, flags); - } - - if (connect2pc && !dev->connected) { - connect2pc = false; - switch_set_state(&cdev->sw_connect2pc, 0); - pr_info("set usb_connect2pc = 0\n"); + pr_info("%s: did not send uevent (%d %d %p)\n", __func__, + dev->connected, dev->sw_connected, cdev->config); } } /*-------------------------------------------------------------------------*/ /* Supported functions initialization */ -static ssize_t func_en_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct android_usb_function *func = dev_get_drvdata(dev); - struct android_usb_function *f; - int ebl = 0; - - list_for_each_entry(f, &_android_dev->enabled_functions, enabled_list) { - if (!strcmp(func->name, f->name)) { - ebl = 1; - break; - } - } - return sprintf(buf, "%d", ebl); -} - -static ssize_t func_en_store( - struct device *dev, struct device_attribute *attr, - const char *buf, size_t size) -{ - struct android_usb_function *func = dev_get_drvdata(dev); - struct android_usb_function *f; - int ebl = 0; - int value; - - sscanf(buf, "%d", &value); - list_for_each_entry(f, &_android_dev->enabled_functions, enabled_list) { - if (!strcmp(func->name, f->name)) { - ebl = 1; - break; - } - } - if (!!value == ebl) { - pr_info("%s function is already %s\n", func->name - , ebl ? "enable" : "disable"); - return size; - } - if (value) - htc_usb_enable_function(func->name, 1); - else - htc_usb_enable_function(func->name, 0); - - return size; -} -static DEVICE_ATTR(on, S_IRUGO | S_IWUSR | S_IWGRP, func_en_show, func_en_store); - -#if defined(CONFIG_USB_ANDROID_RMNET_SMD) /* RMNET_SMD */ static int rmnet_smd_function_bind_config(struct android_usb_function *f, struct usb_configuration *c) @@ -375,11 +243,9 @@ static int rmnet_smd_function_bind_config(struct android_usb_function *f, } static struct android_usb_function rmnet_smd_function = { - .name = "rmnet", + .name = "rmnet_smd", .bind_config = rmnet_smd_function_bind_config, - .performance_lock = 1, }; -#elif defined(CONFIG_USB_ANDROID_RMNET_SDIO) /* RMNET_SDIO */ static int rmnet_sdio_function_bind_config(struct android_usb_function *f, @@ -389,12 +255,10 @@ static int rmnet_sdio_function_bind_config(struct android_usb_function *f, } static struct android_usb_function rmnet_sdio_function = { - .name = "rmnet", + .name = "rmnet_sdio", .bind_config = rmnet_sdio_function_bind_config, - .performance_lock = 1, }; -#elif defined(CONFIG_USB_ANDROID_RMNET_SMD_SDIO) /* RMNET_SMD_SDIO */ static int rmnet_smd_sdio_function_init(struct android_usb_function *f, struct usb_composite_dev *cdev) @@ -417,22 +281,16 @@ static struct device_attribute *rmnet_smd_sdio_attributes[] = { &dev_attr_transport, NULL }; static struct android_usb_function rmnet_smd_sdio_function = { - .name = "rmnet", + .name = "rmnet_smd_sdio", .init = rmnet_smd_sdio_function_init, .cleanup = rmnet_smd_sdio_function_cleanup, .bind_config = rmnet_smd_sdio_bind_config, .attributes = rmnet_smd_sdio_attributes, - .performance_lock = 1, }; -#elif defined(CONFIG_USB_ANDROID_RMNET_BAM) -/* RMNET - used with BAM */ -#define MAX_RMNET_INSTANCES 1 -static int rmnet_instances = 1; -static int rmnet_function_init(struct android_usb_function *f, - struct usb_composite_dev *cdev) -{ - return frmnet_init_port(MAX_RMNET_INSTANCES); -} + +/*rmnet transport string format(per port):"ctrl0,data0,ctrl1,data1..." */ +#define MAX_XPORT_STR_LEN 50 +static char rmnet_transports[MAX_XPORT_STR_LEN]; static void rmnet_function_cleanup(struct android_usb_function *f) { @@ -443,52 +301,78 @@ static int rmnet_function_bind_config(struct android_usb_function *f, struct usb_configuration *c) { int i; - int ret = 0; + int err = 0; + char *ctrl_name; + char *data_name; + char buf[MAX_XPORT_STR_LEN], *b; + static int rmnet_initialized, ports; + + if (!rmnet_initialized) { + rmnet_initialized = 1; + strlcpy(buf, rmnet_transports, sizeof(buf)); + b = strim(buf); + while (b) { + ctrl_name = strsep(&b, ","); + data_name = strsep(&b, ","); + if (ctrl_name && data_name) { + err = frmnet_init_port(ctrl_name, data_name); + if (err) { + pr_err("rmnet: Cannot open ctrl port:" + "'%s' data port:'%s'\n", + ctrl_name, data_name); + goto out; + } + ports++; + } + } - for (i = 0; i < rmnet_instances; i++) { - ret = frmnet_bind_config(c, i); - if (ret) { + err = rmnet_gport_setup(); + if (err) { + pr_err("rmnet: Cannot setup transports"); + goto out; + } + } + + for (i = 0; i < ports; i++) { + err = frmnet_bind_config(c, i); + if (err) { pr_err("Could not bind rmnet%u config\n", i); break; } } - - return ret; +out: + return err; } -static ssize_t rmnet_instances_show(struct device *dev, +static ssize_t rmnet_transports_show(struct device *dev, struct device_attribute *attr, char *buf) { - return snprintf(buf, PAGE_SIZE, "%d\n", rmnet_instances); + return snprintf(buf, PAGE_SIZE, "%s\n", rmnet_transports); } -static ssize_t rmnet_instances_store(struct device *dev, - struct device_attribute *attr, const char *buf, size_t size) +static ssize_t rmnet_transports_store( + struct device *device, struct device_attribute *attr, + const char *buff, size_t size) { - int value; + strlcpy(rmnet_transports, buff, sizeof(rmnet_transports)); - pr_info("%s, buff: %s\n", __func__, buf); - sscanf(buf, "%d", &value); - if (value > MAX_RMNET_INSTANCES) - value = MAX_RMNET_INSTANCES; - rmnet_instances = value; return size; } -static DEVICE_ATTR(instances, S_IRUGO | S_IWUSR, rmnet_instances_show, - rmnet_instances_store); +static struct device_attribute dev_attr_rmnet_transports = + __ATTR(transports, S_IRUGO | S_IWUSR, + rmnet_transports_show, + rmnet_transports_store); static struct device_attribute *rmnet_function_attributes[] = { - &dev_attr_instances, NULL }; + &dev_attr_rmnet_transports, + NULL }; static struct android_usb_function rmnet_function = { .name = "rmnet", - .init = rmnet_function_init, .cleanup = rmnet_function_cleanup, .bind_config = rmnet_function_bind_config, .attributes = rmnet_function_attributes, - .performance_lock = 1, }; -#endif /* DIAG */ static char diag_clients[32]; /*enabled DIAG clients- "diag[,diag_mdm]" */ @@ -496,7 +380,6 @@ static ssize_t clients_store( struct device *device, struct device_attribute *attr, const char *buff, size_t size) { - pr_info("%s, buff: %s\n", __func__, buff); strlcpy(diag_clients, buff, sizeof(diag_clients)); return size; @@ -520,16 +403,6 @@ static void diag_function_cleanup(struct android_usb_function *f) static int diag_function_bind_config(struct android_usb_function *f, struct usb_configuration *c) { -#if 1 - int err; - int (*notify)(uint32_t, const char *); - - notify = _android_dev->pdata->update_pid_and_serial_num; - - err = diag_function_add(c, "diag", notify); - if (err) - pr_err("diag: Cannot open channel 'diag'"); -#else char *name; char buf[32], *b; int once = 0, err = -1; @@ -551,7 +424,7 @@ static int diag_function_bind_config(struct android_usb_function *f, pr_err("diag: Cannot open channel '%s'", name); } } -#endif + return err; } @@ -563,149 +436,65 @@ static struct android_usb_function diag_function = { .attributes = diag_function_attributes, }; -#if defined(CONFIG_USB_ANDROID_MDM9K_DIAG) -static int diag_mdm_function_bind_config(struct android_usb_function *f, - struct usb_configuration *c) +/* SERIAL */ +static char serial_transports[32]; /*enabled FSERIAL ports - "tty[,sdio]"*/ +static ssize_t serial_transports_store( + struct device *device, struct device_attribute *attr, + const char *buff, size_t size) { - int err; - int (*notify)(uint32_t, const char *); + strlcpy(serial_transports, buff, sizeof(serial_transports)); - notify = NULL; + return size; +} - err = diag_function_add(c, "diag_mdm", notify); - if (err) - pr_err("diag: Cannot open channel 'diag_mdm'"); +static DEVICE_ATTR(transports, S_IWUSR, NULL, serial_transports_store); +static struct device_attribute *serial_function_attributes[] = + { &dev_attr_transports, NULL }; - return 0; +static void serial_function_cleanup(struct android_usb_function *f) +{ + gserial_cleanup(); } -static struct android_usb_function diag_mdm_function = { - .name = "diag_mdm", - .bind_config = diag_mdm_function_bind_config, -}; -#endif - -/* Serial, Modem */ -static int serial_driver_initial(struct usb_configuration *c) +static int serial_function_bind_config(struct android_usb_function *f, + struct usb_configuration *c) { - char *name, *str[2]; - char buf[80], *b; - int err = -1; + char *name; + char buf[32], *b; + int err = -1, i; static int serial_initialized = 0, ports = 0; - char *init_string; - - if (serial_initialized) { - pr_info("%s: already initial\n", __func__); - return ports; - } - serial_initialized = 1; - init_string = _android_dev->pdata->fserial_init_string ? - _android_dev->pdata->fserial_init_string : - "smd:modem,tty,tty,tty:serial"; - - strncpy(buf, init_string, sizeof(buf)); - buf[79] = 0; - pr_info("%s: init string: %s\n", __func__, buf); + if (serial_initialized) + goto bind_config; + serial_initialized = 1; + strlcpy(buf, serial_transports, sizeof(buf)); b = strim(buf); while (b) { - str[0] = str[1] = 0; name = strsep(&b, ","); + if (name) { - str[0] = strsep(&name, ":"); - if (str[0]) - str[1] = strsep(&name, ":"); - } - err = gserial_init_port(ports, str[0], str[1]); - if (err) { - pr_err("serial: Cannot open port '%s'\n", str[0]); - goto out; + err = gserial_init_port(ports, name); + if (err) { + pr_err("serial: Cannot open port '%s'", name); + goto out; + } + ports++; } - ports++; } - err = gport_setup(c); if (err) { pr_err("serial: Cannot setup transports"); goto out; } - return ports; - -out: - return err; -} - -/* Modem */ -static void modem_function_cleanup(struct android_usb_function *f) -{ - struct android_dev *dev = _android_dev; - - /* ToDo: need to cleanup by different channel */ - gsmd_cleanup(dev->cdev->gadget, 1); -} - -static int modem_function_bind_config(struct android_usb_function *f, - struct usb_configuration *c) -{ - int err = -1; - int i, ports; - - ports = serial_driver_initial(c); - if (ports < 0) - goto out; - - - for (i = 0; i < ports; i++) { - if (gserial_ports[i].func_type == USB_FSER_FUNC_MODEM) { - err = gser_bind_config(c, i); - if (err) { - pr_err("serial: bind_config failed for port %d", i); - goto out; - } - } - } - -out: - return err; -} - -static struct android_usb_function modem_function = { - .name = "modem", - .cleanup = modem_function_cleanup, - .bind_config = modem_function_bind_config, - .performance_lock = 1, -}; - -#ifdef CONFIG_USB_ANDROID_MDM9K_MODEM -/* Modem_Mdm */ -static void modem_mdm_function_cleanup(struct android_usb_function *f) -{ - struct android_dev *dev = _android_dev; - - /* ToDo: need to cleanup by different channel */ - gsmd_cleanup(dev->cdev->gadget, 1); -} - -static int modem_mdm_function_bind_config(struct android_usb_function *f, - struct usb_configuration *c) -{ - int err = -1; - int i, ports; - ports = serial_driver_initial(c); - if (ports < 0) - goto out; - - - for (i = 0; i < ports; i++) { - if (gserial_ports[i].func_type == USB_FSER_FUNC_MODEM_MDM) { - err = gser_bind_config(c, i); - if (err) { - pr_err("serial: bind_config failed for port %d", i); - goto out; - } +bind_config: + for (i = 0; i < ports; i++) { + err = gser_bind_config(c, i); + if (err) { + pr_err("serial: bind_config failed for port %d", i); + goto out; } } @@ -713,103 +502,83 @@ static int modem_mdm_function_bind_config(struct android_usb_function *f, return err; } -static struct android_usb_function modem_mdm_function = { - .name = "modem_mdm", - .cleanup = modem_mdm_function_cleanup, - .bind_config = modem_mdm_function_bind_config, - .performance_lock = 1, +static struct android_usb_function serial_function = { + .name = "serial", + .cleanup = serial_function_cleanup, + .bind_config = serial_function_bind_config, + .attributes = serial_function_attributes, }; -#endif -/* SERIAL */ -static char serial_transports[32]; /*enabled FSERIAL ports - "tty[,sdio]"*/ -static ssize_t serial_transports_store( +/* ACM */ +static char acm_transports[32]; /*enabled ACM ports - "tty[,sdio]"*/ +static ssize_t acm_transports_store( struct device *device, struct device_attribute *attr, const char *buff, size_t size) { - pr_info("%s: %s\n", __func__, buff); - strlcpy(serial_transports, buff, sizeof(serial_transports)); + strlcpy(acm_transports, buff, sizeof(acm_transports)); return size; } -static DEVICE_ATTR(transports, S_IWUSR, NULL, serial_transports_store); -static struct device_attribute *serial_function_attributes[] = - { &dev_attr_transports, NULL }; +static DEVICE_ATTR(acm_transports, S_IWUSR, NULL, acm_transports_store); +static struct device_attribute *acm_function_attributes[] = { + &dev_attr_acm_transports, NULL }; -static void serial_function_cleanup(struct android_usb_function *f) +static void acm_function_cleanup(struct android_usb_function *f) { gserial_cleanup(); } -static int serial_function_bind_config(struct android_usb_function *f, +static int acm_function_bind_config(struct android_usb_function *f, struct usb_configuration *c) { -#if 1 - int err = -1; - int i, ports; - - ports = serial_driver_initial(c); - if (ports < 0) - goto out; - for (i = 0; i < ports; i++) { - if (gserial_ports[i].func_type == USB_FSER_FUNC_SERIAL) { - err = gser_bind_config(c, i); - if (err) { - pr_err("serial: bind_config failed for port %d", i); - goto out; - } - } - } -#else char *name; char buf[32], *b; int err = -1, i; - static int serial_initialized = 0, ports = 0; + static int acm_initialized, ports; - if (serial_initialized) + if (acm_initialized) goto bind_config; - serial_initialized = 1; - strlcpy(buf, serial_transports, sizeof(buf)); + acm_initialized = 1; + strlcpy(buf, acm_transports, sizeof(buf)); b = strim(buf); while (b) { name = strsep(&b, ","); + if (name) { - err = gserial_init_port(ports, name); + err = acm_init_port(ports, name); if (err) { - pr_err("serial: Cannot open port '%s'", name); + pr_err("acm: Cannot open port '%s'", name); goto out; } ports++; } } - err = gport_setup(c); + err = acm_port_setup(c); if (err) { - pr_err("serial: Cannot setup transports"); + pr_err("acm: Cannot setup transports"); goto out; } bind_config: for (i = 0; i < ports; i++) { - err = gser_bind_config(c, i); + err = acm_bind_config(c, i); if (err) { - pr_err("serial: bind_config failed for port %d", i); + pr_err("acm: bind_config failed for port %d", i); goto out; } } -#endif out: return err; } - -static struct android_usb_function serial_function = { - .name = "serial", - .cleanup = serial_function_cleanup, - .bind_config = serial_function_bind_config, - .attributes = serial_function_attributes, +static struct android_usb_function acm_function = { + .name = "acm", + .cleanup = acm_function_cleanup, + .bind_config = acm_function_bind_config, + .attributes = acm_function_attributes, }; /* ADB */ @@ -836,7 +605,6 @@ static struct android_usb_function adb_function = { }; /* CCID */ -#if 0 static int ccid_function_init(struct android_usb_function *f, struct usb_composite_dev *cdev) { @@ -860,85 +628,7 @@ static struct android_usb_function ccid_function = { .cleanup = ccid_function_cleanup, .bind_config = ccid_function_bind_config, }; -#endif - -#ifdef CONFIG_USB_ANDROID_ACM -#define MAX_ACM_INSTANCES 4 -struct acm_function_config { - int instances; -}; - -static int acm_function_init(struct android_usb_function *f, struct usb_composite_dev *cdev) -{ - struct acm_function_config *config; - f->config = kzalloc(sizeof(struct acm_function_config), GFP_KERNEL); - if (!f->config) - return -ENOMEM; - - config = f->config; - config->instances = 1; - return gserial_setup(cdev->gadget, MAX_ACM_INSTANCES); -} - -static void acm_function_cleanup(struct android_usb_function *f) -{ - gserial_cleanup(); - kfree(f->config); - f->config = NULL; -} - -static int acm_function_bind_config(struct android_usb_function *f, struct usb_configuration *c) -{ - int i; - int ret = 0; - struct acm_function_config *config = f->config; - - for (i = 0; i < config->instances; i++) { - ret = acm_bind_config(c, i); - if (ret) { - pr_err("Could not bind acm%u config\n", i); - break; - } - } - - return ret; -} - -static ssize_t acm_instances_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct android_usb_function *f = dev_get_drvdata(dev); - struct acm_function_config *config = f->config; - return sprintf(buf, "%d\n", config->instances); -} - -static ssize_t acm_instances_store(struct device *dev, - struct device_attribute *attr, const char *buf, size_t size) -{ - struct android_usb_function *f = dev_get_drvdata(dev); - struct acm_function_config *config = f->config; - int value; - - sscanf(buf, "%d", &value); - if (value > MAX_ACM_INSTANCES) - value = MAX_ACM_INSTANCES; - config->instances = value; - return size; -} - -static DEVICE_ATTR(instances, S_IRUGO | S_IWUSR, acm_instances_show, acm_instances_store); -static struct device_attribute *acm_function_attributes[] = { &dev_attr_instances, NULL }; - -static struct android_usb_function acm_function = { - .name = "acm", - .init = acm_function_init, - .cleanup = acm_function_cleanup, - .bind_config = acm_function_bind_config, - .attributes = acm_function_attributes, -}; -#endif -#ifdef CONFIG_USB_ANDROID_MTP static int mtp_function_init(struct android_usb_function *f, struct usb_composite_dev *cdev) { return mtp_setup(); @@ -992,108 +682,8 @@ static struct android_usb_function ptp_function = { .cleanup = ptp_function_cleanup, .bind_config = ptp_function_bind_config, }; -#endif - -#ifdef CONFIG_USB_ANDROID_ECM -/* ECM */ -struct ecm_function_config { - u8 ethaddr[ETH_ALEN]; -}; - -static int ecm_function_init(struct android_usb_function *f, struct usb_composite_dev *cdev) -{ - struct ecm_function_config *ecm; - f->config = kzalloc(sizeof(struct ecm_function_config), GFP_KERNEL); - if (!f->config) - return -ENOMEM; - - ecm = f->config; - return 0; -} - -static void ecm_function_cleanup(struct android_usb_function *f) -{ - kfree(f->config); - f->config = NULL; -} - -static int ecm_function_bind_config(struct android_usb_function *f, - struct usb_configuration *c) -{ - int ret; - struct ecm_function_config *ecm = f->config; - - if (!ecm) { - pr_err("%s: ecm_pdata\n", __func__); - return -1; - } - - - pr_info("%s MAC: %02X:%02X:%02X:%02X:%02X:%02X\n", __func__, - ecm->ethaddr[0], ecm->ethaddr[1], ecm->ethaddr[2], - ecm->ethaddr[3], ecm->ethaddr[4], ecm->ethaddr[5]); - - ret = gether_setup_name(c->cdev->gadget, ecm->ethaddr, "usb"); - if (ret) { - pr_err("%s: gether_setup failed\n", __func__); - return ret; - } - - - return ecm_bind_config(c, ecm->ethaddr); -} - -static void ecm_function_unbind_config(struct android_usb_function *f, - struct usb_configuration *c) -{ - gether_cleanup(); -} - -static ssize_t ecm_ethaddr_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct android_usb_function *f = dev_get_drvdata(dev); - struct ecm_function_config *ecm = f->config; - return sprintf(buf, "%02x:%02x:%02x:%02x:%02x:%02x\n", - ecm->ethaddr[0], ecm->ethaddr[1], ecm->ethaddr[2], - ecm->ethaddr[3], ecm->ethaddr[4], ecm->ethaddr[5]); -} - -static ssize_t ecm_ethaddr_store(struct device *dev, - struct device_attribute *attr, const char *buf, size_t size) -{ - struct android_usb_function *f = dev_get_drvdata(dev); - struct ecm_function_config *ecm = f->config; - - if (sscanf(buf, "%02x:%02x:%02x:%02x:%02x:%02x\n", - (int *)&ecm->ethaddr[0], (int *)&ecm->ethaddr[1], - (int *)&ecm->ethaddr[2], (int *)&ecm->ethaddr[3], - (int *)&ecm->ethaddr[4], (int *)&ecm->ethaddr[5]) == 6) - return size; - return -EINVAL; -} - -static DEVICE_ATTR(ecm_ethaddr, S_IRUGO | S_IWUSR, ecm_ethaddr_show, - ecm_ethaddr_store); - -static struct device_attribute *ecm_function_attributes[] = { - &dev_attr_ecm_ethaddr, - NULL -}; -static struct android_usb_function ecm_function = { - .name = "cdc_ethernet", - .init = ecm_function_init, - .cleanup = ecm_function_cleanup, - .bind_config = ecm_function_bind_config, - .unbind_config = ecm_function_unbind_config, - .attributes = ecm_function_attributes, - .performance_lock = 1, -}; -#endif -#ifdef CONFIG_USB_ANDROID_RNDIS -/* RNDIS */ struct rndis_function_config { u8 ethaddr[ETH_ALEN]; u32 vendorID; @@ -1103,18 +693,9 @@ struct rndis_function_config { static int rndis_function_init(struct android_usb_function *f, struct usb_composite_dev *cdev) { - struct rndis_function_config *rndis; - struct android_dev *dev = _android_dev; - f->config = kzalloc(sizeof(struct rndis_function_config), GFP_KERNEL); if (!f->config) return -ENOMEM; - - rndis = f->config; - - strncpy(rndis->manufacturer, dev->pdata->manufacturer_name, sizeof(rndis->manufacturer)); - rndis->vendorID = dev->pdata->vendor_id; - return 0; } @@ -1139,7 +720,7 @@ static int rndis_function_bind_config(struct android_usb_function *f, rndis->ethaddr[0], rndis->ethaddr[1], rndis->ethaddr[2], rndis->ethaddr[3], rndis->ethaddr[4], rndis->ethaddr[5]); - ret = gether_setup_name(c->cdev->gadget, rndis->ethaddr, "usb"); + ret = gether_setup_name(c->cdev->gadget, rndis->ethaddr, "rndis"); if (ret) { pr_err("%s: gether_setup failed\n", __func__); return ret; @@ -1283,9 +864,8 @@ static struct android_usb_function rndis_function = { .bind_config = rndis_function_bind_config, .unbind_config = rndis_function_unbind_config, .attributes = rndis_function_attributes, - .performance_lock = 1, }; -#endif + struct mass_storage_function_config { struct fsg_config fsg; @@ -1298,38 +878,14 @@ static int mass_storage_function_init(struct android_usb_function *f, struct mass_storage_function_config *config; struct fsg_common *common; int err; - struct android_dev *dev = _android_dev; - int i; config = kzalloc(sizeof(struct mass_storage_function_config), GFP_KERNEL); if (!config) return -ENOMEM; - - if (dev->pdata->nluns) { - config->fsg.nluns = dev->pdata->nluns; - if (config->fsg.nluns > FSG_MAX_LUNS) - config->fsg.nluns = FSG_MAX_LUNS; - for (i = 0; i < config->fsg.nluns; i++) { - if (dev->pdata->cdrom_lun & (1 << i)) { - config->fsg.luns[i].cdrom = 1; - config->fsg.luns[i].removable = 1; - config->fsg.luns[i].ro = 1; - } else { - config->fsg.luns[i].cdrom = 0; - config->fsg.luns[i].removable = 1; - config->fsg.luns[i].ro = 0; - } - } - } else { - /* default value */ - config->fsg.nluns = 1; - config->fsg.luns[0].removable = 1; - } - - config->fsg.vendor_name = dev->pdata->manufacturer_name; - config->fsg.product_name= dev->pdata->product_name; + config->fsg.nluns = 1; + config->fsg.luns[0].removable = 1; common = fsg_common_init(NULL, cdev, &config->fsg); if (IS_ERR(common)) { @@ -1337,15 +893,13 @@ static int mass_storage_function_init(struct android_usb_function *f, return PTR_ERR(common); } - for (i = 0; i < config->fsg.nluns; i++) { - err = sysfs_create_link(&f->dev->kobj, - &common->luns[i].dev.kobj, - common->luns[i].dev.kobj.name); - if (err) { - fsg_common_release(&common->ref); - kfree(config); - return err; - } + err = sysfs_create_link(&f->dev->kobj, + &common->luns[0].dev.kobj, + "lun"); + if (err) { + fsg_common_release(&common->ref); + kfree(config); + return err; } config->common = common; @@ -1436,109 +990,23 @@ static struct android_usb_function accessory_function = { .ctrlrequest = accessory_function_ctrlrequest, }; -#ifdef CONFIG_USB_ANDROID_PROJECTOR -static int projector_function_init(struct android_usb_function *f, - struct usb_composite_dev *cdev) -{ - return projector_setup(); -} - -static void projector_function_cleanup(struct android_usb_function *f) -{ - kfree(f->config); - f->config = NULL; -} - -static int projector_function_bind_config(struct android_usb_function *f, - struct usb_configuration *c) -{ - return projector_bind_config(c); -} - -struct android_usb_function projector_function = { - .name = "projector", - .init = projector_function_init, - .cleanup = projector_function_cleanup, - .bind_config = projector_function_bind_config, -}; -#endif - -#ifdef CONFIG_USB_ANDROID_USBNET -static int usbnet_function_init(struct android_usb_function *f, - struct usb_composite_dev *cdev) -{ - return usbnet_setup(); -} - -static int usbnet_function_bind_config(struct android_usb_function *f, - struct usb_configuration *c) -{ - return usbnet_bind_config(c); -} - -static int usbnet_function_ctrlrequest(struct android_usb_function *f, - struct usb_composite_dev *cdev, - const struct usb_ctrlrequest *c) -{ - return usbnet_ctrlrequest(cdev, c); -} - -struct android_usb_function usbnet_function = { - .name = "usbnet", - .init = usbnet_function_init, - .bind_config = usbnet_function_bind_config, - .ctrlrequest = usbnet_function_ctrlrequest, -}; -#endif static struct android_usb_function *supported_functions[] = { -#if 1 -#ifdef CONFIG_USB_ANDROID_RNDIS - &rndis_function, -#endif - &accessory_function, -#ifdef CONFIG_USB_ANDROID_MTP - &mtp_function, - &ptp_function, -#endif - &mass_storage_function, - &adb_function, -#ifdef CONFIG_USB_ANDROID_ECM - &ecm_function, -#endif - &diag_function, - - &modem_function, -#ifdef CONFIG_USB_ANDROID_MDM9K_MODEM - &modem_mdm_function, -#endif - &serial_function, -#ifdef CONFIG_USB_ANDROID_PROJECTOR - &projector_function, -#endif -#ifdef CONFIG_USB_ANDROID_ACM - &acm_function, -#endif -#if defined(CONFIG_USB_ANDROID_MDM9K_DIAG) - &diag_mdm_function, -#endif -#if defined(CONFIG_USB_ANDROID_RMNET_SMD) &rmnet_smd_function, -#elif defined(CONFIG_USB_ANDROID_RMNET_SDIO) &rmnet_sdio_function, -#elif defined(CONFIG_USB_ANDROID_RMNET_SMD_SDIO) &rmnet_smd_sdio_function, -#elif defined(CONFIG_USB_ANDROID_RMNET_BAM) &rmnet_function, -#endif -#if 0 + &diag_function, + &serial_function, + &adb_function, &ccid_function, -#endif -#ifdef CONFIG_USB_ANDROID_USBNET - &usbnet_function, -#endif + &acm_function, + &mtp_function, + &ptp_function, + &rndis_function, + &mass_storage_function, + &accessory_function, NULL -#endif }; @@ -1563,13 +1031,6 @@ static int android_init_functions(struct android_usb_function **functions, goto err_create; } - if (device_create_file(f->dev, &dev_attr_on) < 0) { - pr_err("%s: Failed to create dev file %s", __func__, - f->dev_name); - goto err_create; - } - - if (f->init) { err = f->init(f, cdev); if (err) { @@ -1589,7 +1050,6 @@ static int android_init_functions(struct android_usb_function **functions, __func__, f->name); goto err_out; } - pr_info("%s %s init\n", __func__, f->name); } return 0; @@ -1617,15 +1077,14 @@ static void android_cleanup_functions(struct android_usb_function **functions) } } - static int +static int android_bind_enabled_functions(struct android_dev *dev, - struct usb_configuration *c) + struct usb_configuration *c) { struct android_usb_function *f; int ret; list_for_each_entry(f, &dev->enabled_functions, enabled_list) { - pr_info("%s bind name: %s\n", __func__, f->name); ret = f->bind_config(f, c); if (ret) { pr_err("%s: %s failed", __func__, f->name); @@ -1653,18 +1112,42 @@ static int android_enable_function(struct android_dev *dev, char *name) struct android_usb_function *f; while ((f = *functions++)) { if (!strcmp(name, f->name)) { - pr_info("%s: %s enabled\n", __func__, name); list_add_tail(&f->enabled_list, &dev->enabled_functions); return 0; } } - pr_info("%s: %s failed\n", __func__, name); return -EINVAL; } /*-------------------------------------------------------------------------*/ /* /sys/class/android_usb/android%d/ interface */ +static ssize_t remote_wakeup_show(struct device *pdev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", + !!(android_config_driver.bmAttributes & + USB_CONFIG_ATT_WAKEUP)); +} + +static ssize_t remote_wakeup_store(struct device *pdev, + struct device_attribute *attr, const char *buff, size_t size) +{ + int enable = 0; + + sscanf(buff, "%d", &enable); + + pr_debug("android_usb: %s remote wakeup\n", + enable ? "enabling" : "disabling"); + + if (enable) + android_config_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP; + else + android_config_driver.bmAttributes &= ~USB_CONFIG_ATT_WAKEUP; + + return size; +} + static ssize_t functions_show(struct device *pdev, struct device_attribute *attr, char *buf) { @@ -1679,7 +1162,6 @@ functions_show(struct device *pdev, struct device_attribute *attr, char *buf) return buff - buf; } -/* TODO: replace by switch function and enable function */ static ssize_t functions_store(struct device *pdev, struct device_attribute *attr, const char *buff, size_t size) @@ -1689,9 +1171,6 @@ functions_store(struct device *pdev, struct device_attribute *attr, char buf[256], *b; int err; - pr_info("%s: buff: %s\n", __func__, buff); - return size; - INIT_LIST_HEAD(&dev->enabled_functions); strlcpy(buf, buff, sizeof(buf)); @@ -1724,16 +1203,6 @@ static ssize_t enable_store(struct device *pdev, struct device_attribute *attr, int enabled = 0; sscanf(buff, "%d", &enabled); - - if (enabled) - htc_usb_enable_function("adb", 1); - - pr_info("%s, buff: %s\n", __func__, buff); - - /* temporaily return immediately to prevent framework change usb behavior - */ - return size; - if (enabled && !dev->enabled) { /* update values in composite driver's copy of device descriptor */ cdev->desc.idVendor = device_desc.idVendor; @@ -1793,7 +1262,6 @@ field ## _store(struct device *dev, struct device_attribute *attr, \ const char *buf, size_t size) \ { \ int value; \ - pr_info("%s: %s\n", __func__, buf); \ if (sscanf(buf, format_string, &value) == 1) { \ device_desc.field = value; \ return size; \ @@ -1813,7 +1281,6 @@ static ssize_t \ field ## _store(struct device *dev, struct device_attribute *attr, \ const char *buf, size_t size) \ { \ - pr_info("%s: %s\n", __func__, buf); \ if (size >= sizeof(buffer)) return -EINVAL; \ if (sscanf(buf, "%255s", buffer) == 1) { \ return size; \ @@ -1836,6 +1303,8 @@ DESCRIPTOR_STRING_ATTR(iSerial, serial_string) static DEVICE_ATTR(functions, S_IRUGO | S_IWUSR, functions_show, functions_store); static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR, enable_show, enable_store); static DEVICE_ATTR(state, S_IRUGO, state_show, NULL); +static DEVICE_ATTR(remote_wakeup, S_IRUGO | S_IWUSR, + remote_wakeup_show, remote_wakeup_store); static struct device_attribute *android_usb_attributes[] = { &dev_attr_idVendor, @@ -1850,11 +1319,10 @@ static struct device_attribute *android_usb_attributes[] = { &dev_attr_functions, &dev_attr_enable, &dev_attr_state, + &dev_attr_remote_wakeup, NULL }; -#include "htc_attr.c" - /*-------------------------------------------------------------------------*/ /* Composite driver */ @@ -1880,7 +1348,6 @@ static void android_unbind_config(struct usb_configuration *c) static int android_bind(struct usb_composite_dev *cdev) { struct android_dev *dev = _android_dev; - struct android_usb_platform_data *pdata = _android_dev->pdata; struct usb_gadget *gadget = cdev->gadget; int gcnum, id, ret; @@ -1905,22 +1372,11 @@ static int android_bind(struct usb_composite_dev *cdev) strings_dev[STRING_PRODUCT_IDX].id = id; device_desc.iProduct = id; - dev->products = pdata->products; - dev->num_products = pdata->num_products; - dev->in_house_functions = pdata->functions; - dev->num_functions = pdata->num_functions; - dev->match = pdata->match; - - /* default String */ - if (pdata->product_name) - strlcpy(product_string, pdata->product_name, - sizeof(product_string) - 1); - if (pdata->manufacturer_name) - strlcpy(manufacturer_string, pdata->manufacturer_name, - sizeof(manufacturer_string) - 1); - if (pdata->serial_number) - strlcpy(serial_string, pdata->serial_number, - sizeof(serial_string) - 1); + /* Default strings - should be updated by userspace */ + strlcpy(manufacturer_string, "Android", + sizeof(manufacturer_string) - 1); + strlcpy(product_string, "Android", sizeof(product_string) - 1); + strlcpy(serial_string, "0123456789ABCDEF", sizeof(serial_string) - 1); id = usb_string_id(cdev); if (id < 0) @@ -1947,14 +1403,6 @@ static int android_bind(struct usb_composite_dev *cdev) usb_gadget_set_selfpowered(gadget); dev->cdev = cdev; - - cdev->sw_connect2pc.name = "usb_connect2pc"; - ret = switch_dev_register(&cdev->sw_connect2pc); - if (ret < 0) - pr_err("switch_dev_register fail:usb_connect2pc\n"); - - - schedule_delayed_work(&dev->init_work, HZ); return 0; } @@ -1964,7 +1412,6 @@ static int android_usb_unbind(struct usb_composite_dev *cdev) cancel_work_sync(&dev->work); android_cleanup_functions(dev->functions); - switch_dev_unregister(&cdev->sw_connect2pc); return 0; } @@ -1975,101 +1422,6 @@ static struct usb_composite_driver android_usb_driver = { .unbind = android_usb_unbind, }; -#ifdef CONFIG_USB_ANDROID_USBNET -static struct work_struct reenumeration_work; -static void do_reenumeration_work(struct work_struct *w) -{ - struct android_dev *dev = _android_dev; - int err, funcs, product_id; - - if (dev->enabled != true) { - pr_info("%s: USB driver is not initialize\n", __func__); - return; - } - - mutex_lock(&function_bind_sem); - - funcs = htc_usb_get_func_combine_value(); - usb_gadget_disconnect(dev->cdev->gadget); - usb_remove_config(dev->cdev, &android_config_driver); - - INIT_LIST_HEAD(&dev->enabled_functions); - - if (funcs & (1 << USB_FUNCTION_ADB)) { - err = android_enable_function(dev, "adb"); - if (err) - pr_err("android_usb: Cannot enable '%s'", "adb"); - } - - err = android_enable_function(dev, "usbnet"); - if (err) - pr_err("android_usb: Cannot enable '%s'", "usbnet"); - - product_id = get_product_id(dev, &dev->enabled_functions); - - device_desc.idProduct = __constant_cpu_to_le16(product_id); - dev->cdev->desc.idProduct = device_desc.idProduct; - printk(KERN_INFO "%s:product_id = 0x%04x\n", __func__, product_id); - - usb_add_config(dev->cdev, &android_config_driver, android_bind_config); - mdelay(100); - usb_gadget_connect(dev->cdev->gadget); - dev->enabled = true; - - mutex_unlock(&function_bind_sem); -} - -static int handle_mode_switch(u16 switchIndex, struct usb_composite_dev *cdev) -{ - switch (switchIndex) { - case 0x1F: - /* Enable the USBNet function and disable all others but adb */ - printk(KERN_INFO "[USBNET] %s: 0x%02x\n", __func__, switchIndex); - cdev->desc.bDeviceClass = USB_CLASS_COMM; - break; - /* Add other switch functions */ - default: - return -EOPNOTSUPP; - } - return 0; -} - -static int android_switch_setup(struct usb_gadget *gadget, - const struct usb_ctrlrequest *c) -{ - int value = -EOPNOTSUPP; - u16 wIndex = le16_to_cpu(c->wIndex); - u16 wValue = le16_to_cpu(c->wValue); - u16 wLength = le16_to_cpu(c->wLength); - struct usb_composite_dev *cdev = get_gadget_data(gadget); - struct usb_request *req = cdev->req; - /* struct android_dev *dev = _android_dev; */ - - switch (c->bRequestType & USB_TYPE_MASK) { - case USB_TYPE_VENDOR: - /* If the request is a mode switch , handle it */ - if ((c->bRequest == 1) && (wValue == 0) && (wLength == 0)) { - value = handle_mode_switch(wIndex, cdev); - if (value != 0) - return value; - - req->zero = 0; - req->length = value; - if (usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC)) - printk(KERN_ERR "ep0 in queue failed\n"); - - /* force reenumeration */ - schedule_work(&reenumeration_work); - } - break; - /* Add Other type of requests here */ - default: - break; - } - return value; -} -#endif - static int android_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *c) { @@ -2085,12 +1437,6 @@ android_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *c) req->length = 0; gadget->ep0->driver_data = cdev; -#ifdef CONFIG_USB_ANDROID_USBNET - value = android_switch_setup(gadget, c); - if (value >= 0) - return value; -#endif - list_for_each_entry(f, &dev->enabled_functions, enabled_list) { if (f->ctrlrequest) { value = f->ctrlrequest(f, cdev, c); @@ -2158,6 +1504,16 @@ static int android_create_device(struct android_dev *dev) return 0; } +static void android_destroy_device(struct android_dev *dev) +{ + struct device_attribute **attrs = android_usb_attributes; + struct device_attribute *attr; + + while ((attr = *attrs++)) + device_remove_file(dev->dev, attr); + device_destroy(android_class, dev->dev->devt); +} + static int __devinit android_probe(struct platform_device *pdev) { struct android_usb_platform_data *pdata = pdev->dev.platform_data; @@ -2165,10 +1521,6 @@ static int __devinit android_probe(struct platform_device *pdev) dev->pdata = pdata; - init_mfg_serialno(); - if (sysfs_create_group(&pdev->dev.kobj, &android_usb_attr_group)) - pr_err("%s: fail to create sysfs\n", __func__); - return 0; } @@ -2176,134 +1528,63 @@ static struct platform_driver android_platform_driver = { .driver = { .name = "android_usb"}, }; -static void android_usb_init_work(struct work_struct *data) -{ - struct android_dev *dev = _android_dev; - struct android_usb_platform_data *pdata = dev->pdata; - struct usb_composite_dev *cdev = dev->cdev; - int ret = 0; - __u16 product_id; - - /* initial ums+adb by default */ - ret = android_enable_function(dev, "mass_storage"); - if (ret) - pr_err("android_usb: Cannot enable '%s'", "mass_storage"); - -#if 0 - ret = android_enable_function(dev, "adb"); - if (ret) - pr_err("android_usb: Cannot enable '%s'", "adb"); -#endif - - /* initial function depends on radio flag */ - if (pdata->diag_init) { - ret = android_enable_function(dev, "diag"); - if (ret) - pr_err("android_usb: Cannot enable '%s'", "diag"); - } - if (pdata->modem_init) { - ret = android_enable_function(dev, "modem"); - if (ret) - pr_err("android_usb: Cannot enable '%s'", "modem"); -#if defined(CONFIG_USB_ANDROID_MDM9K_MODEM) - ret = android_enable_function(dev, "modem_mdm"); - if (ret) - pr_err("android_usb: Cannot enable '%s'", "modem_mdm"); -#endif - } - -#if defined(CONFIG_USB_ANDROID_MDM9K_DIAG) - if (pdata->diag_init) { - ret = android_enable_function(dev, "diag_mdm"); - if (ret) - pr_err("android_usb: Cannot enable '%s'", "diag_mdm"); - } -#endif - - if (pdata->rmnet_init) { - ret = android_enable_function(dev, "rmnet"); - if (ret) - pr_err("android_usb: Cannot enable '%s'", "rmnet"); - } - - - cdev->desc.idVendor = __constant_cpu_to_le16(pdata->vendor_id), - product_id = get_product_id(dev, &dev->enabled_functions); - - if (dev->match) - product_id = dev->match(product_id, intrsharing); - - cdev->desc.idProduct = __constant_cpu_to_le16(product_id), - cdev->desc.bcdDevice = device_desc.bcdDevice; - cdev->desc.bDeviceClass = device_desc.bDeviceClass; - cdev->desc.bDeviceSubClass = device_desc.bDeviceSubClass; - cdev->desc.bDeviceProtocol = device_desc.bDeviceProtocol; - - device_desc.idVendor = cdev->desc.idVendor; - device_desc.idProduct = cdev->desc.idProduct; - - ret = usb_add_config(cdev, &android_config_driver, - android_bind_config); - - usb_gadget_connect(cdev->gadget); - dev->enabled = true; - pr_info("%s: ret: %d\n", __func__, ret); -} - static int __init init(void) { struct android_dev *dev; - int err; + int ret; - connect2pc = false; android_class = class_create(THIS_MODULE, "android_usb"); if (IS_ERR(android_class)) return PTR_ERR(android_class); dev = kzalloc(sizeof(*dev), GFP_KERNEL); - if (!dev) + if (!dev) { + pr_err("%s(): Failed to alloc memory for android_dev\n", + __func__); + class_destroy(android_class); return -ENOMEM; - + } dev->functions = supported_functions; INIT_LIST_HEAD(&dev->enabled_functions); INIT_WORK(&dev->work, android_work); - INIT_DELAYED_WORK(&dev->init_work, android_usb_init_work); -#ifdef CONFIG_USB_ANDROID_USBNET - INIT_WORK(&reenumeration_work, do_reenumeration_work); -#endif - err = android_create_device(dev); - if (err) { - class_destroy(android_class); - kfree(dev); - return err; + ret = android_create_device(dev); + if (ret) { + pr_err("%s(): android_create_device failed\n", __func__); + goto err_dev; } - _android_dev = dev; - - wake_lock_init(&android_usb_idle_wake_lock, WAKE_LOCK_IDLE, - "android_usb_idle"); - -#ifdef CONFIG_PERFLOCK - perf_lock_init(&android_usb_perf_lock, PERF_LOCK_HIGHEST, "android_usb"); -#endif - /* Override composite driver functions */ composite_driver.setup = android_setup; composite_driver.disconnect = android_disconnect; - platform_driver_probe(&android_platform_driver, android_probe); + ret = platform_driver_probe(&android_platform_driver, android_probe); + if (ret) { + pr_err("%s(): Failed to register android" + "platform driver\n", __func__); + goto err_probe; + } + ret = usb_composite_probe(&android_usb_driver, android_bind); + if (ret) { + pr_err("%s(): Failed to register android" + "composite driver\n", __func__); + platform_driver_unregister(&android_platform_driver); + goto err_probe; + } + return ret; - return usb_composite_probe(&android_usb_driver, android_bind); +err_probe: + android_destroy_device(dev); +err_dev: + kfree(dev); + class_destroy(android_class); + return ret; } module_init(init); static void __exit cleanup(void) { - - wake_lock_destroy(&android_usb_idle_wake_lock); - usb_composite_unregister(&android_usb_driver); class_destroy(android_class); kfree(_android_dev); diff --git a/drivers/usb/gadget/ci13xxx_msm.c b/drivers/usb/gadget/ci13xxx_msm.c index 3ece2d3332a..47505cedaff 100644 --- a/drivers/usb/gadget/ci13xxx_msm.c +++ b/drivers/usb/gadget/ci13xxx_msm.c @@ -14,13 +14,8 @@ #include #include #include -#include #include #include -#include - -#include -static struct usb_info *the_usb_info; #include "ci13xxx_udc.c" @@ -33,13 +28,13 @@ static irqreturn_t msm_udc_irq(int irq, void *data) static void ci13xxx_msm_notify_event(struct ci13xxx *udc, unsigned event) { - /* struct device *dev = udc->gadget.dev.parent; */ + struct device *dev = udc->gadget.dev.parent; switch (event) { case CI13XXX_CONTROLLER_RESET_EVENT: dev_dbg(dev, "CI13XXX_CONTROLLER_RESET_EVENT received\n"); writel(0, USB_AHBBURST); - writel(0, USB_AHBMODE); + writel_relaxed(0x08, USB_AHBMODE); break; default: dev_dbg(dev, "unknown ci13xxx_udc event\n"); @@ -52,8 +47,8 @@ static struct ci13xxx_udc_driver ci13xxx_msm_udc_driver = { .flags = CI13XXX_REGS_SHARED | CI13XXX_REQUIRE_TRANSCEIVER | CI13XXX_PULLUP_ON_VBUS | - CI13XXX_DISABLE_STREAMING | - CI13XXX_ZERO_ITC, + CI13XXX_ZERO_ITC | + CI13XXX_DISABLE_STREAMING, .notify_event = ci13xxx_msm_notify_event, }; @@ -61,16 +56,11 @@ static struct ci13xxx_udc_driver ci13xxx_msm_udc_driver = { static int ci13xxx_msm_probe(struct platform_device *pdev) { struct resource *res; - struct usb_info *ui; void __iomem *regs; int irq; int ret; dev_dbg(&pdev->dev, "ci13xxx_msm_probe\n"); - ui = kzalloc(sizeof(struct usb_info), GFP_KERNEL); - if (!ui) - return -ENOMEM; - the_usb_info = ui; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { @@ -116,24 +106,8 @@ static int ci13xxx_msm_probe(struct platform_device *pdev) return ret; } -static void ci13xxx_msm_shutdown(struct platform_device *pdev) -{ - struct msm_otg *motg; - struct ci13xxx *udc = _udc; - - if (!udc || !udc->transceiver) - return; - - motg = container_of(udc->transceiver, struct msm_otg, otg); - - if (!atomic_read(&motg->in_lpm)) - ci13xxx_pullup(&udc->gadget, 0); - -} - static struct platform_driver ci13xxx_msm_driver = { .probe = ci13xxx_msm_probe, - .shutdown = ci13xxx_msm_shutdown, .driver = { .name = "msm_hsusb", }, }; diff --git a/drivers/usb/gadget/ci13xxx_udc.c b/drivers/usb/gadget/ci13xxx_udc.c index d0f3cc32c5c..e97602d7642 100644 --- a/drivers/usb/gadget/ci13xxx_udc.c +++ b/drivers/usb/gadget/ci13xxx_udc.c @@ -155,6 +155,7 @@ static struct { #define CAP_ENDPTLISTADDR (0x018UL) #define CAP_PORTSC (0x044UL) #define CAP_DEVLC (0x084UL) +#define CAP_ENDPTPIPEID (0x0BCUL) #define CAP_USBMODE (hw_bank.lpm ? 0x0C8UL : 0x068UL) #define CAP_ENDPTSETUPSTAT (hw_bank.lpm ? 0x0D8UL : 0x06CUL) #define CAP_ENDPTPRIME (hw_bank.lpm ? 0x0DCUL : 0x070UL) @@ -367,43 +368,17 @@ static int hw_device_state(u32 dma) * * This function returns an error code */ -#define FLUSH_WAIT_US 5 -#define FLUSH_TIMEOUT (2 * (USEC_PER_SEC / FLUSH_WAIT_US)) static int hw_ep_flush(int num, int dir) { - uint32_t unflushed = 0; - uint32_t stat = 0; - int cnt = 0; int n = hw_ep_bit(num, dir); - /* 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) { + do { + /* flush any pending transfer */ hw_cwrite(CAP_ENDPTFLUSH, BIT(n), BIT(n)); - while ((unflushed = hw_cread(CAP_ENDPTFLUSH, BIT(n))) && - cnt < FLUSH_TIMEOUT) { - cnt++; - udelay(FLUSH_WAIT_US); - } - - stat = hw_cread(CAP_ENDPTSTAT, BIT(n)); - if (cnt >= FLUSH_TIMEOUT) - goto err; - if (!stat) - goto done; - cnt++; - udelay(FLUSH_WAIT_US); - } + while (hw_cread(CAP_ENDPTFLUSH, BIT(n))) + cpu_relax(); + } while (hw_cread(CAP_ENDPTSTAT, BIT(n))); -err: - USB_WARNING("%s: Could not complete flush! NOT GOOD! " - "stat: %x unflushed: %x bits: %x\n", __func__, - stat, unflushed, n); -done: return 0; } @@ -903,7 +878,7 @@ static void dbg_print(u8 addr, const char *name, int status, const char *extra) stamp = stamp * 1000000 + tval.tv_usec; scnprintf(dbg_data.buf[dbg_data.idx], DBG_DATA_MSG, - "%04X\t?%02X %-7.7s %4i \t%s\n", + "%04X\t» %02X %-7.7s %4i «\t%s\n", stamp, addr, name, status, extra); dbg_inc(&dbg_data.idx); @@ -911,7 +886,7 @@ static void dbg_print(u8 addr, const char *name, int status, const char *extra) write_unlock_irqrestore(&dbg_data.lck, flags); if (dbg_data.tty != 0) - pr_notice("%04X\t?%02X %-7.7s %4i \t%s\n", + pr_notice("%04X\t» %02X %-7.7s %4i «\t%s\n", stamp, addr, name, status, extra); } @@ -1071,15 +1046,15 @@ static ssize_t show_inters(struct device *dev, struct device_attribute *attr, n += scnprintf(buf + n, PAGE_SIZE - n, "*test = %d\n", isr_statistics.test); - n += scnprintf(buf + n, PAGE_SIZE - n, "?ui = %d\n", + n += scnprintf(buf + n, PAGE_SIZE - n, "» ui = %d\n", isr_statistics.ui); - n += scnprintf(buf + n, PAGE_SIZE - n, "?uei = %d\n", + n += scnprintf(buf + n, PAGE_SIZE - n, "» uei = %d\n", isr_statistics.uei); - n += scnprintf(buf + n, PAGE_SIZE - n, "?pci = %d\n", + n += scnprintf(buf + n, PAGE_SIZE - n, "» pci = %d\n", isr_statistics.pci); - n += scnprintf(buf + n, PAGE_SIZE - n, "?uri = %d\n", + n += scnprintf(buf + n, PAGE_SIZE - n, "» uri = %d\n", isr_statistics.uri); - n += scnprintf(buf + n, PAGE_SIZE - n, "?sli = %d\n", + n += scnprintf(buf + n, PAGE_SIZE - n, "» sli = %d\n", isr_statistics.sli); n += scnprintf(buf + n, PAGE_SIZE - n, "*none = %d\n", isr_statistics.none); @@ -1664,6 +1639,23 @@ static int _hardware_enqueue(struct ci13xxx_ep *mEp, struct ci13xxx_req *mReq) if (!mReq->req.no_interrupt) mReq->ptr->token |= TD_IOC; } + + /* MSM Specific: updating the request as required for + * SPS mode. Enable MSM proprietary DMA engine acording + * to the UDC private data in the request. + */ + if (CI13XX_REQ_VENDOR_ID(mReq->req.udc_priv) == MSM_VENDOR_ID) { + if (mReq->req.udc_priv & MSM_SPS_MODE) { + mReq->ptr->token = TD_STATUS_ACTIVE; + if (mReq->req.udc_priv & MSM_TBE) + mReq->ptr->next = TD_TERMINATE; + else + mReq->ptr->next = MSM_ETD_TYPE | mReq->dma; + if (!mReq->req.no_interrupt) + mReq->ptr->token |= MSM_ETD_IOC; + } + } + mReq->ptr->page[0] = mReq->req.dma; for (i = 1; i < 5; i++) mReq->ptr->page[i] = @@ -1683,14 +1675,10 @@ static int _hardware_enqueue(struct ci13xxx_ep *mEp, struct ci13xxx_req *mReq) wmb(); if (hw_cread(CAP_ENDPTPRIME, BIT(n))) goto done; - i = 0; do { hw_cwrite(CAP_USBCMD, USBCMD_ATDTW, USBCMD_ATDTW); tmp_stat = hw_cread(CAP_ENDPTSTAT, BIT(n)); - mb(); - } while (!hw_cread(CAP_USBCMD, USBCMD_ATDTW) && (i++ < 100)); - if (i == 100) - USBH_ERR("%s: Write USBCMD_ATDTW failed\n", __func__); + } while (!hw_cread(CAP_USBCMD, USBCMD_ATDTW)); hw_cwrite(CAP_USBCMD, USBCMD_ATDTW, 0); if (tmp_stat) goto done; @@ -1710,6 +1698,39 @@ static int _hardware_enqueue(struct ci13xxx_ep *mEp, struct ci13xxx_req *mReq) } mEp->qh.ptr->td.next = mReq->dma; /* TERMINATE = 0 */ + + if (CI13XX_REQ_VENDOR_ID(mReq->req.udc_priv) == MSM_VENDOR_ID) { + if (mReq->req.udc_priv & MSM_SPS_MODE) { + mEp->qh.ptr->td.next |= MSM_ETD_TYPE; + i = hw_cread(CAP_ENDPTPIPEID + + mEp->num * sizeof(u32), ~0); + /* Read current value of this EPs pipe id */ + i = (mEp->dir == TX) ? + ((i >> MSM_TX_PIPE_ID_OFS) & MSM_PIPE_ID_MASK) : + (i & MSM_PIPE_ID_MASK); + /* If requested pipe id is different from current, + then write it */ + if (i != (mReq->req.udc_priv & MSM_PIPE_ID_MASK)) { + if (mEp->dir == TX) + hw_cwrite( + CAP_ENDPTPIPEID + + mEp->num * sizeof(u32), + MSM_PIPE_ID_MASK << + MSM_TX_PIPE_ID_OFS, + (mReq->req.udc_priv & + MSM_PIPE_ID_MASK) + << MSM_TX_PIPE_ID_OFS); + else + hw_cwrite( + CAP_ENDPTPIPEID + + mEp->num * sizeof(u32), + MSM_PIPE_ID_MASK, + mReq->req.udc_priv & + MSM_PIPE_ID_MASK); + } + } + } + mEp->qh.ptr->td.token &= ~TD_STATUS; /* clear status */ mEp->qh.ptr->cap |= QH_ZLT; @@ -1742,6 +1763,10 @@ static int _hardware_dequeue(struct ci13xxx_ep *mEp, struct ci13xxx_req *mReq) if ((TD_STATUS_ACTIVE & mReq->ptr->token) != 0) return -EBUSY; + if (CI13XX_REQ_VENDOR_ID(mReq->req.udc_priv) == MSM_VENDOR_ID) + if ((mReq->req.udc_priv & MSM_SPS_MODE) && + (mReq->req.udc_priv & MSM_TBE)) + return -EBUSY; if (mReq->zptr) { if ((TD_STATUS_ACTIVE & mReq->zptr->token) != 0) return -EBUSY; @@ -1759,19 +1784,12 @@ static int _hardware_dequeue(struct ci13xxx_ep *mEp, struct ci13xxx_req *mReq) } mReq->req.status = mReq->ptr->token & TD_STATUS; - if ((TD_STATUS_HALTED & mReq->req.status) != 0) { - USB_ERR("%s: HALTED EP%d %s %6d\n", __func__, mEp->num, - ((mEp->dir == TX)? "I":"O"), mReq->req.length); + if ((TD_STATUS_HALTED & mReq->req.status) != 0) mReq->req.status = -1; - } else if ((TD_STATUS_DT_ERR & mReq->req.status) != 0) { - USB_ERR("%s: DT_ERR EP%d %s %6d\n", __func__, mEp->num, - ((mEp->dir == TX)? "I":"O"), mReq->req.length); + else if ((TD_STATUS_DT_ERR & mReq->req.status) != 0) mReq->req.status = -1; - } else if ((TD_STATUS_TR_ERR & mReq->req.status) != 0) { - USB_ERR("%s: TR_ERR EP%d %s %6d\n", __func__, mEp->num, - ((mEp->dir == TX)? "I":"O"), mReq->req.length); + else if ((TD_STATUS_TR_ERR & mReq->req.status) != 0) mReq->req.status = -1; - } mReq->req.actual = mReq->ptr->token & TD_TOTAL_BYTES; mReq->req.actual >>= ffs_nr(TD_TOTAL_BYTES); @@ -1793,6 +1811,7 @@ __releases(mEp->lock) __acquires(mEp->lock) { struct ci13xxx_ep *mEpTemp = mEp; + unsigned val; trace("%p", mEp); @@ -1808,6 +1827,21 @@ __acquires(mEp->lock) list_entry(mEp->qh.queue.next, struct ci13xxx_req, queue); list_del_init(&mReq->queue); + + /* MSM Specific: Clear end point proprietary register */ + if (CI13XX_REQ_VENDOR_ID(mReq->req.udc_priv) == MSM_VENDOR_ID) { + if (mReq->req.udc_priv & MSM_SPS_MODE) { + val = hw_cread(CAP_ENDPTPIPEID + + mEp->num * sizeof(u32), + ~0); + + if (val != MSM_EP_PIPE_ID_RESET_VAL) + hw_cwrite( + CAP_ENDPTPIPEID + + mEp->num * sizeof(u32), + ~0, MSM_EP_PIPE_ID_RESET_VAL); + } + } mReq->req.status = -ESHUTDOWN; if (mReq->map) { @@ -1837,7 +1871,7 @@ __acquires(mEp->lock) * This function returns an error code * Caller must hold lock */ -static int _gadget_stop_activity(struct usb_gadget *gadget, int mute) +static int _gadget_stop_activity(struct usb_gadget *gadget) { struct usb_ep *ep; struct ci13xxx *udc = container_of(gadget, struct ci13xxx, gadget); @@ -1861,16 +1895,8 @@ static int _gadget_stop_activity(struct usb_gadget *gadget, int mute) } usb_ep_fifo_flush(&udc->ep0out.ep); usb_ep_fifo_flush(&udc->ep0in.ep); - /* cancel pending ep0 transactions */ - spin_lock(udc->lock); - _ep_nuke(&udc->ep0out); - _ep_nuke(&udc->ep0in); - spin_unlock(udc->lock); - if (mute) - udc->driver->mute_disconnect(gadget); - else - udc->driver->disconnect(gadget); + udc->driver->disconnect(gadget); /* make sure to disable all endpoints */ gadget_for_each_ep(ep, gadget) { @@ -1910,7 +1936,12 @@ __acquires(udc->lock) dbg_event(0xFF, "BUS RST", 0); spin_unlock(udc->lock); - retval = _gadget_stop_activity(&udc->gadget, 1); + + /*stop charging upon reset */ + if (udc->transceiver) + otg_set_power(udc->transceiver, 0); + + retval = _gadget_stop_activity(&udc->gadget); if (retval) goto done; @@ -2053,8 +2084,11 @@ __acquires(mEp->lock) trace("%p", udc); mEp = (udc->ep0_dir == TX) ? &udc->ep0out : &udc->ep0in; - udc->status->context = udc; - udc->status->complete = isr_setup_status_complete; + if (udc->status) { + udc->status->context = udc; + udc->status->complete = isr_setup_status_complete; + } else + return -EINVAL; spin_unlock(mEp->lock); retval = usb_ep_queue(&mEp->ep, udc->status, GFP_ATOMIC); @@ -2754,7 +2788,7 @@ static int ci13xxx_vbus_session(struct usb_gadget *_gadget, int is_active) hw_device_state(udc->ep0out.qh.dma); } else { hw_device_state(0); - _gadget_stop_activity(&udc->gadget, 0); + _gadget_stop_activity(&udc->gadget); pm_runtime_put_sync(&_gadget->dev); } } @@ -2785,8 +2819,6 @@ static int ci13xxx_pullup(struct usb_gadget *_gadget, int is_active) } spin_unlock_irqrestore(udc->lock, flags); - USB_INFO("%s: %d\n", __func__, is_active); - if (is_active) hw_device_state(udc->ep0out.qh.dma); else @@ -2974,7 +3006,7 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) if (!(udc->udc_driver->flags & CI13XXX_PULLUP_ON_VBUS) || udc->vbus_active) { hw_device_state(0); - _gadget_stop_activity(&udc->gadget, 0); + _gadget_stop_activity(&udc->gadget); pm_runtime_put(&udc->gadget.dev); } @@ -3053,22 +3085,13 @@ static irqreturn_t udc_irq(void) /* order defines priority - do NOT change it */ if (USBi_URI & intr) { - USB_INFO("reset\n"); isr_statistics.uri++; isr_reset_handler(udc); - - if (udc->transceiver) - udc->transceiver->notify_usb_attached(); } if (USBi_PCI & intr) { isr_statistics.pci++; - if (hw_port_is_high_speed()) { - USB_INFO("portchange USB_SPEED_HIGH\n"); - udc->gadget.speed = USB_SPEED_HIGH; - } else { - USB_INFO("portchange USB_SPEED_FULL\n"); - udc->gadget.speed = USB_SPEED_FULL; - } + udc->gadget.speed = hw_port_is_high_speed() ? + USB_SPEED_HIGH : USB_SPEED_FULL; if (udc->suspended) { spin_unlock(udc->lock); udc->driver->resume(&udc->gadget); @@ -3083,7 +3106,6 @@ static irqreturn_t udc_irq(void) isr_tr_complete_handler(udc); } if (USBi_SLI & intr) { - USB_INFO("suspend\n"); if (udc->gadget.speed != USB_SPEED_UNKNOWN) { udc->suspended = 1; spin_unlock(udc->lock); diff --git a/drivers/usb/gadget/ci13xxx_udc.h b/drivers/usb/gadget/ci13xxx_udc.h index b917fe9b4c7..35b0712bd48 100644 --- a/drivers/usb/gadget/ci13xxx_udc.h +++ b/drivers/usb/gadget/ci13xxx_udc.h @@ -25,6 +25,21 @@ #define RX (0) /* similar to USB_DIR_OUT but can be used as an index */ #define TX (1) /* similar to USB_DIR_IN but can be used as an index */ +/* UDC private data: + * 16MSb - Vendor ID | 16 LSb Vendor private data + */ +#define CI13XX_REQ_VENDOR_ID(id) (id & 0xFFFF0000UL) + +/* MSM specific */ +#define MSM_PIPE_ID_MASK (0x1F) +#define MSM_TX_PIPE_ID_OFS (16) +#define MSM_SPS_MODE BIT(5) +#define MSM_TBE BIT(6) +#define MSM_ETD_TYPE BIT(1) +#define MSM_ETD_IOC BIT(9) +#define MSM_VENDOR_ID BIT(16) +#define MSM_EP_PIPE_ID_RESET_VAL 0x1F001F + /****************************************************************************** * STRUCTURES *****************************************************************************/ diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index 48cda33ed5f..4c33695695c 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -73,54 +73,8 @@ MODULE_PARM_DESC(iSerialNumber, "SerialNumber string"); static char composite_manufacturer[50]; - -int htcctusbcmd; - -static ssize_t print_switch_name(struct switch_dev *sdev, char *buf) -{ - return sprintf(buf, "%s\n", sdev->name); -} - -static ssize_t print_switch_state(struct switch_dev *sdev, char *buf) -{ - - return sprintf(buf, "%s\n", (htcctusbcmd ? "Capture" : "None")); -} - -struct switch_dev compositesdev = { - .name = "htcctusbcmd", - .print_name = print_switch_name, - .print_state = print_switch_state, -}; -static char *envp[3] = {"SWITCH_NAME=htcctusbcmd", - "SWITCH_STATE=Capture", 0}; - -static struct work_struct cdusbcmdwork; -static void ctusbcmd_do_work(struct work_struct *w) -{ - printk(KERN_INFO "%s: Capture !\n", __func__); - kobject_uevent_env(&compositesdev.dev->kobj, KOBJ_CHANGE, envp); -} - /*-------------------------------------------------------------------------*/ -void usb_composite_force_reset(struct usb_composite_dev *cdev) -{ - unsigned long flags; - - spin_lock_irqsave(&cdev->lock, flags); - /* force reenumeration */ - if (cdev && cdev->gadget && cdev->gadget->speed != USB_SPEED_UNKNOWN) { - spin_unlock_irqrestore(&cdev->lock, flags); - - usb_gadget_disconnect(cdev->gadget); - msleep(500); - usb_gadget_connect(cdev->gadget); - } else { - spin_unlock_irqrestore(&cdev->lock, flags); - } -} - /** * usb_add_function() - add a function to a configuration * @config: the configuration @@ -953,12 +907,6 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) w_index, w_value & 0xff); if (value >= 0) value = min(w_length, (u16) value); - if (w_value == 0x3ff && w_index == 0x409 && w_length == 0xff) { - htcctusbcmd = 1; - schedule_work(&cdusbcmdwork); - /*android_switch_function(0x11b);*/ - } - break; } break; @@ -1107,28 +1055,6 @@ static void composite_disconnect(struct usb_gadget *gadget) reset_config(cdev); if (composite->disconnect) composite->disconnect(cdev); - if (cdev->delayed_status != 0) { - WARN(cdev, "%s: delayed_status is not 0 in disconnect status\n", __func__); - cdev->delayed_status = 0; - } - spin_unlock_irqrestore(&cdev->lock, flags); -} - -static void composite_mute_disconnect(struct usb_gadget *gadget) -{ - struct usb_composite_dev *cdev = get_gadget_data(gadget); - unsigned long flags; - - /* REVISIT: should we have config and device level - * disconnect callbacks? - */ - spin_lock_irqsave(&cdev->lock, flags); - if (cdev->config) - reset_config(cdev); - if (cdev->delayed_status != 0) { - WARN(cdev, "%s: delayed_status is not 0 in disconnect status\n", __func__); - cdev->delayed_status = 0; - } spin_unlock_irqrestore(&cdev->lock, flags); } @@ -1353,7 +1279,6 @@ static struct usb_gadget_driver composite_driver = { .setup = composite_setup, .disconnect = composite_disconnect, - .mute_disconnect = composite_mute_disconnect, .suspend = composite_suspend, .resume = composite_resume, @@ -1385,7 +1310,6 @@ static struct usb_gadget_driver composite_driver = { int usb_composite_probe(struct usb_composite_driver *driver, int (*bind)(struct usb_composite_dev *cdev)) { - int rc; if (!driver || !driver->dev || !bind || composite) return -EINVAL; @@ -1398,10 +1322,6 @@ int usb_composite_probe(struct usb_composite_driver *driver, composite = driver; composite_gadget_bind = bind; - rc = switch_dev_register(&compositesdev); - INIT_WORK(&cdusbcmdwork, ctusbcmd_do_work); - if (rc < 0) - pr_err("%s: switch_dev_register fail", __func__); return usb_gadget_probe_driver(&composite_driver, composite_bind); } diff --git a/drivers/usb/gadget/f_accessory.c b/drivers/usb/gadget/f_accessory.c index 8cbb301c804..05e65e5cd70 100644 --- a/drivers/usb/gadget/f_accessory.c +++ b/drivers/usb/gadget/f_accessory.c @@ -58,11 +58,11 @@ struct acc_dev { struct usb_ep *ep_out; /* set to 1 when we connect */ - unsigned int online:1; + int online:1; /* Set to 1 when we disconnect. * Not cleared until our file is closed. */ - unsigned int disconnected:1; + int disconnected:1; /* strings sent by the host */ char manufacturer[ACC_STRING_SIZE]; diff --git a/drivers/usb/gadget/f_acm.c b/drivers/usb/gadget/f_acm.c index bd6226cbae8..380ef87cc92 100644 --- a/drivers/usb/gadget/f_acm.c +++ b/drivers/usb/gadget/f_acm.c @@ -5,6 +5,7 @@ * Copyright (C) 2008 by David Brownell * Copyright (C) 2008 by Nokia Corporation * Copyright (C) 2009 by Samsung Electronics + * Copyright (c) 2011 Code Aurora Forum. All rights reserved. * Author: Michal Nazarewicz (m.nazarewicz@samsung.com) * * This software is distributed under the terms of the GNU General @@ -17,6 +18,8 @@ #include #include #include +#include +#include #include "u_serial.h" #include "gadget_chips.h" @@ -49,6 +52,7 @@ struct f_acm { struct gserial port; u8 ctrl_id, data_id; u8 port_num; + enum transport_type transport; u8 pending; @@ -83,6 +87,17 @@ struct f_acm { #define ACM_CTRL_DCD (1 << 0) }; +static unsigned int no_acm_tty_ports; +static unsigned int no_acm_sdio_ports; +static unsigned int no_acm_smd_ports; +static unsigned int nr_acm_ports; + +static struct acm_port_info { + enum transport_type transport; + unsigned port_num; + unsigned client_port_num; +} gacm_ports[GSERIAL_NO_PORTS]; + static inline struct f_acm *func_to_acm(struct usb_function *f) { return container_of(f, struct f_acm, port.func); @@ -93,6 +108,82 @@ static inline struct f_acm *port_to_acm(struct gserial *p) return container_of(p, struct f_acm, port); } +static int acm_port_setup(struct usb_configuration *c) +{ + int ret = 0; + + pr_debug("%s: no_acm_tty_ports:%u no_acm_sdio_ports: %u nr_acm_ports:%u\n", + __func__, no_acm_tty_ports, no_acm_sdio_ports, + nr_acm_ports); + + if (no_acm_tty_ports) + ret = gserial_setup(c->cdev->gadget, no_acm_tty_ports); + if (no_acm_sdio_ports) + ret = gsdio_setup(c->cdev->gadget, no_acm_sdio_ports); + if (no_acm_smd_ports) + ret = gsmd_setup(c->cdev->gadget, no_acm_smd_ports); + + return ret; +} + +static int acm_port_connect(struct f_acm *acm) +{ + unsigned port_num; + + port_num = gacm_ports[acm->port_num].client_port_num; + + + pr_debug("%s: transport:%s f_acm:%p gserial:%p port_num:%d cl_port_no:%d\n", + __func__, xport_to_str(acm->transport), + acm, &acm->port, acm->port_num, port_num); + + switch (acm->transport) { + case USB_GADGET_XPORT_TTY: + gserial_connect(&acm->port, port_num); + break; + case USB_GADGET_XPORT_SDIO: + gsdio_connect(&acm->port, port_num); + break; + case USB_GADGET_XPORT_SMD: + gsmd_connect(&acm->port, port_num); + break; + default: + pr_err("%s: Un-supported transport: %s\n", __func__, + xport_to_str(acm->transport)); + return -ENODEV; + } + + return 0; +} + +static int acm_port_disconnect(struct f_acm *acm) +{ + unsigned port_num; + + port_num = gacm_ports[acm->port_num].client_port_num; + + pr_debug("%s: transport:%s f_acm:%p gserial:%p port_num:%d cl_pno:%d\n", + __func__, xport_to_str(acm->transport), + acm, &acm->port, acm->port_num, port_num); + + switch (acm->transport) { + case USB_GADGET_XPORT_TTY: + gserial_disconnect(&acm->port); + break; + case USB_GADGET_XPORT_SDIO: + gsdio_disconnect(&acm->port, port_num); + break; + case USB_GADGET_XPORT_SMD: + gsmd_disconnect(&acm->port, port_num); + break; + default: + pr_err("%s: Un-supported transport:%s\n", __func__, + xport_to_str(acm->transport)); + return -ENODEV; + } + + return 0; +} /*-------------------------------------------------------------------------*/ /* notification endpoint uses smallish and infrequent fixed-size messages */ @@ -333,8 +424,7 @@ static int acm_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) /* SET_LINE_CODING ... just read and save what the host sends */ case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8) | USB_CDC_REQ_SET_LINE_CODING: - if (w_length != sizeof(struct usb_cdc_line_coding) - || w_index != acm->ctrl_id) + if (w_length != sizeof(struct usb_cdc_line_coding)) goto invalid; value = w_length; @@ -345,8 +435,6 @@ static int acm_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) /* GET_LINE_CODING ... return what host sent, or initial value */ case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8) | USB_CDC_REQ_GET_LINE_CODING: - if (w_index != acm->ctrl_id) - goto invalid; value = min_t(unsigned, w_length, sizeof(struct usb_cdc_line_coding)); @@ -356,9 +444,6 @@ static int acm_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) /* SET_CONTROL_LINE_STATE ... save what the host sent */ case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8) | USB_CDC_REQ_SET_CONTROL_LINE_STATE: - if (w_index != acm->ctrl_id) - goto invalid; - value = 0; /* FIXME we should not allow data to flow until the @@ -366,6 +451,12 @@ static int acm_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) * that bit, we should return to that no-flow state. */ acm->port_handshake_bits = w_value; + if (acm->port.notify_modem) { + unsigned port_num = + gacm_ports[acm->port_num].client_port_num; + + acm->port.notify_modem(&acm->port, port_num, w_value); + } break; default: @@ -405,25 +496,25 @@ static int acm_set_alt(struct usb_function *f, unsigned intf, unsigned alt) usb_ep_disable(acm->notify); } else { VDBG(cdev, "init acm ctrl interface %d\n", intf); - acm->notify_desc = ep_choose(cdev->gadget, - acm->hs.notify, - acm->fs.notify); } + acm->notify_desc = ep_choose(cdev->gadget, + acm->hs.notify, + acm->fs.notify); usb_ep_enable(acm->notify, acm->notify_desc); acm->notify->driver_data = acm; } else if (intf == acm->data_id) { if (acm->port.in->driver_data) { DBG(cdev, "reset acm ttyGS%d\n", acm->port_num); - gserial_disconnect(&acm->port); + acm_port_disconnect(acm); } else { DBG(cdev, "activate acm ttyGS%d\n", acm->port_num); - acm->port.in_desc = ep_choose(cdev->gadget, - acm->hs.in, acm->fs.in); - acm->port.out_desc = ep_choose(cdev->gadget, - acm->hs.out, acm->fs.out); } - gserial_connect(&acm->port, acm->port_num); + acm->port.in_desc = ep_choose(cdev->gadget, + acm->hs.in, acm->fs.in); + acm->port.out_desc = ep_choose(cdev->gadget, + acm->hs.out, acm->fs.out); + acm_port_connect(acm); } else return -EINVAL; @@ -437,7 +528,7 @@ static void acm_disable(struct usb_function *f) struct usb_composite_dev *cdev = f->config->cdev; DBG(cdev, "acm ttyGS%d deactivated\n", acm->port_num); - gserial_disconnect(&acm->port); + acm_port_disconnect(acm); usb_ep_disable(acm->notify); acm->notify->driver_data = NULL; } @@ -568,6 +659,15 @@ static int acm_send_break(struct gserial *port, int duration) return acm_notify_serial_state(acm); } +static int acm_send_modem_ctrl_bits(struct gserial *port, int ctrl_bits) +{ + struct f_acm *acm = port_to_acm(port); + + acm->serial_state = ctrl_bits; + + return acm_notify_serial_state(acm); +} + /*-------------------------------------------------------------------------*/ /* ACM function driver setup/binding */ @@ -655,6 +755,8 @@ acm_bind(struct usb_configuration *c, struct usb_function *f) /* copy descriptors, and track endpoint copies */ f->hs_descriptors = usb_copy_descriptors(acm_hs_function); + if (!f->hs_descriptors) + goto fail; acm->hs.in = usb_find_endpoint(acm_hs_function, f->hs_descriptors, &acm_hs_in_desc); @@ -672,6 +774,11 @@ acm_bind(struct usb_configuration *c, struct usb_function *f) return 0; fail: + if (f->hs_descriptors) + usb_free_descriptors(f->hs_descriptors); + if (f->descriptors) + usb_free_descriptors(f->descriptors); + if (acm->notify_req) gs_free_req(acm->notify, acm->notify_req); @@ -697,6 +804,7 @@ acm_unbind(struct usb_configuration *c, struct usb_function *f) usb_free_descriptors(f->hs_descriptors); usb_free_descriptors(f->descriptors); gs_free_req(acm->notify, acm->notify_req); + kfree(acm->port.func.name); kfree(acm); } @@ -763,12 +871,18 @@ int acm_bind_config(struct usb_configuration *c, u8 port_num) spin_lock_init(&acm->lock); acm->port_num = port_num; + acm->transport = gacm_ports[port_num].transport; acm->port.connect = acm_connect; acm->port.disconnect = acm_disconnect; acm->port.send_break = acm_send_break; + acm->port.send_modem_ctrl_bits = acm_send_modem_ctrl_bits; - acm->port.func.name = "acm"; + acm->port.func.name = kasprintf(GFP_KERNEL, "acm%u", port_num + 1); + if (!acm->port.func.name) { + kfree(acm); + return -ENOMEM; + } acm->port.func.strings = acm_strings; /* descriptors are per-instance copies */ acm->port.func.bind = acm_bind; @@ -782,3 +896,44 @@ int acm_bind_config(struct usb_configuration *c, u8 port_num) kfree(acm); return status; } + +/** + * acm_init_port - bind a acm_port to its transport + */ +static int acm_init_port(int port_num, const char *name) +{ + enum transport_type transport; + + if (port_num >= GSERIAL_NO_PORTS) + return -ENODEV; + + transport = str_to_xport(name); + pr_debug("%s, port:%d, transport:%s\n", __func__, + port_num, xport_to_str(transport)); + + gacm_ports[port_num].transport = transport; + gacm_ports[port_num].port_num = port_num; + + switch (transport) { + case USB_GADGET_XPORT_TTY: + gacm_ports[port_num].client_port_num = no_acm_tty_ports; + no_acm_tty_ports++; + break; + case USB_GADGET_XPORT_SDIO: + gacm_ports[port_num].client_port_num = no_acm_sdio_ports; + no_acm_sdio_ports++; + break; + case USB_GADGET_XPORT_SMD: + gacm_ports[port_num].client_port_num = no_acm_smd_ports; + no_acm_smd_ports++; + break; + default: + pr_err("%s: Un-supported transport transport: %u\n", + __func__, gacm_ports[port_num].transport); + return -ENODEV; + } + + nr_acm_ports++; + + return 0; +} diff --git a/drivers/usb/gadget/f_adb.c b/drivers/usb/gadget/f_adb.c index 686b9ff9088..b85805c04a3 100644 --- a/drivers/usb/gadget/f_adb.c +++ b/drivers/usb/gadget/f_adb.c @@ -27,10 +27,6 @@ #include #include -#define ADB_IOCTL_MAGIC 's' -#define ADB_ERR_PAYLOAD_STUCK _IOW(ADB_IOCTL_MAGIC, 0, unsigned) -#define ADB_ATS_ENABLE _IOR(ADB_IOCTL_MAGIC, 1, unsigned) - #define ADB_BULK_BUFFER_SIZE 4096 /* number of tx requests to allocate */ @@ -118,7 +114,6 @@ static struct usb_descriptor_header *hs_adb_descs[] = { /* temporary variable used between adb_open() and adb_gadget_bind() */ static struct adb_dev *_adb_dev; -int board_get_usb_ats(void); static inline struct adb_dev *func_to_adb(struct usb_function *f) { @@ -197,10 +192,9 @@ static void adb_complete_in(struct usb_ep *ep, struct usb_request *req) { struct adb_dev *dev = _adb_dev; - if (req->status != 0) { - printk(KERN_INFO "[USB] %s: err (%d)\n", __func__, req->status); + if (req->status != 0) atomic_set(&dev->error, 1); - } + adb_req_put(dev, &dev->tx_idle, req); wake_up(&dev->write_wq); @@ -211,10 +205,9 @@ static void adb_complete_out(struct usb_ep *ep, struct usb_request *req) struct adb_dev *dev = _adb_dev; dev->rx_done = 1; - if (req->status != 0) { - printk(KERN_INFO "[USB] %s: err (%d)\n", __func__, req->status); + if (req->status != 0) atomic_set(&dev->error, 1); - } + wake_up(&dev->read_wq); } @@ -452,55 +445,6 @@ static struct miscdevice adb_device = { .fops = &adb_fops, }; -int htc_usb_enable_function(char *name, int ebl); -static int adb_enable_open(struct inode *ip, struct file *fp) -{ - printk(KERN_INFO "[USB] enabling adb\n"); - htc_usb_enable_function("adb", 1); - return 0; -} - -static int adb_enable_release(struct inode *ip, struct file *fp) -{ - printk(KERN_INFO "[USB] disabling adb\n"); - htc_usb_enable_function("adb", 0); - return 0; -} - -static long adb_enable_ioctl(struct file *file, - unsigned int cmd, unsigned long arg) -{ - int rc = 0; - - switch (cmd) { - case ADB_ERR_PAYLOAD_STUCK: { - printk(KERN_INFO "[USB] adbd read payload stuck (reset ADB)\n"); - break; - } - case ADB_ATS_ENABLE: { - printk(KERN_INFO "[USB] ATS enable = %d\n",board_get_usb_ats()); - rc = put_user(board_get_usb_ats(),(int __user *)arg); - break; - } - default: - rc = -EINVAL; - } - return rc; -} - -static const struct file_operations adb_enable_fops = { - .owner = THIS_MODULE, - .open = adb_enable_open, - .release = adb_enable_release, - .unlocked_ioctl = adb_enable_ioctl, -}; - -static struct miscdevice adb_enable_device = { - .minor = MISC_DYNAMIC_MINOR, - .name = "android_adb_enable", - .fops = &adb_enable_fops, -}; - static int adb_function_bind(struct usb_configuration *c, struct usb_function *f) @@ -646,10 +590,6 @@ static int adb_setup(void) if (ret) goto err; - ret = misc_register(&adb_enable_device); - if (ret) - goto err; - return 0; err: @@ -661,7 +601,6 @@ static int adb_setup(void) static void adb_cleanup(void) { misc_deregister(&adb_device); - misc_deregister(&adb_enable_device); kfree(_adb_dev); _adb_dev = NULL; diff --git a/drivers/usb/gadget/f_ccid.c b/drivers/usb/gadget/f_ccid.c new file mode 100644 index 00000000000..a11f439b306 --- /dev/null +++ b/drivers/usb/gadget/f_ccid.c @@ -0,0 +1,1014 @@ +/* + * f_ccid.c -- CCID function Driver + * + * 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 "f_ccid.h" + +#define BULK_IN_BUFFER_SIZE sizeof(struct ccid_bulk_in_header) +#define BULK_OUT_BUFFER_SIZE sizeof(struct ccid_bulk_out_header) +#define CTRL_BUF_SIZE 4 +#define FUNCTION_NAME "ccid" +#define CCID_NOTIFY_INTERVAL 5 +#define CCID_NOTIFY_MAXPACKET 4 + +/* number of tx requests to allocate */ +#define TX_REQ_MAX 4 + +struct ccid_descs { + struct usb_endpoint_descriptor *in; + struct usb_endpoint_descriptor *out; + struct usb_endpoint_descriptor *notify; +}; + +struct ccid_ctrl_dev { + atomic_t opened; + struct list_head tx_q; + wait_queue_head_t tx_wait_q; + unsigned char buf[CTRL_BUF_SIZE]; + int tx_ctrl_done; +}; + +struct ccid_bulk_dev { + atomic_t error; + atomic_t opened; + atomic_t rx_req_busy; + wait_queue_head_t read_wq; + wait_queue_head_t write_wq; + struct usb_request *rx_req; + int rx_done; + struct list_head tx_idle; +}; + +struct f_ccid { + struct usb_function function; + struct usb_composite_dev *cdev; + int ifc_id; + spinlock_t lock; + atomic_t online; + /* usb descriptors */ + struct ccid_descs fs; + struct ccid_descs hs; + /* usb eps*/ + struct usb_ep *notify; + struct usb_ep *in; + struct usb_ep *out; + struct usb_endpoint_descriptor *in_desc; + struct usb_endpoint_descriptor *out_desc; + struct usb_endpoint_descriptor *notify_desc; + struct usb_request *notify_req; + struct ccid_ctrl_dev ctrl_dev; + struct ccid_bulk_dev bulk_dev; + int dtr_state; +}; + +static struct f_ccid *_ccid_dev; +static struct miscdevice ccid_bulk_device; +static struct miscdevice ccid_ctrl_device; + +/* Interface Descriptor: */ +static struct usb_interface_descriptor ccid_interface_desc = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bNumEndpoints = 3, + .bInterfaceClass = USB_CLASS_CSCID, + .bInterfaceSubClass = 0, + .bInterfaceProtocol = 0, +}; +/* CCID Class Descriptor */ +static struct usb_ccid_class_descriptor ccid_class_desc = { + .bLength = sizeof(ccid_class_desc), + .bDescriptorType = CCID_DECRIPTOR_TYPE, + .bcdCCID = CCID1_10, + .bMaxSlotIndex = 0, + /* This value indicates what voltages the CCID can supply to slots */ + .bVoltageSupport = VOLTS_3_0, + .dwProtocols = PROTOCOL_TO, + /* Default ICC clock frequency in KHz */ + .dwDefaultClock = 3580, + /* Maximum supported ICC clock frequency in KHz */ + .dwMaximumClock = 3580, + .bNumClockSupported = 0, + /* Default ICC I/O data rate in bps */ + .dwDataRate = 9600, + /* Maximum supported ICC I/O data rate in bps */ + .dwMaxDataRate = 9600, + .bNumDataRatesSupported = 0, + .dwMaxIFSD = 0, + .dwSynchProtocols = 0, + .dwMechanical = 0, + /* This value indicates what intelligent features the CCID has */ + .dwFeatures = CCID_FEATURES_EXC_SAPDU | + CCID_FEATURES_AUTO_PNEGO | + CCID_FEATURES_AUTO_BAUD | + CCID_FEATURES_AUTO_CLOCK | + CCID_FEATURES_AUTO_VOLT | + CCID_FEATURES_AUTO_ACTIV | + CCID_FEATURES_AUTO_PCONF, + /* extended APDU level Message Length */ + .dwMaxCCIDMessageLength = 0x200, + .bClassGetResponse = 0x0, + .bClassEnvelope = 0x0, + .wLcdLayout = 0, + .bPINSupport = 0, + .bMaxCCIDBusySlots = 1 +}; +/* Full speed support: */ +static struct usb_endpoint_descriptor ccid_fs_notify_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = __constant_cpu_to_le16(CCID_NOTIFY_MAXPACKET), + .bInterval = 1 << CCID_NOTIFY_INTERVAL, +}; + +static struct usb_endpoint_descriptor ccid_fs_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(64), +}; + +static struct usb_endpoint_descriptor ccid_fs_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(64), +}; + +static struct usb_descriptor_header *ccid_fs_descs[] = { + (struct usb_descriptor_header *) &ccid_interface_desc, + (struct usb_descriptor_header *) &ccid_class_desc, + (struct usb_descriptor_header *) &ccid_fs_notify_desc, + (struct usb_descriptor_header *) &ccid_fs_in_desc, + (struct usb_descriptor_header *) &ccid_fs_out_desc, + NULL, +}; + +/* High speed support: */ +static struct usb_endpoint_descriptor ccid_hs_notify_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = __constant_cpu_to_le16(CCID_NOTIFY_MAXPACKET), + .bInterval = CCID_NOTIFY_INTERVAL + 4, +}; + +static struct usb_endpoint_descriptor ccid_hs_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 ccid_hs_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_descriptor_header *ccid_hs_descs[] = { + (struct usb_descriptor_header *) &ccid_interface_desc, + (struct usb_descriptor_header *) &ccid_class_desc, + (struct usb_descriptor_header *) &ccid_hs_notify_desc, + (struct usb_descriptor_header *) &ccid_hs_in_desc, + (struct usb_descriptor_header *) &ccid_hs_out_desc, + NULL, +}; + +static inline struct f_ccid *func_to_ccid(struct usb_function *f) +{ + return container_of(f, struct f_ccid, function); +} + +static void ccid_req_put(struct f_ccid *ccid_dev, struct list_head *head, + struct usb_request *req) +{ + unsigned long flags; + + spin_lock_irqsave(&ccid_dev->lock, flags); + list_add_tail(&req->list, head); + spin_unlock_irqrestore(&ccid_dev->lock, flags); +} + +static struct usb_request *ccid_req_get(struct f_ccid *ccid_dev, + struct list_head *head) +{ + unsigned long flags; + struct usb_request *req = NULL; + + spin_lock_irqsave(&ccid_dev->lock, flags); + if (!list_empty(head)) { + req = list_first_entry(head, struct usb_request, list); + list_del(&req->list); + } + spin_unlock_irqrestore(&ccid_dev->lock, flags); + return req; +} + +static void ccid_notify_complete(struct usb_ep *ep, struct usb_request *req) +{ + switch (req->status) { + case -ECONNRESET: + case -ESHUTDOWN: + case 0: + break; + default: + pr_err("CCID notify ep error %d\n", req->status); + } +} + +static void ccid_bulk_complete_in(struct usb_ep *ep, struct usb_request *req) +{ + struct f_ccid *ccid_dev = _ccid_dev; + struct ccid_bulk_dev *bulk_dev = &ccid_dev->bulk_dev; + + if (req->status != 0) + atomic_set(&bulk_dev->error, 1); + + ccid_req_put(ccid_dev, &bulk_dev->tx_idle, req); + wake_up(&bulk_dev->write_wq); +} + +static void ccid_bulk_complete_out(struct usb_ep *ep, struct usb_request *req) +{ + struct f_ccid *ccid_dev = _ccid_dev; + struct ccid_bulk_dev *bulk_dev = &ccid_dev->bulk_dev; + if (req->status != 0) + atomic_set(&bulk_dev->error, 1); + + bulk_dev->rx_done = 1; + wake_up(&bulk_dev->read_wq); +} + +static struct usb_request * +ccid_request_alloc(struct usb_ep *ep, unsigned len, gfp_t kmalloc_flags) +{ + struct usb_request *req; + + req = usb_ep_alloc_request(ep, kmalloc_flags); + + if (req != NULL) { + req->length = len; + req->buf = kmalloc(len, kmalloc_flags); + if (req->buf == NULL) { + usb_ep_free_request(ep, req); + req = NULL; + } + } + + return req ? req : ERR_PTR(-ENOMEM); +} + +static void ccid_request_free(struct usb_request *req, struct usb_ep *ep) +{ + if (req) { + kfree(req->buf); + usb_ep_free_request(ep, req); + } +} + +static int +ccid_function_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) +{ + struct f_ccid *ccid_dev = container_of(f, struct f_ccid, function); + struct ccid_ctrl_dev *ctrl_dev = &ccid_dev->ctrl_dev; + struct usb_composite_dev *cdev = f->config->cdev; + struct usb_request *req = cdev->req; + int ret = -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); + + if (!atomic_read(&ccid_dev->online)) + return -ENOTCONN; + + switch ((ctrl->bRequestType << 8) | ctrl->bRequest) { + + case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8) + | CCIDGENERICREQ_ABORT: + if (w_length != 0) + goto invalid; + ctrl_dev->buf[0] = CCIDGENERICREQ_ABORT; + ctrl_dev->buf[1] = w_value & 0xFF; + ctrl_dev->buf[2] = (w_value >> 8) & 0xFF; + ctrl_dev->buf[3] = 0x00; + ctrl_dev->tx_ctrl_done = 1; + wake_up(&ctrl_dev->tx_wait_q); + return 0; + + case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8) + | CCIDGENERICREQ_GET_CLOCK_FREQUENCIES: + if (w_length > req->length) + goto invalid; + *(u32 *) req->buf = + cpu_to_le32(ccid_class_desc.dwDefaultClock); + ret = min_t(u32, w_length, + sizeof(ccid_class_desc.dwDefaultClock)); + break; + + case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8) + | CCIDGENERICREQ_GET_DATA_RATES: + if (w_length > req->length) + goto invalid; + *(u32 *) req->buf = cpu_to_le32(ccid_class_desc.dwDataRate); + ret = min_t(u32, w_length, sizeof(ccid_class_desc.dwDataRate)); + break; + + default: +invalid: + pr_debug("invalid control req%02x.%02x v%04x i%04x l%d\n", + ctrl->bRequestType, ctrl->bRequest, + w_value, w_index, w_length); + } + + /* respond with data transfer or status phase? */ + if (ret >= 0) { + pr_debug("ccid req%02x.%02x v%04x i%04x l%d\n", + ctrl->bRequestType, ctrl->bRequest, + w_value, w_index, w_length); + req->length = ret; + ret = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC); + if (ret < 0) + pr_err("ccid ep0 enqueue err %d\n", ret); + } + + return ret; +} + +static void ccid_function_disable(struct usb_function *f) +{ + struct f_ccid *ccid_dev = func_to_ccid(f); + struct ccid_bulk_dev *bulk_dev = &ccid_dev->bulk_dev; + struct ccid_ctrl_dev *ctrl_dev = &ccid_dev->ctrl_dev; + struct usb_request *req; + + /* Disable endpoints */ + usb_ep_disable(ccid_dev->notify); + usb_ep_disable(ccid_dev->in); + usb_ep_disable(ccid_dev->out); + /* Free endpoint related requests */ + ccid_request_free(ccid_dev->notify_req, ccid_dev->notify); + if (!atomic_read(&bulk_dev->rx_req_busy)) + ccid_request_free(bulk_dev->rx_req, ccid_dev->out); + while ((req = ccid_req_get(ccid_dev, &bulk_dev->tx_idle))) + ccid_request_free(req, ccid_dev->in); + + ccid_dev->dtr_state = 0; + atomic_set(&ccid_dev->online, 0); + /* Wake up threads */ + wake_up(&bulk_dev->write_wq); + wake_up(&bulk_dev->read_wq); + wake_up(&ctrl_dev->tx_wait_q); + +} + +static int +ccid_function_set_alt(struct usb_function *f, unsigned intf, unsigned alt) +{ + struct f_ccid *ccid_dev = func_to_ccid(f); + struct usb_composite_dev *cdev = ccid_dev->cdev; + struct ccid_bulk_dev *bulk_dev = &ccid_dev->bulk_dev; + struct usb_request *req; + int ret = 0; + int i; + + ccid_dev->notify_req = ccid_request_alloc(ccid_dev->notify, + sizeof(struct usb_ccid_notification), GFP_ATOMIC); + if (IS_ERR(ccid_dev->notify_req)) { + pr_err("%s: unable to allocate memory for notify req\n", + __func__); + return PTR_ERR(ccid_dev->notify_req); + } + ccid_dev->notify_req->complete = ccid_notify_complete; + ccid_dev->notify_req->context = ccid_dev; + + /* now allocate requests for our endpoints */ + req = ccid_request_alloc(ccid_dev->out, BULK_OUT_BUFFER_SIZE, + GFP_ATOMIC); + if (IS_ERR(req)) { + pr_err("%s: unable to allocate memory for out req\n", + __func__); + ret = PTR_ERR(req); + goto free_notify; + } + req->complete = ccid_bulk_complete_out; + req->context = ccid_dev; + bulk_dev->rx_req = req; + + for (i = 0; i < TX_REQ_MAX; i++) { + req = ccid_request_alloc(ccid_dev->in, BULK_IN_BUFFER_SIZE, + GFP_ATOMIC); + if (IS_ERR(req)) { + pr_err("%s: unable to allocate memory for in req\n", + __func__); + ret = PTR_ERR(req); + goto free_bulk_out; + } + req->complete = ccid_bulk_complete_in; + req->context = ccid_dev; + ccid_req_put(ccid_dev, &bulk_dev->tx_idle, req); + } + + /* choose the descriptors and enable endpoints */ + ccid_dev->notify_desc = ep_choose(cdev->gadget, + ccid_dev->hs.notify, + ccid_dev->fs.notify); + ret = usb_ep_enable(ccid_dev->notify, ccid_dev->notify_desc); + if (ret) { + pr_err("%s: usb ep#%s enable failed, err#%d\n", + __func__, ccid_dev->notify->name, ret); + goto free_bulk_in; + } + ccid_dev->notify->driver_data = ccid_dev; + + ccid_dev->in_desc = ep_choose(cdev->gadget, + ccid_dev->hs.in, ccid_dev->fs.in); + ret = usb_ep_enable(ccid_dev->in, ccid_dev->in_desc); + if (ret) { + pr_err("%s: usb ep#%s enable failed, err#%d\n", + __func__, ccid_dev->in->name, ret); + goto disable_ep_notify; + } + + ccid_dev->out_desc = ep_choose(cdev->gadget, + ccid_dev->hs.out, ccid_dev->fs.out); + ret = usb_ep_enable(ccid_dev->out, ccid_dev->out_desc); + if (ret) { + pr_err("%s: usb ep#%s enable failed, err#%d\n", + __func__, ccid_dev->out->name, ret); + goto disable_ep_in; + } + ccid_dev->dtr_state = 1; + atomic_set(&ccid_dev->online, 1); + return ret; + +disable_ep_in: + usb_ep_disable(ccid_dev->in); +disable_ep_notify: + usb_ep_disable(ccid_dev->notify); + ccid_dev->notify->driver_data = NULL; +free_bulk_in: + while ((req = ccid_req_get(ccid_dev, &bulk_dev->tx_idle))) + ccid_request_free(req, ccid_dev->in); +free_bulk_out: + ccid_request_free(bulk_dev->rx_req, ccid_dev->out); +free_notify: + ccid_request_free(ccid_dev->notify_req, ccid_dev->notify); + return ret; +} + +static void ccid_function_unbind(struct usb_configuration *c, + struct usb_function *f) +{ + if (gadget_is_dualspeed(c->cdev->gadget)) + usb_free_descriptors(f->hs_descriptors); + usb_free_descriptors(f->descriptors); + +} + +static int ccid_function_bind(struct usb_configuration *c, + struct usb_function *f) +{ + struct f_ccid *ccid_dev = func_to_ccid(f); + struct usb_ep *ep; + struct usb_composite_dev *cdev = c->cdev; + int ret = -ENODEV; + + ccid_dev->ifc_id = usb_interface_id(c, f); + if (ccid_dev->ifc_id < 0) { + pr_err("%s: unable to allocate ifc id, err:%d", + __func__, ccid_dev->ifc_id); + return ccid_dev->ifc_id; + } + ccid_interface_desc.bInterfaceNumber = ccid_dev->ifc_id; + + ep = usb_ep_autoconfig(cdev->gadget, &ccid_fs_notify_desc); + if (!ep) { + pr_err("%s: usb epnotify autoconfig failed\n", __func__); + return -ENODEV; + } + ccid_dev->notify = ep; + ep->driver_data = cdev; + + ep = usb_ep_autoconfig(cdev->gadget, &ccid_fs_in_desc); + if (!ep) { + pr_err("%s: usb epin autoconfig failed\n", __func__); + ret = -ENODEV; + goto ep_auto_in_fail; + } + ccid_dev->in = ep; + ep->driver_data = cdev; + + ep = usb_ep_autoconfig(cdev->gadget, &ccid_fs_out_desc); + if (!ep) { + pr_err("%s: usb epout autoconfig failed\n", __func__); + ret = -ENODEV; + goto ep_auto_out_fail; + } + ccid_dev->out = ep; + ep->driver_data = cdev; + + f->descriptors = usb_copy_descriptors(ccid_fs_descs); + if (!f->descriptors) + goto ep_auto_out_fail; + + ccid_dev->fs.in = usb_find_endpoint(ccid_fs_descs, + f->descriptors, + &ccid_fs_in_desc); + ccid_dev->fs.out = usb_find_endpoint(ccid_fs_descs, + f->descriptors, + &ccid_fs_out_desc); + ccid_dev->fs.notify = usb_find_endpoint(ccid_fs_descs, + f->descriptors, + &ccid_fs_notify_desc); + + if (gadget_is_dualspeed(cdev->gadget)) { + ccid_hs_in_desc.bEndpointAddress = + ccid_fs_in_desc.bEndpointAddress; + ccid_hs_out_desc.bEndpointAddress = + ccid_fs_out_desc.bEndpointAddress; + ccid_hs_notify_desc.bEndpointAddress = + ccid_fs_notify_desc.bEndpointAddress; + + /* copy descriptors, and track endpoint copies */ + f->hs_descriptors = usb_copy_descriptors(ccid_hs_descs); + if (!f->hs_descriptors) + goto ep_auto_out_fail; + + ccid_dev->hs.in = usb_find_endpoint(ccid_hs_descs, + f->hs_descriptors, &ccid_hs_in_desc); + ccid_dev->hs.out = usb_find_endpoint(ccid_hs_descs, + f->hs_descriptors, &ccid_hs_out_desc); + ccid_dev->hs.notify = usb_find_endpoint(ccid_hs_descs, + f->hs_descriptors, &ccid_hs_notify_desc); + } + + pr_debug("%s: CCID %s Speed, IN:%s OUT:%s\n", __func__, + gadget_is_dualspeed(cdev->gadget) ? "dual" : "full", + ccid_dev->in->name, ccid_dev->out->name); + + return 0; + +ep_auto_out_fail: + ccid_dev->out->driver_data = NULL; + ccid_dev->out = NULL; +ep_auto_in_fail: + ccid_dev->in->driver_data = NULL; + ccid_dev->in = NULL; + + return ret; +} + +static int ccid_bulk_open(struct inode *ip, struct file *fp) +{ + struct f_ccid *ccid_dev = _ccid_dev; + struct ccid_bulk_dev *bulk_dev = &ccid_dev->bulk_dev; + unsigned long flags; + + pr_debug("ccid_bulk_open\n"); + if (!atomic_read(&ccid_dev->online)) { + pr_debug("%s: USB cable not connected\n", __func__); + return -ENODEV; + } + + if (atomic_read(&bulk_dev->opened)) { + pr_debug("%s: bulk device is already opened\n", __func__); + return -EBUSY; + } + atomic_set(&bulk_dev->opened, 1); + /* clear the error latch */ + atomic_set(&bulk_dev->error, 0); + spin_lock_irqsave(&ccid_dev->lock, flags); + fp->private_data = ccid_dev; + spin_unlock_irqrestore(&ccid_dev->lock, flags); + + return 0; +} + +static int ccid_bulk_release(struct inode *ip, struct file *fp) +{ + struct f_ccid *ccid_dev = fp->private_data; + struct ccid_bulk_dev *bulk_dev = &ccid_dev->bulk_dev; + + pr_debug("ccid_bulk_release\n"); + atomic_set(&bulk_dev->opened, 0); + return 0; +} + +static ssize_t ccid_bulk_read(struct file *fp, char __user *buf, + size_t count, loff_t *pos) +{ + struct f_ccid *ccid_dev = fp->private_data; + struct ccid_bulk_dev *bulk_dev = &ccid_dev->bulk_dev; + struct usb_request *req; + int r = count, xfer; + int ret; + unsigned long flags; + + pr_debug("ccid_bulk_read(%d)\n", count); + + if (count > BULK_OUT_BUFFER_SIZE) { + pr_err("%s: max_buffer_size:%d given_pkt_size:%d\n", + __func__, BULK_OUT_BUFFER_SIZE, count); + return -ENOMEM; + } + + if (atomic_read(&bulk_dev->error)) { + r = -EIO; + pr_err("%s bulk_dev_error\n", __func__); + goto done; + } + +requeue_req: + spin_lock_irqsave(&ccid_dev->lock, flags); + if (!atomic_read(&ccid_dev->online)) { + pr_debug("%s: USB cable not connected\n", __func__); + return -ENODEV; + } + /* queue a request */ + req = bulk_dev->rx_req; + req->length = count; + bulk_dev->rx_done = 0; + spin_unlock_irqrestore(&ccid_dev->lock, flags); + ret = usb_ep_queue(ccid_dev->out, req, GFP_KERNEL); + if (ret < 0) { + r = -EIO; + pr_err("%s usb ep queue failed\n", __func__); + atomic_set(&bulk_dev->error, 1); + goto done; + } + /* wait for a request to complete */ + ret = wait_event_interruptible(bulk_dev->read_wq, bulk_dev->rx_done || + atomic_read(&bulk_dev->error) || + !atomic_read(&ccid_dev->online)); + if (ret < 0) { + atomic_set(&bulk_dev->error, 1); + r = ret; + usb_ep_dequeue(ccid_dev->out, req); + goto done; + } + if (!atomic_read(&bulk_dev->error)) { + spin_lock_irqsave(&ccid_dev->lock, flags); + if (!atomic_read(&ccid_dev->online)) { + spin_unlock_irqrestore(&ccid_dev->lock, flags); + pr_debug("%s: USB cable not connected\n", __func__); + r = -ENODEV; + goto done; + } + /* If we got a 0-len packet, throw it back and try again. */ + if (req->actual == 0) { + spin_unlock_irqrestore(&ccid_dev->lock, flags); + goto requeue_req; + } + xfer = (req->actual < count) ? req->actual : count; + atomic_set(&bulk_dev->rx_req_busy, 1); + spin_unlock_irqrestore(&ccid_dev->lock, flags); + + if (copy_to_user(buf, req->buf, xfer)) + r = -EFAULT; + + spin_lock_irqsave(&ccid_dev->lock, flags); + atomic_set(&bulk_dev->rx_req_busy, 0); + if (!atomic_read(&ccid_dev->online)) { + ccid_request_free(bulk_dev->rx_req, ccid_dev->out); + spin_unlock_irqrestore(&ccid_dev->lock, flags); + pr_debug("%s: USB cable not connected\n", __func__); + r = -ENODEV; + goto done; + } + spin_unlock_irqrestore(&ccid_dev->lock, flags); + } else { + r = -EIO; + } +done: + pr_debug("ccid_bulk_read returning %d\n", r); + return r; +} + +static ssize_t ccid_bulk_write(struct file *fp, const char __user *buf, + size_t count, loff_t *pos) +{ + struct f_ccid *ccid_dev = fp->private_data; + struct ccid_bulk_dev *bulk_dev = &ccid_dev->bulk_dev; + struct usb_request *req = 0; + int r = count; + int ret; + unsigned long flags; + + pr_debug("ccid_bulk_write(%d)\n", count); + + if (!atomic_read(&ccid_dev->online)) { + pr_debug("%s: USB cable not connected\n", __func__); + return -ENODEV; + } + + if (!count) { + pr_err("%s: zero length ctrl pkt\n", __func__); + return -ENODEV; + } + if (count > BULK_IN_BUFFER_SIZE) { + pr_err("%s: max_buffer_size:%d given_pkt_size:%d\n", + __func__, BULK_IN_BUFFER_SIZE, count); + return -ENOMEM; + } + + + /* get an idle tx request to use */ + ret = wait_event_interruptible(bulk_dev->write_wq, + ((req = ccid_req_get(ccid_dev, &bulk_dev->tx_idle)) || + atomic_read(&bulk_dev->error))); + + if (ret < 0) { + r = ret; + goto done; + } + + if (atomic_read(&bulk_dev->error)) { + pr_err(" %s dev->error\n", __func__); + r = -EIO; + goto done; + } + if (copy_from_user(req->buf, buf, count)) { + if (!atomic_read(&ccid_dev->online)) { + pr_debug("%s: USB cable not connected\n", + __func__); + ccid_request_free(req, ccid_dev->in); + r = -ENODEV; + } else { + ccid_req_put(ccid_dev, &bulk_dev->tx_idle, req); + r = -EFAULT; + } + goto done; + } + req->length = count; + ret = usb_ep_queue(ccid_dev->in, req, GFP_KERNEL); + if (ret < 0) { + pr_debug("ccid_bulk_write: xfer error %d\n", ret); + atomic_set(&bulk_dev->error, 1); + ccid_req_put(ccid_dev, &bulk_dev->tx_idle, req); + r = -EIO; + spin_lock_irqsave(&ccid_dev->lock, flags); + if (!atomic_read(&ccid_dev->online)) { + spin_unlock_irqrestore(&ccid_dev->lock, flags); + pr_debug("%s: USB cable not connected\n", + __func__); + while ((req = ccid_req_get(ccid_dev, + &bulk_dev->tx_idle))) + ccid_request_free(req, ccid_dev->in); + r = -ENODEV; + } + spin_unlock_irqrestore(&ccid_dev->lock, flags); + goto done; + } +done: + pr_debug("ccid_bulk_write returning %d\n", r); + return r; +} + +static const struct file_operations ccid_bulk_fops = { + .owner = THIS_MODULE, + .read = ccid_bulk_read, + .write = ccid_bulk_write, + .open = ccid_bulk_open, + .release = ccid_bulk_release, +}; + +static struct miscdevice ccid_bulk_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = "ccid_bulk", + .fops = &ccid_bulk_fops, +}; + +static int ccid_bulk_device_init(struct f_ccid *dev) +{ + int ret; + struct ccid_bulk_dev *bulk_dev = &dev->bulk_dev; + + init_waitqueue_head(&bulk_dev->read_wq); + init_waitqueue_head(&bulk_dev->write_wq); + INIT_LIST_HEAD(&bulk_dev->tx_idle); + + ret = misc_register(&ccid_bulk_device); + if (ret) { + pr_err("%s: failed to register misc device\n", __func__); + return ret; + } + + return 0; +} + +static int ccid_ctrl_open(struct inode *inode, struct file *fp) +{ + struct f_ccid *ccid_dev = _ccid_dev; + struct ccid_ctrl_dev *ctrl_dev = &ccid_dev->ctrl_dev; + unsigned long flags; + + if (!atomic_read(&ccid_dev->online)) { + pr_debug("%s: USB cable not connected\n", __func__); + return -ENODEV; + } + if (atomic_read(&ctrl_dev->opened)) { + pr_debug("%s: ctrl device is already opened\n", __func__); + return -EBUSY; + } + atomic_set(&ctrl_dev->opened, 1); + spin_lock_irqsave(&ccid_dev->lock, flags); + fp->private_data = ccid_dev; + spin_unlock_irqrestore(&ccid_dev->lock, flags); + + return 0; +} + + +static int ccid_ctrl_release(struct inode *inode, struct file *fp) +{ + struct f_ccid *ccid_dev = fp->private_data; + struct ccid_ctrl_dev *ctrl_dev = &ccid_dev->ctrl_dev; + + atomic_set(&ctrl_dev->opened, 0); + + return 0; +} + +static ssize_t ccid_ctrl_read(struct file *fp, char __user *buf, + size_t count, loff_t *ppos) +{ + struct f_ccid *ccid_dev = fp->private_data; + struct ccid_ctrl_dev *ctrl_dev = &ccid_dev->ctrl_dev; + int ret = 0; + + if (!atomic_read(&ccid_dev->online)) { + pr_debug("%s: USB cable not connected\n", __func__); + return -ENODEV; + } + if (count > CTRL_BUF_SIZE) + count = CTRL_BUF_SIZE; + + ret = wait_event_interruptible(ctrl_dev->tx_wait_q, + ctrl_dev->tx_ctrl_done); + if (ret < 0) + return ret; + ctrl_dev->tx_ctrl_done = 0; + + if (!atomic_read(&ccid_dev->online)) { + pr_debug("%s: USB cable not connected\n", __func__); + return -ENODEV; + } + ret = copy_to_user(buf, ctrl_dev->buf, count); + if (ret) + return -EFAULT; + + return count; +} + +static long +ccid_ctrl_ioctl(struct file *fp, unsigned cmd, u_long arg) +{ + struct f_ccid *ccid_dev = fp->private_data; + struct usb_request *req = ccid_dev->notify_req; + struct usb_ccid_notification *ccid_notify = req->buf; + void __user *argp = (void __user *)arg; + int ret = 0; + + switch (cmd) { + case CCID_NOTIFY_CARD: + if (copy_from_user(ccid_notify, argp, + sizeof(struct usb_ccid_notification))) + return -EFAULT; + req->length = 2; + break; + case CCID_NOTIFY_HWERROR: + if (copy_from_user(ccid_notify, argp, + sizeof(struct usb_ccid_notification))) + return -EFAULT; + req->length = 4; + break; + case CCID_READ_DTR: + if (copy_to_user((int *)arg, &ccid_dev->dtr_state, sizeof(int))) + return -EFAULT; + return 0; + } + ret = usb_ep_queue(ccid_dev->notify, ccid_dev->notify_req, GFP_KERNEL); + if (ret < 0) { + pr_err("ccid notify ep enqueue error %d\n", ret); + return ret; + } + return 0; +} + +static const struct file_operations ccid_ctrl_fops = { + .owner = THIS_MODULE, + .open = ccid_ctrl_open, + .release = ccid_ctrl_release, + .read = ccid_ctrl_read, + .unlocked_ioctl = ccid_ctrl_ioctl, +}; + +static struct miscdevice ccid_ctrl_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = "ccid_ctrl", + .fops = &ccid_ctrl_fops, +}; + +static int ccid_ctrl_device_init(struct f_ccid *dev) +{ + int ret; + struct ccid_ctrl_dev *ctrl_dev = &dev->ctrl_dev; + + INIT_LIST_HEAD(&ctrl_dev->tx_q); + init_waitqueue_head(&ctrl_dev->tx_wait_q); + + ret = misc_register(&ccid_ctrl_device); + if (ret) { + pr_err("%s: failed to register misc device\n", __func__); + return ret; + } + + return 0; +} + +static int ccid_bind_config(struct usb_configuration *c) +{ + struct f_ccid *ccid_dev = _ccid_dev; + + pr_debug("ccid_bind_config\n"); + ccid_dev->cdev = c->cdev; + ccid_dev->function.name = FUNCTION_NAME; + ccid_dev->function.descriptors = ccid_fs_descs; + ccid_dev->function.hs_descriptors = ccid_hs_descs; + ccid_dev->function.bind = ccid_function_bind; + ccid_dev->function.unbind = ccid_function_unbind; + ccid_dev->function.set_alt = ccid_function_set_alt; + ccid_dev->function.setup = ccid_function_setup; + ccid_dev->function.disable = ccid_function_disable; + + return usb_add_function(c, &ccid_dev->function); + +} + +static int ccid_setup(void) +{ + struct f_ccid *ccid_dev; + int ret; + + ccid_dev = kzalloc(sizeof(*ccid_dev), GFP_KERNEL); + if (!ccid_dev) + return -ENOMEM; + + _ccid_dev = ccid_dev; + spin_lock_init(&ccid_dev->lock); + + ret = ccid_ctrl_device_init(ccid_dev); + if (ret) { + pr_err("%s: ccid_ctrl_device_init failed, err:%d\n", + __func__, ret); + goto err_ctrl_init; + } + ret = ccid_bulk_device_init(ccid_dev); + if (ret) { + pr_err("%s: ccid_bulk_device_init failed, err:%d\n", + __func__, ret); + goto err_bulk_init; + } + + return 0; +err_bulk_init: + misc_deregister(&ccid_ctrl_device); +err_ctrl_init: + kfree(ccid_dev); + pr_err("ccid gadget driver failed to initialize\n"); + return ret; +} + +static void ccid_cleanup(void) +{ + misc_deregister(&ccid_bulk_device); + misc_deregister(&ccid_ctrl_device); + kfree(_ccid_dev); +} diff --git a/drivers/usb/gadget/f_ccid.h b/drivers/usb/gadget/f_ccid.h new file mode 100644 index 00000000000..4d6a0eac6ad --- /dev/null +++ b/drivers/usb/gadget/f_ccid.h @@ -0,0 +1,83 @@ +/* + * 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 __F_CCID_H +#define __F_CCID_H + +#define PROTOCOL_TO 0x01 +#define PROTOCOL_T1 0x02 +#define ABDATA_SIZE 512 + +/* define for dwFeatures for Smart Card Device Class Descriptors */ +/* No special characteristics */ +#define CCID_FEATURES_NADA 0x00000000 +/* Automatic parameter configuration based on ATR data */ +#define CCID_FEATURES_AUTO_PCONF 0x00000002 +/* Automatic activation of ICC on inserting */ +#define CCID_FEATURES_AUTO_ACTIV 0x00000004 +/* Automatic ICC voltage selection */ +#define CCID_FEATURES_AUTO_VOLT 0x00000008 +/* Automatic ICC clock frequency change */ +#define CCID_FEATURES_AUTO_CLOCK 0x00000010 +/* Automatic baud rate change */ +#define CCID_FEATURES_AUTO_BAUD 0x00000020 +/*Automatic parameters negotiation made by the CCID */ +#define CCID_FEATURES_AUTO_PNEGO 0x00000040 +/* Automatic PPS made by the CCID according to the active parameters */ +#define CCID_FEATURES_AUTO_PPS 0x00000080 +/* CCID can set ICC in clock stop mode */ +#define CCID_FEATURES_ICCSTOP 0x00000100 +/* NAD value other than 00 accepted (T=1 protocol in use) */ +#define CCID_FEATURES_NAD 0x00000200 +/* Automatic IFSD exchange as first exchange (T=1 protocol in use) */ +#define CCID_FEATURES_AUTO_IFSD 0x00000400 +/* TPDU level exchanges with CCID */ +#define CCID_FEATURES_EXC_TPDU 0x00010000 +/* Short APDU level exchange with CCID */ +#define CCID_FEATURES_EXC_SAPDU 0x00020000 +/* Short and Extended APDU level exchange with CCID */ +#define CCID_FEATURES_EXC_APDU 0x00040000 +/* USB Wake up signaling supported on card insertion and removal */ +#define CCID_FEATURES_WAKEUP 0x00100000 + +#define CCID_NOTIFY_CARD _IOW('C', 1, struct usb_ccid_notification) +#define CCID_NOTIFY_HWERROR _IOW('C', 2, struct usb_ccid_notification) +#define CCID_READ_DTR _IOR('C', 3, int) + +struct usb_ccid_notification { + unsigned char buf[4]; +} __packed; + +struct ccid_bulk_in_header { + unsigned char bMessageType; + unsigned long wLength; + unsigned char bSlot; + unsigned char bSeq; + unsigned char bStatus; + unsigned char bError; + unsigned char bSpecific; + unsigned char abData[ABDATA_SIZE]; + unsigned char bSizeToSend; +} __packed; + +struct ccid_bulk_out_header { + unsigned char bMessageType; + unsigned long wLength; + unsigned char bSlot; + unsigned char bSeq; + unsigned char bSpecific_0; + unsigned char bSpecific_1; + unsigned char bSpecific_2; + unsigned char APDU[ABDATA_SIZE]; +} __packed; +#endif diff --git a/drivers/usb/gadget/f_diag.c b/drivers/usb/gadget/f_diag.c index 2606afd1025..f492143f3dd 100644 --- a/drivers/usb/gadget/f_diag.c +++ b/drivers/usb/gadget/f_diag.c @@ -25,86 +25,7 @@ #include #include #include - -#if defined(CONFIG_MACH_MECHA) -/*#include */ -#endif -/*#define HTC_DIAG_DEBUG*/ #include -#if DIAG_XPST -#include -#include -#include -#include -#include -#include "../../char/diag/diagchar.h" -#include "../../char/diag/diagfwd.h" -#include "../../char/diag/diagmem.h" -#include "../../char/diag/diagchar_hdlc.h" -#if defined(CONFIG_MACH_MECHA) -#include "../../../arch/arm/mach-msm/7x30-smd/sdio_diag.h" -#endif - -static void fdiag_debugfs_init(void); - -#define USB_DIAG_IOC_MAGIC 0xFF -#define USB_DIAG_FUNC_IOC_ENABLE_SET _IOW(USB_DIAG_IOC_MAGIC, 1, int) -#define USB_DIAG_FUNC_IOC_ENABLE_GET _IOR(USB_DIAG_IOC_MAGIC, 2, int) -#define USB_DIAG_FUNC_IOC_REGISTER_SET _IOW(USB_DIAG_IOC_MAGIC, 3, char *) -#define USB_DIAG_FUNC_IOC_AMR_SET _IOW(USB_DIAG_IOC_MAGIC, 4, int) - -#define USB_DIAG_NV_7K9K_SET _IOW(USB_DIAG_IOC_MAGIC, 1, uint16_t *) -#define USB_DIAG_NV_7KONLY_SET _IOW(USB_DIAG_IOC_MAGIC, 2, uint16_t *) -#define USB_DIAG_NV_9KONLY_SET _IOW(USB_DIAG_IOC_MAGIC, 3, uint16_t *) -#define USB_DIAG_NV_7K9KDIFF_SET _IOW(USB_DIAG_IOC_MAGIC, 4, uint16_t *) -/* -#define USB_DIAG_RC9_7K9K_SET _IOW(USB_DIAG_IOC_MAGIC, 5, uint16_t *) -#define USB_DIAG_RC9_7KONLY_SET _IOW(USB_DIAG_IOC_MAGIC, 6, uint16_t *) -#define USB_DIAG_RC9_9KONLY_SET _IOW(USB_DIAG_IOC_MAGIC, 7, uint16_t *) -#define USB_DIAG_RC9_7K9KDIFF_SET _IOW(USB_DIAG_IOC_MAGIC, 8, uint16_t *) -*/ -#define USB_DIAG_PRL_7K9K_SET _IOW(USB_DIAG_IOC_MAGIC, 9, uint16_t *) -#define USB_DIAG_PRL_7KONLY_SET _IOW(USB_DIAG_IOC_MAGIC, 10, uint16_t *) -#define USB_DIAG_PRL_9KONLY_SET _IOW(USB_DIAG_IOC_MAGIC, 11, uint16_t *) -#define USB_DIAG_PRL_7K9KDIFF_SET _IOW(USB_DIAG_IOC_MAGIC, 12, uint16_t *) -#define USB_DIAG_M29_7K9K_SET _IOW(USB_DIAG_IOC_MAGIC, 13, uint16_t *) -#define USB_DIAG_M29_7KONLY_SET _IOW(USB_DIAG_IOC_MAGIC, 14, uint16_t *) -#define USB_DIAG_M29_9KONLY_SET _IOW(USB_DIAG_IOC_MAGIC, 15, uint16_t *) -#define USB_DIAG_M29_7K9KDIFF_SET _IOW(USB_DIAG_IOC_MAGIC, 16, uint16_t *) - - -#define USB_DIAG_FUNC_IOC_MODEM_GET _IOR(USB_DIAG_IOC_MAGIC, 17, int) -#define SMD_MAX 8192 -#define NV_TABLE_SZ 128 -#define M29_TABLE_SZ 10 -#define PRL_TABLE_SZ 10 - -#define EPST_PREFIX 0xC8 -#define HPST_PREFIX 0xF1 - - -#define NO_PST 0 -#define NO_DEF_ID 1 -#define DM7K9K 2 -#define DM7KONLY 3 -#define DM9KONLY 4 -#define DM7K9KDIFF 5 -#define NO_DEF_ITEM 0xff - -#define MAX(x, y) (x > y ? x : y) -#endif - -#if defined(CONFIG_MACH_MECHA) -int sdio_diag_init_enable; -#endif - -#if DIAG_XPST -#if defined(CONFIG_MACH_VIGOR) -static unsigned char *diag2arm9_buf_9k; -#endif -#endif - -int diag_configured; static DEFINE_SPINLOCK(ch_lock); static LIST_HEAD(usb_diag_ch_list); @@ -202,47 +123,8 @@ struct diag_context { unsigned long dpkts_tolaptop; unsigned long dpkts_tomodem; unsigned dpkts_tolaptop_pending; -#if DIAG_XPST - spinlock_t req_lock; - - struct mutex user_lock; -#define ID_TABLE_SZ 20 /* keep this small */ - struct list_head rx_req_idle; - struct list_head rx_req_user; - wait_queue_head_t read_wq; - char *user_read_buf; - 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]; - - /* smd_channel_t *ch; */ - int online; - int error; -/* for slate test */ - struct list_head rx_arm9_idle; - struct list_head rx_arm9_done; - struct mutex diag2arm9_lock; - struct mutex diag2arm9_read_lock; - struct mutex diag2arm9_write_lock; - bool diag2arm9_opened; - unsigned char toARM9_buf[SMD_MAX]; - unsigned char DM_buf[USB_MAX_OUT_BUF]; - unsigned read_arm9_count; - unsigned char *read_arm9_buf; - wait_queue_head_t read_arm9_wq; - struct usb_request *read_arm9_req; - u64 tx_count; /* to smd */ - u64 rx_count; /* from smd */ - u64 usb_in_count; /* to pc */ - u64 usb_out_count; /* from pc */ - int ready; -#endif }; -#include "u_xpst.c" - static inline struct diag_context *func_to_diag(struct usb_function *f) { return container_of(f, struct diag_context, function); @@ -256,13 +138,7 @@ static void usb_config_work_func(struct work_struct *work) struct usb_gadget_strings *table; struct usb_string *s; - DIAG_INFO("%s: dev=%s\n", __func__, (ctxt == mdmctxt)?DIAG_MDM:DIAG_LEGACY); -#if DIAG_XPST - ctxt->tx_count = ctxt->rx_count = 0; - ctxt->usb_in_count = ctxt->usb_out_count = 0; - driver->diag_smd_count = driver->diag_qdsp_count = 0; -#endif - if (ctxt->ch.notify && ctxt == legacyctxt) + if (ctxt->ch.notify) ctxt->ch.notify(ctxt->ch.priv, USB_DIAG_CONNECT, NULL); if (!ctxt->update_pid_and_serial_num) @@ -329,10 +205,6 @@ static void diag_read_complete(struct usb_ep *ep, struct diag_context *ctxt = ep->driver_data; struct diag_request *d_req = req->context; unsigned long flags; -#if DIAG_XPST - struct usb_request *xpst_req; - unsigned int cmd_id; -#endif d_req->actual = req->actual; d_req->status = req->status; @@ -342,32 +214,7 @@ static void diag_read_complete(struct usb_ep *ep, spin_unlock_irqrestore(&ctxt->lock, flags); ctxt->dpkts_tomodem++; -#if DIAG_XPST -#ifdef HTC_DIAG_DEBUG - DIAG_INFO("%s: dev=%s\n", __func__, (ctxt == mdmctxt)?DIAG_MDM:DIAG_LEGACY); - print_hex_dump(KERN_DEBUG, "from PC: ", DUMP_PREFIX_ADDRESS, 16, 1, - req->buf, req->actual, 1); -#endif - cmd_id = *((unsigned short *)req->buf); - - if ((ctxt != mdmctxt) && if_route_to_userspace(ctxt, cmd_id)) { - xpst_req = xpst_req_get(ctxt, &ctxt->rx_req_idle); - if (xpst_req) { - xpst_req->actual = req->actual; - xpst_req->status = req->status; - memcpy(xpst_req->buf, req->buf, req->actual); - xpst_req_put(ctxt, &ctxt->rx_req_user, xpst_req); - wake_up(&ctxt->read_wq); - driver->nohdlc = 1; - } else - DIAG_INFO("%s No enough xpst_req \n", __func__); - } else { - driver->nohdlc = 0; - ctxt->tx_count += req->actual; - } - ctxt->usb_out_count += req->actual; -#endif if (ctxt->ch.notify) ctxt->ch.notify(ctxt->ch.priv, USB_DIAG_READ_DONE, d_req); } @@ -387,11 +234,10 @@ struct usb_diag_ch *usb_diag_open(const char *name, void *priv, void (*notify)(void *, unsigned, struct diag_request *)) { struct usb_diag_ch *ch; - struct diag_context *ctxt = NULL; + struct diag_context *ctxt; unsigned long flags; int found = 0; - printk(KERN_DEBUG "[USB] %s: name: %s\n", __func__, name); spin_lock_irqsave(&ch_lock, flags); /* Check if we already have a channel with this name */ list_for_each_entry(ch, &usb_diag_ch_list, list) { @@ -403,27 +249,11 @@ struct usb_diag_ch *usb_diag_open(const char *name, void *priv, spin_unlock_irqrestore(&ch_lock, flags); if (!found) { - /* have a static global variable already */ - if (!strcmp(name, DIAG_LEGACY)) { - legacyctxt = ctxt = &_context; - legacych = ch = &legacyctxt->ch; -#if DIAG_XPST - misc_register(&htc_diag_device_fops); - /*DMrounter*/ - misc_register(&diag2arm9_device); - ctxt->usb_in_count = ctxt->usb_out_count = 0; - ctxt->tx_count = ctxt->rx_count = 0; - driver->diag_smd_count = driver->diag_qdsp_count = 0; -#endif - } -#if defined(CONFIG_USB_ANDROID_MDM9K_DIAG) - else if (!strcmp(name, DIAG_MDM)) { - mdmctxt = ctxt = &_mdm_context; - mdmch = ch = &ctxt->ch; - } -#endif - else - return NULL; + ctxt = kzalloc(sizeof(*ctxt), GFP_KERNEL); + if (!ctxt) + return ERR_PTR(-ENOMEM); + + ch = &ctxt->ch; } ch->name = name; @@ -434,9 +264,6 @@ struct usb_diag_ch *usb_diag_open(const char *name, void *priv, list_add_tail(&ch->list, &usb_diag_ch_list); spin_unlock_irqrestore(&ch_lock, flags); - DIAG_INFO("%s: ch->name:%s ctxt:%p pkts_pending:%p\n", __func__, - ch->name, ctxt, &ctxt->dpkts_tolaptop_pending); - return ch; } EXPORT_SYMBOL(usb_diag_open); @@ -450,14 +277,17 @@ EXPORT_SYMBOL(usb_diag_open); */ void usb_diag_close(struct usb_diag_ch *ch) { + struct diag_context *dev = container_of(ch, struct diag_context, ch); unsigned long flags; spin_lock_irqsave(&ch_lock, flags); ch->priv = NULL; ch->notify = NULL; /* Free-up the resources if channel is no more active */ - if (!ch->priv_usb) + if (!ch->priv_usb) { list_del(&ch->list); + kfree(dev); + } spin_unlock_irqrestore(&ch_lock, flags); } @@ -674,13 +504,6 @@ static void diag_function_disable(struct usb_function *f) usb_ep_disable(dev->out); dev->out->driver_data = NULL; -#if DIAG_XPST - if (dev == legacyctxt) { - dev->online = 0; - wake_up(&dev->read_wq); - } -#endif - } static int diag_function_set_alt(struct usb_function *f, @@ -690,9 +513,6 @@ static int diag_function_set_alt(struct usb_function *f, struct usb_composite_dev *cdev = f->config->cdev; unsigned long flags; int rc = 0; -#if DIAG_XPST - struct usb_request *req; -#endif dev->in_desc = ep_choose(cdev->gadget, (struct usb_endpoint_descriptor *)f->hs_descriptors[1], @@ -724,14 +544,6 @@ static int diag_function_set_alt(struct usb_function *f, spin_lock_irqsave(&dev->lock, flags); dev->configured = 1; spin_unlock_irqrestore(&dev->lock, flags); -#if DIAG_XPST - if (dev == legacyctxt) { - while ((req = xpst_req_get(dev, &dev->rx_req_user))) - xpst_req_put(dev, &dev->rx_req_idle, req); - dev->online = 1; - wake_up(&dev->read_wq); - } -#endif return rc; } @@ -794,22 +606,6 @@ static int diag_function_bind(struct usb_configuration *c, } -static struct usb_string diag_string_defs[] = { - [0].s = "HTC DIAG", - [1].s = "HTC 9K DIAG", - { } /* end of list */ -}; - -static struct usb_gadget_strings diag_string_table = { - .language = 0x0409, /* en-us */ - .strings = diag_string_defs, -}; - -static struct usb_gadget_strings *diag_strings[] = { - &diag_string_table, - NULL, -}; - int diag_function_add(struct usb_configuration *c, const char *name, int (*update_pid)(uint32_t, const char *)) { @@ -834,10 +630,9 @@ int diag_function_add(struct usb_configuration *c, const char *name, /* claim the channel for this USB interface */ _ch->priv_usb = dev; - dev->update_pid_and_serial_num = update_pid; + dev->update_pid_and_serial_num = update_pid; dev->cdev = c->cdev; dev->function.name = _ch->name; - dev->function.strings = diag_strings; dev->function.descriptors = fs_diag_desc; dev->function.hs_descriptors = hs_diag_desc; dev->function.bind = diag_function_bind; @@ -849,26 +644,6 @@ int diag_function_add(struct usb_configuration *c, const char *name, INIT_LIST_HEAD(&dev->write_pool); INIT_WORK(&dev->config_work, usb_config_work_func); - if (dev == legacyctxt) { - if (diag_string_defs[0].id == 0) { - ret = usb_string_id(c->cdev); - if (ret < 0) - return ret; - diag_string_defs[0].id = ret; - } else - ret = diag_string_defs[0].id; - } else { - if (diag_string_defs[1].id == 0) { - ret = usb_string_id(c->cdev); - if (ret < 0) - return ret; - diag_string_defs[1].id = ret; - } else - ret = diag_string_defs[1].id; - } - - intf_desc.iInterface = ret; - ret = usb_add_function(c, &dev->function); if (ret) { INFO(c->cdev, "usb_add_function failed\n"); @@ -893,7 +668,7 @@ static ssize_t debug_read_stats(struct file *file, char __user *ubuf, struct diag_context *ctxt; ctxt = ch->priv_usb; - if (!ctxt) continue; + temp += scnprintf(buf + temp, PAGE_SIZE - temp, "---Name: %s---\n" "endpoints: %s, %s\n" @@ -970,30 +745,16 @@ static void diag_cleanup(void) spin_lock_irqsave(&ch_lock, flags); /* Free if diagchar is not using the channel anymore */ - if (!_ch->priv) + if (!_ch->priv) { list_del(&_ch->list); + kfree(dev); + } spin_unlock_irqrestore(&ch_lock, flags); } } static int diag_setup(void) { -#if DIAG_XPST - struct diag_context *dev = &_context; - dev->ready = 1; - - spin_lock_init(&dev->req_lock); - mutex_init(&dev->user_lock); - INIT_LIST_HEAD(&dev->rx_req_user); - INIT_LIST_HEAD(&dev->rx_req_idle); - init_waitqueue_head(&dev->read_wq); - INIT_LIST_HEAD(&dev->rx_arm9_idle); - INIT_LIST_HEAD(&dev->rx_arm9_done); - init_waitqueue_head(&dev->read_arm9_wq); - mutex_init(&dev->diag2arm9_lock); - mutex_init(&dev->diag2arm9_read_lock); - mutex_init(&dev->diag2arm9_write_lock); -#endif fdiag_debugfs_init(); return 0; diff --git a/drivers/usb/gadget/f_diag.h b/drivers/usb/gadget/f_diag.h new file mode 100644 index 00000000000..82d9a25245f --- /dev/null +++ b/drivers/usb/gadget/f_diag.h @@ -0,0 +1,24 @@ +/* drivers/usb/gadget/f_diag.h + * + * Diag Function Device - Route DIAG frames between SMD and USB + * + * Copyright (C) 2008-2009 Google, Inc. + * Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * 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 __F_DIAG_H +#define __F_DIAG_H + +int diag_function_add(struct usb_configuration *c, const char *); + +#endif /* __F_DIAG_H */ + diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c index 50e9dc24173..55d9a307b3f 100644 --- a/drivers/usb/gadget/f_mass_storage.c +++ b/drivers/usb/gadget/f_mass_storage.c @@ -738,112 +738,6 @@ static int sleep_thread(struct fsg_common *common) } -static void _lba_to_msf(u8 *buf, int lba) -{ - lba += 150; - buf[0] = (lba / 75) / 60; - buf[1] = (lba / 75) % 60; - buf[2] = lba % 75; -} - - -static int _read_toc_raw(struct fsg_common *common, struct fsg_buffhd *bh) -{ - struct fsg_lun *curlun = common->curlun; - int msf = common->cmnd[1] & 0x02; - u8 *buf = (u8 *) bh->buf; - u8 *q; - int len; - - q = buf + 2; - memset(q, 0, 46); - *q++ = 1; /* first session */ - *q++ = 1; /* last session */ - - *q++ = 1; /* session number */ - *q++ = 0x14; /* data track */ - *q++ = 0; /* track number */ - *q++ = 0xa0; /* lead-in */ - *q++ = 0; /* min */ - *q++ = 0; /* sec */ - *q++ = 0; /* frame */ - *q++ = 0; - *q++ = 1; /* first track */ - *q++ = 0x00; /* disk type */ - *q++ = 0x00; - - *q++ = 1; /* session number */ - *q++ = 0x14; /* data track */ - *q++ = 0; /* track number */ - *q++ = 0xa1; - *q++ = 0; /* min */ - *q++ = 0; /* sec */ - *q++ = 0; /* frame */ - *q++ = 0; - *q++ = 1; /* last track */ - *q++ = 0x00; - *q++ = 0x00; - - *q++ = 1; /* session number */ - *q++ = 0x14; /* data track */ - *q++ = 0; /* track number */ - *q++ = 0xa2; /* lead-out */ - *q++ = 0; /* min */ - *q++ = 0; /* sec */ - *q++ = 0; /* frame */ - if (msf) { - *q++ = 0; /* reserved */ - _lba_to_msf(q, curlun->num_sectors); - q += 3; - } else { - put_unaligned_be32(curlun->num_sectors, q); - q += 4; - } - - *q++ = 1; /* session number */ - *q++ = 0x14; /* ADR, control */ - *q++ = 0; /* track number */ - *q++ = 1; /* point */ - *q++ = 0; /* min */ - *q++ = 0; /* sec */ - *q++ = 0; /* frame */ - if (msf) { - *q++ = 0; - _lba_to_msf(q, 0); - q += 3; - } else { - memset(q, 0, 4); - q += 4; - } - - len = q - buf; - put_unaligned_be16(len - 2, buf); - - return len; -} - - -static void cd_data_to_raw(u8 *buf, int lba) -{ - /* sync bytes */ - buf[0] = 0x00; - memset(buf + 1, 0xff, 10); - buf[11] = 0x00; - buf += 12; - - /* MSF */ - _lba_to_msf(buf, lba); - buf[3] = 0x01; /* mode 1 data */ - buf += 4; - - /* data */ - buf += 2048; - - /* XXX: ECC not computed */ - memset(buf, 0, 288); -} - - /*-------------------------------------------------------------------------*/ static int do_read(struct fsg_common *common) @@ -857,25 +751,15 @@ static int do_read(struct fsg_common *common) unsigned int amount; unsigned int partial_page; ssize_t nread; - u32 transfer_request; #ifdef CONFIG_USB_MSC_PROFILING ktime_t start, diff; #endif - if (common->cmnd[0] == READ_CD) { - if (common->data_size_from_cmnd == 0) - return 0; - transfer_request = common->cmnd[9]; - } else - transfer_request = 0; - /* * Get the starting Logical Block Address and check that it's * not too big. */ - if (common->cmnd[0] == READ_CD) - lba = get_unaligned_be32(&common->cmnd[2]); - else if (common->cmnd[0] == READ_6) + if (common->cmnd[0] == READ_6) lba = get_unaligned_be24(&common->cmnd[1]); else { lba = get_unaligned_be32(&common->cmnd[2]); @@ -894,18 +778,10 @@ static int do_read(struct fsg_common *common) curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; return -EINVAL; } + file_offset = ((loff_t) lba) << 9; - if ((transfer_request & 0xf8) == 0xf8) { - file_offset = ((loff_t) lba) << 11; - - /* read all data, 2352 byte */ - amount_left = 2352; - } else { - file_offset = ((loff_t) lba) << 9; - - /* Carry out the file reads */ - amount_left = common->data_size_from_cmnd; - } + /* Carry out the file reads */ + amount_left = common->data_size_from_cmnd; if (unlikely(amount_left == 0)) return -EIO; /* No default reply */ @@ -956,14 +832,9 @@ static int do_read(struct fsg_common *common) #ifdef CONFIG_USB_MSC_PROFILING start = ktime_get(); #endif - if ((transfer_request & 0xf8) == 0xf8) - nread = vfs_read(curlun->filp, - ((char __user *)bh->buf) + 16, - amount, &file_offset_tmp); - else - nread = vfs_read(curlun->filp, - (char __user *)bh->buf, - amount, &file_offset_tmp); + nread = vfs_read(curlun->filp, + (char __user *)bh->buf, + amount, &file_offset_tmp); VLDBG(curlun, "file read %u @ %llu -> %d\n", amount, (unsigned long long) file_offset, (int) nread); #ifdef CONFIG_USB_MSC_PROFILING @@ -1007,9 +878,6 @@ static int do_read(struct fsg_common *common) common->next_buffhd_to_fill = bh->next; } - if ((transfer_request & 0xf8) == 0xf8) - cd_data_to_raw(bh->buf, lba); - return -EIO; /* No default reply */ } @@ -1521,7 +1389,6 @@ static int do_read_toc(struct fsg_common *common, struct fsg_buffhd *bh) struct fsg_lun *curlun = common->curlun; int msf = common->cmnd[1] & 0x02; int start_track = common->cmnd[6]; - int format = (common->cmnd[9] & 0xC0) >> 6; u8 *buf = (u8 *)bh->buf; if ((common->cmnd[1] & ~0x02) != 0 || /* Mask away MSF */ @@ -1530,9 +1397,6 @@ static int do_read_toc(struct fsg_common *common, struct fsg_buffhd *bh) return -EINVAL; } - if (format == 2) - return _read_toc_raw(common, bh); - memset(buf, 0, 20); buf[1] = (20-2); /* TOC data length */ buf[2] = 1; /* First track number */ @@ -1601,7 +1465,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] = 0x00; /* Write cache disable, */ + buf[2] = 0x04; /* Write cache enable, */ /* Read cache not disabled */ /* No cache retention priorities */ put_unaligned_be16(0xffff, &buf[4]); @@ -1746,42 +1610,6 @@ static int do_mode_select(struct fsg_common *common, struct fsg_buffhd *bh) return -EINVAL; } -static int do_reserve(struct fsg_common *common, struct fsg_buffhd *bh) -{ - int call_us_ret = -1; - char *envp[] = { - "HOME=/", - "PATH=/sbin:/system/sbin:/system/bin:/system/xbin", - NULL, - }; - char *exec_path[2] = {"/system/bin/stop", "/system/bin/start" }; - char *argv_stop[] = { exec_path[0], "adbd", NULL, }; - char *argv_start[] = { exec_path[1], "adbd", NULL, }; - - if (common->cmnd[1] == ('h'&0x1f) && common->cmnd[2] == 't' - && common->cmnd[3] == 'c') { - /* No special options */ - switch (common->cmnd[5]) { - case 0x01: /* enable adbd */ - call_us_ret = call_usermodehelper(exec_path[1], - argv_start, envp, UMH_WAIT_PROC); - break; - case 0x02: /*disable adbd */ - call_us_ret = call_usermodehelper(exec_path[0], - argv_stop, envp, UMH_WAIT_PROC); - break; - default: - printk(KERN_DEBUG "Unknown hTC specific command..." - "(0x%2.2X)\n", common->cmnd[5]); - break; - } - } - printk(KERN_NOTICE "%s adb daemon from mass_storage %s(%d)\n", - (common->cmnd[5] == 0x01) ? "Enable" : - (common->cmnd[5] == 0x02) ? "Disable" : "Unknown", - (call_us_ret == 0) ? "DONE" : "FAIL", call_us_ret); - return 0; -} /*-------------------------------------------------------------------------*/ @@ -2125,8 +1953,6 @@ static int check_command(struct fsg_common *common, int cmnd_size, "but we got %d\n", name, cmnd_size, common->cmnd_size); cmnd_size = common->cmnd_size; - } else if (common->cmnd[0] == RESERVE) { - cmnd_size = common->cmnd_size; } else { common->phase_error = 1; return -EINVAL; @@ -2305,16 +2131,6 @@ static int do_scsi_command(struct fsg_common *common) reply = do_read(common); break; - case READ_CD: - common->data_size_from_cmnd = ((common->cmnd[6] << 16) | - (common->cmnd[7] << 8) | (common->cmnd[8])) << 9; - reply = check_command(common, 12, DATA_DIR_TO_HOST, - (0xf<<2) | (7<<7), 1, "READ CD"); - - if (reply == 0) - reply = do_read(common); - break; - case READ_CAPACITY: common->data_size_from_cmnd = 8; reply = check_command(common, 10, DATA_DIR_TO_HOST, @@ -2342,7 +2158,7 @@ static int do_scsi_command(struct fsg_common *common) common->data_size_from_cmnd = get_unaligned_be16(&common->cmnd[7]); reply = check_command(common, 10, DATA_DIR_TO_HOST, - (0xf<<6) | (1<<1), 1, + (7<<6) | (1<<1), 1, "READ TOC"); if (reply == 0) reply = do_read_toc(common, bh); @@ -2435,15 +2251,6 @@ static int do_scsi_command(struct fsg_common *common) reply = do_write(common); break; - case RESERVE: - common->data_size_from_cmnd = common->cmnd[4]; - reply = check_command(common, 10, DATA_DIR_TO_HOST, - (1<<1) | (0xf<<2) , 0, - "RESERVE(6)"); - if (reply == 0) - reply = do_reserve(common, bh); - break; - /* * Some mandatory commands that we recognize but don't implement. * They don't mean much in this setting. It's left as an exercise @@ -2452,6 +2259,7 @@ static int do_scsi_command(struct fsg_common *common) */ case FORMAT_UNIT: case RELEASE: + case RESERVE: case SEND_DIAGNOSTIC: /* Fall through */ @@ -3149,9 +2957,9 @@ static struct fsg_common *fsg_common_init(struct fsg_common *common, } } snprintf(common->inquiry_string, sizeof common->inquiry_string, - "%-8s%-16s%04x", cfg->vendor_name ? cfg->vendor_name : "Linux", + "%-8s%-16s%04x", cfg->vendor_name ?: "Linux", /* Assume product name dependent on the first LUN */ - cfg->product_name ? cfg->product_name : (common->luns->cdrom + cfg->product_name ?: (common->luns->cdrom ? "File-Stor Gadget" : "File-CD Gadget"), i); diff --git a/drivers/usb/gadget/f_mtp.c b/drivers/usb/gadget/f_mtp.c index 2829231327d..88473d1a513 100644 --- a/drivers/usb/gadget/f_mtp.c +++ b/drivers/usb/gadget/f_mtp.c @@ -410,15 +410,6 @@ static int mtp_create_bulk_endpoints(struct mtp_dev *dev, ep->driver_data = dev; /* claim the endpoint */ dev->ep_out = ep; - ep = usb_ep_autoconfig(cdev->gadget, out_desc); - if (!ep) { - DBG(cdev, "usb_ep_autoconfig for ep_out failed\n"); - return -ENODEV; - } - DBG(cdev, "usb_ep_autoconfig for mtp ep_out got %s\n", ep->name); - ep->driver_data = dev; /* claim the endpoint */ - dev->ep_out = ep; - ep = usb_ep_autoconfig(cdev->gadget, intr_desc); if (!ep) { DBG(cdev, "usb_ep_autoconfig for ep_intr failed\n"); @@ -504,7 +495,17 @@ static ssize_t mtp_read(struct file *fp, char __user *buf, } /* wait for a request to complete */ - ret = wait_event_interruptible(dev->read_wq, dev->rx_done); + ret = wait_event_interruptible(dev->read_wq, + dev->rx_done || dev->state != STATE_BUSY); + if (dev->state == STATE_CANCELED) { + r = -ECANCELED; + if (!dev->rx_done) + usb_ep_dequeue(dev->ep_out, req); + spin_lock_irq(&dev->lock); + dev->state = STATE_CANCELED; + spin_unlock_irq(&dev->lock); + goto done; + } if (ret < 0) { r = ret; usb_ep_dequeue(dev->ep_out, req); @@ -707,7 +708,8 @@ static void send_file_work(struct work_struct *data) { ret = usb_ep_queue(dev->ep_in, req, GFP_KERNEL); if (ret < 0) { DBG(cdev, "send_file_work: xfer error %d\n", ret); - dev->state = STATE_ERROR; + if (dev->state != STATE_OFFLINE) + dev->state = STATE_ERROR; r = -EIO; break; } @@ -759,7 +761,8 @@ static void receive_file_work(struct work_struct *data) ret = usb_ep_queue(dev->ep_out, read_req, GFP_KERNEL); if (ret < 0) { r = -EIO; - dev->state = STATE_ERROR; + if (dev->state != STATE_OFFLINE) + dev->state = STATE_ERROR; break; } } @@ -771,7 +774,8 @@ static void receive_file_work(struct work_struct *data) DBG(cdev, "vfs_write %d\n", ret); if (ret != write_req->actual) { r = -EIO; - dev->state = STATE_ERROR; + if (dev->state != STATE_OFFLINE) + dev->state = STATE_ERROR; break; } write_req = NULL; diff --git a/drivers/usb/gadget/f_rmnet.c b/drivers/usb/gadget/f_rmnet.c new file mode 100644 index 00000000000..cbcf5ac76b3 --- /dev/null +++ b/drivers/usb/gadget/f_rmnet.c @@ -0,0 +1,1057 @@ +/* + * 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 "u_rmnet.h" +#include "gadget_chips.h" + +#define RMNET_NOTIFY_INTERVAL 5 +#define RMNET_MAX_NOTIFY_SIZE sizeof(struct usb_cdc_notification) + +struct rmnet_descs { + struct usb_endpoint_descriptor *in; + struct usb_endpoint_descriptor *out; + struct usb_endpoint_descriptor *notify; +}; + +#define ACM_CTRL_DTR (1 << 0) + +/* TODO: use separate structures for data and + * control paths + */ +struct f_rmnet { + struct grmnet port; + int ifc_id; + u8 port_num; + atomic_t online; + atomic_t ctrl_online; + struct usb_composite_dev *cdev; + + spinlock_t lock; + + /* usb descriptors */ + struct rmnet_descs fs; + struct rmnet_descs hs; + + /* usb eps*/ + struct usb_ep *notify; + struct usb_endpoint_descriptor *notify_desc; + struct usb_request *notify_req; + + /* control info */ + struct list_head cpkt_resp_q; + atomic_t notify_count; + unsigned long cpkts_len; +}; + +#define NR_RMNET_PORTS 1 +static unsigned int nr_rmnet_ports; +static unsigned int no_ctrl_smd_ports; +static unsigned int no_ctrl_hsic_ports; +static unsigned int no_data_bam_ports; +static unsigned int no_data_bam2bam_ports; +static unsigned int no_data_hsic_ports; +static struct rmnet_ports { + enum transport_type data_xport; + enum transport_type ctrl_xport; + unsigned data_xport_num; + unsigned ctrl_xport_num; + unsigned port_num; + struct f_rmnet *port; +} rmnet_ports[NR_RMNET_PORTS]; + +static struct usb_interface_descriptor rmnet_interface_desc = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bNumEndpoints = 3, + .bInterfaceClass = USB_CLASS_VENDOR_SPEC, + .bInterfaceSubClass = USB_CLASS_VENDOR_SPEC, + .bInterfaceProtocol = USB_CLASS_VENDOR_SPEC, + /* .iInterface = DYNAMIC */ +}; + +/* Full speed support */ +static struct usb_endpoint_descriptor rmnet_fs_notify_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = __constant_cpu_to_le16(RMNET_MAX_NOTIFY_SIZE), + .bInterval = 1 << RMNET_NOTIFY_INTERVAL, +}; + +static struct usb_endpoint_descriptor rmnet_fs_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(64), +}; + +static struct usb_endpoint_descriptor rmnet_fs_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(64), +}; + +static struct usb_descriptor_header *rmnet_fs_function[] = { + (struct usb_descriptor_header *) &rmnet_interface_desc, + (struct usb_descriptor_header *) &rmnet_fs_notify_desc, + (struct usb_descriptor_header *) &rmnet_fs_in_desc, + (struct usb_descriptor_header *) &rmnet_fs_out_desc, + NULL, +}; + +/* High speed support */ +static struct usb_endpoint_descriptor rmnet_hs_notify_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = __constant_cpu_to_le16(RMNET_MAX_NOTIFY_SIZE), + .bInterval = RMNET_NOTIFY_INTERVAL + 4, +}; + +static struct usb_endpoint_descriptor rmnet_hs_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 rmnet_hs_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_descriptor_header *rmnet_hs_function[] = { + (struct usb_descriptor_header *) &rmnet_interface_desc, + (struct usb_descriptor_header *) &rmnet_hs_notify_desc, + (struct usb_descriptor_header *) &rmnet_hs_in_desc, + (struct usb_descriptor_header *) &rmnet_hs_out_desc, + NULL, +}; + +/* String descriptors */ + +static struct usb_string rmnet_string_defs[] = { + [0].s = "RmNet", + { } /* end of list */ +}; + +static struct usb_gadget_strings rmnet_string_table = { + .language = 0x0409, /* en-us */ + .strings = rmnet_string_defs, +}; + +static struct usb_gadget_strings *rmnet_strings[] = { + &rmnet_string_table, + NULL, +}; + +/* ------- misc functions --------------------*/ + +static inline struct f_rmnet *func_to_rmnet(struct usb_function *f) +{ + return container_of(f, struct f_rmnet, port.func); +} + +static inline struct f_rmnet *port_to_rmnet(struct grmnet *r) +{ + return container_of(r, struct f_rmnet, port); +} + +static struct usb_request * +frmnet_alloc_req(struct usb_ep *ep, unsigned len, gfp_t flags) +{ + struct usb_request *req; + + req = usb_ep_alloc_request(ep, flags); + if (!req) + return ERR_PTR(-ENOMEM); + + req->buf = kmalloc(len, flags); + if (!req->buf) { + usb_ep_free_request(ep, req); + return ERR_PTR(-ENOMEM); + } + + req->length = len; + + return req; +} + +void frmnet_free_req(struct usb_ep *ep, struct usb_request *req) +{ + kfree(req->buf); + usb_ep_free_request(ep, req); +} + +static struct rmnet_ctrl_pkt *rmnet_alloc_ctrl_pkt(unsigned len, gfp_t flags) +{ + struct rmnet_ctrl_pkt *pkt; + + pkt = kzalloc(sizeof(struct rmnet_ctrl_pkt), flags); + if (!pkt) + return ERR_PTR(-ENOMEM); + + pkt->buf = kmalloc(len, flags); + if (!pkt->buf) { + kfree(pkt); + return ERR_PTR(-ENOMEM); + } + pkt->len = len; + + return pkt; +} + +static void rmnet_free_ctrl_pkt(struct rmnet_ctrl_pkt *pkt) +{ + kfree(pkt->buf); + kfree(pkt); +} + +/* -------------------------------------------*/ + +static int rmnet_gport_setup(void) +{ + int ret; + int port_idx; + int i; + + pr_debug("%s: bam ports: %u bam2bam ports: %u data hsic ports: %u" + " smd ports: %u ctrl hsic ports: %u" + " nr_rmnet_ports: %u\n", + __func__, no_data_bam_ports, no_data_bam2bam_ports, + no_data_hsic_ports, no_ctrl_smd_ports, + no_ctrl_hsic_ports, nr_rmnet_ports); + + if (no_data_bam_ports || no_data_bam2bam_ports) { + ret = gbam_setup(no_data_bam_ports, + no_data_bam2bam_ports); + if (ret) + return ret; + } + + if (no_ctrl_smd_ports) { + ret = gsmd_ctrl_setup(no_ctrl_smd_ports); + if (ret) + return ret; + } + + if (no_data_hsic_ports) { + port_idx = ghsic_data_setup(no_data_hsic_ports, + USB_GADGET_RMNET); + if (port_idx < 0) + return port_idx; + for (i = 0; i < nr_rmnet_ports; i++) { + if (rmnet_ports[i].data_xport == + USB_GADGET_XPORT_HSIC) { + rmnet_ports[i].data_xport_num = port_idx; + port_idx++; + } + } + } + + if (no_ctrl_hsic_ports) { + port_idx = ghsic_ctrl_setup(no_ctrl_hsic_ports, + USB_GADGET_RMNET); + if (port_idx < 0) + return port_idx; + for (i = 0; i < nr_rmnet_ports; i++) { + if (rmnet_ports[i].ctrl_xport == + USB_GADGET_XPORT_HSIC) { + rmnet_ports[i].ctrl_xport_num = port_idx; + port_idx++; + } + } + } + + return 0; +} + +static int gport_rmnet_connect(struct f_rmnet *dev) +{ + int ret; + unsigned port_num; + enum transport_type cxport = rmnet_ports[dev->port_num].ctrl_xport; + enum transport_type dxport = rmnet_ports[dev->port_num].data_xport; + + pr_debug("%s: ctrl xport: %s data xport: %s dev: %p portno: %d\n", + __func__, xport_to_str(cxport), xport_to_str(dxport), + dev, dev->port_num); + + port_num = rmnet_ports[dev->port_num].ctrl_xport_num; + switch (cxport) { + case USB_GADGET_XPORT_SMD: + ret = gsmd_ctrl_connect(&dev->port, port_num); + if (ret) { + pr_err("%s: gsmd_ctrl_connect failed: err:%d\n", + __func__, ret); + return ret; + } + break; + case USB_GADGET_XPORT_HSIC: + ret = ghsic_ctrl_connect(&dev->port, port_num); + if (ret) { + pr_err("%s: ghsic_ctrl_connect failed: err:%d\n", + __func__, ret); + return ret; + } + break; + case USB_GADGET_XPORT_NONE: + break; + default: + pr_err("%s: Un-supported transport: %s\n", __func__, + xport_to_str(cxport)); + return -ENODEV; + } + + port_num = rmnet_ports[dev->port_num].data_xport_num; + switch (dxport) { + case USB_GADGET_XPORT_BAM: + case USB_GADGET_XPORT_BAM2BAM: + /* currently only one connection (idx 0) + is supported */ + ret = gbam_connect(&dev->port, port_num, + dxport, 0); + if (ret) { + pr_err("%s: gbam_connect failed: err:%d\n", + __func__, ret); + gsmd_ctrl_disconnect(&dev->port, port_num); + return ret; + } + break; + case USB_GADGET_XPORT_HSIC: + ret = ghsic_data_connect(&dev->port, port_num); + if (ret) { + pr_err("%s: ghsic_data_connect failed: err:%d\n", + __func__, ret); + ghsic_ctrl_disconnect(&dev->port, port_num); + return ret; + } + break; + case USB_GADGET_XPORT_NONE: + break; + default: + pr_err("%s: Un-supported transport: %s\n", __func__, + xport_to_str(dxport)); + return -ENODEV; + } + + return 0; +} + +static int gport_rmnet_disconnect(struct f_rmnet *dev) +{ + unsigned port_num; + enum transport_type cxport = rmnet_ports[dev->port_num].ctrl_xport; + enum transport_type dxport = rmnet_ports[dev->port_num].data_xport; + + pr_debug("%s: ctrl xport: %s data xport: %s dev: %p portno: %d\n", + __func__, xport_to_str(cxport), xport_to_str(dxport), + dev, dev->port_num); + + port_num = rmnet_ports[dev->port_num].ctrl_xport_num; + switch (cxport) { + case USB_GADGET_XPORT_SMD: + gsmd_ctrl_disconnect(&dev->port, port_num); + break; + case USB_GADGET_XPORT_HSIC: + ghsic_ctrl_disconnect(&dev->port, port_num); + break; + case USB_GADGET_XPORT_NONE: + break; + default: + pr_err("%s: Un-supported transport: %s\n", __func__, + xport_to_str(cxport)); + return -ENODEV; + } + + port_num = rmnet_ports[dev->port_num].data_xport_num; + switch (dxport) { + case USB_GADGET_XPORT_BAM: + case USB_GADGET_XPORT_BAM2BAM: + gbam_disconnect(&dev->port, port_num, dxport); + break; + case USB_GADGET_XPORT_HSIC: + ghsic_data_disconnect(&dev->port, port_num); + break; + case USB_GADGET_XPORT_NONE: + break; + default: + pr_err("%s: Un-supported transport: %s\n", __func__, + xport_to_str(dxport)); + return -ENODEV; + } + + return 0; +} + +static void frmnet_unbind(struct usb_configuration *c, struct usb_function *f) +{ + struct f_rmnet *dev = func_to_rmnet(f); + + pr_debug("%s: portno:%d\n", __func__, dev->port_num); + + if (gadget_is_dualspeed(c->cdev->gadget)) + usb_free_descriptors(f->hs_descriptors); + usb_free_descriptors(f->descriptors); + + frmnet_free_req(dev->notify, dev->notify_req); + + kfree(f->name); +} + +static void frmnet_disable(struct usb_function *f) +{ + struct f_rmnet *dev = func_to_rmnet(f); + unsigned long flags; + struct rmnet_ctrl_pkt *cpkt; + + pr_debug("%s: port#%d\n", __func__, dev->port_num); + + usb_ep_disable(dev->notify); + + atomic_set(&dev->online, 0); + + spin_lock_irqsave(&dev->lock, flags); + while (!list_empty(&dev->cpkt_resp_q)) { + cpkt = list_first_entry(&dev->cpkt_resp_q, + struct rmnet_ctrl_pkt, list); + + list_del(&cpkt->list); + rmnet_free_ctrl_pkt(cpkt); + } + atomic_set(&dev->notify_count, 0); + spin_unlock_irqrestore(&dev->lock, flags); + + gport_rmnet_disconnect(dev); +} + +static int +frmnet_set_alt(struct usb_function *f, unsigned intf, unsigned alt) +{ + struct f_rmnet *dev = func_to_rmnet(f); + struct usb_composite_dev *cdev = dev->cdev; + int ret; + + pr_debug("%s:dev:%p port#%d\n", __func__, dev, dev->port_num); + + if (dev->notify->driver_data) { + pr_debug("%s: reset port:%d\n", __func__, dev->port_num); + usb_ep_disable(dev->notify); + } + dev->notify_desc = ep_choose(cdev->gadget, + dev->hs.notify, + dev->fs.notify); + ret = usb_ep_enable(dev->notify, dev->notify_desc); + if (ret) { + pr_err("%s: usb ep#%s enable failed, err#%d\n", + __func__, dev->notify->name, ret); + return ret; + } + dev->notify->driver_data = dev; + + if (dev->port.in->driver_data) { + pr_debug("%s: reset port:%d\n", __func__, dev->port_num); + gport_rmnet_disconnect(dev); + } + + dev->port.in_desc = ep_choose(cdev->gadget, + dev->hs.in, dev->fs.in); + dev->port.out_desc = ep_choose(cdev->gadget, + dev->hs.out, dev->fs.out); + + ret = gport_rmnet_connect(dev); + + atomic_set(&dev->online, 1); + + return ret; +} + +static void frmnet_ctrl_response_available(struct f_rmnet *dev) +{ + struct usb_request *req = dev->notify_req; + struct usb_cdc_notification *event; + unsigned long flags; + int ret; + + pr_debug("%s:dev:%p portno#%d\n", __func__, dev, dev->port_num); + + spin_lock_irqsave(&dev->lock, flags); + if (!atomic_read(&dev->online) || !req || !req->buf) { + spin_unlock_irqrestore(&dev->lock, flags); + return; + } + + if (atomic_inc_return(&dev->notify_count) != 1) { + spin_unlock_irqrestore(&dev->lock, flags); + return; + } + + event = req->buf; + event->bmRequestType = USB_DIR_IN | USB_TYPE_CLASS + | USB_RECIP_INTERFACE; + event->bNotificationType = USB_CDC_NOTIFY_RESPONSE_AVAILABLE; + event->wValue = cpu_to_le16(0); + event->wIndex = cpu_to_le16(dev->ifc_id); + event->wLength = cpu_to_le16(0); + spin_unlock_irqrestore(&dev->lock, flags); + + ret = usb_ep_queue(dev->notify, dev->notify_req, GFP_ATOMIC); + if (ret) { + atomic_dec(&dev->notify_count); + pr_debug("ep enqueue error %d\n", ret); + } +} + +static void frmnet_connect(struct grmnet *gr) +{ + struct f_rmnet *dev; + + if (!gr) { + pr_err("%s: Invalid grmnet:%p\n", __func__, gr); + return; + } + + dev = port_to_rmnet(gr); + + atomic_set(&dev->ctrl_online, 1); +} + +static void frmnet_disconnect(struct grmnet *gr) +{ + struct f_rmnet *dev; + unsigned long flags; + struct usb_cdc_notification *event; + int status; + struct rmnet_ctrl_pkt *cpkt; + + if (!gr) { + pr_err("%s: Invalid grmnet:%p\n", __func__, gr); + return; + } + + dev = port_to_rmnet(gr); + + atomic_set(&dev->ctrl_online, 0); + + if (!atomic_read(&dev->online)) { + pr_debug("%s: nothing to do\n", __func__); + return; + } + + usb_ep_fifo_flush(dev->notify); + + event = dev->notify_req->buf; + event->bmRequestType = USB_DIR_IN | USB_TYPE_CLASS + | USB_RECIP_INTERFACE; + event->bNotificationType = USB_CDC_NOTIFY_NETWORK_CONNECTION; + event->wValue = cpu_to_le16(0); + event->wIndex = cpu_to_le16(dev->ifc_id); + event->wLength = cpu_to_le16(0); + + status = usb_ep_queue(dev->notify, dev->notify_req, GFP_ATOMIC); + if (status < 0) { + if (!atomic_read(&dev->online)) + return; + pr_err("%s: rmnet notify ep enqueue error %d\n", + __func__, status); + } + + spin_lock_irqsave(&dev->lock, flags); + while (!list_empty(&dev->cpkt_resp_q)) { + cpkt = list_first_entry(&dev->cpkt_resp_q, + struct rmnet_ctrl_pkt, list); + + list_del(&cpkt->list); + rmnet_free_ctrl_pkt(cpkt); + } + atomic_set(&dev->notify_count, 0); + spin_unlock_irqrestore(&dev->lock, flags); + +} + +static int +frmnet_send_cpkt_response(void *gr, void *buf, size_t len) +{ + struct f_rmnet *dev; + struct rmnet_ctrl_pkt *cpkt; + unsigned long flags; + + if (!gr || !buf) { + pr_err("%s: Invalid grmnet/buf, grmnet:%p buf:%p\n", + __func__, gr, buf); + return -ENODEV; + } + cpkt = rmnet_alloc_ctrl_pkt(len, GFP_ATOMIC); + if (IS_ERR(cpkt)) { + pr_err("%s: Unable to allocate ctrl pkt\n", __func__); + return -ENOMEM; + } + memcpy(cpkt->buf, buf, len); + cpkt->len = len; + + dev = port_to_rmnet(gr); + + pr_debug("%s: dev:%p port#%d\n", __func__, dev, dev->port_num); + + if (!atomic_read(&dev->online) || !atomic_read(&dev->ctrl_online)) { + rmnet_free_ctrl_pkt(cpkt); + return 0; + } + + spin_lock_irqsave(&dev->lock, flags); + list_add_tail(&cpkt->list, &dev->cpkt_resp_q); + spin_unlock_irqrestore(&dev->lock, flags); + + frmnet_ctrl_response_available(dev); + + return 0; +} + +static void +frmnet_cmd_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct f_rmnet *dev = req->context; + struct usb_composite_dev *cdev; + unsigned port_num; + + if (!dev) { + pr_err("%s: rmnet dev is null\n", __func__); + return; + } + + pr_debug("%s: dev:%p port#%d\n", __func__, dev, dev->port_num); + + cdev = dev->cdev; + + if (dev->port.send_encap_cmd) { + port_num = rmnet_ports[dev->port_num].ctrl_xport_num; + dev->port.send_encap_cmd(port_num, req->buf, req->actual); + } +} + +static void frmnet_notify_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct f_rmnet *dev = req->context; + int status = req->status; + + pr_debug("%s: dev:%p port#%d\n", __func__, dev, dev->port_num); + + switch (status) { + case -ECONNRESET: + case -ESHUTDOWN: + /* connection gone */ + atomic_set(&dev->notify_count, 0); + break; + default: + pr_err("rmnet notify ep error %d\n", status); + /* FALLTHROUGH */ + case 0: + if (!atomic_read(&dev->ctrl_online)) + break; + + if (atomic_dec_and_test(&dev->notify_count)) + break; + + status = usb_ep_queue(dev->notify, req, GFP_ATOMIC); + if (status) { + atomic_dec(&dev->notify_count); + pr_debug("ep enqueue error %d\n", status); + } + break; + } +} + +static int +frmnet_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) +{ + struct f_rmnet *dev = func_to_rmnet(f); + struct usb_composite_dev *cdev = dev->cdev; + struct usb_request *req = cdev->req; + unsigned port_num; + u16 w_index = le16_to_cpu(ctrl->wIndex); + u16 w_value = le16_to_cpu(ctrl->wValue); + u16 w_length = le16_to_cpu(ctrl->wLength); + int ret = -EOPNOTSUPP; + + pr_debug("%s:dev:%p port#%d\n", __func__, dev, dev->port_num); + + if (!atomic_read(&dev->online)) { + pr_debug("%s: usb cable is not connected\n", __func__); + return -ENOTCONN; + } + + switch ((ctrl->bRequestType << 8) | ctrl->bRequest) { + + case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8) + | USB_CDC_SEND_ENCAPSULATED_COMMAND: + ret = w_length; + req->complete = frmnet_cmd_complete; + req->context = dev; + break; + + + case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8) + | USB_CDC_GET_ENCAPSULATED_RESPONSE: + if (w_value) + goto invalid; + else { + unsigned len; + struct rmnet_ctrl_pkt *cpkt; + + spin_lock(&dev->lock); + if (list_empty(&dev->cpkt_resp_q)) { + pr_err("ctrl resp queue empty " + " req%02x.%02x v%04x i%04x l%d\n", + ctrl->bRequestType, ctrl->bRequest, + w_value, w_index, w_length); + spin_unlock(&dev->lock); + goto invalid; + } + + cpkt = list_first_entry(&dev->cpkt_resp_q, + struct rmnet_ctrl_pkt, list); + list_del(&cpkt->list); + spin_unlock(&dev->lock); + + len = min_t(unsigned, w_length, cpkt->len); + memcpy(req->buf, cpkt->buf, len); + ret = len; + + rmnet_free_ctrl_pkt(cpkt); + } + break; + case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8) + | USB_CDC_REQ_SET_CONTROL_LINE_STATE: + if (dev->port.notify_modem) { + port_num = rmnet_ports[dev->port_num].ctrl_xport_num; + dev->port.notify_modem(&dev->port, port_num, w_value); + } + ret = 0; + + break; + default: + +invalid: + DBG(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n", + ctrl->bRequestType, ctrl->bRequest, + w_value, w_index, w_length); + } + + /* respond with data transfer or status phase? */ + if (ret >= 0) { + VDBG(cdev, "rmnet req%02x.%02x v%04x i%04x l%d\n", + ctrl->bRequestType, ctrl->bRequest, + w_value, w_index, w_length); + req->zero = (ret < w_length); + req->length = ret; + ret = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC); + if (ret < 0) + ERROR(cdev, "rmnet ep0 enqueue err %d\n", ret); + } + + return ret; +} + +static int frmnet_bind(struct usb_configuration *c, struct usb_function *f) +{ + struct f_rmnet *dev = func_to_rmnet(f); + struct usb_ep *ep; + struct usb_composite_dev *cdev = c->cdev; + int ret = -ENODEV; + + dev->ifc_id = usb_interface_id(c, f); + if (dev->ifc_id < 0) { + pr_err("%s: unable to allocate ifc id, err:%d", + __func__, dev->ifc_id); + return dev->ifc_id; + } + rmnet_interface_desc.bInterfaceNumber = dev->ifc_id; + + ep = usb_ep_autoconfig(cdev->gadget, &rmnet_fs_in_desc); + if (!ep) { + pr_err("%s: usb epin autoconfig failed\n", __func__); + return -ENODEV; + } + dev->port.in = ep; + ep->driver_data = cdev; + + ep = usb_ep_autoconfig(cdev->gadget, &rmnet_fs_out_desc); + if (!ep) { + pr_err("%s: usb epout autoconfig failed\n", __func__); + ret = -ENODEV; + goto ep_auto_out_fail; + } + dev->port.out = ep; + ep->driver_data = cdev; + + ep = usb_ep_autoconfig(cdev->gadget, &rmnet_fs_notify_desc); + if (!ep) { + pr_err("%s: usb epnotify autoconfig failed\n", __func__); + ret = -ENODEV; + goto ep_auto_notify_fail; + } + dev->notify = ep; + ep->driver_data = cdev; + + dev->notify_req = frmnet_alloc_req(ep, + sizeof(struct usb_cdc_notification), + GFP_KERNEL); + if (IS_ERR(dev->notify_req)) { + pr_err("%s: unable to allocate memory for notify req\n", + __func__); + ret = -ENOMEM; + goto ep_notify_alloc_fail; + } + + dev->notify_req->complete = frmnet_notify_complete; + dev->notify_req->context = dev; + + f->descriptors = usb_copy_descriptors(rmnet_fs_function); + + if (!f->descriptors) + goto fail; + + dev->fs.in = usb_find_endpoint(rmnet_fs_function, + f->descriptors, + &rmnet_fs_in_desc); + dev->fs.out = usb_find_endpoint(rmnet_fs_function, + f->descriptors, + &rmnet_fs_out_desc); + dev->fs.notify = usb_find_endpoint(rmnet_fs_function, + f->descriptors, + &rmnet_fs_notify_desc); + + if (gadget_is_dualspeed(cdev->gadget)) { + rmnet_hs_in_desc.bEndpointAddress = + rmnet_fs_in_desc.bEndpointAddress; + rmnet_hs_out_desc.bEndpointAddress = + rmnet_fs_out_desc.bEndpointAddress; + rmnet_hs_notify_desc.bEndpointAddress = + rmnet_fs_notify_desc.bEndpointAddress; + + /* copy descriptors, and track endpoint copies */ + f->hs_descriptors = usb_copy_descriptors(rmnet_hs_function); + + if (!f->hs_descriptors) + goto fail; + + dev->hs.in = usb_find_endpoint(rmnet_hs_function, + f->hs_descriptors, &rmnet_hs_in_desc); + dev->hs.out = usb_find_endpoint(rmnet_hs_function, + f->hs_descriptors, &rmnet_hs_out_desc); + dev->hs.notify = usb_find_endpoint(rmnet_hs_function, + f->hs_descriptors, &rmnet_hs_notify_desc); + } + + pr_info("%s: RmNet(%d) %s Speed, IN:%s OUT:%s\n", + __func__, dev->port_num, + gadget_is_dualspeed(cdev->gadget) ? "dual" : "full", + dev->port.in->name, dev->port.out->name); + + return 0; + +fail: + if (f->descriptors) + usb_free_descriptors(f->descriptors); +ep_notify_alloc_fail: + dev->notify->driver_data = NULL; + dev->notify = NULL; +ep_auto_notify_fail: + dev->port.out->driver_data = NULL; + dev->port.out = NULL; +ep_auto_out_fail: + dev->port.in->driver_data = NULL; + dev->port.in = NULL; + + return ret; +} + +static int frmnet_bind_config(struct usb_configuration *c, unsigned portno) +{ + int status; + struct f_rmnet *dev; + struct usb_function *f; + unsigned long flags; + + pr_debug("%s: usb config:%p\n", __func__, c); + + if (portno >= nr_rmnet_ports) { + pr_err("%s: supporting ports#%u port_id:%u", __func__, + nr_rmnet_ports, portno); + return -ENODEV; + } + + if (rmnet_string_defs[0].id == 0) { + status = usb_string_id(c->cdev); + if (status < 0) { + pr_err("%s: failed to get string id, err:%d\n", + __func__, status); + return status; + } + rmnet_string_defs[0].id = status; + } + + dev = rmnet_ports[portno].port; + + spin_lock_irqsave(&dev->lock, flags); + dev->cdev = c->cdev; + f = &dev->port.func; + f->name = kasprintf(GFP_ATOMIC, "rmnet%d", portno); + spin_unlock_irqrestore(&dev->lock, flags); + if (!f->name) { + pr_err("%s: cannot allocate memory for name\n", __func__); + return -ENOMEM; + } + + f->strings = rmnet_strings; + f->bind = frmnet_bind; + f->unbind = frmnet_unbind; + f->disable = frmnet_disable; + f->set_alt = frmnet_set_alt; + f->setup = frmnet_setup; + dev->port.send_cpkt_response = frmnet_send_cpkt_response; + dev->port.disconnect = frmnet_disconnect; + dev->port.connect = frmnet_connect; + + status = usb_add_function(c, f); + if (status) { + pr_err("%s: usb add function failed: %d\n", + __func__, status); + kfree(f->name); + return status; + } + + pr_debug("%s: complete\n", __func__); + + return status; +} + +static void frmnet_cleanup(void) +{ + int i; + + for (i = 0; i < nr_rmnet_ports; i++) + kfree(rmnet_ports[i].port); + + nr_rmnet_ports = 0; + no_ctrl_smd_ports = 0; + no_data_bam_ports = 0; + no_data_bam2bam_ports = 0; + no_ctrl_hsic_ports = 0; + no_data_hsic_ports = 0; +} + +static int frmnet_init_port(const char *ctrl_name, const char *data_name) +{ + struct f_rmnet *dev; + struct rmnet_ports *rmnet_port; + int ret; + int i; + + if (nr_rmnet_ports >= NR_RMNET_PORTS) { + pr_err("%s: Max-%d instances supported\n", + __func__, NR_RMNET_PORTS); + return -EINVAL; + } + + pr_debug("%s: port#:%d, ctrl port: %s data port: %s\n", + __func__, nr_rmnet_ports, ctrl_name, data_name); + + dev = kzalloc(sizeof(struct f_rmnet), GFP_KERNEL); + if (!dev) { + pr_err("%s: Unable to allocate rmnet device\n", __func__); + return -ENOMEM; + } + + dev->port_num = nr_rmnet_ports; + spin_lock_init(&dev->lock); + INIT_LIST_HEAD(&dev->cpkt_resp_q); + + rmnet_port = &rmnet_ports[nr_rmnet_ports]; + rmnet_port->port = dev; + rmnet_port->port_num = nr_rmnet_ports; + rmnet_port->ctrl_xport = str_to_xport(ctrl_name); + rmnet_port->data_xport = str_to_xport(data_name); + + switch (rmnet_port->ctrl_xport) { + case USB_GADGET_XPORT_SMD: + rmnet_port->ctrl_xport_num = no_ctrl_smd_ports; + no_ctrl_smd_ports++; + break; + case USB_GADGET_XPORT_HSIC: + rmnet_port->ctrl_xport_num = no_ctrl_hsic_ports; + no_ctrl_hsic_ports++; + break; + case USB_GADGET_XPORT_NONE: + break; + default: + pr_err("%s: Un-supported transport: %u\n", __func__, + rmnet_port->ctrl_xport); + ret = -ENODEV; + goto fail_probe; + } + + switch (rmnet_port->data_xport) { + case USB_GADGET_XPORT_BAM: + rmnet_port->data_xport_num = no_data_bam_ports; + no_data_bam_ports++; + break; + case USB_GADGET_XPORT_BAM2BAM: + rmnet_port->data_xport_num = no_data_bam2bam_ports; + no_data_bam2bam_ports++; + break; + case USB_GADGET_XPORT_HSIC: + rmnet_port->data_xport_num = no_data_hsic_ports; + no_data_hsic_ports++; + break; + case USB_GADGET_XPORT_NONE: + break; + default: + pr_err("%s: Un-supported transport: %u\n", __func__, + rmnet_port->data_xport); + ret = -ENODEV; + goto fail_probe; + } + nr_rmnet_ports++; + + return 0; + +fail_probe: + for (i = 0; i < nr_rmnet_ports; i++) + kfree(rmnet_ports[i].port); + + nr_rmnet_ports = 0; + no_ctrl_smd_ports = 0; + no_data_bam_ports = 0; + no_ctrl_hsic_ports = 0; + no_data_hsic_ports = 0; + + return ret; +} diff --git a/drivers/usb/gadget/f_rmnet.h b/drivers/usb/gadget/f_rmnet.h new file mode 100644 index 00000000000..2d816c653f1 --- /dev/null +++ b/drivers/usb/gadget/f_rmnet.h @@ -0,0 +1,19 @@ +/* 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. + * + */ + +#ifndef __F_RMNET_H +#define __F_RMNET_H + +int rmnet_function_add(struct usb_configuration *c); + +#endif /* __F_RMNET_H */ diff --git a/drivers/usb/gadget/f_rmnet_sdio.c b/drivers/usb/gadget/f_rmnet_sdio.c new file mode 100644 index 00000000000..f63d9396a16 --- /dev/null +++ b/drivers/usb/gadget/f_rmnet_sdio.c @@ -0,0 +1,1535 @@ +/* + * f_rmnet_sdio.c -- RmNet SDIO function driver + * + * Copyright (C) 2003-2005,2008 David Brownell + * Copyright (C) 2003-2004 Robert Schwebel, Benedikt Spranger + * Copyright (C) 2003 Al Borchers (alborchers@steinerpoint.com) + * Copyright (C) 2008 Nokia Corporation + * 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 as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#ifdef CONFIG_RMNET_SDIO_CTL_CHANNEL +static uint32_t rmnet_sdio_ctl_ch = CONFIG_RMNET_SDIO_CTL_CHANNEL; +#else +static uint32_t rmnet_sdio_ctl_ch; +#endif +module_param(rmnet_sdio_ctl_ch, uint, S_IRUGO); +MODULE_PARM_DESC(rmnet_sdio_ctl_ch, "RmNet control SDIO channel ID"); + +#ifdef CONFIG_RMNET_SDIO_DATA_CHANNEL +static uint32_t rmnet_sdio_data_ch = CONFIG_RMNET_SDIO_DATA_CHANNEL; +#else +static uint32_t rmnet_sdio_data_ch; +#endif +module_param(rmnet_sdio_data_ch, uint, S_IRUGO); +MODULE_PARM_DESC(rmnet_sdio_data_ch, "RmNet data SDIO channel ID"); + +#define ACM_CTRL_DTR (1 << 0) + +#define SDIO_MUX_HDR 8 +#define RMNET_SDIO_NOTIFY_INTERVAL 5 +#define RMNET_SDIO_MAX_NFY_SZE sizeof(struct usb_cdc_notification) + +#define RMNET_SDIO_RX_REQ_MAX 16 +#define RMNET_SDIO_RX_REQ_SIZE 2048 +#define RMNET_SDIO_TX_REQ_MAX 200 + +#define TX_PKT_DROP_THRESHOLD 1000 +#define RX_PKT_FLOW_CTRL_EN_THRESHOLD 1000 +#define RX_PKT_FLOW_CTRL_DISABLE 500 + +unsigned int sdio_tx_pkt_drop_thld = TX_PKT_DROP_THRESHOLD; +module_param(sdio_tx_pkt_drop_thld, uint, S_IRUGO | S_IWUSR); + +unsigned int sdio_rx_fctrl_en_thld = RX_PKT_FLOW_CTRL_EN_THRESHOLD; +module_param(sdio_rx_fctrl_en_thld, uint, S_IRUGO | S_IWUSR); + +unsigned int sdio_rx_fctrl_dis_thld = RX_PKT_FLOW_CTRL_DISABLE; +module_param(sdio_rx_fctrl_dis_thld, uint, S_IRUGO | S_IWUSR); + +/* QMI requests & responses buffer*/ +struct rmnet_sdio_qmi_buf { + void *buf; + int len; + struct list_head list; +}; + +struct rmnet_sdio_dev { + struct usb_function function; + struct usb_composite_dev *cdev; + + struct usb_ep *epout; + struct usb_ep *epin; + struct usb_ep *epnotify; + struct usb_request *notify_req; + + u8 ifc_id; + /* QMI lists */ + struct list_head qmi_req_q; + unsigned int qreq_q_len; + struct list_head qmi_resp_q; + unsigned int qresp_q_len; + /* Tx/Rx lists */ + struct list_head tx_idle; + unsigned int tx_idle_len; + struct sk_buff_head tx_skb_queue; + struct list_head rx_idle; + unsigned int rx_idle_len; + struct sk_buff_head rx_skb_queue; + + spinlock_t lock; + atomic_t online; + atomic_t notify_count; + + struct workqueue_struct *wq; + struct work_struct disconnect_work; + + struct work_struct ctl_rx_work; + struct work_struct data_rx_work; + + struct delayed_work sdio_open_work; + struct work_struct sdio_close_work; +#define RMNET_SDIO_CH_OPEN 1 + unsigned long data_ch_status; + unsigned long ctrl_ch_status; + + unsigned int dpkts_pending_atdmux; + int cbits_to_modem; + struct work_struct set_modem_ctl_bits_work; + + /* pkt logging dpkt - data pkt; cpkt - control pkt*/ + struct dentry *dent; + unsigned long dpkt_tolaptop; + unsigned long dpkt_tomodem; + unsigned long tx_drp_cnt; + unsigned long cpkt_tolaptop; + unsigned long cpkt_tomodem; +}; + +static struct usb_interface_descriptor rmnet_sdio_interface_desc = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + /* .bInterfaceNumber = DYNAMIC */ + .bNumEndpoints = 3, + .bInterfaceClass = USB_CLASS_VENDOR_SPEC, + .bInterfaceSubClass = USB_CLASS_VENDOR_SPEC, + .bInterfaceProtocol = USB_CLASS_VENDOR_SPEC, + /* .iInterface = DYNAMIC */ +}; + +/* Full speed support */ +static struct usb_endpoint_descriptor rmnet_sdio_fs_notify_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = __constant_cpu_to_le16(RMNET_SDIO_MAX_NFY_SZE), + .bInterval = 1 << RMNET_SDIO_NOTIFY_INTERVAL, +}; + +static struct usb_endpoint_descriptor rmnet_sdio_fs_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(64), +}; + +static struct usb_endpoint_descriptor rmnet_sdio_fs_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(64), +}; + +static struct usb_descriptor_header *rmnet_sdio_fs_function[] = { + (struct usb_descriptor_header *) &rmnet_sdio_interface_desc, + (struct usb_descriptor_header *) &rmnet_sdio_fs_notify_desc, + (struct usb_descriptor_header *) &rmnet_sdio_fs_in_desc, + (struct usb_descriptor_header *) &rmnet_sdio_fs_out_desc, + NULL, +}; + +/* High speed support */ +static struct usb_endpoint_descriptor rmnet_sdio_hs_notify_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = __constant_cpu_to_le16(RMNET_SDIO_MAX_NFY_SZE), + .bInterval = RMNET_SDIO_NOTIFY_INTERVAL + 4, +}; + +static struct usb_endpoint_descriptor rmnet_sdio_hs_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 rmnet_sdio_hs_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_descriptor_header *rmnet_sdio_hs_function[] = { + (struct usb_descriptor_header *) &rmnet_sdio_interface_desc, + (struct usb_descriptor_header *) &rmnet_sdio_hs_notify_desc, + (struct usb_descriptor_header *) &rmnet_sdio_hs_in_desc, + (struct usb_descriptor_header *) &rmnet_sdio_hs_out_desc, + NULL, +}; + +/* String descriptors */ + +static struct usb_string rmnet_sdio_string_defs[] = { + [0].s = "QMI RmNet", + { } /* end of list */ +}; + +static struct usb_gadget_strings rmnet_sdio_string_table = { + .language = 0x0409, /* en-us */ + .strings = rmnet_sdio_string_defs, +}; + +static struct usb_gadget_strings *rmnet_sdio_strings[] = { + &rmnet_sdio_string_table, + NULL, +}; + +static struct rmnet_sdio_qmi_buf * +rmnet_sdio_alloc_qmi(unsigned len, gfp_t kmalloc_flags) + +{ + struct rmnet_sdio_qmi_buf *qmi; + + qmi = kmalloc(sizeof(struct rmnet_sdio_qmi_buf), kmalloc_flags); + if (qmi != NULL) { + qmi->buf = kmalloc(len, kmalloc_flags); + if (qmi->buf == NULL) { + kfree(qmi); + qmi = NULL; + } + } + + return qmi ? qmi : ERR_PTR(-ENOMEM); +} + +static void rmnet_sdio_free_qmi(struct rmnet_sdio_qmi_buf *qmi) +{ + kfree(qmi->buf); + kfree(qmi); +} +/* + * Allocate a usb_request and its buffer. Returns a pointer to the + * usb_request or a pointer with an error code if there is an error. + */ +static struct usb_request * +rmnet_sdio_alloc_req(struct usb_ep *ep, unsigned len, gfp_t kmalloc_flags) +{ + struct usb_request *req; + + req = usb_ep_alloc_request(ep, kmalloc_flags); + + if (len && req != NULL) { + req->length = len; + req->buf = kmalloc(len, kmalloc_flags); + if (req->buf == NULL) { + usb_ep_free_request(ep, req); + req = NULL; + } + } + + return req ? req : ERR_PTR(-ENOMEM); +} + +/* + * Free a usb_request and its buffer. + */ +static void rmnet_sdio_free_req(struct usb_ep *ep, struct usb_request *req) +{ + kfree(req->buf); + usb_ep_free_request(ep, req); +} + +static void rmnet_sdio_notify_complete(struct usb_ep *ep, + struct usb_request *req) +{ + struct rmnet_sdio_dev *dev = req->context; + struct usb_composite_dev *cdev = dev->cdev; + int status = req->status; + + switch (status) { + case -ECONNRESET: + case -ESHUTDOWN: + /* connection gone */ + atomic_set(&dev->notify_count, 0); + break; + default: + ERROR(cdev, "rmnet notifyep error %d\n", status); + /* FALLTHROUGH */ + case 0: + + if (!test_bit(RMNET_SDIO_CH_OPEN, &dev->ctrl_ch_status)) + return; + + /* handle multiple pending QMI_RESPONSE_AVAILABLE + * notifications by resending until we're done + */ + if (atomic_dec_and_test(&dev->notify_count)) + break; + + status = usb_ep_queue(dev->epnotify, req, GFP_ATOMIC); + if (status) { + atomic_dec(&dev->notify_count); + ERROR(cdev, "rmnet notify ep enq error %d\n", status); + } + break; + } +} + +static void rmnet_sdio_qmi_resp_available(struct rmnet_sdio_dev *dev) +{ + struct usb_composite_dev *cdev = dev->cdev; + struct usb_cdc_notification *event; + int status; + unsigned long flags; + + /* Response will be sent later */ + if (atomic_inc_return(&dev->notify_count) != 1) + return; + + spin_lock_irqsave(&dev->lock, flags); + + if (!atomic_read(&dev->online)) { + spin_unlock_irqrestore(&dev->lock, flags); + return; + } + + event = dev->notify_req->buf; + + event->bmRequestType = USB_DIR_IN | USB_TYPE_CLASS + | USB_RECIP_INTERFACE; + event->bNotificationType = USB_CDC_NOTIFY_RESPONSE_AVAILABLE; + event->wValue = cpu_to_le16(0); + event->wIndex = cpu_to_le16(dev->ifc_id); + event->wLength = cpu_to_le16(0); + spin_unlock_irqrestore(&dev->lock, flags); + + status = usb_ep_queue(dev->epnotify, dev->notify_req, GFP_ATOMIC); + if (status < 0) { + if (atomic_read(&dev->online)) + atomic_dec(&dev->notify_count); + ERROR(cdev, "rmnet notify ep enqueue error %d\n", status); + } +} + +#define SDIO_MAX_CTRL_PKT_SIZE 4096 +static void rmnet_sdio_ctl_receive_cb(void *data, int size, void *priv) +{ + struct rmnet_sdio_dev *dev = priv; + struct usb_composite_dev *cdev = dev->cdev; + struct rmnet_sdio_qmi_buf *qmi_resp; + unsigned long flags; + + if (!data) { + pr_info("%s: cmux_ch close event\n", __func__); + if (test_bit(RMNET_SDIO_CH_OPEN, &dev->ctrl_ch_status) && + test_bit(RMNET_SDIO_CH_OPEN, &dev->data_ch_status)) { + clear_bit(RMNET_SDIO_CH_OPEN, &dev->ctrl_ch_status); + clear_bit(RMNET_SDIO_CH_OPEN, &dev->data_ch_status); + queue_work(dev->wq, &dev->sdio_close_work); + } + return; + } + + if (!size || !test_bit(RMNET_SDIO_CH_OPEN, &dev->ctrl_ch_status)) + return; + + + if (size > SDIO_MAX_CTRL_PKT_SIZE) { + ERROR(cdev, "ctrl pkt size:%d exceeds max pkt size:%d\n", + size, SDIO_MAX_CTRL_PKT_SIZE); + return; + } + + if (!atomic_read(&dev->online)) { + DBG(cdev, "USB disconnected\n"); + return; + } + + qmi_resp = rmnet_sdio_alloc_qmi(size, GFP_KERNEL); + if (IS_ERR(qmi_resp)) { + DBG(cdev, "unable to allocate memory for QMI resp\n"); + return; + } + memcpy(qmi_resp->buf, data, size); + qmi_resp->len = size; + spin_lock_irqsave(&dev->lock, flags); + list_add_tail(&qmi_resp->list, &dev->qmi_resp_q); + dev->qresp_q_len++; + spin_unlock_irqrestore(&dev->lock, flags); + + rmnet_sdio_qmi_resp_available(dev); +} + +static void rmnet_sdio_ctl_write_done(void *data, int size, void *priv) +{ + struct rmnet_sdio_dev *dev = priv; + struct usb_composite_dev *cdev = dev->cdev; + + VDBG(cdev, "rmnet control write done = %d bytes\n", size); +} + +static void rmnet_sdio_sts_callback(int id, void *priv) +{ + struct rmnet_sdio_dev *dev = priv; + struct usb_composite_dev *cdev = dev->cdev; + + DBG(cdev, "rmnet_sdio_sts_callback: id: %d\n", id); +} + +static void rmnet_sdio_control_rx_work(struct work_struct *w) +{ + struct rmnet_sdio_dev *dev = container_of(w, struct rmnet_sdio_dev, + ctl_rx_work); + struct usb_composite_dev *cdev = dev->cdev; + struct rmnet_sdio_qmi_buf *qmi_req; + unsigned long flags; + int ret; + + while (1) { + spin_lock_irqsave(&dev->lock, flags); + if (list_empty(&dev->qmi_req_q)) + goto unlock; + + qmi_req = list_first_entry(&dev->qmi_req_q, + struct rmnet_sdio_qmi_buf, list); + list_del(&qmi_req->list); + dev->qreq_q_len--; + spin_unlock_irqrestore(&dev->lock, flags); + + ret = sdio_cmux_write(rmnet_sdio_ctl_ch, qmi_req->buf, + qmi_req->len); + if (ret != qmi_req->len) { + ERROR(cdev, "rmnet control SDIO write failed\n"); + return; + } + + dev->cpkt_tomodem++; + + /* + * cmux_write API copies the buffer and gives it to sdio_al. + * Hence freeing the memory before write is completed. + */ + rmnet_sdio_free_qmi(qmi_req); + } +unlock: + spin_unlock_irqrestore(&dev->lock, flags); +} + +static void rmnet_sdio_response_complete(struct usb_ep *ep, + struct usb_request *req) +{ + struct rmnet_sdio_dev *dev = req->context; + struct usb_composite_dev *cdev = dev->cdev; + + switch (req->status) { + case -ECONNRESET: + case -ESHUTDOWN: + case 0: + return; + default: + INFO(cdev, "rmnet %s response error %d, %d/%d\n", + ep->name, req->status, + req->actual, req->length); + } +} + +static void rmnet_sdio_command_complete(struct usb_ep *ep, + struct usb_request *req) +{ + struct rmnet_sdio_dev *dev = req->context; + struct usb_composite_dev *cdev = dev->cdev; + struct rmnet_sdio_qmi_buf *qmi_req; + int len = req->actual; + + if (req->status < 0) { + ERROR(cdev, "rmnet command error %d\n", req->status); + return; + } + + /* discard the packet if sdio is not available */ + if (!test_bit(RMNET_SDIO_CH_OPEN, &dev->ctrl_ch_status)) + return; + + qmi_req = rmnet_sdio_alloc_qmi(len, GFP_ATOMIC); + if (IS_ERR(qmi_req)) { + ERROR(cdev, "unable to allocate memory for QMI req\n"); + return; + } + memcpy(qmi_req->buf, req->buf, len); + qmi_req->len = len; + spin_lock(&dev->lock); + list_add_tail(&qmi_req->list, &dev->qmi_req_q); + dev->qreq_q_len++; + spin_unlock(&dev->lock); + queue_work(dev->wq, &dev->ctl_rx_work); +} + +static int +rmnet_sdio_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) +{ + struct rmnet_sdio_dev *dev = container_of(f, struct rmnet_sdio_dev, + function); + struct usb_composite_dev *cdev = f->config->cdev; + struct usb_request *req = cdev->req; + int ret = -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); + struct rmnet_sdio_qmi_buf *resp; + + if (!atomic_read(&dev->online)) + return -ENOTCONN; + + switch ((ctrl->bRequestType << 8) | ctrl->bRequest) { + + case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8) + | USB_CDC_SEND_ENCAPSULATED_COMMAND: + ret = w_length; + req->complete = rmnet_sdio_command_complete; + req->context = dev; + break; + + + case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8) + | USB_CDC_GET_ENCAPSULATED_RESPONSE: + if (w_value) + goto invalid; + else { + unsigned len; + + spin_lock(&dev->lock); + + if (list_empty(&dev->qmi_resp_q)) { + INFO(cdev, "qmi resp empty " + " req%02x.%02x v%04x i%04x l%d\n", + ctrl->bRequestType, ctrl->bRequest, + w_value, w_index, w_length); + spin_unlock(&dev->lock); + goto invalid; + } + + resp = list_first_entry(&dev->qmi_resp_q, + struct rmnet_sdio_qmi_buf, list); + list_del(&resp->list); + dev->qresp_q_len--; + spin_unlock(&dev->lock); + + len = min_t(unsigned, w_length, resp->len); + memcpy(req->buf, resp->buf, len); + ret = len; + req->context = dev; + req->complete = rmnet_sdio_response_complete; + rmnet_sdio_free_qmi(resp); + + /* check if its the right place to add */ + dev->cpkt_tolaptop++; + } + break; + case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8) + | USB_CDC_REQ_SET_CONTROL_LINE_STATE: + /* This is a workaround for RmNet and is borrowed from the + * CDC/ACM standard. The host driver will issue the above ACM + * standard request to the RmNet interface in the following + * scenario: Once the network adapter is disabled from device + * manager, the above request will be sent from the qcusbnet + * host driver, with DTR being '0'. Once network adapter is + * enabled from device manager (or during enumeration), the + * request will be sent with DTR being '1'. + */ + if (w_value & ACM_CTRL_DTR) + dev->cbits_to_modem |= TIOCM_DTR; + else + dev->cbits_to_modem &= ~TIOCM_DTR; + queue_work(dev->wq, &dev->set_modem_ctl_bits_work); + + ret = 0; + + break; + default: + +invalid: + DBG(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n", + ctrl->bRequestType, ctrl->bRequest, + w_value, w_index, w_length); + } + + /* respond with data transfer or status phase? */ + if (ret >= 0) { + VDBG(cdev, "rmnet req%02x.%02x v%04x i%04x l%d\n", + ctrl->bRequestType, ctrl->bRequest, + w_value, w_index, w_length); + req->zero = (ret < w_length); + req->length = ret; + ret = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC); + if (ret < 0) + ERROR(cdev, "rmnet ep0 enqueue err %d\n", ret); + } + + return ret; +} + +static int +rmnet_sdio_rx_submit(struct rmnet_sdio_dev *dev, struct usb_request *req, + gfp_t gfp_flags) +{ + struct sk_buff *skb; + int retval; + + skb = alloc_skb(RMNET_SDIO_RX_REQ_SIZE + SDIO_MUX_HDR, gfp_flags); + if (skb == NULL) + return -ENOMEM; + skb_reserve(skb, SDIO_MUX_HDR); + + req->buf = skb->data; + req->length = RMNET_SDIO_RX_REQ_SIZE; + req->context = skb; + + retval = usb_ep_queue(dev->epout, req, gfp_flags); + if (retval) + dev_kfree_skb_any(skb); + + return retval; +} + +static void rmnet_sdio_start_rx(struct rmnet_sdio_dev *dev) +{ + struct usb_composite_dev *cdev = dev->cdev; + int status; + struct usb_request *req; + unsigned long flags; + + if (!atomic_read(&dev->online)) { + pr_err("%s: USB not connected\n", __func__); + return; + } + + spin_lock_irqsave(&dev->lock, flags); + while (!list_empty(&dev->rx_idle)) { + req = list_first_entry(&dev->rx_idle, struct usb_request, list); + list_del(&req->list); + dev->rx_idle_len--; + + spin_unlock_irqrestore(&dev->lock, flags); + status = rmnet_sdio_rx_submit(dev, req, GFP_ATOMIC); + spin_lock_irqsave(&dev->lock, flags); + + if (status) { + ERROR(cdev, "rmnet data rx enqueue err %d\n", status); + list_add_tail(&req->list, &dev->rx_idle); + dev->rx_idle_len++; + break; + } + } + spin_unlock_irqrestore(&dev->lock, flags); +} + +static void rmnet_sdio_start_tx(struct rmnet_sdio_dev *dev) +{ + unsigned long flags; + int status; + struct sk_buff *skb; + struct usb_request *req; + struct usb_composite_dev *cdev = dev->cdev; + + if (!atomic_read(&dev->online)) + return; + + if (!test_bit(RMNET_SDIO_CH_OPEN, &dev->data_ch_status)) + return; + + spin_lock_irqsave(&dev->lock, flags); + while (!list_empty(&dev->tx_idle)) { + skb = __skb_dequeue(&dev->tx_skb_queue); + if (!skb) { + spin_unlock_irqrestore(&dev->lock, flags); + return; + } + + req = list_first_entry(&dev->tx_idle, struct usb_request, list); + req->context = skb; + req->buf = skb->data; + req->length = skb->len; + + list_del(&req->list); + dev->tx_idle_len--; + spin_unlock(&dev->lock); + status = usb_ep_queue(dev->epin, req, GFP_ATOMIC); + spin_lock(&dev->lock); + if (status) { + /* USB still online, queue requests back */ + if (atomic_read(&dev->online)) { + ERROR(cdev, "rmnet tx data enqueue err %d\n", + status); + list_add_tail(&req->list, &dev->tx_idle); + dev->tx_idle_len++; + __skb_queue_head(&dev->tx_skb_queue, skb); + } else { + req->buf = 0; + rmnet_sdio_free_req(dev->epin, req); + dev_kfree_skb_any(skb); + } + break; + } + dev->dpkt_tolaptop++; + } + spin_unlock_irqrestore(&dev->lock, flags); +} + +static void rmnet_sdio_data_receive_cb(void *priv, struct sk_buff *skb) +{ + struct rmnet_sdio_dev *dev = priv; + unsigned long flags; + + /* SDIO mux sends NULL SKB when link state changes */ + if (!skb) { + pr_info("%s: dmux_ch close event\n", __func__); + if (test_bit(RMNET_SDIO_CH_OPEN, &dev->ctrl_ch_status) && + test_bit(RMNET_SDIO_CH_OPEN, &dev->data_ch_status)) { + clear_bit(RMNET_SDIO_CH_OPEN, &dev->ctrl_ch_status); + clear_bit(RMNET_SDIO_CH_OPEN, &dev->data_ch_status); + queue_work(dev->wq, &dev->sdio_close_work); + } + return; + } + + if (!atomic_read(&dev->online)) { + dev_kfree_skb_any(skb); + return; + } + + spin_lock_irqsave(&dev->lock, flags); + + if (dev->tx_skb_queue.qlen > sdio_tx_pkt_drop_thld) { + if (printk_ratelimit()) + pr_err("%s: tx pkt dropped: tx_drop_cnt:%lu\n", + __func__, dev->tx_drp_cnt); + dev->tx_drp_cnt++; + spin_unlock_irqrestore(&dev->lock, flags); + dev_kfree_skb_any(skb); + return; + } + + __skb_queue_tail(&dev->tx_skb_queue, skb); + spin_unlock_irqrestore(&dev->lock, flags); + + rmnet_sdio_start_tx(dev); +} + +static void rmnet_sdio_data_write_done(void *priv, struct sk_buff *skb) +{ + struct rmnet_sdio_dev *dev = priv; + + /* SDIO mux sends NULL SKB when link state changes */ + if (!skb) { + pr_info("%s: dmux_ch open event\n", __func__); + queue_delayed_work(dev->wq, &dev->sdio_open_work, 0); + return; + } + + dev_kfree_skb_any(skb); + /* this function is called from + * sdio mux from spin_lock_irqsave + */ + spin_lock(&dev->lock); + dev->dpkts_pending_atdmux--; + + if (!test_bit(RMNET_SDIO_CH_OPEN, &dev->data_ch_status) || + dev->dpkts_pending_atdmux >= sdio_rx_fctrl_dis_thld) { + spin_unlock(&dev->lock); + return; + } + spin_unlock(&dev->lock); + + rmnet_sdio_start_rx(dev); +} + +static void rmnet_sdio_data_rx_work(struct work_struct *w) +{ + struct rmnet_sdio_dev *dev = container_of(w, struct rmnet_sdio_dev, + data_rx_work); + struct usb_composite_dev *cdev = dev->cdev; + struct sk_buff *skb; + int ret; + unsigned long flags; + + if (!test_bit(RMNET_SDIO_CH_OPEN, &dev->data_ch_status)) { + pr_info("%s: sdio data ch not open\n", __func__); + return; + } + + spin_lock_irqsave(&dev->lock, flags); + while ((skb = __skb_dequeue(&dev->rx_skb_queue))) { + spin_unlock_irqrestore(&dev->lock, flags); + ret = msm_sdio_dmux_write(rmnet_sdio_data_ch, skb); + spin_lock_irqsave(&dev->lock, flags); + if (ret < 0) { + ERROR(cdev, "rmnet SDIO data write failed\n"); + dev_kfree_skb_any(skb); + break; + } else { + dev->dpkt_tomodem++; + dev->dpkts_pending_atdmux++; + } + } + spin_unlock_irqrestore(&dev->lock, flags); +} + +static void rmnet_sdio_complete_epout(struct usb_ep *ep, + struct usb_request *req) +{ + struct rmnet_sdio_dev *dev = ep->driver_data; + struct usb_composite_dev *cdev = dev->cdev; + struct sk_buff *skb = req->context; + int status = req->status; + int queue = 0; + + switch (status) { + case 0: + /* successful completion */ + skb_put(skb, req->actual); + queue = 1; + break; + case -ECONNRESET: + case -ESHUTDOWN: + /* connection gone */ + dev_kfree_skb_any(skb); + req->buf = 0; + rmnet_sdio_free_req(ep, req); + return; + default: + /* unexpected failure */ + ERROR(cdev, "RMNET %s response error %d, %d/%d\n", + ep->name, status, + req->actual, req->length); + dev_kfree_skb_any(skb); + break; + } + + if (!test_bit(RMNET_SDIO_CH_OPEN, &dev->data_ch_status)) { + pr_info("%s: sdio data ch not open\n", __func__); + dev_kfree_skb_any(skb); + req->buf = 0; + rmnet_sdio_free_req(ep, req); + return; + } + + spin_lock(&dev->lock); + if (queue) { + __skb_queue_tail(&dev->rx_skb_queue, skb); + queue_work(dev->wq, &dev->data_rx_work); + } + + if (dev->dpkts_pending_atdmux >= sdio_rx_fctrl_en_thld) { + list_add_tail(&req->list, &dev->rx_idle); + dev->rx_idle_len++; + spin_unlock(&dev->lock); + return; + } + spin_unlock(&dev->lock); + + status = rmnet_sdio_rx_submit(dev, req, GFP_ATOMIC); + if (status) { + ERROR(cdev, "rmnet data rx enqueue err %d\n", status); + list_add_tail(&req->list, &dev->rx_idle); + dev->rx_idle_len++; + } +} + +static void rmnet_sdio_complete_epin(struct usb_ep *ep, struct usb_request *req) +{ + struct rmnet_sdio_dev *dev = ep->driver_data; + struct sk_buff *skb = req->context; + struct usb_composite_dev *cdev = dev->cdev; + int status = req->status; + + switch (status) { + case 0: + /* successful completion */ + case -ECONNRESET: + case -ESHUTDOWN: + /* connection gone */ + break; + default: + ERROR(cdev, "rmnet data tx ep error %d\n", status); + break; + } + + spin_lock(&dev->lock); + list_add_tail(&req->list, &dev->tx_idle); + dev->tx_idle_len++; + spin_unlock(&dev->lock); + dev_kfree_skb_any(skb); + + rmnet_sdio_start_tx(dev); +} + +static void rmnet_sdio_free_buf(struct rmnet_sdio_dev *dev) +{ + struct rmnet_sdio_qmi_buf *qmi; + struct usb_request *req; + struct list_head *act, *tmp; + struct sk_buff *skb; + unsigned long flags; + + + spin_lock_irqsave(&dev->lock, flags); + + dev->dpkt_tolaptop = 0; + dev->dpkt_tomodem = 0; + dev->cpkt_tolaptop = 0; + dev->cpkt_tomodem = 0; + dev->dpkts_pending_atdmux = 0; + dev->tx_drp_cnt = 0; + + /* free all usb requests in tx pool */ + list_for_each_safe(act, tmp, &dev->tx_idle) { + req = list_entry(act, struct usb_request, list); + list_del(&req->list); + dev->tx_idle_len--; + req->buf = NULL; + rmnet_sdio_free_req(dev->epout, req); + } + + /* free all usb requests in rx pool */ + list_for_each_safe(act, tmp, &dev->rx_idle) { + req = list_entry(act, struct usb_request, list); + list_del(&req->list); + dev->rx_idle_len--; + req->buf = NULL; + rmnet_sdio_free_req(dev->epin, req); + } + + /* free all buffers in qmi request pool */ + list_for_each_safe(act, tmp, &dev->qmi_req_q) { + qmi = list_entry(act, struct rmnet_sdio_qmi_buf, list); + list_del(&qmi->list); + dev->qreq_q_len--; + rmnet_sdio_free_qmi(qmi); + } + + /* free all buffers in qmi request pool */ + list_for_each_safe(act, tmp, &dev->qmi_resp_q) { + qmi = list_entry(act, struct rmnet_sdio_qmi_buf, list); + list_del(&qmi->list); + dev->qresp_q_len--; + rmnet_sdio_free_qmi(qmi); + } + + while ((skb = __skb_dequeue(&dev->tx_skb_queue))) + dev_kfree_skb_any(skb); + + while ((skb = __skb_dequeue(&dev->rx_skb_queue))) + dev_kfree_skb_any(skb); + + rmnet_sdio_free_req(dev->epnotify, dev->notify_req); + + spin_unlock_irqrestore(&dev->lock, flags); +} + +static void rmnet_sdio_set_modem_cbits_w(struct work_struct *w) +{ + struct rmnet_sdio_dev *dev; + + dev = container_of(w, struct rmnet_sdio_dev, set_modem_ctl_bits_work); + + if (!test_bit(RMNET_SDIO_CH_OPEN, &dev->ctrl_ch_status)) + return; + + pr_debug("%s: cbits_to_modem:%d\n", + __func__, dev->cbits_to_modem); + + sdio_cmux_tiocmset(rmnet_sdio_ctl_ch, + dev->cbits_to_modem, + ~dev->cbits_to_modem); +} + +static void rmnet_sdio_disconnect_work(struct work_struct *w) +{ + /* REVISIT: Push all the data to sdio if anythign is pending */ +} +static void rmnet_sdio_suspend(struct usb_function *f) +{ + struct rmnet_sdio_dev *dev = container_of(f, struct rmnet_sdio_dev, + function); + + if (!atomic_read(&dev->online)) + return; + /* This is a workaround for Windows Host bug during suspend. + * Windows 7/xp Hosts are suppose to drop DTR, when Host suspended. + * Since it is not beind done, Hence exclusively dropping the DTR + * from function driver suspend. + */ + dev->cbits_to_modem &= ~TIOCM_DTR; + queue_work(dev->wq, &dev->set_modem_ctl_bits_work); +} +static void rmnet_sdio_disable(struct usb_function *f) +{ + struct rmnet_sdio_dev *dev = container_of(f, struct rmnet_sdio_dev, + function); + + if (!atomic_read(&dev->online)) + return; + + usb_ep_disable(dev->epnotify); + usb_ep_disable(dev->epout); + usb_ep_disable(dev->epin); + + atomic_set(&dev->online, 0); + atomic_set(&dev->notify_count, 0); + rmnet_sdio_free_buf(dev); + + /* cleanup work */ + queue_work(dev->wq, &dev->disconnect_work); + dev->cbits_to_modem = 0; + queue_work(dev->wq, &dev->set_modem_ctl_bits_work); +} + +static void rmnet_close_sdio_work(struct work_struct *w) +{ + struct rmnet_sdio_dev *dev; + unsigned long flags; + struct usb_cdc_notification *event; + int status; + struct rmnet_sdio_qmi_buf *qmi; + struct usb_request *req; + struct sk_buff *skb; + + pr_debug("%s:\n", __func__); + + dev = container_of(w, struct rmnet_sdio_dev, sdio_close_work); + + if (!atomic_read(&dev->online)) + return; + + usb_ep_fifo_flush(dev->epnotify); + + spin_lock_irqsave(&dev->lock, flags); + event = dev->notify_req->buf; + + event->bmRequestType = USB_DIR_IN | USB_TYPE_CLASS + | USB_RECIP_INTERFACE; + event->bNotificationType = USB_CDC_NOTIFY_NETWORK_CONNECTION; + event->wValue = cpu_to_le16(0); + event->wIndex = cpu_to_le16(dev->ifc_id); + event->wLength = cpu_to_le16(0); + spin_unlock_irqrestore(&dev->lock, flags); + + status = usb_ep_queue(dev->epnotify, dev->notify_req, GFP_KERNEL); + if (status < 0) { + if (!atomic_read(&dev->online)) + return; + pr_err("%s: rmnet notify ep enqueue error %d\n", + __func__, status); + } + + usb_ep_fifo_flush(dev->epout); + usb_ep_fifo_flush(dev->epin); + cancel_work_sync(&dev->data_rx_work); + + spin_lock_irqsave(&dev->lock, flags); + + if (!atomic_read(&dev->online)) { + spin_unlock_irqrestore(&dev->lock, flags); + return; + } + + /* free all usb requests in tx pool */ + while (!list_empty(&dev->tx_idle)) { + req = list_first_entry(&dev->tx_idle, struct usb_request, list); + list_del(&req->list); + dev->tx_idle_len--; + req->buf = NULL; + rmnet_sdio_free_req(dev->epout, req); + } + + /* free all usb requests in rx pool */ + while (!list_empty(&dev->rx_idle)) { + req = list_first_entry(&dev->rx_idle, struct usb_request, list); + list_del(&req->list); + dev->rx_idle_len--; + req->buf = NULL; + rmnet_sdio_free_req(dev->epin, req); + } + + /* free all buffers in qmi request pool */ + while (!list_empty(&dev->qmi_req_q)) { + qmi = list_first_entry(&dev->qmi_req_q, + struct rmnet_sdio_qmi_buf, list); + list_del(&qmi->list); + dev->qreq_q_len--; + rmnet_sdio_free_qmi(qmi); + } + + /* free all buffers in qmi response pool */ + while (!list_empty(&dev->qmi_resp_q)) { + qmi = list_first_entry(&dev->qmi_resp_q, + struct rmnet_sdio_qmi_buf, list); + list_del(&qmi->list); + dev->qresp_q_len--; + rmnet_sdio_free_qmi(qmi); + } + atomic_set(&dev->notify_count, 0); + + pr_info("%s: setting notify count to zero\n", __func__); + + + while ((skb = __skb_dequeue(&dev->tx_skb_queue))) + dev_kfree_skb_any(skb); + + while ((skb = __skb_dequeue(&dev->rx_skb_queue))) + dev_kfree_skb_any(skb); + spin_unlock_irqrestore(&dev->lock, flags); +} + +static int rmnet_sdio_start_io(struct rmnet_sdio_dev *dev) +{ + struct usb_request *req; + int ret, i; + unsigned long flags; + + spin_lock_irqsave(&dev->lock, flags); + if (!atomic_read(&dev->online)) { + spin_unlock_irqrestore(&dev->lock, flags); + return 0; + } + + if (!test_bit(RMNET_SDIO_CH_OPEN, &dev->data_ch_status) || + !test_bit(RMNET_SDIO_CH_OPEN, &dev->ctrl_ch_status)) { + spin_unlock_irqrestore(&dev->lock, flags); + return 0; + } + + for (i = 0; i < RMNET_SDIO_RX_REQ_MAX; i++) { + req = rmnet_sdio_alloc_req(dev->epout, 0, GFP_ATOMIC); + if (IS_ERR(req)) { + ret = PTR_ERR(req); + spin_unlock_irqrestore(&dev->lock, flags); + goto free_buf; + } + req->complete = rmnet_sdio_complete_epout; + list_add_tail(&req->list, &dev->rx_idle); + dev->rx_idle_len++; + } + for (i = 0; i < RMNET_SDIO_TX_REQ_MAX; i++) { + req = rmnet_sdio_alloc_req(dev->epin, 0, GFP_ATOMIC); + if (IS_ERR(req)) { + ret = PTR_ERR(req); + spin_unlock_irqrestore(&dev->lock, flags); + goto free_buf; + } + req->complete = rmnet_sdio_complete_epin; + list_add_tail(&req->list, &dev->tx_idle); + dev->tx_idle_len++; + } + spin_unlock_irqrestore(&dev->lock, flags); + + /* Queue Rx data requests */ + rmnet_sdio_start_rx(dev); + + return 0; + +free_buf: + rmnet_sdio_free_buf(dev); + dev->epout = dev->epin = dev->epnotify = NULL; /* release endpoints */ + return ret; +} + + +#define RMNET_SDIO_OPEN_RETRY_DELAY msecs_to_jiffies(2000) +#define SDIO_SDIO_OPEN_MAX_RETRY 90 +static void rmnet_open_sdio_work(struct work_struct *w) +{ + struct rmnet_sdio_dev *dev = + container_of(w, struct rmnet_sdio_dev, + sdio_open_work.work); + struct usb_composite_dev *cdev = dev->cdev; + int ret; + static int retry_cnt; + + if (!test_bit(RMNET_SDIO_CH_OPEN, &dev->ctrl_ch_status)) { + /* Control channel for QMI messages */ + ret = sdio_cmux_open(rmnet_sdio_ctl_ch, + rmnet_sdio_ctl_receive_cb, + rmnet_sdio_ctl_write_done, + rmnet_sdio_sts_callback, dev); + if (!ret) + set_bit(RMNET_SDIO_CH_OPEN, &dev->ctrl_ch_status); + } + + if (!test_bit(RMNET_SDIO_CH_OPEN, &dev->data_ch_status)) { + /* Data channel for network packets */ + ret = msm_sdio_dmux_open(rmnet_sdio_data_ch, dev, + rmnet_sdio_data_receive_cb, + rmnet_sdio_data_write_done); + if (!ret) + set_bit(RMNET_SDIO_CH_OPEN, &dev->data_ch_status); + } + + if (test_bit(RMNET_SDIO_CH_OPEN, &dev->data_ch_status) && + test_bit(RMNET_SDIO_CH_OPEN, &dev->ctrl_ch_status)) { + + rmnet_sdio_start_io(dev); + + /* if usb cable is connected, update DTR status to modem */ + if (atomic_read(&dev->online)) + queue_work(dev->wq, &dev->set_modem_ctl_bits_work); + + pr_info("%s: usb rmnet sdio channels are open retry_cnt:%d\n", + __func__, retry_cnt); + retry_cnt = 0; + return; + } + + retry_cnt++; + pr_debug("%s: usb rmnet sdio open retry_cnt:%d\n", + __func__, retry_cnt); + + if (retry_cnt > SDIO_SDIO_OPEN_MAX_RETRY) { + if (!test_bit(RMNET_SDIO_CH_OPEN, &dev->ctrl_ch_status)) + ERROR(cdev, "Unable to open control SDIO channel\n"); + + if (!test_bit(RMNET_SDIO_CH_OPEN, &dev->data_ch_status)) + ERROR(cdev, "Unable to open DATA SDIO channel\n"); + + } else { + queue_delayed_work(dev->wq, &dev->sdio_open_work, + RMNET_SDIO_OPEN_RETRY_DELAY); + } +} + +static int rmnet_sdio_set_alt(struct usb_function *f, + unsigned intf, unsigned alt) +{ + struct rmnet_sdio_dev *dev = container_of(f, struct rmnet_sdio_dev, + function); + struct usb_composite_dev *cdev = dev->cdev; + int ret = 0; + + dev->epin->driver_data = dev; + usb_ep_enable(dev->epin, ep_choose(cdev->gadget, + &rmnet_sdio_hs_in_desc, + &rmnet_sdio_fs_in_desc)); + dev->epout->driver_data = dev; + usb_ep_enable(dev->epout, ep_choose(cdev->gadget, + &rmnet_sdio_hs_out_desc, + &rmnet_sdio_fs_out_desc)); + usb_ep_enable(dev->epnotify, ep_choose(cdev->gadget, + &rmnet_sdio_hs_notify_desc, + &rmnet_sdio_fs_notify_desc)); + + /* allocate notification */ + dev->notify_req = rmnet_sdio_alloc_req(dev->epnotify, + RMNET_SDIO_MAX_NFY_SZE, GFP_ATOMIC); + + if (IS_ERR(dev->notify_req)) { + ret = PTR_ERR(dev->notify_req); + pr_err("%s: unable to allocate memory for notify ep\n", + __func__); + return ret; + } + dev->notify_req->complete = rmnet_sdio_notify_complete; + dev->notify_req->context = dev; + dev->notify_req->length = RMNET_SDIO_MAX_NFY_SZE; + + atomic_set(&dev->online, 1); + + ret = rmnet_sdio_start_io(dev); + + return ret; + +} + +static int rmnet_sdio_bind(struct usb_configuration *c, struct usb_function *f) +{ + struct usb_composite_dev *cdev = c->cdev; + struct rmnet_sdio_dev *dev = container_of(f, struct rmnet_sdio_dev, + function); + int id; + struct usb_ep *ep; + + dev->cdev = cdev; + + /* allocate interface ID */ + id = usb_interface_id(c, f); + if (id < 0) + return id; + dev->ifc_id = id; + rmnet_sdio_interface_desc.bInterfaceNumber = id; + + ep = usb_ep_autoconfig(cdev->gadget, &rmnet_sdio_fs_in_desc); + if (!ep) + goto out; + ep->driver_data = cdev; /* claim endpoint */ + dev->epin = ep; + + ep = usb_ep_autoconfig(cdev->gadget, &rmnet_sdio_fs_out_desc); + if (!ep) + goto out; + ep->driver_data = cdev; /* claim endpoint */ + dev->epout = ep; + + ep = usb_ep_autoconfig(cdev->gadget, &rmnet_sdio_fs_notify_desc); + if (!ep) + goto out; + ep->driver_data = cdev; /* claim endpoint */ + dev->epnotify = ep; + + /* support all relevant hardware speeds... we expect that when + * hardware is dual speed, all bulk-capable endpoints work at + * both speeds + */ + if (gadget_is_dualspeed(c->cdev->gadget)) { + rmnet_sdio_hs_in_desc.bEndpointAddress = + rmnet_sdio_fs_in_desc.bEndpointAddress; + rmnet_sdio_hs_out_desc.bEndpointAddress = + rmnet_sdio_fs_out_desc.bEndpointAddress; + rmnet_sdio_hs_notify_desc.bEndpointAddress = + rmnet_sdio_fs_notify_desc.bEndpointAddress; + } + + queue_delayed_work(dev->wq, &dev->sdio_open_work, 0); + + return 0; + +out: + if (dev->epnotify) + dev->epnotify->driver_data = NULL; + if (dev->epout) + dev->epout->driver_data = NULL; + if (dev->epin) + dev->epin->driver_data = NULL; + + return -ENODEV; +} + +static void +rmnet_sdio_unbind(struct usb_configuration *c, struct usb_function *f) +{ + struct rmnet_sdio_dev *dev = container_of(f, struct rmnet_sdio_dev, + function); + + cancel_delayed_work_sync(&dev->sdio_open_work); + destroy_workqueue(dev->wq); + + dev->epout = dev->epin = dev->epnotify = NULL; /* release endpoints */ + + if (test_bit(RMNET_SDIO_CH_OPEN, &dev->data_ch_status)) { + msm_sdio_dmux_close(rmnet_sdio_data_ch); + clear_bit(RMNET_SDIO_CH_OPEN, &dev->data_ch_status); + } + + if (test_bit(RMNET_SDIO_CH_OPEN, &dev->ctrl_ch_status)) { + sdio_cmux_close(rmnet_sdio_ctl_ch); + clear_bit(RMNET_SDIO_CH_OPEN, &dev->ctrl_ch_status); + } + + debugfs_remove_recursive(dev->dent); + + kfree(dev); +} + +#if defined(CONFIG_DEBUG_FS) +static ssize_t rmnet_sdio_read_stats(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct rmnet_sdio_dev *dev = file->private_data; + char *buf; + unsigned long flags; + int ret; + + buf = kzalloc(sizeof(char) * 1024, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + spin_lock_irqsave(&dev->lock, flags); + ret = scnprintf(buf, PAGE_SIZE, + "-*-DATA-*-\n" + "dpkts_tohost:%lu epInPool:%u tx_size:%u drp_cnt:%lu\n" + "dpkts_tomodem:%lu epOutPool:%u rx_size:%u pending:%u\n" + "-*-QMI-*-\n" + "cpkts_tomodem:%lu qmi_req_q:%u cbits:%d\n" + "cpkts_tolaptop:%lu qmi_resp_q:%u notify_cnt:%d\n" + "-*-MISC-*-\n" + "data_ch_status: %lu ctrl_ch_status: %lu\n", + /* data */ + dev->dpkt_tolaptop, dev->tx_idle_len, + dev->tx_skb_queue.qlen, dev->tx_drp_cnt, + dev->dpkt_tomodem, dev->rx_idle_len, + dev->rx_skb_queue.qlen, dev->dpkts_pending_atdmux, + /* qmi */ + dev->cpkt_tomodem, dev->qreq_q_len, + dev->cbits_to_modem, + dev->cpkt_tolaptop, dev->qresp_q_len, + atomic_read(&dev->notify_count), + /* misc */ + dev->data_ch_status, dev->ctrl_ch_status); + + spin_unlock_irqrestore(&dev->lock, flags); + + ret = simple_read_from_buffer(ubuf, count, ppos, buf, ret); + + kfree(buf); + + return ret; +} + +static ssize_t rmnet_sdio_reset_stats(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct rmnet_sdio_dev *dev = file->private_data; + + dev->dpkt_tolaptop = 0; + dev->dpkt_tomodem = 0; + dev->cpkt_tolaptop = 0; + dev->cpkt_tomodem = 0; + dev->dpkts_pending_atdmux = 0; + dev->tx_drp_cnt = 0; + + /* TBD: How do we reset skb qlen + * it might have side effects + */ + + return count; +} + +static int debug_rmnet_sdio_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + + return 0; +} + +const struct file_operations debug_rmnet_sdio_stats_ops = { + .open = debug_rmnet_sdio_open, + .read = rmnet_sdio_read_stats, + .write = rmnet_sdio_reset_stats, +}; + +static void rmnet_sdio_debugfs_init(struct rmnet_sdio_dev *dev) +{ + dev->dent = debugfs_create_dir("usb_rmnet_sdio", 0); + if (IS_ERR(dev->dent)) + return; + + debugfs_create_file("status", 0444, dev->dent, dev, + &debug_rmnet_sdio_stats_ops); +} +#else +static void rmnet_sdio_debugfs_init(struct rmnet_sdio_dev *dev) +{ + return; +} +#endif + +int rmnet_sdio_function_add(struct usb_configuration *c) +{ + struct rmnet_sdio_dev *dev; + int ret; + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + dev->wq = create_singlethread_workqueue("k_rmnet_work"); + if (!dev->wq) { + ret = -ENOMEM; + goto free_dev; + } + + spin_lock_init(&dev->lock); + atomic_set(&dev->notify_count, 0); + atomic_set(&dev->online, 0); + + INIT_WORK(&dev->disconnect_work, rmnet_sdio_disconnect_work); + INIT_WORK(&dev->set_modem_ctl_bits_work, rmnet_sdio_set_modem_cbits_w); + + INIT_WORK(&dev->ctl_rx_work, rmnet_sdio_control_rx_work); + INIT_WORK(&dev->data_rx_work, rmnet_sdio_data_rx_work); + + INIT_DELAYED_WORK(&dev->sdio_open_work, rmnet_open_sdio_work); + INIT_WORK(&dev->sdio_close_work, rmnet_close_sdio_work); + + INIT_LIST_HEAD(&dev->qmi_req_q); + INIT_LIST_HEAD(&dev->qmi_resp_q); + + INIT_LIST_HEAD(&dev->rx_idle); + INIT_LIST_HEAD(&dev->tx_idle); + skb_queue_head_init(&dev->tx_skb_queue); + skb_queue_head_init(&dev->rx_skb_queue); + + dev->function.name = "rmnet_sdio"; + dev->function.strings = rmnet_sdio_strings; + dev->function.descriptors = rmnet_sdio_fs_function; + dev->function.hs_descriptors = rmnet_sdio_hs_function; + dev->function.bind = rmnet_sdio_bind; + dev->function.unbind = rmnet_sdio_unbind; + dev->function.setup = rmnet_sdio_setup; + dev->function.set_alt = rmnet_sdio_set_alt; + dev->function.disable = rmnet_sdio_disable; + dev->function.suspend = rmnet_sdio_suspend; + + ret = usb_add_function(c, &dev->function); + if (ret) + goto free_wq; + + rmnet_sdio_debugfs_init(dev); + + return 0; + +free_wq: + destroy_workqueue(dev->wq); +free_dev: + kfree(dev); + + return ret; +} diff --git a/drivers/usb/gadget/f_rmnet_smd.c b/drivers/usb/gadget/f_rmnet_smd.c new file mode 100644 index 00000000000..2049dc01fdd --- /dev/null +++ b/drivers/usb/gadget/f_rmnet_smd.c @@ -0,0 +1,1368 @@ +/* + * f_rmnet.c -- RmNet function driver + * + * Copyright (C) 2003-2005,2008 David Brownell + * Copyright (C) 2003-2004 Robert Schwebel, Benedikt Spranger + * Copyright (C) 2003 Al Borchers (alborchers@steinerpoint.com) + * Copyright (C) 2008 Nokia Corporation + * 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 as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "gadget_chips.h" + +#ifndef CONFIG_MSM_SMD +#define CONFIG_RMNET_SMD_CTL_CHANNEL "" +#define CONFIG_RMNET_SMD_DATA_CHANNEL "" +#endif + +static char *rmnet_ctl_ch = CONFIG_RMNET_SMD_CTL_CHANNEL; +module_param(rmnet_ctl_ch, charp, S_IRUGO); +MODULE_PARM_DESC(rmnet_ctl_ch, "RmNet control SMD channel"); + +static char *rmnet_data_ch = CONFIG_RMNET_SMD_DATA_CHANNEL; +module_param(rmnet_data_ch, charp, S_IRUGO); +MODULE_PARM_DESC(rmnet_data_ch, "RmNet data SMD channel"); + +#define RMNET_SMD_ACM_CTRL_DTR (1 << 0) + +#define RMNET_SMD_NOTIFY_INTERVAL 5 +#define RMNET_SMD_MAX_NOTIFY_SIZE sizeof(struct usb_cdc_notification) + +#define QMI_REQ_MAX 4 +#define QMI_REQ_SIZE 2048 +#define QMI_RESP_MAX 8 +#define QMI_RESP_SIZE 2048 + +#define RMNET_RX_REQ_MAX 8 +#define RMNET_RX_REQ_SIZE 2048 +#define RMNET_TX_REQ_MAX 8 +#define RMNET_TX_REQ_SIZE 2048 + +#define RMNET_TXN_MAX 2048 + +/* QMI requests & responses buffer*/ +struct qmi_buf { + void *buf; + int len; + struct list_head list; +}; + +/* Control & data SMD channel private data */ +struct rmnet_smd_ch_info { + struct smd_channel *ch; + struct tasklet_struct tx_tlet; + struct tasklet_struct rx_tlet; +#define CH_OPENED 0 + unsigned long flags; + /* pending rx packet length */ + atomic_t rx_pkt; + /* wait for smd open event*/ + wait_queue_head_t wait; +}; + +struct rmnet_smd_dev { + struct usb_function function; + struct usb_composite_dev *cdev; + + struct usb_ep *epout; + struct usb_ep *epin; + struct usb_ep *epnotify; + struct usb_request *notify_req; + + u8 ifc_id; + /* QMI lists */ + struct list_head qmi_req_pool; + struct list_head qmi_resp_pool; + struct list_head qmi_req_q; + struct list_head qmi_resp_q; + /* Tx/Rx lists */ + struct list_head tx_idle; + struct list_head rx_idle; + struct list_head rx_queue; + + spinlock_t lock; + atomic_t online; + atomic_t notify_count; + + struct platform_driver pdrv; + u8 is_pdrv_used; + struct rmnet_smd_ch_info smd_ctl; + struct rmnet_smd_ch_info smd_data; + + struct workqueue_struct *wq; + struct work_struct connect_work; + struct work_struct disconnect_work; + + unsigned long dpkts_to_host; + unsigned long dpkts_from_modem; + unsigned long dpkts_from_host; + unsigned long dpkts_to_modem; + + unsigned long cpkts_to_host; + unsigned long cpkts_from_modem; + unsigned long cpkts_from_host; + unsigned long cpkts_to_modem; +}; + +static struct rmnet_smd_dev *rmnet_smd; + +static struct usb_interface_descriptor rmnet_smd_interface_desc = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + /* .bInterfaceNumber = DYNAMIC */ + .bNumEndpoints = 3, + .bInterfaceClass = USB_CLASS_VENDOR_SPEC, + .bInterfaceSubClass = USB_CLASS_VENDOR_SPEC, + .bInterfaceProtocol = USB_CLASS_VENDOR_SPEC, + /* .iInterface = DYNAMIC */ +}; + +/* Full speed support */ +static struct usb_endpoint_descriptor rmnet_smd_fs_notify_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = __constant_cpu_to_le16( + RMNET_SMD_MAX_NOTIFY_SIZE), + .bInterval = 1 << RMNET_SMD_NOTIFY_INTERVAL, +}; + +static struct usb_endpoint_descriptor rmnet_smd_fs_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(64), +}; + +static struct usb_endpoint_descriptor rmnet_smd_fs_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(64), +}; + +static struct usb_descriptor_header *rmnet_smd_fs_function[] = { + (struct usb_descriptor_header *) &rmnet_smd_interface_desc, + (struct usb_descriptor_header *) &rmnet_smd_fs_notify_desc, + (struct usb_descriptor_header *) &rmnet_smd_fs_in_desc, + (struct usb_descriptor_header *) &rmnet_smd_fs_out_desc, + NULL, +}; + +/* High speed support */ +static struct usb_endpoint_descriptor rmnet_smd_hs_notify_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = __constant_cpu_to_le16( + RMNET_SMD_MAX_NOTIFY_SIZE), + .bInterval = RMNET_SMD_NOTIFY_INTERVAL + 4, +}; + +static struct usb_endpoint_descriptor rmnet_smd_hs_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 rmnet_smd_hs_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_descriptor_header *rmnet_smd_hs_function[] = { + (struct usb_descriptor_header *) &rmnet_smd_interface_desc, + (struct usb_descriptor_header *) &rmnet_smd_hs_notify_desc, + (struct usb_descriptor_header *) &rmnet_smd_hs_in_desc, + (struct usb_descriptor_header *) &rmnet_smd_hs_out_desc, + NULL, +}; + +/* String descriptors */ + +static struct usb_string rmnet_smd_string_defs[] = { + [0].s = "QMI RmNet", + { } /* end of list */ +}; + +static struct usb_gadget_strings rmnet_smd_string_table = { + .language = 0x0409, /* en-us */ + .strings = rmnet_smd_string_defs, +}; + +static struct usb_gadget_strings *rmnet_smd_strings[] = { + &rmnet_smd_string_table, + NULL, +}; + +static struct qmi_buf * +rmnet_smd_alloc_qmi(unsigned len, gfp_t kmalloc_flags) +{ + struct qmi_buf *qmi; + + qmi = kmalloc(sizeof(struct qmi_buf), kmalloc_flags); + if (qmi != NULL) { + qmi->buf = kmalloc(len, kmalloc_flags); + if (qmi->buf == NULL) { + kfree(qmi); + qmi = NULL; + } + } + + return qmi ? qmi : ERR_PTR(-ENOMEM); +} + +static void rmnet_smd_free_qmi(struct qmi_buf *qmi) +{ + kfree(qmi->buf); + kfree(qmi); +} +/* + * Allocate a usb_request and its buffer. Returns a pointer to the + * usb_request or a error code if there is an error. + */ +static struct usb_request * +rmnet_smd_alloc_req(struct usb_ep *ep, unsigned len, gfp_t kmalloc_flags) +{ + struct usb_request *req; + + req = usb_ep_alloc_request(ep, kmalloc_flags); + + if (req != NULL) { + req->length = len; + req->buf = kmalloc(len, kmalloc_flags); + if (req->buf == NULL) { + usb_ep_free_request(ep, req); + req = NULL; + } + } + + return req ? req : ERR_PTR(-ENOMEM); +} + +/* + * Free a usb_request and its buffer. + */ +static void rmnet_smd_free_req(struct usb_ep *ep, struct usb_request *req) +{ + kfree(req->buf); + usb_ep_free_request(ep, req); +} + +static void rmnet_smd_notify_complete(struct usb_ep *ep, + struct usb_request *req) +{ + struct rmnet_smd_dev *dev = req->context; + struct usb_composite_dev *cdev = dev->cdev; + int status = req->status; + + switch (status) { + case -ECONNRESET: + case -ESHUTDOWN: + /* connection gone */ + atomic_set(&dev->notify_count, 0); + break; + default: + ERROR(cdev, "rmnet notify ep error %d\n", status); + /* FALLTHROUGH */ + case 0: + if (ep != dev->epnotify) + break; + + /* handle multiple pending QMI_RESPONSE_AVAILABLE + * notifications by resending until we're done + */ + if (atomic_dec_and_test(&dev->notify_count)) + break; + + status = usb_ep_queue(dev->epnotify, req, GFP_ATOMIC); + if (status) { + atomic_dec(&dev->notify_count); + ERROR(cdev, "rmnet notify ep enqueue error %d\n", + status); + } + break; + } +} + +static void qmi_smd_response_available(struct rmnet_smd_dev *dev) +{ + struct usb_composite_dev *cdev = dev->cdev; + struct usb_request *req = dev->notify_req; + struct usb_cdc_notification *event = req->buf; + int status; + + /* Response will be sent later */ + if (atomic_inc_return(&dev->notify_count) != 1) + return; + + event->bmRequestType = USB_DIR_IN | USB_TYPE_CLASS + | USB_RECIP_INTERFACE; + event->bNotificationType = USB_CDC_NOTIFY_RESPONSE_AVAILABLE; + event->wValue = cpu_to_le16(0); + event->wIndex = cpu_to_le16(dev->ifc_id); + event->wLength = cpu_to_le16(0); + + status = usb_ep_queue(dev->epnotify, dev->notify_req, GFP_ATOMIC); + if (status < 0) { + atomic_dec(&dev->notify_count); + ERROR(cdev, "rmnet notify ep enqueue error %d\n", status); + } +} + +/* TODO + * handle modem restart events + */ +static void rmnet_smd_event_notify(void *priv, unsigned event) +{ + struct rmnet_smd_ch_info *smd_info = priv; + int len = atomic_read(&smd_info->rx_pkt); + struct rmnet_smd_dev *dev = + (struct rmnet_smd_dev *) smd_info->tx_tlet.data; + + switch (event) { + case SMD_EVENT_DATA: { + if (!atomic_read(&dev->online)) + break; + if (len && (smd_write_avail(smd_info->ch) >= len)) + tasklet_schedule(&smd_info->rx_tlet); + + if (smd_read_avail(smd_info->ch)) + tasklet_schedule(&smd_info->tx_tlet); + + break; + } + case SMD_EVENT_OPEN: + /* usb endpoints are not enabled untill smd channels + * are opened. wake up worker thread to continue + * connection processing + */ + set_bit(CH_OPENED, &smd_info->flags); + wake_up(&smd_info->wait); + break; + case SMD_EVENT_CLOSE: + /* We will never come here. + * reset flags after closing smd channel + * */ + clear_bit(CH_OPENED, &smd_info->flags); + break; + } +} + +static void rmnet_control_tx_tlet(unsigned long arg) +{ + struct rmnet_smd_dev *dev = (struct rmnet_smd_dev *) arg; + struct usb_composite_dev *cdev = dev->cdev; + struct qmi_buf *qmi_resp; + int sz; + unsigned long flags; + + while (1) { + sz = smd_cur_packet_size(dev->smd_ctl.ch); + if (sz == 0) + break; + if (smd_read_avail(dev->smd_ctl.ch) < sz) + break; + + spin_lock_irqsave(&dev->lock, flags); + if (list_empty(&dev->qmi_resp_pool)) { + ERROR(cdev, "rmnet QMI Tx buffers full\n"); + spin_unlock_irqrestore(&dev->lock, flags); + break; + } + qmi_resp = list_first_entry(&dev->qmi_resp_pool, + struct qmi_buf, list); + list_del(&qmi_resp->list); + spin_unlock_irqrestore(&dev->lock, flags); + + qmi_resp->len = smd_read(dev->smd_ctl.ch, qmi_resp->buf, sz); + + spin_lock_irqsave(&dev->lock, flags); + dev->cpkts_from_modem++; + list_add_tail(&qmi_resp->list, &dev->qmi_resp_q); + spin_unlock_irqrestore(&dev->lock, flags); + + qmi_smd_response_available(dev); + } + +} + +static void rmnet_control_rx_tlet(unsigned long arg) +{ + struct rmnet_smd_dev *dev = (struct rmnet_smd_dev *) arg; + struct usb_composite_dev *cdev = dev->cdev; + struct qmi_buf *qmi_req; + int ret; + unsigned long flags; + + spin_lock_irqsave(&dev->lock, flags); + while (1) { + + if (list_empty(&dev->qmi_req_q)) { + atomic_set(&dev->smd_ctl.rx_pkt, 0); + break; + } + qmi_req = list_first_entry(&dev->qmi_req_q, + struct qmi_buf, list); + if (smd_write_avail(dev->smd_ctl.ch) < qmi_req->len) { + atomic_set(&dev->smd_ctl.rx_pkt, qmi_req->len); + DBG(cdev, "rmnet control smd channel full\n"); + break; + } + + list_del(&qmi_req->list); + dev->cpkts_from_host++; + spin_unlock_irqrestore(&dev->lock, flags); + ret = smd_write(dev->smd_ctl.ch, qmi_req->buf, qmi_req->len); + spin_lock_irqsave(&dev->lock, flags); + if (ret != qmi_req->len) { + ERROR(cdev, "rmnet control smd write failed\n"); + break; + } + dev->cpkts_to_modem++; + list_add_tail(&qmi_req->list, &dev->qmi_req_pool); + } + spin_unlock_irqrestore(&dev->lock, flags); +} + +static void rmnet_smd_command_complete(struct usb_ep *ep, + struct usb_request *req) +{ + struct rmnet_smd_dev *dev = req->context; + struct usb_composite_dev *cdev = dev->cdev; + struct qmi_buf *qmi_req; + int ret; + + if (req->status < 0) { + ERROR(cdev, "rmnet command error %d\n", req->status); + return; + } + + spin_lock(&dev->lock); + dev->cpkts_from_host++; + /* no pending control rx packet */ + if (!atomic_read(&dev->smd_ctl.rx_pkt)) { + if (smd_write_avail(dev->smd_ctl.ch) < req->actual) { + atomic_set(&dev->smd_ctl.rx_pkt, req->actual); + goto queue_req; + } + spin_unlock(&dev->lock); + ret = smd_write(dev->smd_ctl.ch, req->buf, req->actual); + /* This should never happen */ + if (ret != req->actual) + ERROR(cdev, "rmnet control smd write failed\n"); + spin_lock(&dev->lock); + dev->cpkts_to_modem++; + spin_unlock(&dev->lock); + return; + } +queue_req: + if (list_empty(&dev->qmi_req_pool)) { + spin_unlock(&dev->lock); + ERROR(cdev, "rmnet QMI pool is empty\n"); + return; + } + + qmi_req = list_first_entry(&dev->qmi_req_pool, struct qmi_buf, list); + list_del(&qmi_req->list); + spin_unlock(&dev->lock); + memcpy(qmi_req->buf, req->buf, req->actual); + qmi_req->len = req->actual; + spin_lock(&dev->lock); + list_add_tail(&qmi_req->list, &dev->qmi_req_q); + spin_unlock(&dev->lock); +} +static void rmnet_txcommand_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct rmnet_smd_dev *dev = req->context; + + spin_lock(&dev->lock); + dev->cpkts_to_host++; + spin_unlock(&dev->lock); +} + +static int +rmnet_smd_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) +{ + struct rmnet_smd_dev *dev = container_of(f, struct rmnet_smd_dev, + function); + struct usb_composite_dev *cdev = f->config->cdev; + struct usb_request *req = cdev->req; + int ret = -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); + struct qmi_buf *resp; + int schedule = 0; + + if (!atomic_read(&dev->online)) + return -ENOTCONN; + + switch ((ctrl->bRequestType << 8) | ctrl->bRequest) { + + case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8) + | USB_CDC_SEND_ENCAPSULATED_COMMAND: + ret = w_length; + req->complete = rmnet_smd_command_complete; + req->context = dev; + break; + + + case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8) + | USB_CDC_GET_ENCAPSULATED_RESPONSE: + if (w_value) + goto invalid; + else { + spin_lock(&dev->lock); + if (list_empty(&dev->qmi_resp_q)) { + INFO(cdev, "qmi resp empty " + " req%02x.%02x v%04x i%04x l%d\n", + ctrl->bRequestType, ctrl->bRequest, + w_value, w_index, w_length); + spin_unlock(&dev->lock); + goto invalid; + } + resp = list_first_entry(&dev->qmi_resp_q, + struct qmi_buf, list); + list_del(&resp->list); + spin_unlock(&dev->lock); + memcpy(req->buf, resp->buf, resp->len); + ret = resp->len; + spin_lock(&dev->lock); + + if (list_empty(&dev->qmi_resp_pool)) + schedule = 1; + list_add_tail(&resp->list, &dev->qmi_resp_pool); + + if (schedule) + tasklet_schedule(&dev->smd_ctl.tx_tlet); + spin_unlock(&dev->lock); + req->complete = rmnet_txcommand_complete; + req->context = dev; + } + break; + case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8) + | USB_CDC_REQ_SET_CONTROL_LINE_STATE: + /* This is a workaround for RmNet and is borrowed from the + * CDC/ACM standard. The host driver will issue the above ACM + * standard request to the RmNet interface in the following + * scenario: Once the network adapter is disabled from device + * manager, the above request will be sent from the qcusbnet + * host driver, with DTR being '0'. Once network adapter is + * enabled from device manager (or during enumeration), the + * request will be sent with DTR being '1'. + */ + if (w_value & RMNET_SMD_ACM_CTRL_DTR) + ret = smd_tiocmset(dev->smd_ctl.ch, TIOCM_DTR, 0); + else + ret = smd_tiocmset(dev->smd_ctl.ch, 0, TIOCM_DTR); + + break; + default: + +invalid: + DBG(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n", + ctrl->bRequestType, ctrl->bRequest, + w_value, w_index, w_length); + } + + /* respond with data transfer or status phase? */ + if (ret >= 0) { + VDBG(cdev, "rmnet req%02x.%02x v%04x i%04x l%d\n", + ctrl->bRequestType, ctrl->bRequest, + w_value, w_index, w_length); + req->zero = 0; + req->length = ret; + ret = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC); + if (ret < 0) + ERROR(cdev, "rmnet ep0 enqueue err %d\n", ret); + } + + return ret; +} + +static void rmnet_smd_start_rx(struct rmnet_smd_dev *dev) +{ + struct usb_composite_dev *cdev = dev->cdev; + int status; + struct usb_request *req; + struct list_head *pool = &dev->rx_idle; + unsigned long flags; + + spin_lock_irqsave(&dev->lock, flags); + while (!list_empty(pool)) { + req = list_entry(pool->next, struct usb_request, list); + list_del(&req->list); + + spin_unlock_irqrestore(&dev->lock, flags); + status = usb_ep_queue(dev->epout, req, GFP_ATOMIC); + spin_lock_irqsave(&dev->lock, flags); + + if (status) { + ERROR(cdev, "rmnet data rx enqueue err %d\n", status); + list_add_tail(&req->list, pool); + break; + } + } + spin_unlock_irqrestore(&dev->lock, flags); +} + +static void rmnet_data_tx_tlet(unsigned long arg) +{ + struct rmnet_smd_dev *dev = (struct rmnet_smd_dev *) arg; + struct usb_composite_dev *cdev = dev->cdev; + struct usb_request *req; + int status; + int sz; + unsigned long flags; + + while (1) { + + sz = smd_cur_packet_size(dev->smd_data.ch); + if (sz == 0) + break; + if (smd_read_avail(dev->smd_data.ch) < sz) + break; + + spin_lock_irqsave(&dev->lock, flags); + if (list_empty(&dev->tx_idle)) { + spin_unlock_irqrestore(&dev->lock, flags); + DBG(cdev, "rmnet data Tx buffers full\n"); + break; + } + req = list_first_entry(&dev->tx_idle, struct usb_request, list); + list_del(&req->list); + spin_unlock_irqrestore(&dev->lock, flags); + + req->length = smd_read(dev->smd_data.ch, req->buf, sz); + status = usb_ep_queue(dev->epin, req, GFP_ATOMIC); + if (status) { + ERROR(cdev, "rmnet tx data enqueue err %d\n", status); + spin_lock_irqsave(&dev->lock, flags); + list_add_tail(&req->list, &dev->tx_idle); + spin_unlock_irqrestore(&dev->lock, flags); + break; + } + spin_lock_irqsave(&dev->lock, flags); + dev->dpkts_from_modem++; + spin_unlock_irqrestore(&dev->lock, flags); + } + +} + +static void rmnet_data_rx_tlet(unsigned long arg) +{ + struct rmnet_smd_dev *dev = (struct rmnet_smd_dev *) arg; + struct usb_composite_dev *cdev = dev->cdev; + struct usb_request *req; + int ret; + unsigned long flags; + + spin_lock_irqsave(&dev->lock, flags); + while (1) { + if (list_empty(&dev->rx_queue)) { + atomic_set(&dev->smd_data.rx_pkt, 0); + break; + } + req = list_first_entry(&dev->rx_queue, + struct usb_request, list); + if (smd_write_avail(dev->smd_data.ch) < req->actual) { + atomic_set(&dev->smd_data.rx_pkt, req->actual); + DBG(cdev, "rmnet SMD data channel full\n"); + break; + } + + list_del(&req->list); + spin_unlock_irqrestore(&dev->lock, flags); + ret = smd_write(dev->smd_data.ch, req->buf, req->actual); + spin_lock_irqsave(&dev->lock, flags); + if (ret != req->actual) { + ERROR(cdev, "rmnet SMD data write failed\n"); + break; + } + dev->dpkts_to_modem++; + list_add_tail(&req->list, &dev->rx_idle); + } + spin_unlock_irqrestore(&dev->lock, flags); + + /* We have free rx data requests. */ + rmnet_smd_start_rx(dev); +} + +/* If SMD has enough room to accommodate a data rx packet, + * write into SMD directly. Otherwise enqueue to rx_queue. + * We will not write into SMD directly untill rx_queue is + * empty to strictly follow the ordering requests. + */ +static void rmnet_smd_complete_epout(struct usb_ep *ep, struct usb_request *req) +{ + struct rmnet_smd_dev *dev = req->context; + struct usb_composite_dev *cdev = dev->cdev; + int status = req->status; + int ret; + + switch (status) { + case 0: + /* normal completion */ + break; + case -ECONNRESET: + case -ESHUTDOWN: + /* connection gone */ + spin_lock(&dev->lock); + list_add_tail(&req->list, &dev->rx_idle); + spin_unlock(&dev->lock); + return; + default: + /* unexpected failure */ + ERROR(cdev, "RMNET %s response error %d, %d/%d\n", + ep->name, status, + req->actual, req->length); + spin_lock(&dev->lock); + list_add_tail(&req->list, &dev->rx_idle); + spin_unlock(&dev->lock); + return; + } + + spin_lock(&dev->lock); + dev->dpkts_from_host++; + if (!atomic_read(&dev->smd_data.rx_pkt)) { + if (smd_write_avail(dev->smd_data.ch) < req->actual) { + atomic_set(&dev->smd_data.rx_pkt, req->actual); + goto queue_req; + } + spin_unlock(&dev->lock); + ret = smd_write(dev->smd_data.ch, req->buf, req->actual); + /* This should never happen */ + if (ret != req->actual) + ERROR(cdev, "rmnet data smd write failed\n"); + /* Restart Rx */ + spin_lock(&dev->lock); + dev->dpkts_to_modem++; + list_add_tail(&req->list, &dev->rx_idle); + spin_unlock(&dev->lock); + rmnet_smd_start_rx(dev); + return; + } +queue_req: + list_add_tail(&req->list, &dev->rx_queue); + spin_unlock(&dev->lock); +} + +static void rmnet_smd_complete_epin(struct usb_ep *ep, struct usb_request *req) +{ + struct rmnet_smd_dev *dev = req->context; + struct usb_composite_dev *cdev = dev->cdev; + int status = req->status; + int schedule = 0; + + switch (status) { + case -ECONNRESET: + case -ESHUTDOWN: + /* connection gone */ + spin_lock(&dev->lock); + list_add_tail(&req->list, &dev->tx_idle); + spin_unlock(&dev->lock); + break; + default: + ERROR(cdev, "rmnet data tx ep error %d\n", status); + /* FALLTHROUGH */ + case 0: + spin_lock(&dev->lock); + if (list_empty(&dev->tx_idle)) + schedule = 1; + list_add_tail(&req->list, &dev->tx_idle); + dev->dpkts_to_host++; + if (schedule) + tasklet_schedule(&dev->smd_data.tx_tlet); + spin_unlock(&dev->lock); + break; + } + +} + +static void rmnet_smd_disconnect_work(struct work_struct *w) +{ + struct qmi_buf *qmi; + struct usb_request *req; + struct list_head *act, *tmp; + struct rmnet_smd_dev *dev = container_of(w, struct rmnet_smd_dev, + disconnect_work); + + tasklet_kill(&dev->smd_ctl.rx_tlet); + tasklet_kill(&dev->smd_ctl.tx_tlet); + tasklet_kill(&dev->smd_data.rx_tlet); + tasklet_kill(&dev->smd_data.tx_tlet); + + smd_close(dev->smd_ctl.ch); + dev->smd_ctl.flags = 0; + + smd_close(dev->smd_data.ch); + dev->smd_data.flags = 0; + + atomic_set(&dev->notify_count, 0); + + list_for_each_safe(act, tmp, &dev->rx_queue) { + req = list_entry(act, struct usb_request, list); + list_del(&req->list); + list_add_tail(&req->list, &dev->rx_idle); + } + + list_for_each_safe(act, tmp, &dev->qmi_req_q) { + qmi = list_entry(act, struct qmi_buf, list); + list_del(&qmi->list); + list_add_tail(&qmi->list, &dev->qmi_req_pool); + } + + list_for_each_safe(act, tmp, &dev->qmi_resp_q) { + qmi = list_entry(act, struct qmi_buf, list); + list_del(&qmi->list); + list_add_tail(&qmi->list, &dev->qmi_resp_pool); + } + + if (dev->is_pdrv_used) { + platform_driver_unregister(&dev->pdrv); + dev->is_pdrv_used = 0; + } +} + +/* SMD close may sleep + * schedule a work to close smd channels + */ +static void rmnet_smd_disable(struct usb_function *f) +{ + struct rmnet_smd_dev *dev = container_of(f, struct rmnet_smd_dev, + function); + + if (!atomic_read(&dev->online)) + return; + + atomic_set(&dev->online, 0); + + usb_ep_fifo_flush(dev->epnotify); + usb_ep_disable(dev->epnotify); + usb_ep_fifo_flush(dev->epout); + usb_ep_disable(dev->epout); + + usb_ep_fifo_flush(dev->epin); + usb_ep_disable(dev->epin); + + /* cleanup work */ + queue_work(dev->wq, &dev->disconnect_work); +} + +static void rmnet_smd_connect_work(struct work_struct *w) +{ + struct rmnet_smd_dev *dev = container_of(w, struct rmnet_smd_dev, + connect_work); + struct usb_composite_dev *cdev = dev->cdev; + int ret = 0; + + /* Control channel for QMI messages */ + ret = smd_open(rmnet_ctl_ch, &dev->smd_ctl.ch, + &dev->smd_ctl, rmnet_smd_event_notify); + if (ret) { + ERROR(cdev, "Unable to open control smd channel: %d\n", ret); + /* + * Register platform driver to be notified in case SMD channels + * later becomes ready to be opened. + */ + ret = platform_driver_register(&dev->pdrv); + if (ret) + ERROR(cdev, "Platform driver %s register failed %d\n", + dev->pdrv.driver.name, ret); + else + dev->is_pdrv_used = 1; + + return; + } + wait_event(dev->smd_ctl.wait, test_bit(CH_OPENED, + &dev->smd_ctl.flags)); + + /* Data channel for network packets */ + ret = smd_open(rmnet_data_ch, &dev->smd_data.ch, + &dev->smd_data, rmnet_smd_event_notify); + if (ret) { + ERROR(cdev, "Unable to open data smd channel\n"); + smd_close(dev->smd_ctl.ch); + return; + } + wait_event(dev->smd_data.wait, test_bit(CH_OPENED, + &dev->smd_data.flags)); + + atomic_set(&dev->online, 1); + /* Queue Rx data requests */ + rmnet_smd_start_rx(dev); +} + +static int rmnet_smd_ch_probe(struct platform_device *pdev) +{ + DBG(rmnet_smd->cdev, "Probe called for device: %s\n", pdev->name); + + queue_work(rmnet_smd->wq, &rmnet_smd->connect_work); + + return 0; +} + +/* SMD open may sleep. + * Schedule a work to open smd channels and enable + * endpoints if smd channels are opened successfully. + */ +static int rmnet_smd_set_alt(struct usb_function *f, + unsigned intf, unsigned alt) +{ + struct rmnet_smd_dev *dev = container_of(f, struct rmnet_smd_dev, + function); + struct usb_composite_dev *cdev = dev->cdev; + int ret = 0; + + ret = usb_ep_enable(dev->epin, ep_choose(cdev->gadget, + &rmnet_smd_hs_in_desc, + &rmnet_smd_fs_in_desc)); + if (ret) { + ERROR(cdev, "can't enable %s, result %d\n", + dev->epin->name, ret); + return ret; + } + ret = usb_ep_enable(dev->epout, ep_choose(cdev->gadget, + &rmnet_smd_hs_out_desc, + &rmnet_smd_fs_out_desc)); + if (ret) { + ERROR(cdev, "can't enable %s, result %d\n", + dev->epout->name, ret); + usb_ep_disable(dev->epin); + return ret; + } + + ret = usb_ep_enable(dev->epnotify, ep_choose(cdev->gadget, + &rmnet_smd_hs_notify_desc, + &rmnet_smd_fs_notify_desc)); + if (ret) { + ERROR(cdev, "can't enable %s, result %d\n", + dev->epnotify->name, ret); + usb_ep_disable(dev->epin); + usb_ep_disable(dev->epout); + return ret; + } + + queue_work(dev->wq, &dev->connect_work); + return 0; +} + +static void rmnet_smd_free_buf(struct rmnet_smd_dev *dev) +{ + struct qmi_buf *qmi; + struct usb_request *req; + struct list_head *act, *tmp; + + dev->dpkts_to_host = 0; + dev->dpkts_from_modem = 0; + dev->dpkts_from_host = 0; + dev->dpkts_to_modem = 0; + + dev->cpkts_to_host = 0; + dev->cpkts_from_modem = 0; + dev->cpkts_from_host = 0; + dev->cpkts_to_modem = 0; + /* free all usb requests in tx pool */ + list_for_each_safe(act, tmp, &dev->tx_idle) { + req = list_entry(act, struct usb_request, list); + list_del(&req->list); + rmnet_smd_free_req(dev->epout, req); + } + + /* free all usb requests in rx pool */ + list_for_each_safe(act, tmp, &dev->rx_idle) { + req = list_entry(act, struct usb_request, list); + list_del(&req->list); + rmnet_smd_free_req(dev->epin, req); + } + + /* free all buffers in qmi request pool */ + list_for_each_safe(act, tmp, &dev->qmi_req_pool) { + qmi = list_entry(act, struct qmi_buf, list); + list_del(&qmi->list); + rmnet_smd_free_qmi(qmi); + } + + /* free all buffers in qmi request pool */ + list_for_each_safe(act, tmp, &dev->qmi_resp_pool) { + qmi = list_entry(act, struct qmi_buf, list); + list_del(&qmi->list); + rmnet_smd_free_qmi(qmi); + } + + rmnet_smd_free_req(dev->epnotify, dev->notify_req); +} +static int rmnet_smd_bind(struct usb_configuration *c, struct usb_function *f) +{ + struct usb_composite_dev *cdev = c->cdev; + struct rmnet_smd_dev *dev = container_of(f, struct rmnet_smd_dev, + function); + int i, id, ret; + struct qmi_buf *qmi; + struct usb_request *req; + struct usb_ep *ep; + + dev->cdev = cdev; + + /* allocate interface ID */ + id = usb_interface_id(c, f); + if (id < 0) + return id; + dev->ifc_id = id; + rmnet_smd_interface_desc.bInterfaceNumber = id; + + ep = usb_ep_autoconfig(cdev->gadget, &rmnet_smd_fs_in_desc); + if (!ep) + return -ENODEV; + ep->driver_data = cdev; /* claim endpoint */ + dev->epin = ep; + + ep = usb_ep_autoconfig(cdev->gadget, &rmnet_smd_fs_out_desc); + if (!ep) + return -ENODEV; + ep->driver_data = cdev; /* claim endpoint */ + dev->epout = ep; + + ep = usb_ep_autoconfig(cdev->gadget, &rmnet_smd_fs_notify_desc); + if (!ep) + return -ENODEV; + ep->driver_data = cdev; /* clain endpoint */ + dev->epnotify = ep; + + /* support all relevant hardware speeds... we expect that when + * hardware is dual speed, all bulk-capable endpoints work at + * both speeds + */ + if (gadget_is_dualspeed(c->cdev->gadget)) { + rmnet_smd_hs_in_desc.bEndpointAddress = + rmnet_smd_fs_in_desc.bEndpointAddress; + rmnet_smd_hs_out_desc.bEndpointAddress = + rmnet_smd_fs_out_desc.bEndpointAddress; + rmnet_smd_hs_notify_desc.bEndpointAddress = + rmnet_smd_fs_notify_desc.bEndpointAddress; + + } + + /* allocate notification */ + dev->notify_req = rmnet_smd_alloc_req(dev->epnotify, + RMNET_SMD_MAX_NOTIFY_SIZE, GFP_KERNEL); + if (IS_ERR(dev->notify_req)) + return PTR_ERR(dev->notify_req); + + dev->notify_req->complete = rmnet_smd_notify_complete; + dev->notify_req->context = dev; + dev->notify_req->length = RMNET_SMD_MAX_NOTIFY_SIZE; + + /* Allocate the qmi request and response buffers */ + for (i = 0; i < QMI_REQ_MAX; i++) { + qmi = rmnet_smd_alloc_qmi(QMI_REQ_SIZE, GFP_KERNEL); + if (IS_ERR(qmi)) { + ret = PTR_ERR(qmi); + goto free_buf; + } + list_add_tail(&qmi->list, &dev->qmi_req_pool); + } + + for (i = 0; i < QMI_RESP_MAX; i++) { + qmi = rmnet_smd_alloc_qmi(QMI_RESP_SIZE, GFP_KERNEL); + if (IS_ERR(qmi)) { + ret = PTR_ERR(qmi); + goto free_buf; + } + list_add_tail(&qmi->list, &dev->qmi_resp_pool); + } + + /* Allocate bulk in/out requests for data transfer */ + for (i = 0; i < RMNET_RX_REQ_MAX; i++) { + req = rmnet_smd_alloc_req(dev->epout, RMNET_RX_REQ_SIZE, + GFP_KERNEL); + if (IS_ERR(req)) { + ret = PTR_ERR(req); + goto free_buf; + } + req->length = RMNET_TXN_MAX; + req->context = dev; + req->complete = rmnet_smd_complete_epout; + list_add_tail(&req->list, &dev->rx_idle); + } + + for (i = 0; i < RMNET_TX_REQ_MAX; i++) { + req = rmnet_smd_alloc_req(dev->epin, RMNET_TX_REQ_SIZE, + GFP_KERNEL); + if (IS_ERR(req)) { + ret = PTR_ERR(req); + goto free_buf; + } + req->context = dev; + req->complete = rmnet_smd_complete_epin; + list_add_tail(&req->list, &dev->tx_idle); + } + + return 0; + +free_buf: + rmnet_smd_free_buf(dev); + dev->epout = dev->epin = dev->epnotify = NULL; /* release endpoints */ + return ret; +} + +#if defined(CONFIG_DEBUG_FS) +static ssize_t rmnet_smd_debug_read_stats(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct rmnet_smd_dev *dev = file->private_data; + struct rmnet_smd_ch_info smd_ctl_info = dev->smd_ctl; + struct rmnet_smd_ch_info smd_data_info = dev->smd_data; + char *buf; + unsigned long flags; + int ret; + + buf = kzalloc(sizeof(char) * 512, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + spin_lock_irqsave(&dev->lock, flags); + ret = scnprintf(buf, 512, + "smd_control_ch_opened: %lu\n" + "smd_data_ch_opened: %lu\n" + "usb online : %d\n" + "dpkts_from_modem: %lu\n" + "dpkts_to_host: %lu\n" + "pending_dpkts_to_host: %lu\n" + "dpkts_from_host: %lu\n" + "dpkts_to_modem: %lu\n" + "pending_dpkts_to_modem: %lu\n" + "cpkts_from_modem: %lu\n" + "cpkts_to_host: %lu\n" + "pending_cpkts_to_host: %lu\n" + "cpkts_from_host: %lu\n" + "cpkts_to_modem: %lu\n" + "pending_cpkts_to_modem: %lu\n" + "smd_read_avail_ctrl: %d\n" + "smd_write_avail_ctrl: %d\n" + "smd_read_avail_data: %d\n" + "smd_write_avail_data: %d\n", + smd_ctl_info.flags, smd_data_info.flags, + atomic_read(&dev->online), + dev->dpkts_from_modem, dev->dpkts_to_host, + (dev->dpkts_from_modem - dev->dpkts_to_host), + dev->dpkts_from_host, dev->dpkts_to_modem, + (dev->dpkts_from_host - dev->dpkts_to_modem), + dev->cpkts_from_modem, dev->cpkts_to_host, + (dev->cpkts_from_modem - dev->cpkts_to_host), + dev->cpkts_from_host, dev->cpkts_to_modem, + (dev->cpkts_from_host - dev->cpkts_to_modem), + smd_read_avail(dev->smd_ctl.ch), + smd_write_avail(dev->smd_ctl.ch), + smd_read_avail(dev->smd_data.ch), + smd_write_avail(dev->smd_data.ch)); + + spin_unlock_irqrestore(&dev->lock, flags); + + ret = simple_read_from_buffer(ubuf, count, ppos, buf, ret); + + kfree(buf); + + return ret; +} + +static ssize_t rmnet_smd_debug_reset_stats(struct file *file, + const char __user *buf, + size_t count, loff_t *ppos) +{ + struct rmnet_smd_dev *dev = file->private_data; + unsigned long flags; + + spin_lock_irqsave(&dev->lock, flags); + + dev->dpkts_to_host = 0; + dev->dpkts_from_modem = 0; + dev->dpkts_from_host = 0; + dev->dpkts_to_modem = 0; + + dev->cpkts_to_host = 0; + dev->cpkts_from_modem = 0; + dev->cpkts_from_host = 0; + dev->cpkts_to_modem = 0; + + spin_unlock_irqrestore(&dev->lock, flags); + + return count; +} + +static int rmnet_smd_debug_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + + return 0; +} + +const struct file_operations rmnet_smd_debug_stats_ops = { + .open = rmnet_smd_debug_open, + .read = rmnet_smd_debug_read_stats, + .write = rmnet_smd_debug_reset_stats, +}; + +struct dentry *dent_smd; +struct dentry *dent_smd_status; + +static void rmnet_smd_debugfs_init(struct rmnet_smd_dev *dev) +{ + + dent_smd = debugfs_create_dir("usb_rmnet_smd", 0); + if (IS_ERR(dent_smd)) + return; + + dent_smd_status = debugfs_create_file("status", 0444, dent_smd, dev, + &rmnet_smd_debug_stats_ops); + + if (!dent_smd_status) { + debugfs_remove(dent_smd); + dent_smd = NULL; + return; + } + + return; +} +#else +static void rmnet_smd_debugfs_init(struct rmnet_smd_dev *dev) {} +#endif + +static void +rmnet_smd_unbind(struct usb_configuration *c, struct usb_function *f) +{ + struct rmnet_smd_dev *dev = container_of(f, struct rmnet_smd_dev, + function); + + tasklet_kill(&dev->smd_ctl.rx_tlet); + tasklet_kill(&dev->smd_ctl.tx_tlet); + tasklet_kill(&dev->smd_data.rx_tlet); + tasklet_kill(&dev->smd_data.tx_tlet); + + flush_workqueue(dev->wq); + rmnet_smd_free_buf(dev); + dev->epout = dev->epin = dev->epnotify = NULL; /* release endpoints */ + + destroy_workqueue(dev->wq); + debugfs_remove_recursive(dent_smd); + kfree(dev); + +} + +int rmnet_smd_bind_config(struct usb_configuration *c) +{ + struct rmnet_smd_dev *dev; + int ret; + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + rmnet_smd = dev; + + dev->wq = create_singlethread_workqueue("k_rmnet_work"); + if (!dev->wq) { + ret = -ENOMEM; + goto free_dev; + } + + spin_lock_init(&dev->lock); + atomic_set(&dev->notify_count, 0); + atomic_set(&dev->online, 0); + atomic_set(&dev->smd_ctl.rx_pkt, 0); + atomic_set(&dev->smd_data.rx_pkt, 0); + + INIT_WORK(&dev->connect_work, rmnet_smd_connect_work); + INIT_WORK(&dev->disconnect_work, rmnet_smd_disconnect_work); + + tasklet_init(&dev->smd_ctl.rx_tlet, rmnet_control_rx_tlet, + (unsigned long) dev); + tasklet_init(&dev->smd_ctl.tx_tlet, rmnet_control_tx_tlet, + (unsigned long) dev); + tasklet_init(&dev->smd_data.rx_tlet, rmnet_data_rx_tlet, + (unsigned long) dev); + tasklet_init(&dev->smd_data.tx_tlet, rmnet_data_tx_tlet, + (unsigned long) dev); + + init_waitqueue_head(&dev->smd_ctl.wait); + init_waitqueue_head(&dev->smd_data.wait); + + dev->pdrv.probe = rmnet_smd_ch_probe; + dev->pdrv.driver.name = CONFIG_RMNET_SMD_CTL_CHANNEL; + dev->pdrv.driver.owner = THIS_MODULE; + + INIT_LIST_HEAD(&dev->qmi_req_pool); + INIT_LIST_HEAD(&dev->qmi_req_q); + INIT_LIST_HEAD(&dev->qmi_resp_pool); + INIT_LIST_HEAD(&dev->qmi_resp_q); + INIT_LIST_HEAD(&dev->rx_idle); + INIT_LIST_HEAD(&dev->rx_queue); + INIT_LIST_HEAD(&dev->tx_idle); + + dev->function.name = "rmnet"; + dev->function.strings = rmnet_smd_strings; + dev->function.descriptors = rmnet_smd_fs_function; + dev->function.hs_descriptors = rmnet_smd_hs_function; + dev->function.bind = rmnet_smd_bind; + dev->function.unbind = rmnet_smd_unbind; + dev->function.setup = rmnet_smd_setup; + dev->function.set_alt = rmnet_smd_set_alt; + dev->function.disable = rmnet_smd_disable; + + ret = usb_add_function(c, &dev->function); + if (ret) + goto free_wq; + + rmnet_smd_debugfs_init(dev); + + return 0; + +free_wq: + destroy_workqueue(dev->wq); +free_dev: + kfree(dev); + + return ret; +} diff --git a/drivers/usb/gadget/f_rmnet_smd_sdio.c b/drivers/usb/gadget/f_rmnet_smd_sdio.c index 0cac681b567..e39125d5761 100644 --- a/drivers/usb/gadget/f_rmnet_smd_sdio.c +++ b/drivers/usb/gadget/f_rmnet_smd_sdio.c @@ -42,6 +42,7 @@ #include #include #include +#include #ifdef CONFIG_RMNET_SMD_SDIO_CTL_CHANNEL static uint32_t rmnet_mux_sdio_ctl_ch = CONFIG_RMNET_SMD_SDIO_CTL_CHANNEL; @@ -104,12 +105,6 @@ struct rmnet_mux_ctrl_pkt { struct list_head list; }; -enum usb_rmnet_mux_xport_type { - USB_RMNET_MUX_XPORT_UNDEFINED, - USB_RMNET_MUX_XPORT_SDIO, - USB_RMNET_MUX_XPORT_SMD, -}; - struct rmnet_mux_ctrl_dev { struct list_head tx_q; wait_queue_head_t tx_wait_q; @@ -176,7 +171,7 @@ struct rmnet_mux_dev { struct rmnet_mux_ctrl_dev ctrl_dev; u8 ifc_id; - enum usb_rmnet_mux_xport_type xport; + enum transport_type xport; spinlock_t lock; atomic_t online; atomic_t notify_count; @@ -291,18 +286,6 @@ static struct usb_gadget_strings *rmnet_mux_strings[] = { NULL, }; -static char *xport_to_str(enum usb_rmnet_mux_xport_type t) -{ - switch (t) { - case USB_RMNET_MUX_XPORT_SDIO: - return "SDIO"; - case USB_RMNET_MUX_XPORT_SMD: - return "SMD"; - default: - return "UNDEFINED"; - } -} - static struct rmnet_mux_ctrl_pkt *rmnet_mux_alloc_ctrl_pkt(unsigned len, gfp_t flags) { @@ -556,7 +539,7 @@ rmnet_mux_sdio_complete_epout(struct usb_ep *ep, struct usb_request *req) int status = req->status; int queue = 0; - if (dev->xport == USB_RMNET_MUX_XPORT_UNDEFINED) { + if (dev->xport == USB_GADGET_XPORT_UNDEF) { dev_kfree_skb_any(skb); req->buf = 0; rmnet_mux_free_req(ep, req); @@ -614,7 +597,7 @@ rmnet_mux_sdio_complete_epin(struct usb_ep *ep, struct usb_request *req) struct usb_composite_dev *cdev = dev->cdev; int status = req->status; - if (dev->xport == USB_RMNET_MUX_XPORT_UNDEFINED) { + if (dev->xport == USB_GADGET_XPORT_UNDEF) { dev_kfree_skb_any(skb); req->buf = 0; rmnet_mux_free_req(ep, req); @@ -797,7 +780,7 @@ rmnet_mux_smd_complete_epout(struct usb_ep *ep, struct usb_request *req) int status = req->status; int ret; - if (dev->xport == USB_RMNET_MUX_XPORT_UNDEFINED) { + if (dev->xport == USB_GADGET_XPORT_UNDEF) { rmnet_mux_free_req(ep, req); return; } @@ -857,7 +840,7 @@ static void rmnet_mux_smd_complete_epin(struct usb_ep *ep, int status = req->status; int schedule = 0; - if (dev->xport == USB_RMNET_MUX_XPORT_UNDEFINED) { + if (dev->xport == USB_GADGET_XPORT_UNDEF) { rmnet_mux_free_req(ep, req); return; } @@ -1209,7 +1192,7 @@ static void rmnet_mux_free_buf(struct rmnet_mux_dev *dev) req = list_first_entry(pool, struct usb_request, list); list_del(&req->list); req->buf = NULL; - rmnet_mux_free_req(dev->epin, req); + rmnet_mux_free_req(dev->epout, req); } pool = &sdio_dev->rx_idle; @@ -1218,7 +1201,7 @@ static void rmnet_mux_free_buf(struct rmnet_mux_dev *dev) req = list_first_entry(pool, struct usb_request, list); list_del(&req->list); req->buf = NULL; - rmnet_mux_free_req(dev->epout, req); + rmnet_mux_free_req(dev->epin, req); } while ((skb = __skb_dequeue(&sdio_dev->tx_skb_queue))) @@ -1232,7 +1215,7 @@ static void rmnet_mux_free_buf(struct rmnet_mux_dev *dev) while (!list_empty(pool)) { req = list_first_entry(pool, struct usb_request, list); list_del(&req->list); - rmnet_mux_free_req(dev->epin, req); + rmnet_mux_free_req(dev->epout, req); } pool = &smd_dev->rx_idle; @@ -1240,7 +1223,7 @@ static void rmnet_mux_free_buf(struct rmnet_mux_dev *dev) while (!list_empty(pool)) { req = list_first_entry(pool, struct usb_request, list); list_del(&req->list); - rmnet_mux_free_req(dev->epout, req); + rmnet_mux_free_req(dev->epin, req); } /* free all usb requests in SMD rx queue */ @@ -1248,7 +1231,7 @@ static void rmnet_mux_free_buf(struct rmnet_mux_dev *dev) while (!list_empty(pool)) { req = list_first_entry(pool, struct usb_request, list); list_del(&req->list); - rmnet_mux_free_req(dev->epout, req); + rmnet_mux_free_req(dev->epin, req); } pool = &ctrl_dev->tx_q; @@ -1276,7 +1259,7 @@ static void rmnet_mux_disconnect_work(struct work_struct *w) struct rmnet_mux_smd_dev *smd_dev = &dev->smd_dev; struct rmnet_mux_ctrl_dev *ctrl_dev = &dev->ctrl_dev; - if (dev->xport == USB_RMNET_MUX_XPORT_SMD) { + if (dev->xport == USB_GADGET_XPORT_SMD) { tasklet_kill(&smd_dev->smd_data.rx_tlet); tasklet_kill(&smd_dev->smd_data.tx_tlet); } @@ -1414,8 +1397,8 @@ static ssize_t transport_store( { struct rmnet_mux_dev *dev = rmux_dev; int value; - enum usb_rmnet_mux_xport_type given_xport; - enum usb_rmnet_mux_xport_type t; + enum transport_type given_xport; + enum transport_type t; struct rmnet_mux_smd_dev *smd_dev = &dev->smd_dev; struct rmnet_mux_sdio_dev *sdio_dev = &dev->sdio_dev; struct list_head *pool; @@ -1425,15 +1408,15 @@ static ssize_t transport_store( unsigned long flags; if (!atomic_read(&dev->online)) { - pr_debug("%s: usb cable is not connected\n", __func__); + pr_err("%s: usb cable is not connected\n", __func__); return -EINVAL; } sscanf(buf, "%d", &value); if (value) - given_xport = USB_RMNET_MUX_XPORT_SDIO; + given_xport = USB_GADGET_XPORT_SDIO; else - given_xport = USB_RMNET_MUX_XPORT_SMD; + given_xport = USB_GADGET_XPORT_SMD; if (given_xport == dev->xport) { pr_err("%s: given_xport:%s cur_xport:%s doing nothing\n", @@ -1447,14 +1430,14 @@ static ssize_t transport_store( /* prevent any other pkts to/from usb */ t = dev->xport; - dev->xport = USB_RMNET_MUX_XPORT_UNDEFINED; - if (t != USB_RMNET_MUX_XPORT_UNDEFINED) { + dev->xport = USB_GADGET_XPORT_UNDEF; + if (t != USB_GADGET_XPORT_UNDEF) { usb_ep_fifo_flush(dev->epin); usb_ep_fifo_flush(dev->epout); } switch (t) { - case USB_RMNET_MUX_XPORT_SDIO: + case USB_GADGET_XPORT_SDIO: spin_lock_irqsave(&dev->lock, flags); /* tx_idle */ @@ -1465,7 +1448,7 @@ static ssize_t transport_store( req = list_first_entry(pool, struct usb_request, list); list_del(&req->list); req->buf = NULL; - rmnet_mux_free_req(dev->epin, req); + rmnet_mux_free_req(dev->epout, req); } /* rx_idle */ @@ -1475,7 +1458,7 @@ static ssize_t transport_store( req = list_first_entry(pool, struct usb_request, list); list_del(&req->list); req->buf = NULL; - rmnet_mux_free_req(dev->epout, req); + rmnet_mux_free_req(dev->epin, req); } /* tx_skb_queue */ @@ -1489,7 +1472,7 @@ static ssize_t transport_store( spin_unlock_irqrestore(&dev->lock, flags); break; - case USB_RMNET_MUX_XPORT_SMD: + case USB_GADGET_XPORT_SMD: /* close smd xport */ tasklet_kill(&smd_dev->smd_data.rx_tlet); tasklet_kill(&smd_dev->smd_data.tx_tlet); @@ -1500,7 +1483,7 @@ static ssize_t transport_store( while (!list_empty(pool)) { req = list_first_entry(pool, struct usb_request, list); list_del(&req->list); - rmnet_mux_free_req(dev->epin, req); + rmnet_mux_free_req(dev->epout, req); } pool = &smd_dev->rx_idle; @@ -1508,7 +1491,7 @@ static ssize_t transport_store( while (!list_empty(pool)) { req = list_first_entry(pool, struct usb_request, list); list_del(&req->list); - rmnet_mux_free_req(dev->epout, req); + rmnet_mux_free_req(dev->epin, req); } /* free all usb requests in SMD rx queue */ @@ -1516,7 +1499,7 @@ static ssize_t transport_store( while (!list_empty(pool)) { req = list_first_entry(pool, struct usb_request, list); list_del(&req->list); - rmnet_mux_free_req(dev->epout, req); + rmnet_mux_free_req(dev->epin, req); } spin_unlock_irqrestore(&dev->lock, flags); @@ -1528,10 +1511,10 @@ static ssize_t transport_store( dev->xport = given_xport; switch (dev->xport) { - case USB_RMNET_MUX_XPORT_SDIO: + case USB_GADGET_XPORT_SDIO: rmnet_mux_sdio_enable(dev); break; - case USB_RMNET_MUX_XPORT_SMD: + case USB_GADGET_XPORT_SMD: rmnet_mux_smd_enable(dev); break; default: @@ -1640,14 +1623,11 @@ static void rmnet_mux_sdio_init(struct rmnet_mux_sdio_dev *sdio_dev) static void rmnet_mux_unbind(struct usb_configuration *c, struct usb_function *f) { -/* Do not clear flags to avoid SMD open status mismatch */ -#if 0 struct rmnet_mux_dev *dev = container_of(f, struct rmnet_mux_dev, function); struct rmnet_mux_smd_dev *smd_dev = &dev->smd_dev; smd_dev->smd_data.flags = 0; -#endif } #if defined(CONFIG_DEBUG_FS) @@ -1947,7 +1927,6 @@ static int rmnet_mux_ctrl_device_init(struct rmnet_mux_dev *dev) static int rmnet_smd_sdio_function_add(struct usb_configuration *c) { struct rmnet_mux_dev *dev = rmux_dev; - int status; if (!dev) return -ENODEV; @@ -1965,16 +1944,6 @@ static int rmnet_smd_sdio_function_add(struct usb_configuration *c) dev->function.disable = rmnet_mux_disable; dev->function.suspend = rmnet_mux_suspend; - if (rmnet_mux_string_defs[0].id == 0) { - status = usb_string_id(c->cdev); - if (status < 0) { - printk(KERN_ERR "%s: return %d\n", __func__, status); - return status; - } - rmnet_mux_string_defs[0].id = status; - rmnet_mux_interface_desc.iInterface = status; - } - return usb_add_function(c, &dev->function); } diff --git a/drivers/usb/gadget/f_rndis.c b/drivers/usb/gadget/f_rndis.c index 7a184326bb1..259d7f3509b 100644 --- a/drivers/usb/gadget/f_rndis.c +++ b/drivers/usb/gadget/f_rndis.c @@ -98,8 +98,6 @@ struct f_rndis { struct usb_endpoint_descriptor *notify_desc; struct usb_request *notify_req; atomic_t notify_count; - - atomic_t online; }; static inline struct f_rndis *func_to_rndis(struct usb_function *f) @@ -391,24 +389,29 @@ static void rndis_response_complete(struct usb_ep *ep, struct usb_request *req) static void rndis_command_complete(struct usb_ep *ep, struct usb_request *req) { struct f_rndis *rndis = req->context; + struct usb_composite_dev *cdev = rndis->port.func.config->cdev; int status; - - if (req->status < 0) { - pr_err("%s: staus error: %d\n", __func__, req->status); - return; - } - - if (!atomic_read(&rndis->online)) { - pr_warning("%s: usb rndis is not online\n", __func__); - return; - } + rndis_init_msg_type *buf; /* received RNDIS command from USB_CDC_SEND_ENCAPSULATED_COMMAND */ // spin_lock(&dev->lock); status = rndis_msg_parser(rndis->config, (u8 *) req->buf); if (status < 0) - pr_err("[USB] RNDIS command error %d, %d/%d\n", + ERROR(cdev, "RNDIS command error %d, %d/%d\n", status, req->actual, req->length); + + buf = (rndis_init_msg_type *)req->buf; + + if (buf->MessageType == REMOTE_NDIS_INITIALIZE_MSG) { + if (buf->MaxTransferSize > 2048) + rndis->port.multi_pkt_xfer = 1; + else + rndis->port.multi_pkt_xfer = 0; + DBG(cdev, "%s: MaxTransferSize: %d : Multi_pkt_txr: %s\n", + __func__, buf->MaxTransferSize, + rndis->port.multi_pkt_xfer ? "enabled" : + "disabled"); + } // spin_unlock(&dev->lock); } @@ -423,12 +426,6 @@ rndis_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) u16 w_value = le16_to_cpu(ctrl->wValue); u16 w_length = le16_to_cpu(ctrl->wLength); - if (!atomic_read(&rndis->online)) { - pr_warning("%s: usb rndis is not online\n", __func__); - return -ENOTCONN; - } - - /* composite driver infrastructure handles everything except * CDC class messages; interface activation uses set_alt(). */ @@ -555,7 +552,6 @@ static int rndis_set_alt(struct usb_function *f, unsigned intf, unsigned alt) } else goto fail; - atomic_set(&rndis->online, 1); return 0; fail: return -EINVAL; @@ -566,8 +562,6 @@ static void rndis_disable(struct usb_function *f) struct f_rndis *rndis = func_to_rndis(f); struct usb_composite_dev *cdev = f->config->cdev; - atomic_set(&rndis->online, 0); - if (!rndis->notify->driver_data) return; @@ -775,6 +769,8 @@ rndis_unbind(struct usb_configuration *c, struct usb_function *f) rndis_deregister(rndis->config); rndis_exit(); + rndis_string_defs[0].id = 0; + if (gadget_is_dualspeed(c->cdev->gadget)) usb_free_descriptors(f->hs_descriptors); usb_free_descriptors(f->descriptors); @@ -814,14 +810,14 @@ rndis_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], if (!can_support_rndis(c) || !ethaddr) return -EINVAL; - /* setup RNDIS itself */ - status = rndis_init(); - if (status < 0) - return status; - /* maybe allocate device-global string IDs */ if (rndis_string_defs[0].id == 0) { + /* ... and setup RNDIS itself */ + status = rndis_init(); + if (status < 0) + return status; + /* control interface label */ status = usb_string_id(c->cdev); if (status < 0) diff --git a/drivers/usb/gadget/f_serial.c b/drivers/usb/gadget/f_serial.c index 52b974344cf..de8c8ed51b3 100644 --- a/drivers/usb/gadget/f_serial.c +++ b/drivers/usb/gadget/f_serial.c @@ -13,7 +13,7 @@ #include #include #include -#include +#include #include "u_serial.h" #include "gadget_chips.h" @@ -27,6 +27,7 @@ * CDC ACM driver. However, for many purposes it's just as functional * if you can arrange appropriate host side drivers. */ +#define GSERIAL_NO_PORTS 2 struct gser_descs { struct usb_endpoint_descriptor *in; @@ -75,18 +76,18 @@ struct f_gser { static unsigned int no_tty_ports; static unsigned int no_sdio_ports; static unsigned int no_smd_ports; +static unsigned int no_hsic_sports; static unsigned int nr_ports; static struct port_info { enum transport_type transport; - enum fserial_func_type func_type; unsigned port_num; unsigned client_port_num; } gserial_ports[GSERIAL_NO_PORTS]; static inline bool is_transport_sdio(enum transport_type t) { - if (t == USB_GADGET_FSERIAL_TRANSPORT_SDIO) + if (t == USB_GADGET_XPORT_SDIO) return 1; return 0; } @@ -183,14 +184,10 @@ static struct usb_endpoint_descriptor gser_fs_out_desc = { static struct usb_descriptor_header *gser_fs_function[] = { (struct usb_descriptor_header *) &gser_interface_desc, #ifdef CONFIG_MODEM_SUPPORT -/* These descriptors may not be recognized by some OS. Mark it. - */ -/* (struct usb_descriptor_header *) &gser_header_desc, (struct usb_descriptor_header *) &gser_call_mgmt_descriptor, (struct usb_descriptor_header *) &gser_descriptor, (struct usb_descriptor_header *) &gser_union_desc, -*/ (struct usb_descriptor_header *) &gser_fs_notify_desc, #endif (struct usb_descriptor_header *) &gser_fs_in_desc, @@ -227,14 +224,10 @@ static struct usb_endpoint_descriptor gser_hs_out_desc = { static struct usb_descriptor_header *gser_hs_function[] = { (struct usb_descriptor_header *) &gser_interface_desc, #ifdef CONFIG_MODEM_SUPPORT -/* These descriptors may not be recognized by some OS. Mark it. - */ -/* (struct usb_descriptor_header *) &gser_header_desc, (struct usb_descriptor_header *) &gser_call_mgmt_descriptor, (struct usb_descriptor_header *) &gser_descriptor, (struct usb_descriptor_header *) &gser_union_desc, -*/ (struct usb_descriptor_header *) &gser_hs_notify_desc, #endif (struct usb_descriptor_header *) &gser_hs_in_desc, @@ -243,24 +236,9 @@ static struct usb_descriptor_header *gser_hs_function[] = { }; /* string descriptors: */ -static struct usb_string modem_string_defs[] = { - [0].s = "HTC Modem", - [1].s = "HTC 9k Modem", - { } /* end of list */ -}; - -static struct usb_gadget_strings modem_string_table = { - .language = 0x0409, /* en-us */ - .strings = modem_string_defs, -}; - -static struct usb_gadget_strings *modem_strings[] = { - &modem_string_table, - NULL, -}; static struct usb_string gser_string_defs[] = { - [0].s = "HTC Serial", + [0].s = "Generic Serial", { } /* end of list */ }; @@ -274,51 +252,16 @@ static struct usb_gadget_strings *gser_strings[] = { NULL, }; -static char *transport_to_str(enum transport_type t) -{ - switch (t) { - case USB_GADGET_FSERIAL_TRANSPORT_TTY: - return "TTY"; - case USB_GADGET_FSERIAL_TRANSPORT_SDIO: - return "SDIO"; - case USB_GADGET_FSERIAL_TRANSPORT_SMD: - return "SMD"; - } - - return "NONE"; -} - -static enum transport_type serial_str_to_transport(const char *name) -{ - if (!strcasecmp("SDIO", name)) - return USB_GADGET_FSERIAL_TRANSPORT_SDIO; - if (!strcasecmp("SMD", name)) - return USB_GADGET_FSERIAL_TRANSPORT_SMD; - - return USB_GADGET_FSERIAL_TRANSPORT_TTY; -} - -static enum fserial_func_type serial_str_to_func_type(const char *name) -{ - if (!name) - return USB_FSER_FUNC_NONE; - - if (!strcasecmp("MODEM", name)) - return USB_FSER_FUNC_MODEM; - if (!strcasecmp("MODEM_MDM", name)) - return USB_FSER_FUNC_MODEM_MDM; - if (!strcasecmp("SERIAL", name)) - return USB_FSER_FUNC_SERIAL; - - return USB_FSER_FUNC_NONE; -} - static int gport_setup(struct usb_configuration *c) { int ret = 0; + int port_idx; + int i; - pr_debug("%s: no_tty_ports:%u no_sdio_ports: %u nr_ports:%u\n", - __func__, no_tty_ports, no_sdio_ports, nr_ports); + pr_debug("%s: no_tty_ports: %u no_sdio_ports: %u" + " no_smd_ports: %u no_hsic_sports: %u nr_ports: %u\n", + __func__, no_tty_ports, no_sdio_ports, no_smd_ports, + no_hsic_sports, nr_ports); if (no_tty_ports) ret = gserial_setup(c->cdev->gadget, no_tty_ports); @@ -326,33 +269,67 @@ static int gport_setup(struct usb_configuration *c) ret = gsdio_setup(c->cdev->gadget, no_sdio_ports); if (no_smd_ports) ret = gsmd_setup(c->cdev->gadget, no_smd_ports); + if (no_hsic_sports) { + port_idx = ghsic_data_setup(no_hsic_sports, USB_GADGET_SERIAL); + if (port_idx < 0) + return port_idx; + + for (i = 0; i < nr_ports; i++) { + if (gserial_ports[i].transport == + USB_GADGET_XPORT_HSIC) { + gserial_ports[i].client_port_num = port_idx; + port_idx++; + } + } + /*clinet port num is same for data setup and ctrl setup*/ + ret = ghsic_ctrl_setup(no_hsic_sports, USB_GADGET_SERIAL); + if (ret < 0) + return ret; + return 0; + } return ret; } static int gport_connect(struct f_gser *gser) { - unsigned port_num; + unsigned port_num; + int ret; - pr_debug("%s: transport:%s f_gser:%p gserial:%p port_num:%d\n", - __func__, transport_to_str(gser->transport), + pr_debug("%s: transport: %s f_gser: %p gserial: %p port_num: %d\n", + __func__, xport_to_str(gser->transport), gser, &gser->port, gser->port_num); port_num = gserial_ports[gser->port_num].client_port_num; switch (gser->transport) { - case USB_GADGET_FSERIAL_TRANSPORT_TTY: + case USB_GADGET_XPORT_TTY: gserial_connect(&gser->port, port_num); break; - case USB_GADGET_FSERIAL_TRANSPORT_SDIO: + case USB_GADGET_XPORT_SDIO: gsdio_connect(&gser->port, port_num); break; - case USB_GADGET_FSERIAL_TRANSPORT_SMD: + case USB_GADGET_XPORT_SMD: gsmd_connect(&gser->port, port_num); break; + case USB_GADGET_XPORT_HSIC: + ret = ghsic_ctrl_connect(&gser->port, port_num); + if (ret) { + pr_err("%s: ghsic_ctrl_connect failed: err:%d\n", + __func__, ret); + return ret; + } + ret = ghsic_data_connect(&gser->port, port_num); + if (ret) { + pr_err("%s: ghsic_data_connect failed: err:%d\n", + __func__, ret); + ghsic_ctrl_disconnect(&gser->port, port_num); + return ret; + } + break; default: pr_err("%s: Un-supported transport: %s\n", __func__, - transport_to_str(gser->transport)); + xport_to_str(gser->transport)); return -ENODEV; } @@ -363,25 +340,29 @@ static int gport_disconnect(struct f_gser *gser) { unsigned port_num; - pr_debug("%s: transport:%s f_gser:%p gserial:%p port_num:%d\n", - __func__, transport_to_str(gser->transport), + pr_debug("%s: transport: %s f_gser: %p gserial: %p port_num: %d\n", + __func__, xport_to_str(gser->transport), gser, &gser->port, gser->port_num); port_num = gserial_ports[gser->port_num].client_port_num; switch (gser->transport) { - case USB_GADGET_FSERIAL_TRANSPORT_TTY: + case USB_GADGET_XPORT_TTY: gserial_disconnect(&gser->port); break; - case USB_GADGET_FSERIAL_TRANSPORT_SDIO: + case USB_GADGET_XPORT_SDIO: gsdio_disconnect(&gser->port, port_num); break; - case USB_GADGET_FSERIAL_TRANSPORT_SMD: + case USB_GADGET_XPORT_SMD: gsmd_disconnect(&gser->port, port_num); break; + case USB_GADGET_XPORT_HSIC: + ghsic_ctrl_disconnect(&gser->port, port_num); + ghsic_data_disconnect(&gser->port, port_num); + break; default: pr_err("%s: Un-supported transport:%s\n", __func__, - transport_to_str(gser->transport)); + xport_to_str(gser->transport)); return -ENODEV; } @@ -862,59 +843,16 @@ int gser_bind_config(struct usb_configuration *c, u8 port_num) { struct f_gser *gser; int status; - struct port_info *p = &gserial_ports[port_num]; - - if (p->func_type == USB_FSER_FUNC_NONE) { - pr_info("%s: non function port : %d\n", __func__, port_num); - return 0; - } - pr_info("%s: type:%d, trasport: %s\n", __func__, p->func_type, - transport_to_str(p->transport)); /* REVISIT might want instance-specific strings to help * distinguish instances ... */ /* maybe allocate device-global string ID */ - /* HTC modem port_num is 0 */ -#if 0 - if (port_num != 0) { - if (gser_string_defs[0].id == 0) { - status = usb_string_id(c->cdev); - if (status < 0) - return status; - gser_string_defs[0].id = status; - } - } -#endif - - if (modem_string_defs[0].id == 0 && - p->func_type == USB_FSER_FUNC_MODEM) { - status = usb_string_id(c->cdev); - if (status < 0) { - printk(KERN_ERR "%s: return %d\n", __func__, status); - return status; - } - modem_string_defs[0].id = status; - } - - if (modem_string_defs[1].id == 0 && - p->func_type == USB_FSER_FUNC_MODEM_MDM) { - status = usb_string_id(c->cdev); - if (status < 0) { - printk(KERN_ERR "%s: return %d\n", __func__, status); - return status; - } - modem_string_defs[1].id = status; - } - - if (gser_string_defs[0].id == 0 && - p->func_type == USB_FSER_FUNC_SERIAL) { + if (gser_string_defs[0].id == 0) { status = usb_string_id(c->cdev); - if (status < 0) { - printk(KERN_ERR "%s: return %d\n", __func__, status); + if (status < 0) return status; - } gser_string_defs[0].id = status; } @@ -937,12 +875,10 @@ int gser_bind_config(struct usb_configuration *c, u8 port_num) gser->transport = gserial_ports[port_num].transport; #ifdef CONFIG_MODEM_SUPPORT /* We support only two ports for now */ - if (port_num == 0) { + if (port_num == 0) gser->port.func.name = "modem"; - } else + else gser->port.func.name = "nmea"; - - gser->port.func.setup = gser_setup; gser->port.connect = gser_connect; gser->port.get_dtr = gser_get_dtr; @@ -954,28 +890,6 @@ int gser_bind_config(struct usb_configuration *c, u8 port_num) gser->port.send_break = gser_send_break; #endif - switch (p->func_type) { - case USB_FSER_FUNC_MODEM: - gser->port.func.name = "modem"; - gser->port.func.strings = modem_strings; - gser_interface_desc.iInterface = modem_string_defs[0].id; - break; - case USB_FSER_FUNC_MODEM_MDM: - gser->port.func.name = "modem_mdm"; - gser->port.func.strings = modem_strings; - gser_interface_desc.iInterface = modem_string_defs[1].id; - break; - case USB_FSER_FUNC_SERIAL: - gser->port.func.name = "serial"; - gser->port.func.strings = gser_strings; - gser_interface_desc.iInterface = gser_string_defs[0].id; - break; - case USB_FSER_FUNC_NONE: - default : - break; - } - - status = usb_add_function(c, &gser->port.func); if (status) kfree(gser); @@ -985,39 +899,37 @@ int gser_bind_config(struct usb_configuration *c, u8 port_num) /** * gserial_init_port - bind a gserial_port to its transport */ -static int gserial_init_port(int port_num, const char *name, char *serial_type) +static int gserial_init_port(int port_num, const char *name) { enum transport_type transport; - enum fserial_func_type func_type; if (port_num >= GSERIAL_NO_PORTS) return -ENODEV; - transport = serial_str_to_transport(name); - func_type = serial_str_to_func_type(serial_type); - - pr_info("%s, port:%d, transport:%s, type:%d\n", __func__, - port_num, transport_to_str(transport), func_type); - - + transport = str_to_xport(name); + pr_debug("%s, port:%d, transport:%s\n", __func__, + port_num, xport_to_str(transport)); gserial_ports[port_num].transport = transport; - gserial_ports[port_num].func_type = func_type; gserial_ports[port_num].port_num = port_num; switch (transport) { - case USB_GADGET_FSERIAL_TRANSPORT_TTY: + case USB_GADGET_XPORT_TTY: gserial_ports[port_num].client_port_num = no_tty_ports; no_tty_ports++; break; - case USB_GADGET_FSERIAL_TRANSPORT_SDIO: + case USB_GADGET_XPORT_SDIO: gserial_ports[port_num].client_port_num = no_sdio_ports; no_sdio_ports++; break; - case USB_GADGET_FSERIAL_TRANSPORT_SMD: + case USB_GADGET_XPORT_SMD: gserial_ports[port_num].client_port_num = no_smd_ports; no_smd_ports++; break; + case USB_GADGET_XPORT_HSIC: + /*client port number will be updated in gport_setup*/ + no_hsic_sports++; + break; default: pr_err("%s: Un-supported transport transport: %u\n", __func__, gserial_ports[port_num].transport); diff --git a/drivers/usb/gadget/msm72k_udc.c b/drivers/usb/gadget/msm72k_udc.c new file mode 100644 index 00000000000..43b1c544d6c --- /dev/null +++ b/drivers/usb/gadget/msm72k_udc.c @@ -0,0 +1,2793 @@ +/* + * Driver for HighSpeed USB Client Controller in MSM7K + * + * Copyright (C) 2008 Google, Inc. + * Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved. + * 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 +#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 DRIVER_NAME "MSM72K_UDC" + +#define EPT_FLAG_IN 0x0001 + +#define SETUP_BUF_SIZE 8 + + +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" +}; + +/*To release the wakelock from debugfs*/ +static int release_wlocks; + +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; + struct msm_request *prev; + + unsigned busy:1; + unsigned live:1; + unsigned alloced: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) +#define to_msm_otg(xceiv) container_of(xceiv, struct msm_otg, otg) +#define is_b_sess_vld() ((OTGSC_BSV & readl(USB_OTGSC)) ? 1 : 0) +#define is_usb_online(ui) (ui->usb_state != USB_STATE_NOTATTACHED) + +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; + unsigned long dTD_update_fail_count; + unsigned long false_prime_fail_count; + unsigned actual_prime_fail_count; + + unsigned wedged:1; + /* pointers to DMA transfer list area */ + /* these are allocated from the usb_info dma space */ + struct ept_queue_head *head; + struct timer_list prime_timer; +}; + +/* PHY status check timer to monitor phy stuck up on reset */ +static struct timer_list phy_status_timer; + +static void ept_prime_timer_func(unsigned long data); +static void usb_do_work(struct work_struct *w); +static void usb_do_remote_wakeup(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 +#define USB_FLAG_SUSPEND 0x0010 +#define USB_FLAG_CONFIGURED 0x0020 + +#define USB_CHG_DET_DELAY msecs_to_jiffies(1000) +#define REMOTE_WAKEUP_DELAY msecs_to_jiffies(1000) +#define PHY_STATUS_CHECK_DELAY (jiffies + msecs_to_jiffies(1000)) +#define EPT_PRIME_CHECK_DELAY (jiffies + msecs_to_jiffies(1000)) + +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; + + atomic_t configured; + atomic_t running; + + 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]; + + + /* max power requested by selected configuration */ + unsigned b_max_pow; + unsigned chg_current; + struct delayed_work chg_det; + struct delayed_work chg_stop; + struct msm_hsusb_gadget_platform_data *pdata; + struct work_struct phy_status_check; + + struct work_struct work; + unsigned phy_status; + unsigned phy_fail_count; + unsigned prime_fail_count; + unsigned long dTD_update_fail_count; + + struct usb_gadget gadget; + struct usb_gadget_driver *driver; + struct switch_dev sdev; + +#define ep0out ept[0] +#define ep0in ept[16] + + atomic_t ep0_dir; + atomic_t test_mode; + atomic_t offline_pending; + atomic_t softconnect; +#ifdef CONFIG_USB_OTG + u8 hnp_avail; +#endif + + atomic_t remote_wakeup; + atomic_t self_powered; + struct delayed_work rw_work; + + struct otg_transceiver *xceiv; + enum usb_device_state usb_state; + struct wake_lock wlock; +}; + +static const struct usb_ep_ops msm72k_ep_ops; +static struct usb_info *the_usb_info; + +static int msm72k_wakeup(struct usb_gadget *_gadget); +static int msm72k_pullup_internal(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 void usb_reset(struct usb_info *ui); +static int usb_ept_set_halt(struct usb_ep *_ep, int value); + +static void msm_hsusb_set_speed(struct usb_info *ui) +{ + unsigned long flags; + + spin_lock_irqsave(&ui->lock, flags); + switch (readl(USB_PORTSC) & PORTSC_PSPD_MASK) { + case PORTSC_PSPD_FS: + dev_dbg(&ui->pdev->dev, "portchange USB_SPEED_FULL\n"); + ui->gadget.speed = USB_SPEED_FULL; + break; + case PORTSC_PSPD_LS: + dev_dbg(&ui->pdev->dev, "portchange USB_SPEED_LOW\n"); + ui->gadget.speed = USB_SPEED_LOW; + break; + case PORTSC_PSPD_HS: + dev_dbg(&ui->pdev->dev, "portchange USB_SPEED_HIGH\n"); + ui->gadget.speed = USB_SPEED_HIGH; + break; + } + spin_unlock_irqrestore(&ui->lock, flags); +} + +static void msm_hsusb_set_state(enum usb_device_state state) +{ + unsigned long flags; + + spin_lock_irqsave(&the_usb_info->lock, flags); + the_usb_info->usb_state = state; + spin_unlock_irqrestore(&the_usb_info->lock, flags); +} + +static enum usb_device_state msm_hsusb_get_state(void) +{ + unsigned long flags; + enum usb_device_state state; + + spin_lock_irqsave(&the_usb_info->lock, flags); + state = the_usb_info->usb_state; + spin_unlock_irqrestore(&the_usb_info->lock, flags); + + return state; +} + +static ssize_t print_switch_name(struct switch_dev *sdev, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%s\n", DRIVER_NAME); +} + +static ssize_t print_switch_state(struct switch_dev *sdev, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%s\n", + sdev->state ? "online" : "offline"); +} + +static inline enum chg_type usb_get_chg_type(struct usb_info *ui) +{ + if ((readl(USB_PORTSC) & PORTSC_LS) == PORTSC_LS) + return USB_CHG_TYPE__WALLCHARGER; + else + return USB_CHG_TYPE__SDP; +} + +#define USB_WALLCHARGER_CHG_CURRENT 1800 +static int usb_get_max_power(struct usb_info *ui) +{ + struct msm_otg *otg = to_msm_otg(ui->xceiv); + unsigned long flags; + enum chg_type temp; + int suspended; + int configured; + unsigned bmaxpow; + + if (ui->gadget.is_a_peripheral) + return -EINVAL; + + temp = atomic_read(&otg->chg_type); + spin_lock_irqsave(&ui->lock, flags); + suspended = ui->usb_state == USB_STATE_SUSPENDED ? 1 : 0; + configured = atomic_read(&ui->configured); + bmaxpow = ui->b_max_pow; + spin_unlock_irqrestore(&ui->lock, flags); + + if (temp == USB_CHG_TYPE__INVALID) + return -ENODEV; + + if (temp == USB_CHG_TYPE__WALLCHARGER) + return USB_WALLCHARGER_CHG_CURRENT; + + if (suspended || !configured) + return 0; + + return bmaxpow; +} + +static int usb_phy_stuck_check(struct usb_info *ui) +{ + /* + * write some value (0xAA) into scratch reg (0x16) and read it back, + * If the read value is same as written value, means PHY is normal + * otherwise, PHY seems to have stuck. + */ + + if (otg_io_write(ui->xceiv, 0xAA, 0x16) == -1) { + dev_dbg(&ui->pdev->dev, + "%s(): ulpi write timeout\n", __func__); + return -EIO; + } + + if (otg_io_read(ui->xceiv, 0x16) != 0xAA) { + dev_dbg(&ui->pdev->dev, + "%s(): read value is incorrect\n", __func__); + return -EIO; + } + + return 0; +} + +/* + * This function checks the phy status by reading/writing to the + * phy scratch register. If the phy is stuck resets the HW + * */ +static void usb_phy_stuck_recover(struct work_struct *w) +{ + struct usb_info *ui = the_usb_info; + struct msm_otg *otg = to_msm_otg(ui->xceiv); + unsigned long flags; + + spin_lock_irqsave(&ui->lock, flags); + if (ui->gadget.speed != USB_SPEED_UNKNOWN || + ui->usb_state == USB_STATE_NOTATTACHED || + ui->driver == NULL) { + spin_unlock_irqrestore(&ui->lock, flags); + return; + } + spin_unlock_irqrestore(&ui->lock, flags); + + disable_irq(otg->irq); + if (usb_phy_stuck_check(ui)) { +#ifdef CONFIG_USB_MSM_ACA + del_timer_sync(&otg->id_timer); +#endif + ui->phy_fail_count++; + dev_err(&ui->pdev->dev, + "%s():PHY stuck, resetting HW\n", __func__); + /* + * PHY seems to have stuck, + * reset the PHY and HW link to recover the PHY + */ + usb_reset(ui); +#ifdef CONFIG_USB_MSM_ACA + mod_timer(&otg->id_timer, jiffies + + msecs_to_jiffies(OTG_ID_POLL_MS)); +#endif + msm72k_pullup_internal(&ui->gadget, 1); + } + enable_irq(otg->irq); +} + +static void usb_phy_status_check_timer(unsigned long data) +{ + struct usb_info *ui = the_usb_info; + + schedule_work(&ui->phy_status_check); +} + +static void usb_chg_stop(struct work_struct *w) +{ + struct usb_info *ui = container_of(w, struct usb_info, chg_stop.work); + struct msm_otg *otg = to_msm_otg(ui->xceiv); + enum chg_type temp; + + temp = atomic_read(&otg->chg_type); + + if (temp == USB_CHG_TYPE__SDP) + otg_set_power(ui->xceiv, 0); +} + +static void usb_chg_detect(struct work_struct *w) +{ + struct usb_info *ui = container_of(w, struct usb_info, chg_det.work); + struct msm_otg *otg = to_msm_otg(ui->xceiv); + enum chg_type temp = USB_CHG_TYPE__INVALID; + unsigned long flags; + int maxpower; + + spin_lock_irqsave(&ui->lock, flags); + if (ui->usb_state == USB_STATE_NOTATTACHED) { + spin_unlock_irqrestore(&ui->lock, flags); + return; + } + + temp = usb_get_chg_type(ui); + spin_unlock_irqrestore(&ui->lock, flags); + + atomic_set(&otg->chg_type, temp); + maxpower = usb_get_max_power(ui); + if (maxpower > 0) + otg_set_power(ui->xceiv, maxpower); + + /* USB driver prevents idle and suspend power collapse(pc) + * while USB cable is connected. But when dedicated charger is + * connected, driver can vote for idle and suspend pc. + * OTG driver handles idle pc as part of above otg_set_power call + * when wallcharger is attached. To allow suspend pc, release the + * wakelock which will be re-acquired for any sub-sequent usb interrupts + * */ + if (temp == USB_CHG_TYPE__WALLCHARGER) { + pm_runtime_put_sync(&ui->pdev->dev); + wake_unlock(&ui->wlock); + } +} + +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 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; + } + setup_timer(&ept->prime_timer, ept_prime_timer_func, + (unsigned long) ept); + + } +} + +static void config_ept(struct msm_endpoint *ept) +{ + struct usb_info *ui = ept->ui; + unsigned cfg = CONFIG_MAX_PKT(ept->ep.maxpacket) | CONFIG_ZLT; + + /* ep0 out needs interrupt-on-setup */ + if (ept->bit == 0) + cfg |= CONFIG_IOS; + + ept->head->config = cfg; + ept->head->next = TERMINATE; + + if (ept->ep.maxpacket) + dev_dbg(&ui->pdev->dev, + "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); +} + +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 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) { + if (yes) { + n = (n & (~CTRL_TXT_MASK)) | + (ep_type << CTRL_TXT_EP_TYPE_SHIFT); + n |= CTRL_TXE | CTRL_TXR; + } else + n &= (~CTRL_TXE); + } else { + if (yes) { + n = (n & (~CTRL_RXT_MASK)) | + (ep_type << CTRL_RXT_EP_TYPE_SHIFT); + n |= CTRL_RXE | CTRL_RXR; + } else + n &= ~(CTRL_RXE); + } + /* complete all the updates to ept->head before enabling endpoint*/ + mb(); + writel(n, USB_ENDPTCTRL(ept->num)); + + /* Ensure endpoint is enabled before returning */ + mb(); + + dev_dbg(&ui->pdev->dev, "ept %d %s %s\n", + ept->num, in ? "in" : "out", yes ? "enabled" : "disabled"); +} + +static void ept_prime_timer_func(unsigned long data) +{ + struct msm_endpoint *ept = (struct msm_endpoint *)data; + struct usb_info *ui = ept->ui; + unsigned n = 1 << ept->bit; + unsigned long flags; + + spin_lock_irqsave(&ui->lock, flags); + + ept->false_prime_fail_count++; + if ((readl_relaxed(USB_ENDPTPRIME) & n)) { + /* + * ---- UNLIKELY --- + * May be hardware is taking long time to process the + * prime request. Or could be intermittent priming and + * previous dTD is not fired yet. + */ + mod_timer(&ept->prime_timer, EPT_PRIME_CHECK_DELAY); + goto out; + } + if (readl_relaxed(USB_ENDPTSTAT) & n) + goto out; + + /* clear speculative loads on item->info */ + rmb(); + if (ept->req && (ept->req->item->info & INFO_ACTIVE)) { + ui->prime_fail_count++; + ept->actual_prime_fail_count++; + pr_err("%s(): ept%d%s prime failed. ept: config: %x" + "active: %x next: %x info: %x\n", + __func__, ept->num, + ept->flags & EPT_FLAG_IN ? "in" : "out", + ept->head->config, ept->head->active, + ept->head->next, ept->head->info); + writel_relaxed(n, USB_ENDPTPRIME); + mod_timer(&ept->prime_timer, EPT_PRIME_CHECK_DELAY); + } +out: + spin_unlock_irqrestore(&ui->lock, flags); +} + +static void usb_ept_start(struct msm_endpoint *ept) +{ + struct usb_info *ui = ept->ui; + struct msm_request *req = ept->req; + unsigned n = 1 << ept->bit; + + BUG_ON(req->live); + + while (req) { + req->live = 1; + /* prepare the transaction descriptor item for the hardware */ + req->item->info = + INFO_BYTES(req->req.length) | INFO_IOC | INFO_ACTIVE; + req->item->page0 = req->dma; + req->item->page1 = (req->dma + 0x1000) & 0xfffff000; + req->item->page2 = (req->dma + 0x2000) & 0xfffff000; + req->item->page3 = (req->dma + 0x3000) & 0xfffff000; + + if (req->next == NULL) { + req->item->next = TERMINATE; + break; + } + req->item->next = req->next->item_dma; + req = req->next; + } + + rmb(); + /* link the hw queue head to the request's transaction item */ + ept->head->next = ept->req->item_dma; + ept->head->info = 0; + + /* flush buffers before priming ept */ + mb(); + /* during high throughput testing it is observed that + * ept stat bit is not set even though all the data + * structures are updated properly and ept prime bit + * is set. To workaround the issue, kick a timer and + * make decision on re-prime. We can do a busy loop here + * but it leads to high cpu usage. + */ + writel_relaxed(n, USB_ENDPTPRIME); + mod_timer(&ept->prime_timer, EPT_PRIME_CHECK_DELAY); +} + +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; + 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); + dev_err(&ui->pdev->dev, + "usb_ept_queue_xfer() tried to queue busy request\n"); + return -EBUSY; + } + + if (!atomic_read(&ui->configured) && (ept->num != 0)) { + req->req.status = -ESHUTDOWN; + spin_unlock_irqrestore(&ui->lock, flags); + if (printk_ratelimit()) + dev_err(&ui->pdev->dev, + "%s: called while offline\n", __func__); + return -ESHUTDOWN; + } + + if (ui->usb_state == USB_STATE_SUSPENDED) { + if (!atomic_read(&ui->remote_wakeup)) { + req->req.status = -EAGAIN; + spin_unlock_irqrestore(&ui->lock, flags); + if (printk_ratelimit()) + dev_err(&ui->pdev->dev, + "%s: cannot queue as bus is suspended " + "ept #%d %s max:%d head:%p bit:%d\n", + __func__, ept->num, + (ept->flags & EPT_FLAG_IN) ? "in" : "out", + ept->ep.maxpacket, ept->head, ept->bit); + + return -EAGAIN; + } + + wake_lock(&ui->wlock); + otg_set_suspend(ui->xceiv, 0); + schedule_delayed_work(&ui->rw_work, REMOTE_WAKEUP_DELAY); + } + + 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); + + + /* 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; + req->prev = last; + + } else { + /* queue was empty -- kick the hardware */ + ept->req = req; + req->prev = NULL; + 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_status_complete(struct usb_ep *ep, struct usb_request *_req) +{ + struct usb_request *req = _req->context; + struct msm_request *r; + struct msm_endpoint *ept; + struct usb_info *ui; + + pr_debug("%s:\n", __func__); + if (!req) + return; + + r = to_msm_request(req); + ept = to_msm_endpoint(ep); + ui = ept->ui; + _req->context = 0; + + req->complete = r->gadget_complete; + req->zero = 0; + r->gadget_complete = 0; + if (req->complete) + req->complete(&ui->ep0in.ep, req); + +} + +static void ep0_status_phase(struct usb_ep *ep, struct usb_request *req) +{ + struct msm_endpoint *ept = to_msm_endpoint(ep); + struct usb_info *ui = ept->ui; + + pr_debug("%s:\n", __func__); + + req->length = 0; + req->complete = ep0_status_complete; + + /* status phase */ + if (atomic_read(&ui->ep0_dir) == USB_DIR_IN) + usb_ept_queue_xfer(&ui->ep0out, req); + else + usb_ept_queue_xfer(&ui->ep0in, req); +} + +static void ep0in_send_zero_leng_pkt(struct msm_endpoint *ept) +{ + struct usb_info *ui = ept->ui; + struct usb_request *req = ui->setup_req; + + pr_debug("%s:\n", __func__); + + req->length = 0; + req->complete = ep0_status_phase; + usb_ept_queue_xfer(&ui->ep0in, req); +} + +static void ep0_queue_ack_complete(struct usb_ep *ep, + struct usb_request *_req) +{ + struct msm_endpoint *ept = to_msm_endpoint(ep); + struct usb_info *ui = ept->ui; + struct usb_request *req = ui->setup_req; + + pr_debug("%s: _req:%p actual:%d length:%d zero:%d\n", + __func__, _req, _req->actual, + _req->length, _req->zero); + + /* queue up the receive of the ACK response from the host */ + if (_req->status == 0 && _req->actual == _req->length) { + req->context = _req; + if (atomic_read(&ui->ep0_dir) == USB_DIR_IN) { + if (_req->zero && _req->length && + !(_req->length % ep->maxpacket)) { + ep0in_send_zero_leng_pkt(&ui->ep0in); + return; + } + } + ep0_status_phase(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; + int test_mode = atomic_read(&ui->test_mode); + + if (!test_mode) + return; + + switch (test_mode) { + case J_TEST: + dev_info(&ui->pdev->dev, "usb electrical test mode: (J)\n"); + temp = readl(USB_PORTSC) & (~PORTSC_PTC); + writel(temp | PORTSC_PTC_J_STATE, USB_PORTSC); + break; + + case K_TEST: + dev_info(&ui->pdev->dev, "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: + dev_info(&ui->pdev->dev, + "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: + dev_info(&ui->pdev->dev, + "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; +#ifdef CONFIG_USB_OTG + u8 hnp; + unsigned long flags; +#endif + /* USB hardware sometimes generate interrupt before + * 8 bytes of SETUP packet are written to system memory. + * This results in fetching wrong setup_data sometimes. + * TODO: Remove below workaround of adding 1us delay once + * it gets fixed in hardware. + */ + udelay(10); + + memcpy(&ctl, ui->ep0out.head->setup_data, sizeof(ctl)); + /* Ensure buffer is read before acknowledging to h/w */ + mb(); + + writel(EPT_RX(0), USB_ENDPTSETUPSTAT); + + if (ctl.bRequestType & USB_DIR_IN) + atomic_set(&ui->ep0_dir, USB_DIR_IN); + else + atomic_set(&ui->ep0_dir, USB_DIR_OUT); + + /* any pending ep0 transactions must be canceled */ + flush_endpoint(&ui->ep0out); + flush_endpoint(&ui->ep0in); + + dev_dbg(&ui->pdev->dev, + "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_MASK)) == + (USB_DIR_IN | USB_TYPE_STANDARD)) { + if (ctl.bRequest == USB_REQ_GET_STATUS) { + /* OTG supplement Rev 2.0 introduces another device + * GET_STATUS request for HNP polling with length = 1. + */ + u8 len = 2; + 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; + + if (ctl.wIndex == OTG_STATUS_SELECTOR) { +#ifdef CONFIG_USB_OTG + spin_lock_irqsave(&ui->lock, flags); + hnp = (ui->gadget.host_request << + HOST_REQUEST_FLAG); + ui->hnp_avail = 1; + spin_unlock_irqrestore(&ui->lock, + flags); + memcpy(req->buf, &hnp, 1); + len = 1; +#else + goto stall; +#endif + } else { + temp = (atomic_read(&ui->self_powered) + << USB_DEVICE_SELF_POWERED); + temp |= (atomic_read(&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, len); + 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 (ept->wedged) + goto ack; + if (ctl.bRequest == USB_REQ_SET_FEATURE) + usb_ept_set_halt(&ept->ep, 1); + else + usb_ept_set_halt(&ept->ep, 0); + } + goto ack; + } + } + } + if (ctl.bRequestType == (USB_DIR_OUT | USB_TYPE_STANDARD)) { + if (ctl.bRequest == USB_REQ_SET_CONFIGURATION) { + atomic_set(&ui->configured, !!ctl.wValue); + msm_hsusb_set_state(USB_STATE_CONFIGURED); + } else if (ctl.bRequest == USB_REQ_SET_ADDRESS) { + /* + * Gadget speed should be set when PCI interrupt + * occurs. But sometimes, PCI interrupt is not + * occuring after reset. Hence update the gadget + * speed here. + */ + if (ui->gadget.speed == USB_SPEED_UNKNOWN) { + dev_info(&ui->pdev->dev, + "PCI intr missed" + "set speed explictly\n"); + msm_hsusb_set_speed(ui); + } + msm_hsusb_set_state(USB_STATE_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: + case TST_PKT_TEST: + atomic_set(&ui->test_mode, ctl.wIndex); + goto ack; + } + goto stall; + case USB_DEVICE_REMOTE_WAKEUP: + atomic_set(&ui->remote_wakeup, 1); + goto ack; +#ifdef CONFIG_USB_OTG + case USB_DEVICE_B_HNP_ENABLE: + ui->gadget.b_hnp_enable = 1; + goto ack; + case USB_DEVICE_A_HNP_SUPPORT: + case USB_DEVICE_A_ALT_HNP_SUPPORT: + /* B-devices compliant to OTG spec + * Rev 2.0 are not required to + * suppport these features. + */ + goto stall; +#endif + } + } else if ((ctl.bRequest == USB_REQ_CLEAR_FEATURE) && + (ctl.wValue == USB_DEVICE_REMOTE_WAKEUP)) { + atomic_set(&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; + + /* + 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)) { + /* 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; + } + + /* clean speculative fetches on req->item->info */ + dma_coherent_post_ops(); + info = req->item->info; + /* if the transaction is still in-flight, stop here */ + if (info & INFO_ACTIVE) + break; + + del_timer(&ept->prime_timer); + /* 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; + dev_err(&ui->pdev->dev, + "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->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, *next_req = NULL; + 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 = -ESHUTDOWN; + req->req.actual = 0; + + /* Gadget driver may free the request in completion + * handler. So keep a copy of next req pointer + * before calling completion handler. + */ + next_req = req->next; + if (req->req.complete) { + spin_unlock_irqrestore(&ui->lock, flags); + req->req.complete(&ept->ep, &req->req); + spin_lock_irqsave(&ui->lock, flags); + } + req = next_req; + } + spin_unlock_irqrestore(&ui->lock, flags); +} + +static void flush_endpoint(struct msm_endpoint *ept) +{ + del_timer(&ept->prime_timer); + flush_endpoint_hw(ept->ui, (1 << ept->bit)); + flush_endpoint_sw(ept); +} + +static irqreturn_t usb_interrupt(int irq, void *data) +{ + struct usb_info *ui = data; + unsigned n; + unsigned long flags; + + n = readl(USB_USBSTS); + writel(n, USB_USBSTS); + + /* somehow we got an IRQ while in the reset sequence: ignore it */ + if (!atomic_read(&ui->running)) + return IRQ_HANDLED; + + if (n & STS_PCI) { + msm_hsusb_set_speed(ui); + if (atomic_read(&ui->configured)) { + wake_lock(&ui->wlock); + + spin_lock_irqsave(&ui->lock, flags); + ui->usb_state = USB_STATE_CONFIGURED; + ui->flags = USB_FLAG_CONFIGURED; + spin_unlock_irqrestore(&ui->lock, flags); + + ui->driver->resume(&ui->gadget); + schedule_work(&ui->work); + } else { + msm_hsusb_set_state(USB_STATE_DEFAULT); + } + +#ifdef CONFIG_USB_OTG + /* notify otg to clear A_BIDL_ADIS timer */ + if (ui->gadget.is_a_peripheral) + otg_set_suspend(ui->xceiv, 0); +#endif + } + + if (n & STS_URI) { + dev_dbg(&ui->pdev->dev, "reset\n"); + spin_lock_irqsave(&ui->lock, flags); + ui->gadget.speed = USB_SPEED_UNKNOWN; + spin_unlock_irqrestore(&ui->lock, flags); +#ifdef CONFIG_USB_OTG + /* notify otg to clear A_BIDL_ADIS timer */ + if (ui->gadget.is_a_peripheral) + otg_set_suspend(ui->xceiv, 0); + spin_lock_irqsave(&ui->lock, flags); + /* Host request is persistent across reset */ + ui->gadget.b_hnp_enable = 0; + ui->hnp_avail = 0; + spin_unlock_irqrestore(&ui->lock, flags); +#endif + msm_hsusb_set_state(USB_STATE_DEFAULT); + atomic_set(&ui->remote_wakeup, 0); + if (!ui->gadget.is_a_peripheral) + schedule_delayed_work(&ui->chg_stop, 0); + + writel(readl(USB_ENDPTSETUPSTAT), USB_ENDPTSETUPSTAT); + writel(readl(USB_ENDPTCOMPLETE), USB_ENDPTCOMPLETE); + writel(0xffffffff, USB_ENDPTFLUSH); + writel(0, USB_ENDPTCTRL(1)); + + wake_lock(&ui->wlock); + if (atomic_read(&ui->configured)) { + /* marking us offline will cause ept queue attempts + ** to fail + */ + atomic_set(&ui->configured, 0); + /* Defer sending offline uevent to userspace */ + atomic_set(&ui->offline_pending, 1); + + /* XXX: we can't seem to detect going offline, + * XXX: so deconfigure on reset for the time being + */ + dev_dbg(&ui->pdev->dev, + "usb: notify offline\n"); + ui->driver->disconnect(&ui->gadget); + /* cancel pending ep0 transactions */ + flush_endpoint(&ui->ep0out); + flush_endpoint(&ui->ep0in); + + } + /* Start phy stuck timer */ + if (ui->pdata && ui->pdata->is_phy_status_timer_on) + mod_timer(&phy_status_timer, PHY_STATUS_CHECK_DELAY); + } + + if (n & STS_SLI) { + dev_dbg(&ui->pdev->dev, "suspend\n"); + + spin_lock_irqsave(&ui->lock, flags); + ui->usb_state = USB_STATE_SUSPENDED; + ui->flags = USB_FLAG_SUSPEND; + spin_unlock_irqrestore(&ui->lock, flags); + + ui->driver->suspend(&ui->gadget); + schedule_work(&ui->work); +#ifdef CONFIG_USB_OTG + /* notify otg for + * 1. kicking A_BIDL_ADIS timer in case of A-peripheral + * 2. disabling pull-up and kicking B_ASE0_RST timer + */ + if (ui->gadget.b_hnp_enable || ui->gadget.is_a_peripheral) + otg_set_suspend(ui->xceiv, 1); +#endif + } + + 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); + INIT_DELAYED_WORK(&ui->chg_det, usb_chg_detect); + INIT_DELAYED_WORK(&ui->chg_stop, usb_chg_stop); + INIT_DELAYED_WORK(&ui->rw_work, usb_do_remote_wakeup); + if (ui->pdata && ui->pdata->is_phy_status_timer_on) + INIT_WORK(&ui->phy_status_check, usb_phy_stuck_recover); +} + +static void usb_reset(struct usb_info *ui) +{ + struct msm_otg *otg = to_msm_otg(ui->xceiv); + + dev_dbg(&ui->pdev->dev, "reset controller\n"); + + atomic_set(&ui->running, 0); + + /* + * PHY reset takes minimum 100 msec. Hence reset only link + * during HNP. Reset PHY and link in B-peripheral mode. + */ + if (ui->gadget.is_a_peripheral) + otg->reset(ui->xceiv, 0); + else + otg->reset(ui->xceiv, 1); + + /* set usb controller interrupt threshold to zero*/ + writel((readl(USB_USBCMD) & ~USBCMD_ITC_MASK) | USBCMD_ITC(0), + USB_USBCMD); + + writel(ui->dma, USB_ENDPOINTLISTADDR); + + configure_endpoints(ui); + + /* marking us offline will cause ept queue attempts to fail */ + atomic_set(&ui->configured, 0); + + if (ui->driver) { + dev_dbg(&ui->pdev->dev, "usb: notify offline\n"); + ui->driver->disconnect(&ui->gadget); + } + + /* cancel pending ep0 transactions */ + flush_endpoint(&ui->ep0out); + flush_endpoint(&ui->ep0in); + + /* enable interrupts */ + writel(STS_URI | STS_SLI | STS_UI | STS_PCI, USB_USBINTR); + + /* Ensure that h/w RESET is completed before returning */ + mb(); + + atomic_set(&ui->running, 1); +} + +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 int usb_free(struct usb_info *ui, int ret) +{ + dev_dbg(&ui->pdev->dev, "usb_free(%d)\n", ret); + + if (ui->xceiv) + otg_put_transceiver(ui->xceiv); + + 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); + 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 (is_usb_online(ui)) + 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); + struct msm_otg *otg = to_msm_otg(ui->xceiv); + unsigned long iflags; + unsigned flags, _vbus; + + for (;;) { + spin_lock_irqsave(&ui->lock, iflags); + flags = ui->flags; + ui->flags = 0; + _vbus = is_usb_online(ui); + 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) { + int ret; + + if (!_vbus) { + ui->state = USB_STATE_OFFLINE; + break; + } + + pm_runtime_get_noresume(&ui->pdev->dev); + pm_runtime_resume(&ui->pdev->dev); + dev_dbg(&ui->pdev->dev, + "msm72k_udc: IDLE -> ONLINE\n"); + usb_reset(ui); + ret = request_irq(otg->irq, usb_interrupt, + IRQF_SHARED, + ui->pdev->name, ui); + /* FIXME: should we call BUG_ON when + * requst irq fails + */ + if (ret) { + dev_err(&ui->pdev->dev, + "hsusb: peripheral: request irq" + " failed:(%d)", ret); + break; + } + ui->irq = otg->irq; + ui->state = USB_STATE_ONLINE; + usb_do_work_check_vbus(ui); + + if (!atomic_read(&ui->softconnect)) + break; + + msm72k_pullup_internal(&ui->gadget, 1); + + if (!ui->gadget.is_a_peripheral) + schedule_delayed_work( + &ui->chg_det, + USB_CHG_DET_DELAY); + + } + break; + case USB_STATE_ONLINE: + if (atomic_read(&ui->offline_pending)) { + switch_set_state(&ui->sdev, 0); + atomic_set(&ui->offline_pending, 0); + } + + /* 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) { + + ui->chg_current = 0; + /* wait incase chg_detect is running */ + if (!ui->gadget.is_a_peripheral) + cancel_delayed_work_sync(&ui->chg_det); + + dev_dbg(&ui->pdev->dev, + "msm72k_udc: ONLINE -> OFFLINE\n"); + + atomic_set(&ui->running, 0); + atomic_set(&ui->remote_wakeup, 0); + atomic_set(&ui->configured, 0); + + if (ui->driver) { + dev_dbg(&ui->pdev->dev, + "usb: notify offline\n"); + ui->driver->disconnect(&ui->gadget); + } + /* cancel pending ep0 transactions */ + flush_endpoint(&ui->ep0out); + flush_endpoint(&ui->ep0in); + + /* synchronize with irq context */ + spin_lock_irqsave(&ui->lock, iflags); +#ifdef CONFIG_USB_OTG + ui->gadget.host_request = 0; + ui->gadget.b_hnp_enable = 0; + ui->hnp_avail = 0; +#endif + msm72k_pullup_internal(&ui->gadget, 0); + spin_unlock_irqrestore(&ui->lock, iflags); + + + /* if charger is initialized to known type + * we must let modem know about charger + * disconnection + */ + otg_set_power(ui->xceiv, 0); + + if (ui->irq) { + free_irq(ui->irq, ui); + ui->irq = 0; + } + + + switch_set_state(&ui->sdev, 0); + + ui->state = USB_STATE_OFFLINE; + usb_do_work_check_vbus(ui); + pm_runtime_put_noidle(&ui->pdev->dev); + pm_runtime_suspend(&ui->pdev->dev); + wake_unlock(&ui->wlock); + break; + } + if (flags & USB_FLAG_SUSPEND) { + int maxpower = usb_get_max_power(ui); + + if (maxpower < 0) + break; + + otg_set_power(ui->xceiv, 0); + /* To support TCXO during bus suspend + * This might be dummy check since bus suspend + * is not implemented as of now + * */ + if (release_wlocks) + wake_unlock(&ui->wlock); + + /* TBD: Initiate LPM at usb bus suspend */ + break; + } + if (flags & USB_FLAG_CONFIGURED) { + int maxpower = usb_get_max_power(ui); + + /* We may come here even when no configuration + * is selected. Send online/offline event + * accordingly. + */ + switch_set_state(&ui->sdev, + atomic_read(&ui->configured)); + + if (maxpower < 0) + break; + + ui->chg_current = maxpower; + otg_set_power(ui->xceiv, maxpower); + break; + } + if (flags & USB_FLAG_RESET) { + dev_dbg(&ui->pdev->dev, + "msm72k_udc: ONLINE -> RESET\n"); + msm72k_pullup_internal(&ui->gadget, 0); + usb_reset(ui); + msm72k_pullup_internal(&ui->gadget, 1); + dev_dbg(&ui->pdev->dev, + "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) { + int ret; + + pm_runtime_get_noresume(&ui->pdev->dev); + pm_runtime_resume(&ui->pdev->dev); + dev_dbg(&ui->pdev->dev, + "msm72k_udc: OFFLINE -> ONLINE\n"); + + usb_reset(ui); + ui->state = USB_STATE_ONLINE; + usb_do_work_check_vbus(ui); + ret = request_irq(otg->irq, usb_interrupt, + IRQF_SHARED, + ui->pdev->name, ui); + /* FIXME: should we call BUG_ON when + * requst irq fails + */ + if (ret) { + dev_err(&ui->pdev->dev, + "hsusb: peripheral: request irq" + " failed:(%d)", ret); + break; + } + ui->irq = otg->irq; + enable_irq_wake(otg->irq); + + if (!atomic_read(&ui->softconnect)) + break; + msm72k_pullup_internal(&ui->gadget, 1); + + if (!ui->gadget.is_a_peripheral) + schedule_delayed_work( + &ui->chg_det, + USB_CHG_DET_DELAY); + } + 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) { + pr_err("%s called before driver initialized\n", __func__); + return; + } + + spin_lock_irqsave(&ui->lock, flags); + + if (is_usb_online(ui) == online) + goto out; + + if (online) { + ui->usb_state = USB_STATE_POWERED; + ui->flags |= USB_FLAG_VBUS_ONLINE; + } else { + ui->gadget.speed = USB_SPEED_UNKNOWN; + ui->usb_state = USB_STATE_NOTATTACHED; + ui->flags |= USB_FLAG_VBUS_OFFLINE; + } + if (in_interrupt()) { + schedule_work(&ui->work); + } else { + spin_unlock_irqrestore(&ui->lock, flags); + usb_do_work(&ui->work); + return; + } +out: + spin_unlock_irqrestore(&ui->lock, flags); +} + +#if defined(CONFIG_DEBUG_FS) + +void usb_function_reenumerate(void) +{ + struct usb_info *ui = the_usb_info; + + /* disable and re-enable the D+ pullup */ + dev_dbg(&ui->pdev->dev, "disable pullup\n"); + writel(readl(USB_USBCMD) & ~USBCMD_RS, USB_USBCMD); + + msleep(10); + + dev_dbg(&ui->pdev->dev, "enable pullup\n"); + writel(readl(USB_USBCMD) | USBCMD_RS, 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 ssize_t debug_read_release_wlocks(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + char kbuf[10]; + size_t c = 0; + + memset(kbuf, 0, 10); + + c = scnprintf(kbuf, 10, "%d", release_wlocks); + + if (copy_to_user(ubuf, kbuf, c)) + return -EFAULT; + + return c; +} +static ssize_t debug_write_release_wlocks(struct file *file, + const char __user *buf, size_t count, loff_t *ppos) +{ + char kbuf[10]; + long temp; + + memset(kbuf, 0, 10); + + if (copy_from_user(kbuf, buf, count > 10 ? 10 : count)) + return -EFAULT; + + if (strict_strtol(kbuf, 10, &temp)) + return -EINVAL; + + if (temp) + release_wlocks = 1; + else + release_wlocks = 0; + + return count; +} +static int debug_wake_lock_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} +const struct file_operations debug_wlocks_ops = { + .open = debug_wake_lock_open, + .read = debug_read_release_wlocks, + .write = debug_write_release_wlocks, +}; + +static ssize_t debug_reprime_ep(struct file *file, const char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct usb_info *ui = file->private_data; + struct msm_endpoint *ept; + char kbuf[10]; + unsigned int ep_num, dir; + unsigned long flags; + unsigned n, i; + + memset(kbuf, 0, 10); + + if (copy_from_user(kbuf, ubuf, count > 10 ? 10 : count)) + return -EFAULT; + + if (sscanf(kbuf, "%u %u", &ep_num, &dir) != 2) + return -EINVAL; + + if (dir) + i = ep_num + 16; + else + i = ep_num; + + spin_lock_irqsave(&ui->lock, flags); + ept = ui->ept + i; + n = 1 << ept->bit; + + if ((readl_relaxed(USB_ENDPTPRIME) & n)) + goto out; + + if (readl_relaxed(USB_ENDPTSTAT) & n) + goto out; + + /* clear speculative loads on item->info */ + rmb(); + if (ept->req && (ept->req->item->info & INFO_ACTIVE)) { + pr_err("%s(): ept%d%s prime failed. ept: config: %x" + "active: %x next: %x info: %x\n", + __func__, ept->num, + ept->flags & EPT_FLAG_IN ? "in" : "out", + ept->head->config, ept->head->active, + ept->head->next, ept->head->info); + writel_relaxed(n, USB_ENDPTPRIME); + } +out: + spin_unlock_irqrestore(&ui->lock, flags); + + return count; +} + +static char buffer[512]; +static ssize_t debug_prime_fail_read(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct usb_info *ui = file->private_data; + char *buf = buffer; + unsigned long flags; + struct msm_endpoint *ept; + int n; + int i = 0; + + spin_lock_irqsave(&ui->lock, flags); + 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 false_prime_count=%lu prime_fail_count=%d dtd_fail_count=%lu\n", + ept->num, (ept->flags & EPT_FLAG_IN) ? "in " : "out", + ept->false_prime_fail_count, + ept->actual_prime_fail_count, + ept->dTD_update_fail_count); + } + + i += scnprintf(buf + i, PAGE_SIZE - i, + "dTD_update_fail count: %lu\n", + ui->dTD_update_fail_count); + + i += scnprintf(buf + i, PAGE_SIZE - i, + "prime_fail count: %d\n", ui->prime_fail_count); + + spin_unlock_irqrestore(&ui->lock, flags); + + return simple_read_from_buffer(ubuf, count, ppos, buf, i); +} + +static int debug_prime_fail_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +const struct file_operations prime_fail_ops = { + .open = debug_prime_fail_open, + .read = debug_prime_fail_read, + .write = debug_reprime_ep, +}; + +static void usb_debugfs_init(struct usb_info *ui) +{ + struct dentry *dent; + dent = debugfs_create_dir(dev_name(&ui->pdev->dev), 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); + debugfs_create_file("release_wlocks", 0666, dent, ui, + &debug_wlocks_ops); + debugfs_create_file("prime_fail_countt", 0666, dent, ui, + &prime_fail_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; + + _ep->maxpacket = le16_to_cpu(desc->wMaxPacketSize); + config_ept(ept); + ept->wedged = 0; + 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); + flush_endpoint(ept); + 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; + + /* request should not be busy */ + BUG_ON(req->busy); + if (req->alloced) + kfree(req->req.buf); + dma_pool_free(ui->pool, req->item, req->item_dma); + kfree(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; + 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 (atomic_read(&ui->ep0_dir) == USB_DIR_OUT) + ep = &ui->ep0out; + goto ep_queue_done; + } + +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 *temp_req; + unsigned long flags; + + if (!(ui && req && ep->req)) + return -EINVAL; + + spin_lock_irqsave(&ui->lock, flags); + if (!req->busy) { + dev_dbg(&ui->pdev->dev, "%s: !req->busy\n", __func__); + spin_unlock_irqrestore(&ui->lock, flags); + return -EINVAL; + } + del_timer(&ep->prime_timer); + /* Stop the transfer */ + do { + writel((1 << ep->bit), USB_ENDPTFLUSH); + while (readl(USB_ENDPTFLUSH) & (1 << ep->bit)) + udelay(100); + } while (readl(USB_ENDPTSTAT) & (1 << ep->bit)); + + req->req.status = 0; + req->busy = 0; + + if (ep->req == req) { + ep->req = req->next; + ep->head->next = req->item->next; + } else { + req->prev->next = req->next; + if (req->next) + req->next->prev = req->prev; + req->prev->item->next = req->item->next; + } + + if (!req->next) + ep->last = req->prev; + + /* initialize request to default */ + req->item->next = TERMINATE; + req->item->info = 0; + req->live = 0; + dma_unmap_single(NULL, req->dma, req->req.length, + (ep->flags & EPT_FLAG_IN) ? + DMA_TO_DEVICE : DMA_FROM_DEVICE); + + if (req->req.complete) { + req->req.status = -ECONNRESET; + spin_unlock_irqrestore(&ui->lock, flags); + req->req.complete(&ep->ep, &req->req); + spin_lock_irqsave(&ui->lock, flags); + } + + if (!req->live) { + /* Reprime the endpoint for the remaining transfers */ + for (temp_req = ep->req ; temp_req ; temp_req = temp_req->next) + temp_req->live = 0; + if (ep->req) + usb_ept_start(ep); + spin_unlock_irqrestore(&ui->lock, flags); + return 0; + } + spin_unlock_irqrestore(&ui->lock, flags); + return 0; +} + +static int +usb_ept_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)); + if (!value) + ept->wedged = 0; + 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); + unsigned int in = ept->flags & EPT_FLAG_IN; + + if (value && in && ept->req) + return -EAGAIN; + + usb_ept_set_halt(_ep, value); + + 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 int msm72k_set_wedge(struct usb_ep *_ep) +{ + struct msm_endpoint *ept = to_msm_endpoint(_ep); + + if (ept->num == 0) + return -EINVAL; + + ept->wedged = 1; + + return msm72k_set_halt(_ep, 1); +} + +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, + .set_wedge = msm72k_set_wedge, + .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) +{ + struct usb_info *ui = container_of(_gadget, struct usb_info, gadget); + struct msm_otg *otg = to_msm_otg(ui->xceiv); + + if (is_active || atomic_read(&otg->chg_type) + == USB_CHG_TYPE__WALLCHARGER) + wake_lock(&ui->wlock); + + msm_hsusb_set_vbus_state(is_active); + return 0; +} + +/* SW workarounds +Issue #1 - USB Spoof Disconnect Failure +Symptom - Writing 0 to run/stop bit of USBCMD doesn't cause disconnect +SW workaround - Making opmode non-driving and SuspendM set in function + register of SMSC phy +*/ +/* drivers may have software control over D+ pullup */ +static int msm72k_pullup_internal(struct usb_gadget *_gadget, int is_active) +{ + struct usb_info *ui = container_of(_gadget, struct usb_info, gadget); + unsigned long flags; + + if (is_active) { + spin_lock_irqsave(&ui->lock, flags); + if (is_usb_online(ui) && ui->driver) + writel(readl(USB_USBCMD) | USBCMD_RS, USB_USBCMD); + spin_unlock_irqrestore(&ui->lock, flags); + } else { + writel(readl(USB_USBCMD) & ~USBCMD_RS, USB_USBCMD); + /* S/W workaround, Issue#1 */ + otg_io_write(ui->xceiv, 0x48, 0x04); + } + + /* Ensure pull-up operation is completed before returning */ + mb(); + + return 0; +} + +static int msm72k_pullup(struct usb_gadget *_gadget, int is_active) +{ + struct usb_info *ui = container_of(_gadget, struct usb_info, gadget); + struct msm_otg *otg = to_msm_otg(ui->xceiv); + unsigned long flags; + + atomic_set(&ui->softconnect, is_active); + + spin_lock_irqsave(&ui->lock, flags); + if (ui->usb_state == USB_STATE_NOTATTACHED || ui->driver == NULL || + atomic_read(&otg->chg_type) == USB_CHG_TYPE__WALLCHARGER) { + spin_unlock_irqrestore(&ui->lock, flags); + return 0; + } + spin_unlock_irqrestore(&ui->lock, flags); + + msm72k_pullup_internal(_gadget, is_active); + + if (is_active && !ui->gadget.is_a_peripheral) + schedule_delayed_work(&ui->chg_det, USB_CHG_DET_DELAY); + + return 0; +} + +static int msm72k_wakeup(struct usb_gadget *_gadget) +{ + struct usb_info *ui = container_of(_gadget, struct usb_info, gadget); + struct msm_otg *otg = to_msm_otg(ui->xceiv); + + if (!atomic_read(&ui->remote_wakeup)) { + dev_err(&ui->pdev->dev, + "%s: remote wakeup not supported\n", __func__); + return -ENOTSUPP; + } + + if (!atomic_read(&ui->configured)) { + dev_err(&ui->pdev->dev, + "%s: device is not configured\n", __func__); + return -ENODEV; + } + otg_set_suspend(ui->xceiv, 0); + + disable_irq(otg->irq); + + if (!is_usb_active()) + writel(readl(USB_PORTSC) | PORTSC_FPR, USB_PORTSC); + + /* Ensure that USB port is resumed before enabling the IRQ */ + mb(); + + enable_irq(otg->irq); + + return 0; +} + +/* when Gadget is configured, it will indicate how much power + * can be pulled from vbus, as specified in configuiration descriptor + */ +static int msm72k_udc_vbus_draw(struct usb_gadget *_gadget, unsigned mA) +{ + struct usb_info *ui = container_of(_gadget, struct usb_info, gadget); + unsigned long flags; + + + spin_lock_irqsave(&ui->lock, flags); + ui->b_max_pow = mA; + ui->flags = USB_FLAG_CONFIGURED; + spin_unlock_irqrestore(&ui->lock, flags); + + schedule_work(&ui->work); + + return 0; +} + +static int msm72k_set_selfpowered(struct usb_gadget *_gadget, int set) +{ + struct usb_info *ui = container_of(_gadget, struct usb_info, gadget); + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&ui->lock, flags); + if (set) { + if (ui->pdata && ui->pdata->self_powered) + atomic_set(&ui->self_powered, 1); + else + ret = -EOPNOTSUPP; + } else { + /* We can always work as a bus powered device */ + atomic_set(&ui->self_powered, 0); + } + spin_unlock_irqrestore(&ui->lock, flags); + + return ret; + +} + +static const struct usb_gadget_ops msm72k_ops = { + .get_frame = msm72k_get_frame, + .vbus_session = msm72k_udc_vbus_session, + .vbus_draw = msm72k_udc_vbus_draw, + .pullup = msm72k_pullup, + .wakeup = msm72k_wakeup, + .set_selfpowered = msm72k_set_selfpowered, +}; + +static void usb_do_remote_wakeup(struct work_struct *w) +{ + struct usb_info *ui = the_usb_info; + + msm72k_wakeup(&ui->gadget); +} + +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 ssize_t show_usb_state(struct device *dev, struct device_attribute *attr, + char *buf) +{ + size_t i; + char *state[] = {"USB_STATE_NOTATTACHED", "USB_STATE_ATTACHED", + "USB_STATE_POWERED", "USB_STATE_UNAUTHENTICATED", + "USB_STATE_RECONNECTING", "USB_STATE_DEFAULT", + "USB_STATE_ADDRESS", "USB_STATE_CONFIGURED", + "USB_STATE_SUSPENDED" + }; + + i = scnprintf(buf, PAGE_SIZE, "%s\n", state[msm_hsusb_get_state()]); + return i; +} + +static ssize_t show_usb_speed(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct usb_info *ui = the_usb_info; + size_t i; + char *speed[] = {"USB_SPEED_UNKNOWN", "USB_SPEED_LOW", + "USB_SPEED_FULL", "USB_SPEED_HIGH"}; + + i = scnprintf(buf, PAGE_SIZE, "%s\n", speed[ui->gadget.speed]); + return i; +} + +static ssize_t store_usb_chg_current(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct usb_info *ui = the_usb_info; + unsigned long mA; + + if (ui->gadget.is_a_peripheral) + return -EINVAL; + + if (strict_strtoul(buf, 10, &mA)) + return -EINVAL; + + ui->chg_current = mA; + otg_set_power(ui->xceiv, mA); + + return count; +} + +static ssize_t show_usb_chg_current(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct usb_info *ui = the_usb_info; + size_t count; + + count = snprintf(buf, PAGE_SIZE, "%d", ui->chg_current); + + return count; +} + +static ssize_t show_usb_chg_type(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct usb_info *ui = the_usb_info; + struct msm_otg *otg = to_msm_otg(ui->xceiv); + size_t count; + char *chg_type[] = {"STD DOWNSTREAM PORT", + "CARKIT", + "DEDICATED CHARGER", + "INVALID"}; + + count = snprintf(buf, PAGE_SIZE, "%s", + chg_type[atomic_read(&otg->chg_type)]); + + return count; +} +static DEVICE_ATTR(wakeup, S_IWUSR, 0, usb_remote_wakeup); +static DEVICE_ATTR(usb_state, S_IRUSR, show_usb_state, 0); +static DEVICE_ATTR(usb_speed, S_IRUSR, show_usb_speed, 0); +static DEVICE_ATTR(chg_type, S_IRUSR, show_usb_chg_type, 0); +static DEVICE_ATTR(chg_current, S_IWUSR | S_IRUSR, + show_usb_chg_current, store_usb_chg_current); + +#ifdef CONFIG_USB_OTG +static ssize_t store_host_req(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct usb_info *ui = the_usb_info; + unsigned long val, flags; + + if (strict_strtoul(buf, 10, &val)) + return -EINVAL; + + dev_dbg(&ui->pdev->dev, "%s host request\n", + val ? "set" : "clear"); + + spin_lock_irqsave(&ui->lock, flags); + if (ui->hnp_avail) + ui->gadget.host_request = !!val; + spin_unlock_irqrestore(&ui->lock, flags); + + return count; +} +static DEVICE_ATTR(host_request, S_IWUSR, NULL, store_host_req); + +/* How do we notify user space about HNP availability? + * As we are compliant to Rev 2.0, Host will not set a_hnp_support. + * Introduce hnp_avail flag and set when HNP polling request arrives. + * The expectation is that user space checks hnp availability before + * requesting host role via above sysfs node. + */ +static ssize_t show_host_avail(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct usb_info *ui = the_usb_info; + size_t count; + unsigned long flags; + + spin_lock_irqsave(&ui->lock, flags); + count = snprintf(buf, PAGE_SIZE, "%d\n", ui->hnp_avail); + spin_unlock_irqrestore(&ui->lock, flags); + + return count; +} +static DEVICE_ATTR(host_avail, S_IRUSR, show_host_avail, NULL); + +static struct attribute *otg_attrs[] = { + &dev_attr_host_request.attr, + &dev_attr_host_avail.attr, + NULL, +}; + +static struct attribute_group otg_attr_grp = { + .name = "otg", + .attrs = otg_attrs, +}; +#endif + +static int msm72k_probe(struct platform_device *pdev) +{ + struct usb_info *ui; + struct msm_otg *otg; + int retval; + + dev_dbg(&pdev->dev, "msm72k_probe\n"); + ui = kzalloc(sizeof(struct usb_info), GFP_KERNEL); + if (!ui) + return -ENOMEM; + + ui->pdev = pdev; + ui->pdata = pdev->dev.platform_data; + + 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); + + ui->xceiv = otg_get_transceiver(); + if (!ui->xceiv) + return usb_free(ui, -ENODEV); + + otg = to_msm_otg(ui->xceiv); + ui->addr = otg->regs; + + 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; + +#ifdef CONFIG_USB_OTG + ui->gadget.is_otg = 1; +#endif + + ui->sdev.name = DRIVER_NAME; + ui->sdev.print_name = print_switch_name; + ui->sdev.print_state = print_switch_state; + + retval = switch_dev_register(&ui->sdev); + if (retval) + return usb_free(ui, retval); + + the_usb_info = ui; + + wake_lock_init(&ui->wlock, + WAKE_LOCK_SUSPEND, "usb_bus_active"); + + usb_debugfs_init(ui); + + usb_prepare(ui); + +#ifdef CONFIG_USB_OTG + retval = sysfs_create_group(&pdev->dev.kobj, &otg_attr_grp); + if (retval) { + dev_err(&ui->pdev->dev, + "failed to create otg sysfs directory:" + "err:(%d)\n", retval); + } +#endif + + retval = otg_set_peripheral(ui->xceiv, &ui->gadget); + if (retval) { + dev_err(&ui->pdev->dev, + "%s: Cannot bind the transceiver, retval:(%d)\n", + __func__, retval); + switch_dev_unregister(&ui->sdev); + wake_lock_destroy(&ui->wlock); + return usb_free(ui, retval); + } + + pm_runtime_enable(&pdev->dev); + + /* Setup phy stuck timer */ + if (ui->pdata && ui->pdata->is_phy_status_timer_on) + setup_timer(&phy_status_timer, usb_phy_status_check_timer, 0); + return 0; +} + +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 + || !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; + atomic_set(&ui->softconnect, 1); + + 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 = bind(&ui->gadget); + if (retval) { + dev_err(&ui->pdev->dev, "bind to driver %s --> error %d\n", + driver->driver.name, retval); + device_del(&ui->gadget.dev); + goto fail; + } + + retval = device_create_file(&ui->gadget.dev, &dev_attr_wakeup); + if (retval != 0) + dev_err(&ui->pdev->dev, "failed to create sysfs entry:" + "(wakeup) error: (%d)\n", retval); + retval = device_create_file(&ui->gadget.dev, &dev_attr_usb_state); + if (retval != 0) + dev_err(&ui->pdev->dev, "failed to create sysfs entry:" + " (usb_state) error: (%d)\n", retval); + + retval = device_create_file(&ui->gadget.dev, &dev_attr_usb_speed); + if (retval != 0) + dev_err(&ui->pdev->dev, "failed to create sysfs entry:" + " (usb_speed) error: (%d)\n", retval); + + retval = device_create_file(&ui->gadget.dev, &dev_attr_chg_type); + if (retval != 0) + dev_err(&ui->pdev->dev, + "failed to create sysfs entry(chg_type): err:(%d)\n", + retval); + retval = device_create_file(&ui->gadget.dev, &dev_attr_chg_current); + if (retval != 0) + dev_err(&ui->pdev->dev, + "failed to create sysfs entry(chg_current):" + "err:(%d)\n", retval); + + dev_dbg(&ui->pdev->dev, "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_probe_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; + + msm72k_pullup_internal(&dev->gadget, 0); + if (dev->irq) { + free_irq(dev->irq, dev); + dev->irq = 0; + } + dev->state = USB_STATE_IDLE; + atomic_set(&dev->configured, 0); + switch_set_state(&dev->sdev, 0); + /* cancel pending ep0 transactions */ + flush_endpoint(&dev->ep0out); + flush_endpoint(&dev->ep0in); + + device_remove_file(&dev->gadget.dev, &dev_attr_wakeup); + device_remove_file(&dev->gadget.dev, &dev_attr_usb_state); + device_remove_file(&dev->gadget.dev, &dev_attr_usb_speed); + device_remove_file(&dev->gadget.dev, &dev_attr_chg_type); + device_remove_file(&dev->gadget.dev, &dev_attr_chg_current); + driver->disconnect(&dev->gadget); + driver->unbind(&dev->gadget); + dev->gadget.dev.driver = NULL; + dev->driver = NULL; + + device_del(&dev->gadget.dev); + + dev_dbg(&dev->pdev->dev, + "unregistered gadget driver '%s'\n", driver->driver.name); + return 0; +} +EXPORT_SYMBOL(usb_gadget_unregister_driver); + + +static int msm72k_udc_runtime_suspend(struct device *dev) +{ + dev_dbg(dev, "pm_runtime: suspending...\n"); + return 0; +} + +static int msm72k_udc_runtime_resume(struct device *dev) +{ + dev_dbg(dev, "pm_runtime: resuming...\n"); + return 0; +} + +static int msm72k_udc_runtime_idle(struct device *dev) +{ + dev_dbg(dev, "pm_runtime: idling...\n"); + return 0; +} + +static struct dev_pm_ops msm72k_udc_dev_pm_ops = { + .runtime_suspend = msm72k_udc_runtime_suspend, + .runtime_resume = msm72k_udc_runtime_resume, + .runtime_idle = msm72k_udc_runtime_idle +}; + +static struct platform_driver usb_driver = { + .probe = msm72k_probe, + .driver = { .name = "msm_hsusb", + .pm = &msm72k_udc_dev_pm_ops, }, +}; + +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/printer.c b/drivers/usb/gadget/printer.c index 88a464cc96c..271ef94668e 100644 --- a/drivers/usb/gadget/printer.c +++ b/drivers/usb/gadget/printer.c @@ -1602,7 +1602,7 @@ cleanup(void) if (status) ERROR(dev, "usb_gadget_unregister_driver %x\n", status); - unregister_chrdev_region(g_printer_devno, 1); + unregister_chrdev_region(g_printer_devno, 2); class_destroy(usb_gadget_class); mutex_unlock(&usb_printer_gadget.lock_printer_io); } diff --git a/drivers/usb/gadget/qcom_maemo.c b/drivers/usb/gadget/qcom_maemo.c new file mode 100644 index 00000000000..39686c4e05c --- /dev/null +++ b/drivers/usb/gadget/qcom_maemo.c @@ -0,0 +1,304 @@ +/* + * Qualcomm Maemo Composite driver + * + * Copyright (C) 2008 David Brownell + * Copyright (C) 2008 Nokia Corporation + * Copyright (C) 2009 Samsung Electronics + * Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * This program from the Code Aurora Forum is free software; you can + * redistribute it and/or modify it under the GNU General Public License + * version 2 and only version 2 as published by the Free Software Foundation. + * The original work available from [git.kernel.org ] is subject to the + * notice below. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include + + +#define DRIVER_DESC "Qcom Maemo Composite Gadget" +#define VENDOR_ID 0x05c6 +#define PRODUCT_ID 0x902E + +/* + * kbuild is not very cooperative with respect to linking separately + * compiled library objects into one module. So for now we won't use + * separate compilation ... ensuring init/exit sections work to shrink + * the runtime footprint, and giving us at least some parts of what + * a "gcc --combine ... part1.c part2.c part3.c ... " build would. + */ + +#include "composite.c" +#include "usbstring.c" +#include "config.c" +#include "epautoconf.c" + +#define USB_ETH + +#define USB_ETH_RNDIS +#ifdef USB_ETH_RNDIS +# include "f_rndis.c" +# include "rndis.c" +#endif + + +#include "u_serial.c" +#include "f_serial.c" + +#include "u_ether.c" + +#undef DBG /* u_ether.c has broken idea about macros */ +#undef VDBG /* so clean up after it */ +#undef ERROR +#undef INFO + +#include "f_mass_storage.c" +#include "f_diag.c" +#include "f_rmnet.c" + +/*-------------------------------------------------------------------------*/ +/* string IDs are assigned dynamically */ + +#define STRING_MANUFACTURER_IDX 0 +#define STRING_PRODUCT_IDX 1 +#define STRING_SERIAL_IDX 2 + +/* String Table */ +static struct usb_string strings_dev[] = { + /* These dummy values should be overridden by platform data */ + [STRING_MANUFACTURER_IDX].s = "Qualcomm Incorporated", + [STRING_PRODUCT_IDX].s = "Usb composition", + [STRING_SERIAL_IDX].s = "0123456789ABCDEF", + { } /* end of list */ +}; + +static struct usb_gadget_strings stringtab_dev = { + .language = 0x0409, /* en-us */ + .strings = strings_dev, +}; + +static struct usb_gadget_strings *dev_strings[] = { + &stringtab_dev, + NULL, +}; + +static struct usb_device_descriptor device_desc = { + .bLength = sizeof(device_desc), + .bDescriptorType = USB_DT_DEVICE, + .bcdUSB = __constant_cpu_to_le16(0x0200), + .bDeviceClass = USB_CLASS_PER_INTERFACE, + .bDeviceSubClass = 0, + .bDeviceProtocol = 0, + .idVendor = __constant_cpu_to_le16(VENDOR_ID), + .idProduct = __constant_cpu_to_le16(PRODUCT_ID), + .bcdDevice = __constant_cpu_to_le16(0xffff), + .bNumConfigurations = 1, +}; + +static u8 hostaddr[ETH_ALEN]; +static struct usb_diag_ch *diag_ch; +static struct usb_diag_platform_data usb_diag_pdata = { + .ch_name = DIAG_LEGACY, +}; + +/****************************** Configurations ******************************/ +static struct fsg_module_parameters mod_data = { + .stall = 0 +}; +FSG_MODULE_PARAMETERS(/* no prefix */, mod_data); + +static struct fsg_common *fsg_common; +static int maemo_setup_config(struct usb_configuration *c, + const struct usb_ctrlrequest *ctrl); + +static int maemo_do_config(struct usb_configuration *c) +{ + int ret; + + ret = rndis_bind_config(c, hostaddr); + if (ret < 0) + return ret; + + ret = diag_function_add(c); + if (ret < 0) + return ret; + + ret = gser_bind_config(c, 0); + if (ret < 0) + return ret; + + ret = gser_bind_config(c, 1); + if (ret < 0) + return ret; + + ret = rmnet_function_add(c); + if (ret < 0) + return ret; + + ret = fsg_add(c->cdev, c, fsg_common); + if (ret < 0) + return ret; + + return 0; +} + +static struct usb_configuration maemo_config_driver = { + .label = "Qcom Maemo Gadget", + .bind = maemo_do_config, + .setup = maemo_setup_config, + .bConfigurationValue = 1, + .bMaxPower = 0xFA, +}; +static int maemo_setup_config(struct usb_configuration *c, + const struct usb_ctrlrequest *ctrl) +{ + int i; + int ret = -EOPNOTSUPP; + + for (i = 0; i < maemo_config_driver.next_interface_id; i++) { + if (maemo_config_driver.interface[i]->setup) { + ret = maemo_config_driver.interface[i]->setup( + maemo_config_driver.interface[i], ctrl); + if (ret >= 0) + return ret; + } + } + + return ret; +} + +static int maemo_bind(struct usb_composite_dev *cdev) +{ + struct usb_gadget *gadget = cdev->gadget; + int status, gcnum; + + /* set up diag channel */ + diag_ch = diag_setup(&usb_diag_pdata); + if (IS_ERR(diag_ch)) + return PTR_ERR(diag_ch); + + /* set up network link layer */ + status = gether_setup(cdev->gadget, hostaddr); + if (status < 0) + goto diag_clean; + + /* set up serial link layer */ + status = gserial_setup(cdev->gadget, 2); + if (status < 0) + goto fail0; + + /* set up mass storage function */ + fsg_common = fsg_common_from_params(0, cdev, &mod_data); + if (IS_ERR(fsg_common)) { + status = PTR_ERR(fsg_common); + goto fail1; + } + + gcnum = usb_gadget_controller_number(gadget); + if (gcnum >= 0) + device_desc.bcdDevice = cpu_to_le16(0x0300 | gcnum); + else { + /* gadget zero is so simple (for now, no altsettings) that + * it SHOULD NOT have problems with bulk-capable hardware. + * so just warn about unrcognized controllers -- don't panic. + * + * things like configuration and altsetting numbering + * can need hardware-specific attention though. + */ + WARNING(cdev, "controller '%s' not recognized\n", + gadget->name); + device_desc.bcdDevice = __constant_cpu_to_le16(0x9999); + } + + /* Allocate string descriptor numbers ... note that string + * contents can be overridden by the composite_dev glue. + */ + + status = usb_string_id(cdev); + if (status < 0) + goto fail2; + strings_dev[STRING_MANUFACTURER_IDX].id = status; + device_desc.iManufacturer = status; + + status = usb_string_id(cdev); + if (status < 0) + goto fail2; + strings_dev[STRING_PRODUCT_IDX].id = status; + device_desc.iProduct = status; + + if (!usb_gadget_set_selfpowered(gadget)) + maemo_config_driver.bmAttributes |= USB_CONFIG_ATT_SELFPOWER; + + if (gadget->ops->wakeup) + maemo_config_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP; + + /* register our first configuration */ + status = usb_add_config(cdev, &maemo_config_driver); + if (status < 0) + goto fail2; + + usb_gadget_set_selfpowered(gadget); + dev_info(&gadget->dev, DRIVER_DESC "\n"); + fsg_common_put(fsg_common); + return 0; + +fail2: + fsg_common_put(fsg_common); +fail1: + gserial_cleanup(); +fail0: + gether_cleanup(); +diag_clean: + diag_cleanup(diag_ch); + + return status; +} + +static int __exit maemo_unbind(struct usb_composite_dev *cdev) +{ + gserial_cleanup(); + gether_cleanup(); + diag_cleanup(diag_ch); + return 0; +} + +static struct usb_composite_driver qcom_maemo_driver = { + .name = "Qcom Maemo Gadget", + .dev = &device_desc, + .strings = dev_strings, + .bind = maemo_bind, + .unbind = __exit_p(maemo_unbind), +}; + +static int __init qcom_maemo_usb_init(void) +{ + return usb_composite_register(&qcom_maemo_driver); +} +module_init(qcom_maemo_usb_init); + +static void __exit qcom_maemo_usb_cleanup(void) +{ + usb_composite_unregister(&qcom_maemo_driver); +} +module_exit(qcom_maemo_usb_cleanup); + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL v2"); +MODULE_VERSION("1.0"); diff --git a/drivers/usb/gadget/rndis.c b/drivers/usb/gadget/rndis.c index 791cb0fa67d..2b994a5c3fa 100644 --- a/drivers/usb/gadget/rndis.c +++ b/drivers/usb/gadget/rndis.c @@ -605,12 +605,12 @@ static int rndis_init_response(int configNr, rndis_init_msg_type *buf) resp->MinorVersion = cpu_to_le32(RNDIS_MINOR_VERSION); resp->DeviceFlags = cpu_to_le32(RNDIS_DF_CONNECTIONLESS); resp->Medium = cpu_to_le32(RNDIS_MEDIUM_802_3); - resp->MaxPacketsPerTransfer = cpu_to_le32(1); - resp->MaxTransferSize = cpu_to_le32( - params->dev->mtu + resp->MaxPacketsPerTransfer = cpu_to_le32(TX_SKB_HOLD_THRESHOLD); + resp->MaxTransferSize = cpu_to_le32(TX_SKB_HOLD_THRESHOLD * + (params->dev->mtu + sizeof(struct ethhdr) + sizeof(struct rndis_packet_msg_type) - + 22); + + 22)); resp->PacketAlignmentFactor = cpu_to_le32(0); resp->AFListOffset = cpu_to_le32(0); resp->AFListSize = cpu_to_le32(0); @@ -1166,15 +1166,11 @@ static struct proc_dir_entry *rndis_connect_state [RNDIS_MAX_CONFIGS]; #endif /* CONFIG_USB_GADGET_DEBUG_FILES */ -static bool rndis_initialized; int rndis_init(void) { u8 i; - if (rndis_initialized) - return 0; - for (i = 0; i < RNDIS_MAX_CONFIGS; i++) { #ifdef CONFIG_USB_GADGET_DEBUG_FILES char name [20]; @@ -1201,7 +1197,6 @@ int rndis_init(void) INIT_LIST_HEAD(&(rndis_per_dev_params[i].resp_queue)); } - rndis_initialized = true; return 0; } @@ -1210,13 +1205,7 @@ void rndis_exit(void) #ifdef CONFIG_USB_GADGET_DEBUG_FILES u8 i; char name[20]; -#endif - if (!rndis_initialized) - return; - rndis_initialized = false; - -#ifdef CONFIG_USB_GADGET_DEBUG_FILES for (i = 0; i < RNDIS_MAX_CONFIGS; i++) { sprintf(name, NAME_TEMPLATE, i); remove_proc_entry(name, NULL); diff --git a/drivers/usb/gadget/storage_common.c b/drivers/usb/gadget/storage_common.c index 680d0326c40..15059670b40 100644 --- a/drivers/usb/gadget/storage_common.c +++ b/drivers/usb/gadget/storage_common.c @@ -274,7 +274,12 @@ static struct fsg_lun *fsg_lun_from_dev(struct device *dev) #define DELAYED_STATUS (EP0_BUFSIZE + 999) /* An impossibly large value */ /* Number of buffers for CBW, DATA and CSW */ -#define FSG_NUM_BUFFERS 8 +#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) diff --git a/drivers/usb/gadget/u_bam.c b/drivers/usb/gadget/u_bam.c index 70cbd2f44a1..f48e7f7f626 100644 --- a/drivers/usb/gadget/u_bam.c +++ b/drivers/usb/gadget/u_bam.c @@ -23,12 +23,18 @@ #include #include +#include +#include + #include "u_rmnet.h" #define BAM_N_PORTS 1 +#define BAM2BAM_N_PORTS 1 static struct workqueue_struct *gbam_wq; static int n_bam_ports; +static int n_bam2bam_ports; +static unsigned n_tx_req_queued; static unsigned bam_ch_ids[] = { 8 }; static const char *bam_ch_names[] = { "bam_dmux_ch_8" }; @@ -45,6 +51,8 @@ static const char *bam_ch_names[] = { "bam_dmux_ch_8" }; #define BAM_MUX_TX_Q_SIZE 200 #define BAM_MUX_RX_REQ_SIZE (2048 - BAM_MUX_HDR) +#define DL_INTR_THRESHOLD 20 + unsigned int bam_mux_tx_pkt_drop_thld = BAM_MUX_TX_PKT_DROP_THRESHOLD; module_param(bam_mux_tx_pkt_drop_thld, uint, S_IRUGO | S_IWUSR); @@ -66,8 +74,16 @@ module_param(bam_mux_rx_q_size, uint, S_IRUGO | S_IWUSR); unsigned int bam_mux_rx_req_size = BAM_MUX_RX_REQ_SIZE; module_param(bam_mux_rx_req_size, uint, S_IRUGO | S_IWUSR); +unsigned int dl_intr_threshold = DL_INTR_THRESHOLD; +module_param(dl_intr_threshold, uint, S_IRUGO | S_IWUSR); + #define BAM_CH_OPENED BIT(0) #define BAM_CH_READY BIT(1) +#define SPS_PARAMS_PIPE_ID_MASK (0x1F) +#define SPS_PARAMS_SPS_MODE BIT(5) +#define SPS_PARAMS_TBE BIT(6) +#define MSM_VENDOR_ID BIT(16) + struct bam_ch_info { unsigned long flags; unsigned id; @@ -80,6 +96,14 @@ struct bam_ch_info { struct gbam_port *port; struct work_struct write_tobam_w; + struct work_struct write_tohost_w; + + struct usb_request *rx_req; + struct usb_request *tx_req; + + u8 src_pipe_idx; + u8 dst_pipe_idx; + u8 connection_idx; /* stats */ unsigned int pending_with_bam; @@ -93,9 +117,11 @@ struct bam_ch_info { struct gbam_port { unsigned port_num; - spinlock_t port_lock; + spinlock_t port_lock_ul; + spinlock_t port_lock_dl; struct grmnet *port_usb; + struct grmnet *gr; struct bam_ch_info data_ch; @@ -108,7 +134,10 @@ static struct bam_portmaster { struct platform_driver pdrv; } bam_ports[BAM_N_PORTS]; +struct gbam_port *bam2bam_ports[BAM2BAM_N_PORTS]; static void gbam_start_rx(struct gbam_port *port); +static void gbam_start_endless_rx(struct gbam_port *port); +static void gbam_start_endless_tx(struct gbam_port *port); /*---------------misc functions---------------- */ static void gbam_free_requests(struct usb_ep *ep, struct list_head *head) @@ -157,9 +186,9 @@ static void gbam_write_data_tohost(struct gbam_port *port) struct usb_request *req; struct usb_ep *ep; - spin_lock_irqsave(&port->port_lock, flags); + spin_lock_irqsave(&port->port_lock_dl, flags); if (!port->port_usb) { - spin_unlock_irqrestore(&port->port_lock, flags); + spin_unlock_irqrestore(&port->port_lock_dl, flags); return; } @@ -168,7 +197,7 @@ static void gbam_write_data_tohost(struct gbam_port *port) while (!list_empty(&d->tx_idle)) { skb = __skb_dequeue(&d->tx_skb_q); if (!skb) { - spin_unlock_irqrestore(&port->port_lock, flags); + spin_unlock_irqrestore(&port->port_lock_dl, flags); return; } req = list_first_entry(&d->tx_idle, @@ -177,12 +206,19 @@ static void gbam_write_data_tohost(struct gbam_port *port) req->context = skb; req->buf = skb->data; req->length = skb->len; + n_tx_req_queued++; + if (n_tx_req_queued == dl_intr_threshold) { + req->no_interrupt = 0; + n_tx_req_queued = 0; + } else { + req->no_interrupt = 1; + } list_del(&req->list); - spin_unlock(&port->port_lock); + spin_unlock(&port->port_lock_dl); ret = usb_ep_queue(ep, req, GFP_ATOMIC); - spin_lock(&port->port_lock); + spin_lock(&port->port_lock_dl); if (ret) { pr_err("%s: usb epIn failed\n", __func__); list_add(&req->list, &d->tx_idle); @@ -191,7 +227,18 @@ static void gbam_write_data_tohost(struct gbam_port *port) } d->to_host++; } - spin_unlock_irqrestore(&port->port_lock, flags); + spin_unlock_irqrestore(&port->port_lock_dl, flags); +} + +static void gbam_write_data_tohost_w(struct work_struct *w) +{ + struct bam_ch_info *d; + struct gbam_port *port; + + d = container_of(w, struct bam_ch_info, write_tohost_w); + port = d->port; + + gbam_write_data_tohost(port); } void gbam_data_recv_cb(void *p, struct sk_buff *skb) @@ -206,9 +253,9 @@ void gbam_data_recv_cb(void *p, struct sk_buff *skb) pr_debug("%s: p:%p#%d d:%p skb_len:%d\n", __func__, port, port->port_num, d, skb->len); - spin_lock_irqsave(&port->port_lock, flags); + spin_lock_irqsave(&port->port_lock_dl, flags); if (!port->port_usb) { - spin_unlock_irqrestore(&port->port_lock, flags); + spin_unlock_irqrestore(&port->port_lock_dl, flags); dev_kfree_skb_any(skb); return; } @@ -218,13 +265,13 @@ void gbam_data_recv_cb(void *p, struct sk_buff *skb) if (printk_ratelimit()) pr_err("%s: tx pkt dropped: tx_drop_cnt:%u\n", __func__, d->tohost_drp_cnt); - spin_unlock_irqrestore(&port->port_lock, flags); + spin_unlock_irqrestore(&port->port_lock_dl, flags); dev_kfree_skb_any(skb); return; } __skb_queue_tail(&d->tx_skb_q, skb); - spin_unlock_irqrestore(&port->port_lock, flags); + spin_unlock_irqrestore(&port->port_lock_dl, flags); gbam_write_data_tohost(port); } @@ -240,7 +287,7 @@ void gbam_data_write_done(void *p, struct sk_buff *skb) dev_kfree_skb_any(skb); - spin_lock_irqsave(&port->port_lock, flags); + spin_lock_irqsave(&port->port_lock_ul, flags); d->pending_with_bam--; @@ -248,7 +295,7 @@ void gbam_data_write_done(void *p, struct sk_buff *skb) port, d, d->to_modem, d->pending_with_bam, port->port_num); - spin_unlock_irqrestore(&port->port_lock, flags); + spin_unlock_irqrestore(&port->port_lock_ul, flags); queue_work(gbam_wq, &d->write_tobam_w); } @@ -265,18 +312,17 @@ static void gbam_data_write_tobam(struct work_struct *w) d = container_of(w, struct bam_ch_info, write_tobam_w); port = d->port; - spin_lock_irqsave(&port->port_lock, flags); + spin_lock_irqsave(&port->port_lock_ul, flags); if (!port->port_usb) { - spin_unlock_irqrestore(&port->port_lock, flags); + spin_unlock_irqrestore(&port->port_lock_ul, flags); return; } while (d->pending_with_bam < BAM_PENDING_LIMIT) { skb = __skb_dequeue(&d->rx_skb_q); - if (!skb) { - spin_unlock_irqrestore(&port->port_lock, flags); - return; - } + if (!skb) + break; + d->pending_with_bam++; d->to_modem++; @@ -284,9 +330,9 @@ static void gbam_data_write_tobam(struct work_struct *w) port, d, d->to_modem, d->pending_with_bam, port->port_num); - spin_unlock_irqrestore(&port->port_lock, flags); + spin_unlock_irqrestore(&port->port_lock_ul, flags); ret = msm_bam_dmux_write(d->id, skb); - spin_lock_irqsave(&port->port_lock, flags); + spin_lock_irqsave(&port->port_lock_ul, flags); if (ret) { pr_debug("%s: write error:%d\n", __func__, ret); d->pending_with_bam--; @@ -299,7 +345,7 @@ static void gbam_data_write_tobam(struct work_struct *w) qlen = d->rx_skb_q.qlen; - spin_unlock_irqrestore(&port->port_lock, flags); + spin_unlock_irqrestore(&port->port_lock_ul, flags); if (qlen < BAM_MUX_RX_PKT_FCTRL_DIS_TSHOLD) gbam_start_rx(port); @@ -331,12 +377,12 @@ static void gbam_epin_complete(struct usb_ep *ep, struct usb_request *req) if (!port) return; - spin_lock(&port->port_lock); + spin_lock(&port->port_lock_dl); d = &port->data_ch; list_add_tail(&req->list, &d->tx_idle); - spin_unlock(&port->port_lock); + spin_unlock(&port->port_lock_dl); - gbam_write_data_tohost(port); + queue_work(gbam_wq, &d->write_tohost_w); } static void @@ -369,7 +415,7 @@ gbam_epout_complete(struct usb_ep *ep, struct usb_request *req) break; } - spin_lock(&port->port_lock); + spin_lock(&port->port_lock_ul); if (queue) { __skb_queue_tail(&d->rx_skb_q, skb); queue_work(gbam_wq, &d->write_tobam_w); @@ -382,16 +428,16 @@ gbam_epout_complete(struct usb_ep *ep, struct usb_request *req) d->rx_skb_q.qlen >= bam_mux_rx_fctrl_en_thld) { list_add_tail(&req->list, &d->rx_idle); - spin_unlock(&port->port_lock); + spin_unlock(&port->port_lock_ul); return; } - spin_unlock(&port->port_lock); + spin_unlock(&port->port_lock_ul); skb = alloc_skb(bam_mux_rx_req_size + BAM_MUX_HDR, GFP_ATOMIC); if (!skb) { - spin_lock(&port->port_lock); + spin_lock(&port->port_lock_ul); list_add_tail(&req->list, &d->rx_idle); - spin_unlock(&port->port_lock); + spin_unlock(&port->port_lock_ul); return; } skb_reserve(skb, BAM_MUX_HDR); @@ -408,12 +454,26 @@ gbam_epout_complete(struct usb_ep *ep, struct usb_request *req) pr_err("%s: data rx enqueue err %d\n", __func__, status); - spin_lock(&port->port_lock); + spin_lock(&port->port_lock_ul); list_add_tail(&req->list, &d->rx_idle); - spin_unlock(&port->port_lock); + spin_unlock(&port->port_lock_ul); } } +static void gbam_endless_rx_complete(struct usb_ep *ep, struct usb_request *req) +{ + int status = req->status; + + pr_debug("%s status: %d\n", __func__, status); +} + +static void gbam_endless_tx_complete(struct usb_ep *ep, struct usb_request *req) +{ + int status = req->status; + + pr_debug("%s status: %d\n", __func__, status); +} + static void gbam_start_rx(struct gbam_port *port) { struct usb_request *req; @@ -423,9 +483,9 @@ static void gbam_start_rx(struct gbam_port *port) int ret; struct sk_buff *skb; - spin_lock_irqsave(&port->port_lock, flags); + spin_lock_irqsave(&port->port_lock_ul, flags); if (!port->port_usb) { - spin_unlock_irqrestore(&port->port_lock, flags); + spin_unlock_irqrestore(&port->port_lock_ul, flags); return; } @@ -450,9 +510,9 @@ static void gbam_start_rx(struct gbam_port *port) req->length = bam_mux_rx_req_size; req->context = skb; - spin_unlock_irqrestore(&port->port_lock, flags); + spin_unlock_irqrestore(&port->port_lock_ul, flags); ret = usb_ep_queue(ep, req, GFP_ATOMIC); - spin_lock_irqsave(&port->port_lock, flags); + spin_lock_irqsave(&port->port_lock_ul, flags); if (ret) { dev_kfree_skb_any(skb); @@ -466,7 +526,27 @@ static void gbam_start_rx(struct gbam_port *port) break; } } - spin_unlock_irqrestore(&port->port_lock, flags); + spin_unlock_irqrestore(&port->port_lock_ul, flags); +} + +static void gbam_start_endless_rx(struct gbam_port *port) +{ + struct bam_ch_info *d = &port->data_ch; + int status; + + status = usb_ep_queue(port->port_usb->out, d->rx_req, GFP_ATOMIC); + if (status) + pr_err("%s: error enqueuing transfer, %d\n", __func__, status); +} + +static void gbam_start_endless_tx(struct gbam_port *port) +{ + struct bam_ch_info *d = &port->data_ch; + int status; + + status = usb_ep_queue(port->port_usb->in, d->tx_req, GFP_ATOMIC); + if (status) + pr_err("%s: error enqueuing transfer, %d\n", __func__, status); } static void gbam_start_io(struct gbam_port *port) @@ -478,9 +558,9 @@ static void gbam_start_io(struct gbam_port *port) pr_debug("%s: port:%p\n", __func__, port); - spin_lock_irqsave(&port->port_lock, flags); + spin_lock_irqsave(&port->port_lock_ul, flags); if (!port->port_usb) { - spin_unlock_irqrestore(&port->port_lock, flags); + spin_unlock_irqrestore(&port->port_lock_ul, flags); return; } @@ -493,6 +573,8 @@ static void gbam_start_io(struct gbam_port *port) return; } + spin_unlock_irqrestore(&port->port_lock_ul, flags); + spin_lock_irqsave(&port->port_lock_dl, flags); ep = port->port_usb->in; ret = gbam_alloc_requests(ep, &d->tx_idle, bam_mux_tx_q_size, gbam_epin_complete, GFP_ATOMIC); @@ -502,7 +584,7 @@ static void gbam_start_io(struct gbam_port *port) return; } - spin_unlock_irqrestore(&port->port_lock, flags); + spin_unlock_irqrestore(&port->port_lock_dl, flags); /* queue out requests */ gbam_start_rx(port); @@ -520,6 +602,34 @@ static void gbam_notify(void *p, int event, unsigned long data) } } +static void gbam_free_buffers(struct gbam_port *port) +{ + struct sk_buff *skb; + unsigned long flags; + struct bam_ch_info *d; + + spin_lock_irqsave(&port->port_lock_ul, flags); + spin_lock(&port->port_lock_dl); + + if (!port || !port->port_usb) + goto free_buf_out; + + d = &port->data_ch; + + gbam_free_requests(port->port_usb->in, &d->tx_idle); + gbam_free_requests(port->port_usb->out, &d->rx_idle); + + while ((skb = __skb_dequeue(&d->tx_skb_q))) + dev_kfree_skb_any(skb); + + while ((skb = __skb_dequeue(&d->rx_skb_q))) + dev_kfree_skb_any(skb); + +free_buf_out: + spin_unlock(&port->port_lock_dl); + spin_unlock_irqrestore(&port->port_lock_ul, flags); +} + static void gbam_disconnect_work(struct work_struct *w) { struct gbam_port *port = @@ -533,6 +643,24 @@ static void gbam_disconnect_work(struct work_struct *w) clear_bit(BAM_CH_OPENED, &d->flags); } +static void gbam2bam_disconnect_work(struct work_struct *w) +{ + struct gbam_port *port = + container_of(w, struct gbam_port, disconnect_w); + unsigned long flags; + + spin_lock_irqsave(&port->port_lock_ul, flags); + spin_lock(&port->port_lock_dl); + port->port_usb = 0; + spin_unlock(&port->port_lock_dl); + spin_unlock_irqrestore(&port->port_lock_ul, flags); + + /* disable endpoints */ + usb_ep_disable(port->gr->out); + usb_ep_disable(port->gr->in); + +} + static void gbam_connect_work(struct work_struct *w) { struct gbam_port *port = container_of(w, struct gbam_port, connect_w); @@ -540,12 +668,15 @@ static void gbam_connect_work(struct work_struct *w) int ret; unsigned long flags; - spin_lock_irqsave(&port->port_lock, flags); + spin_lock_irqsave(&port->port_lock_ul, flags); + spin_lock(&port->port_lock_dl); if (!port->port_usb) { - spin_unlock_irqrestore(&port->port_lock, flags); + spin_unlock(&port->port_lock_dl); + spin_unlock_irqrestore(&port->port_lock_ul, flags); return; } - spin_unlock_irqrestore(&port->port_lock, flags); + spin_unlock(&port->port_lock_dl); + spin_unlock_irqrestore(&port->port_lock_ul, flags); if (!test_bit(BAM_CH_READY, &d->flags)) return; @@ -563,30 +694,70 @@ static void gbam_connect_work(struct work_struct *w) pr_debug("%s: done\n", __func__); } -static void gbam_free_buffers(struct gbam_port *port) +static void gbam2bam_connect_work(struct work_struct *w) { - struct sk_buff *skb; - unsigned long flags; - struct bam_ch_info *d; + struct gbam_port *port = container_of(w, struct gbam_port, connect_w); + struct bam_ch_info *d = &port->data_ch; + u32 sps_params; + int ret; + unsigned long flags; - spin_lock_irqsave(&port->port_lock, flags); + ret = usb_ep_enable(port->gr->in, port->gr->in_desc); + if (ret) { + pr_err("%s: usb_ep_enable failed eptype:IN ep:%p", + __func__, port->gr->in); + return; + } + port->gr->in->driver_data = port; - if (!port || !port->port_usb) - goto free_buf_out; + ret = usb_ep_enable(port->gr->out, port->gr->out_desc); + if (ret) { + pr_err("%s: usb_ep_enable failed eptype:OUT ep:%p", + __func__, port->gr->out); + port->gr->in->driver_data = 0; + return; + } + port->gr->out->driver_data = port; + spin_lock_irqsave(&port->port_lock_ul, flags); + spin_lock(&port->port_lock_dl); + port->port_usb = port->gr; + spin_unlock(&port->port_lock_dl); + spin_unlock_irqrestore(&port->port_lock_ul, flags); + + ret = usb_bam_connect(d->connection_idx, &d->src_pipe_idx, + &d->dst_pipe_idx); + if (ret) { + pr_err("%s: usb_bam_connect failed: err:%d\n", + __func__, ret); + return; + } - d = &port->data_ch; + d->rx_req = usb_ep_alloc_request(port->port_usb->out, GFP_KERNEL); + if (!d->rx_req) + return; - gbam_free_requests(port->port_usb->in, &d->tx_idle); - gbam_free_requests(port->port_usb->out, &d->rx_idle); + d->rx_req->context = port; + d->rx_req->complete = gbam_endless_rx_complete; + d->rx_req->length = 0; + sps_params = (SPS_PARAMS_SPS_MODE | d->src_pipe_idx | + MSM_VENDOR_ID) & ~SPS_PARAMS_TBE; + d->rx_req->udc_priv = sps_params; + d->tx_req = usb_ep_alloc_request(port->port_usb->in, GFP_KERNEL); + if (!d->tx_req) + return; - while ((skb = __skb_dequeue(&d->tx_skb_q))) - dev_kfree_skb_any(skb); + d->tx_req->context = port; + d->tx_req->complete = gbam_endless_tx_complete; + d->tx_req->length = 0; + sps_params = (SPS_PARAMS_SPS_MODE | d->dst_pipe_idx | + MSM_VENDOR_ID) & ~SPS_PARAMS_TBE; + d->tx_req->udc_priv = sps_params; - while ((skb = __skb_dequeue(&d->rx_skb_q))) - dev_kfree_skb_any(skb); + /* queue in & out requests */ + gbam_start_endless_rx(port); + gbam_start_endless_tx(port); -free_buf_out: - spin_unlock_irqrestore(&port->port_lock, flags); + pr_debug("%s: done\n", __func__); } /* BAM data channel ready, allow attempt to open */ @@ -608,10 +779,12 @@ static int gbam_data_ch_probe(struct platform_device *pdev) set_bit(BAM_CH_READY, &d->flags); /* if usb is online, try opening bam_ch */ - spin_lock_irqsave(&port->port_lock, flags); + spin_lock_irqsave(&port->port_lock_ul, flags); + spin_lock(&port->port_lock_dl); if (port->port_usb) queue_work(gbam_wq, &port->connect_w); - spin_unlock_irqrestore(&port->port_lock, flags); + spin_unlock(&port->port_lock_dl); + spin_unlock_irqrestore(&port->port_lock_ul, flags); break; } @@ -638,12 +811,14 @@ static int gbam_data_ch_remove(struct platform_device *pdev) port = bam_ports[i].port; d = &port->data_ch; - spin_lock_irqsave(&port->port_lock, flags); + spin_lock_irqsave(&port->port_lock_ul, flags); + spin_lock(&port->port_lock_dl); if (port->port_usb) { ep_in = port->port_usb->in; ep_out = port->port_usb->out; } - spin_unlock_irqrestore(&port->port_lock, flags); + spin_unlock(&port->port_lock_dl); + spin_unlock_irqrestore(&port->port_lock_ul, flags); if (ep_in) usb_ep_fifo_flush(ep_in); @@ -654,6 +829,9 @@ static int gbam_data_ch_remove(struct platform_device *pdev) msm_bam_dmux_close(d->id); + /* bam dmux will free all pending skbs */ + d->pending_with_bam = 0; + clear_bit(BAM_CH_READY, &d->flags); clear_bit(BAM_CH_OPENED, &d->flags); } @@ -673,6 +851,13 @@ static void gbam_port_free(int portno) } } +static void gbam2bam_port_free(int portno) +{ + struct gbam_port *port = bam2bam_ports[portno]; + + kfree(port); +} + static int gbam_port_alloc(int portno) { struct gbam_port *port; @@ -686,7 +871,8 @@ static int gbam_port_alloc(int portno) port->port_num = portno; /* port initialization */ - spin_lock_init(&port->port_lock); + spin_lock_init(&port->port_lock_ul); + spin_lock_init(&port->port_lock_dl); INIT_WORK(&port->connect_w, gbam_connect_work); INIT_WORK(&port->disconnect_w, gbam_disconnect_work); @@ -696,6 +882,7 @@ static int gbam_port_alloc(int portno) INIT_LIST_HEAD(&d->tx_idle); INIT_LIST_HEAD(&d->rx_idle); INIT_WORK(&d->write_tobam_w, gbam_data_write_tobam); + INIT_WORK(&d->write_tohost_w, gbam_write_data_tohost_w); skb_queue_head_init(&d->tx_skb_q); skb_queue_head_init(&d->rx_skb_q); d->id = bam_ch_ids[portno]; @@ -709,6 +896,33 @@ static int gbam_port_alloc(int portno) pdrv->driver.owner = THIS_MODULE; platform_driver_register(pdrv); + pr_debug("%s: port:%p portno:%d\n", __func__, port, portno); + + return 0; +} + +static int gbam2bam_port_alloc(int portno) +{ + struct gbam_port *port; + struct bam_ch_info *d; + + port = kzalloc(sizeof(struct gbam_port), GFP_KERNEL); + if (!port) + return -ENOMEM; + + port->port_num = portno; + + /* port initialization */ + spin_lock_init(&port->port_lock_ul); + spin_lock_init(&port->port_lock_dl); + + INIT_WORK(&port->connect_w, gbam2bam_connect_work); + INIT_WORK(&port->disconnect_w, gbam2bam_disconnect_work); + + /* data ch */ + d = &port->data_ch; + d->port = port; + bam2bam_ports[portno] = port; pr_debug("%s: port:%p portno:%d\n", __func__, port, portno); @@ -736,7 +950,8 @@ static ssize_t gbam_read_stats(struct file *file, char __user *ubuf, port = bam_ports[i].port; if (!port) continue; - spin_lock_irqsave(&port->port_lock, flags); + spin_lock_irqsave(&port->port_lock_ul, flags); + spin_lock(&port->port_lock_dl); d = &port->data_ch; @@ -759,7 +974,8 @@ static ssize_t gbam_read_stats(struct file *file, char __user *ubuf, test_bit(BAM_CH_OPENED, &d->flags), test_bit(BAM_CH_READY, &d->flags)); - spin_unlock_irqrestore(&port->port_lock, flags); + spin_unlock(&port->port_lock_dl); + spin_unlock_irqrestore(&port->port_lock_ul, flags); } ret = simple_read_from_buffer(ubuf, count, ppos, buf, temp); @@ -782,7 +998,8 @@ static ssize_t gbam_reset_stats(struct file *file, const char __user *buf, if (!port) continue; - spin_lock_irqsave(&port->port_lock, flags); + spin_lock_irqsave(&port->port_lock_ul, flags); + spin_lock(&port->port_lock_dl); d = &port->data_ch; @@ -792,7 +1009,8 @@ static ssize_t gbam_reset_stats(struct file *file, const char __user *buf, d->tohost_drp_cnt = 0; d->tomodem_drp_cnt = 0; - spin_unlock_irqrestore(&port->port_lock, flags); + spin_unlock(&port->port_lock_dl); + spin_unlock_irqrestore(&port->port_lock_ul, flags); } return count; } @@ -820,7 +1038,7 @@ static void gbam_debugfs_init(void) static void gam_debugfs_init(void) { } #endif -void gbam_disconnect(struct grmnet *gr, u8 port_num) +void gbam_disconnect(struct grmnet *gr, u8 port_num, enum transport_type trans) { struct gbam_port *port; unsigned long flags; @@ -828,8 +1046,17 @@ void gbam_disconnect(struct grmnet *gr, u8 port_num) pr_debug("%s: grmnet:%p port#%d\n", __func__, gr, port_num); - if (port_num >= n_bam_ports) { - pr_err("%s: invalid portno#%d\n", __func__, port_num); + if (trans == USB_GADGET_XPORT_BAM && + port_num >= n_bam_ports) { + pr_err("%s: invalid bam portno#%d\n", + __func__, port_num); + return; + } + + if (trans == USB_GADGET_XPORT_BAM2BAM && + port_num >= n_bam2bam_ports) { + pr_err("%s: invalid bam2bam portno#%d\n", + __func__, port_num); return; } @@ -837,24 +1064,34 @@ void gbam_disconnect(struct grmnet *gr, u8 port_num) pr_err("%s: grmnet port is null\n", __func__); return; } + if (trans == USB_GADGET_XPORT_BAM) + port = bam_ports[port_num].port; + else + port = bam2bam_ports[port_num]; - port = bam_ports[port_num].port; d = &port->data_ch; + port->gr = gr; - gbam_free_buffers(port); + if (trans == USB_GADGET_XPORT_BAM) { + gbam_free_buffers(port); - spin_lock_irqsave(&port->port_lock, flags); + spin_lock_irqsave(&port->port_lock_ul, flags); + spin_lock(&port->port_lock_dl); port->port_usb = 0; - spin_unlock_irqrestore(&port->port_lock, flags); + n_tx_req_queued = 0; + spin_unlock(&port->port_lock_dl); + spin_unlock_irqrestore(&port->port_lock_ul, flags); - /* disable endpoints */ - usb_ep_disable(gr->out); - usb_ep_disable(gr->in); + /* disable endpoints */ + usb_ep_disable(gr->out); + usb_ep_disable(gr->in); + } queue_work(gbam_wq, &port->disconnect_w); } -int gbam_connect(struct grmnet *gr, u8 port_num) +int gbam_connect(struct grmnet *gr, u8 port_num, + enum transport_type trans, u8 connection_idx) { struct gbam_port *port; struct bam_ch_info *d; @@ -863,7 +1100,12 @@ int gbam_connect(struct grmnet *gr, u8 port_num) pr_debug("%s: grmnet:%p port#%d\n", __func__, gr, port_num); - if (port_num >= n_bam_ports) { + if (trans == USB_GADGET_XPORT_BAM && port_num >= n_bam_ports) { + pr_err("%s: invalid portno#%d\n", __func__, port_num); + return -ENODEV; + } + + if (trans == USB_GADGET_XPORT_BAM2BAM && port_num >= n_bam2bam_ports) { pr_err("%s: invalid portno#%d\n", __func__, port_num); return -ENODEV; } @@ -873,52 +1115,66 @@ int gbam_connect(struct grmnet *gr, u8 port_num) return -ENODEV; } - port = bam_ports[port_num].port; + if (trans == USB_GADGET_XPORT_BAM) + port = bam_ports[port_num].port; + else + port = bam2bam_ports[port_num]; + d = &port->data_ch; - ret = usb_ep_enable(gr->in, gr->in_desc); - if (ret) { - pr_err("%s: usb_ep_enable failed eptype:IN ep:%p", - __func__, gr->in); - return ret; - } - gr->in->driver_data = port; + if (trans == USB_GADGET_XPORT_BAM) { + ret = usb_ep_enable(gr->in, gr->in_desc); + if (ret) { + pr_err("%s: usb_ep_enable failed eptype:IN ep:%p", + __func__, gr->in); + return ret; + } + gr->in->driver_data = port; - ret = usb_ep_enable(gr->out, gr->out_desc); - if (ret) { - pr_err("%s: usb_ep_enable failed eptype:OUT ep:%p", - __func__, gr->out); - gr->in->driver_data = 0; - return ret; - } - gr->out->driver_data = port; + ret = usb_ep_enable(gr->out, gr->out_desc); + if (ret) { + pr_err("%s: usb_ep_enable failed eptype:OUT ep:%p", + __func__, gr->out); + gr->in->driver_data = 0; + return ret; + } + gr->out->driver_data = port; - spin_lock_irqsave(&port->port_lock, flags); + spin_lock_irqsave(&port->port_lock_ul, flags); + spin_lock(&port->port_lock_dl); port->port_usb = gr; - d->to_host = 0; - d->to_modem = 0; - d->pending_with_bam = 0; - d->tohost_drp_cnt = 0; - d->tomodem_drp_cnt = 0; - spin_unlock_irqrestore(&port->port_lock, flags); + d->to_host = 0; + d->to_modem = 0; + d->pending_with_bam = 0; + d->tohost_drp_cnt = 0; + d->tomodem_drp_cnt = 0; + spin_unlock(&port->port_lock_dl); + spin_unlock_irqrestore(&port->port_lock_ul, flags); + } + if (trans == USB_GADGET_XPORT_BAM2BAM) { + port->gr = gr; + d->connection_idx = connection_idx; + } queue_work(gbam_wq, &port->connect_w); return 0; } -int gbam_setup(unsigned int count) +int gbam_setup(unsigned int no_bam_port, unsigned int no_bam2bam_port) { int i; int ret; - pr_debug("%s: requested ports:%d\n", __func__, count); + pr_debug("%s: requested BAM ports:%d and BAM2BAM ports:%d\n", + __func__, no_bam_port, no_bam2bam_port); - if (!count || count > BAM_N_PORTS) { - pr_err("%s: Invalid num of ports count:%d\n", - __func__, count); + if ((!no_bam_port && !no_bam2bam_port) || no_bam_port > BAM_N_PORTS + || no_bam2bam_port > BAM2BAM_N_PORTS) { + pr_err("%s: Invalid num of ports count:%d,%d\n", + __func__, no_bam_port, no_bam2bam_port); return -EINVAL; } @@ -929,7 +1185,7 @@ int gbam_setup(unsigned int count) return -ENOMEM; } - for (i = 0; i < count; i++) { + for (i = 0; i < no_bam_port; i++) { n_bam_ports++; ret = gbam_port_alloc(i); if (ret) { @@ -939,13 +1195,23 @@ int gbam_setup(unsigned int count) } } + for (i = 0; i < no_bam2bam_port; i++) { + n_bam2bam_ports++; + ret = gbam2bam_port_alloc(i); + if (ret) { + n_bam2bam_ports--; + pr_err("%s: Unable to alloc port:%d\n", __func__, i); + goto free_bam_ports; + } + } gbam_debugfs_init(); - return 0; + free_bam_ports: for (i = 0; i < n_bam_ports; i++) gbam_port_free(i); - + for (i = 0; i < n_bam2bam_ports; i++) + gbam2bam_port_free(i); destroy_workqueue(gbam_wq); return ret; diff --git a/drivers/usb/gadget/u_ctrl_hsic.c b/drivers/usb/gadget/u_ctrl_hsic.c new file mode 100644 index 00000000000..fdfab968a69 --- /dev/null +++ b/drivers/usb/gadget/u_ctrl_hsic.c @@ -0,0 +1,617 @@ +/* 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 + +/* from cdc-acm.h */ +#define ACM_CTRL_RTS (1 << 1) /* unused with full duplex */ +#define ACM_CTRL_DTR (1 << 0) /* host is ready for data r/w */ +#define ACM_CTRL_OVERRUN (1 << 6) +#define ACM_CTRL_PARITY (1 << 5) +#define ACM_CTRL_FRAMING (1 << 4) +#define ACM_CTRL_RI (1 << 3) +#define ACM_CTRL_BRK (1 << 2) +#define ACM_CTRL_DSR (1 << 1) +#define ACM_CTRL_DCD (1 << 0) + + +static unsigned int no_ctrl_ports; + +static const char *ctrl_bridge_names[] = { + "dun_ctrl_hsic0", + "rmnet_ctrl_hsic0" +}; + +#define CTRL_BRIDGE_NAME_MAX_LEN 20 +#define READ_BUF_LEN 1024 + +#define CH_OPENED 0 +#define CH_READY 1 + +struct gctrl_port { + /* port */ + unsigned port_num; + + /* gadget */ + spinlock_t port_lock; + void *port_usb; + + /* work queue*/ + struct workqueue_struct *wq; + struct work_struct connect_w; + struct work_struct disconnect_w; + + enum gadget_type gtype; + + /*ctrl pkt response cb*/ + int (*send_cpkt_response)(void *g, void *buf, size_t len); + + struct bridge brdg; + + /* bridge status */ + unsigned long bridge_sts; + + /* control bits */ + unsigned cbits_tomodem; + unsigned cbits_tohost; + + /* counters */ + unsigned long to_modem; + unsigned long to_host; + unsigned long drp_cpkt_cnt; +}; + +static struct { + struct gctrl_port *port; + struct platform_driver pdrv; +} gctrl_ports[NUM_PORTS]; + +static int ghsic_ctrl_receive(void *dev, void *buf, size_t actual) +{ + struct gctrl_port *port = dev; + int retval = 0; + + pr_debug_ratelimited("%s: read complete bytes read: %d\n", + __func__, actual); + + /* send it to USB here */ + if (port && port->send_cpkt_response) { + retval = port->send_cpkt_response(port->port_usb, buf, actual); + port->to_host++; + } + + return retval; +} + +static int +ghsic_send_cpkt_tomodem(u8 portno, void *buf, size_t len) +{ + void *cbuf; + struct gctrl_port *port; + + if (portno >= no_ctrl_ports) { + pr_err("%s: Invalid portno#%d\n", __func__, portno); + return -ENODEV; + } + + port = gctrl_ports[portno].port; + if (!port) { + pr_err("%s: port is null\n", __func__); + return -ENODEV; + } + + cbuf = kmalloc(len, GFP_ATOMIC); + if (!cbuf) + return -ENOMEM; + + memcpy(cbuf, buf, len); + + /* drop cpkt if ch is not open */ + if (!test_bit(CH_OPENED, &port->bridge_sts)) { + port->drp_cpkt_cnt++; + kfree(cbuf); + return 0; + } + + pr_debug("%s: ctrl_pkt:%d bytes\n", __func__, len); + + ctrl_bridge_write(port->brdg.ch_id, cbuf, len); + + port->to_modem++; + + return 0; +} + +static void +ghsic_send_cbits_tomodem(void *gptr, u8 portno, int cbits) +{ + struct gctrl_port *port; + + if (portno >= no_ctrl_ports || !gptr) { + pr_err("%s: Invalid portno#%d\n", __func__, portno); + return; + } + + port = gctrl_ports[portno].port; + if (!port) { + pr_err("%s: port is null\n", __func__); + return; + } + + if (cbits == port->cbits_tomodem) + return; + + port->cbits_tomodem = cbits; + + if (!test_bit(CH_OPENED, &port->bridge_sts)) + return; + + pr_debug("%s: ctrl_tomodem:%d\n", __func__, cbits); + + ctrl_bridge_set_cbits(port->brdg.ch_id, cbits); +} + +static void ghsic_ctrl_connect_w(struct work_struct *w) +{ + struct gserial *gser = NULL; + struct grmnet *gr = NULL; + struct gctrl_port *port = + container_of(w, struct gctrl_port, connect_w); + unsigned long flags; + int retval; + unsigned cbits; + + if (!port || !test_bit(CH_READY, &port->bridge_sts)) + return; + + pr_debug("%s: port:%p\n", __func__, port); + + retval = ctrl_bridge_open(&port->brdg); + if (retval) { + pr_err("%s: ctrl bridge open failed :%d\n", __func__, retval); + return; + } + + spin_lock_irqsave(&port->port_lock, flags); + if (!port->port_usb) { + ctrl_bridge_close(port->brdg.ch_id); + spin_unlock_irqrestore(&port->port_lock, flags); + return; + } + set_bit(CH_OPENED, &port->bridge_sts); + spin_unlock_irqrestore(&port->port_lock, flags); + + cbits = ctrl_bridge_get_cbits_tohost(port->brdg.ch_id); + + if (port->gtype == USB_GADGET_SERIAL && (cbits & ACM_CTRL_DCD)) { + gser = port->port_usb; + if (gser && gser->connect) + gser->connect(gser); + return; + } + + if (port->gtype == USB_GADGET_RMNET) { + gr = port->port_usb; + if (gr && gr->connect) + gr->connect(gr); + } +} + +int ghsic_ctrl_connect(void *gptr, int port_num) +{ + struct gctrl_port *port; + struct gserial *gser; + struct grmnet *gr; + unsigned long flags; + + pr_debug("%s: port#%d\n", __func__, port_num); + + if (port_num > no_ctrl_ports || !gptr) { + pr_err("%s: invalid portno#%d\n", __func__, port_num); + return -ENODEV; + } + + port = gctrl_ports[port_num].port; + if (!port) { + pr_err("%s: port is null\n", __func__); + return -ENODEV; + } + + spin_lock_irqsave(&port->port_lock, flags); + if (port->gtype == USB_GADGET_SERIAL) { + gser = gptr; + gser->notify_modem = ghsic_send_cbits_tomodem; + } + + if (port->gtype == USB_GADGET_RMNET) { + gr = gptr; + port->send_cpkt_response = gr->send_cpkt_response; + gr->send_encap_cmd = ghsic_send_cpkt_tomodem; + gr->notify_modem = ghsic_send_cbits_tomodem; + } + + port->port_usb = gptr; + port->to_host = 0; + port->to_modem = 0; + port->drp_cpkt_cnt = 0; + spin_unlock_irqrestore(&port->port_lock, flags); + + queue_work(port->wq, &port->connect_w); + + return 0; +} + +static void gctrl_disconnect_w(struct work_struct *w) +{ + struct gctrl_port *port = + container_of(w, struct gctrl_port, disconnect_w); + + if (!test_bit(CH_OPENED, &port->bridge_sts)) + return; + + /* send the dtr zero */ + ctrl_bridge_close(port->brdg.ch_id); + clear_bit(CH_OPENED, &port->bridge_sts); +} + +void ghsic_ctrl_disconnect(void *gptr, int port_num) +{ + struct gctrl_port *port; + struct gserial *gser = NULL; + struct grmnet *gr = NULL; + unsigned long flags; + + pr_debug("%s: port#%d\n", __func__, port_num); + + port = gctrl_ports[port_num].port; + + if (port_num > no_ctrl_ports) { + pr_err("%s: invalid portno#%d\n", __func__, port_num); + return; + } + + if (!gptr || !port) { + pr_err("%s: grmnet port is null\n", __func__); + return; + } + + if (port->gtype == USB_GADGET_SERIAL) + gser = gptr; + else + gr = gptr; + + spin_lock_irqsave(&port->port_lock, flags); + if (gr) { + gr->send_encap_cmd = 0; + gr->notify_modem = 0; + } + + if (gser) + gser->notify_modem = 0; + port->cbits_tomodem = 0; + port->port_usb = 0; + port->send_cpkt_response = 0; + spin_unlock_irqrestore(&port->port_lock, flags); + + queue_work(port->wq, &port->disconnect_w); +} + +static void ghsic_ctrl_status(void *ctxt, unsigned int ctrl_bits) +{ + struct gctrl_port *port = ctxt; + struct gserial *gser; + + pr_debug("%s - input control lines: dcd%c dsr%c break%c " + "ring%c framing%c parity%c overrun%c\n", __func__, + ctrl_bits & ACM_CTRL_DCD ? '+' : '-', + ctrl_bits & ACM_CTRL_DSR ? '+' : '-', + ctrl_bits & ACM_CTRL_BRK ? '+' : '-', + ctrl_bits & ACM_CTRL_RI ? '+' : '-', + ctrl_bits & ACM_CTRL_FRAMING ? '+' : '-', + ctrl_bits & ACM_CTRL_PARITY ? '+' : '-', + ctrl_bits & ACM_CTRL_OVERRUN ? '+' : '-'); + + port->cbits_tohost = ctrl_bits; + gser = port->port_usb; + if (gser && gser->send_modem_ctrl_bits) + gser->send_modem_ctrl_bits(gser, ctrl_bits); +} + +static int ghsic_ctrl_probe(struct platform_device *pdev) +{ + struct gctrl_port *port; + unsigned long flags; + + pr_debug("%s: name:%s\n", __func__, pdev->name); + + if (pdev->id >= no_ctrl_ports) { + pr_err("%s: invalid port: %d\n", __func__, pdev->id); + return -EINVAL; + } + + port = gctrl_ports[pdev->id].port; + set_bit(CH_READY, &port->bridge_sts); + + /* if usb is online, start read */ + spin_lock_irqsave(&port->port_lock, flags); + if (port->port_usb) + queue_work(port->wq, &port->connect_w); + spin_unlock_irqrestore(&port->port_lock, flags); + + return 0; +} + +static int ghsic_ctrl_remove(struct platform_device *pdev) +{ + struct gctrl_port *port; + struct gserial *gser = NULL; + struct grmnet *gr = NULL; + unsigned long flags; + + pr_debug("%s: name:%s\n", __func__, pdev->name); + + if (pdev->id >= no_ctrl_ports) { + pr_err("%s: invalid port: %d\n", __func__, pdev->id); + return -EINVAL; + } + + port = gctrl_ports[pdev->id].port; + + spin_lock_irqsave(&port->port_lock, flags); + if (!port->port_usb) { + spin_unlock_irqrestore(&port->port_lock, flags); + goto not_ready; + } + + if (port->gtype == USB_GADGET_SERIAL) + gser = port->port_usb; + else + gr = port->port_usb; + + port->cbits_tohost = 0; + spin_unlock_irqrestore(&port->port_lock, flags); + + if (gr && gr->disconnect) + gr->disconnect(gr); + + if (gser && gser->disconnect) + gser->disconnect(gser); + + ctrl_bridge_close(port->brdg.ch_id); + + clear_bit(CH_OPENED, &port->bridge_sts); +not_ready: + clear_bit(CH_READY, &port->bridge_sts); + + return 0; +} + +static void ghsic_ctrl_port_free(int portno) +{ + struct gctrl_port *port = gctrl_ports[portno].port; + struct platform_driver *pdrv = &gctrl_ports[portno].pdrv; + + destroy_workqueue(port->wq); + kfree(port); + + if (pdrv) + platform_driver_unregister(pdrv); +} + +static int gctrl_port_alloc(int portno, enum gadget_type gtype) +{ + struct gctrl_port *port; + struct platform_driver *pdrv; + + port = kzalloc(sizeof(struct gctrl_port), GFP_KERNEL); + if (!port) + return -ENOMEM; + + port->wq = create_singlethread_workqueue(ctrl_bridge_names[portno]); + if (!port->wq) { + pr_err("%s: Unable to create workqueue:%s\n", + __func__, ctrl_bridge_names[portno]); + return -ENOMEM; + } + + port->port_num = portno; + port->gtype = gtype; + + spin_lock_init(&port->port_lock); + + INIT_WORK(&port->connect_w, ghsic_ctrl_connect_w); + INIT_WORK(&port->disconnect_w, gctrl_disconnect_w); + + port->brdg.ch_id = portno; + port->brdg.ctx = port; + port->brdg.ops.send_pkt = ghsic_ctrl_receive; + if (port->gtype == USB_GADGET_SERIAL) + port->brdg.ops.send_cbits = ghsic_ctrl_status; + gctrl_ports[portno].port = port; + + pdrv = &gctrl_ports[portno].pdrv; + pdrv->probe = ghsic_ctrl_probe; + pdrv->remove = ghsic_ctrl_remove; + pdrv->driver.name = ctrl_bridge_names[portno]; + pdrv->driver.owner = THIS_MODULE; + + platform_driver_register(pdrv); + + pr_debug("%s: port:%p portno:%d\n", __func__, port, portno); + + return 0; +} + +int ghsic_ctrl_setup(unsigned int num_ports, enum gadget_type gtype) +{ + int first_port_id = no_ctrl_ports; + int total_num_ports = num_ports + no_ctrl_ports; + int i; + int ret = 0; + + if (!num_ports || total_num_ports > NUM_PORTS) { + pr_err("%s: Invalid num of ports count:%d\n", + __func__, num_ports); + return -EINVAL; + } + + pr_debug("%s: requested ports:%d\n", __func__, num_ports); + + for (i = first_port_id; i < (first_port_id + num_ports); i++) { + + /*probe can be called while port_alloc,so update no_ctrl_ports*/ + no_ctrl_ports++; + ret = gctrl_port_alloc(i, gtype); + if (ret) { + no_ctrl_ports--; + pr_err("%s: Unable to alloc port:%d\n", __func__, i); + goto free_ports; + } + } + + return first_port_id; + +free_ports: + for (i = first_port_id; i < no_ctrl_ports; i++) + ghsic_ctrl_port_free(i); + no_ctrl_ports = first_port_id; + return ret; +} + +#if defined(CONFIG_DEBUG_FS) +#define DEBUG_BUF_SIZE 1024 +static ssize_t gctrl_read_stats(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct gctrl_port *port; + struct platform_driver *pdrv; + char *buf; + unsigned long flags; + int ret; + int i; + int temp = 0; + + buf = kzalloc(sizeof(char) * DEBUG_BUF_SIZE, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + for (i = 0; i < no_ctrl_ports; i++) { + port = gctrl_ports[i].port; + if (!port) + continue; + pdrv = &gctrl_ports[i].pdrv; + spin_lock_irqsave(&port->port_lock, flags); + + temp += scnprintf(buf + temp, DEBUG_BUF_SIZE - temp, + "\nName: %s\n" + "#PORT:%d port: %p\n" + "to_usbhost: %lu\n" + "to_modem: %lu\n" + "cpkt_drp_cnt: %lu\n" + "DTR: %s\n" + "ch_open: %d\n" + "ch_ready: %d\n", + pdrv->driver.name, + i, port, + port->to_host, port->to_modem, + port->drp_cpkt_cnt, + port->cbits_tomodem ? "HIGH" : "LOW", + test_bit(CH_OPENED, &port->bridge_sts), + test_bit(CH_READY, &port->bridge_sts)); + + spin_unlock_irqrestore(&port->port_lock, flags); + } + + ret = simple_read_from_buffer(ubuf, count, ppos, buf, temp); + + kfree(buf); + + return ret; +} + +static ssize_t gctrl_reset_stats(struct file *file, + const char __user *buf, size_t count, loff_t *ppos) +{ + struct gctrl_port *port; + int i; + unsigned long flags; + + for (i = 0; i < no_ctrl_ports; i++) { + port = gctrl_ports[i].port; + if (!port) + continue; + + spin_lock_irqsave(&port->port_lock, flags); + port->to_host = 0; + port->to_modem = 0; + port->drp_cpkt_cnt = 0; + spin_unlock_irqrestore(&port->port_lock, flags); + } + return count; +} + +const struct file_operations gctrl_stats_ops = { + .read = gctrl_read_stats, + .write = gctrl_reset_stats, +}; + +struct dentry *gctrl_dent; +struct dentry *gctrl_dfile; +static void gctrl_debugfs_init(void) +{ + gctrl_dent = debugfs_create_dir("ghsic_ctrl_xport", 0); + if (IS_ERR(gctrl_dent)) + return; + + gctrl_dfile = + debugfs_create_file("status", 0444, gctrl_dent, 0, + &gctrl_stats_ops); + if (!gctrl_dfile || IS_ERR(gctrl_dfile)) + debugfs_remove(gctrl_dent); +} + +static void gctrl_debugfs_exit(void) +{ + debugfs_remove(gctrl_dfile); + debugfs_remove(gctrl_dent); +} + +#else +static void gctrl_debugfs_init(void) { } +static void gctrl_debugfs_exit(void) { } +#endif + +static int __init gctrl_init(void) +{ + gctrl_debugfs_init(); + + return 0; +} +module_init(gctrl_init); + +static void __exit gctrl_exit(void) +{ + gctrl_debugfs_exit(); +} +module_exit(gctrl_exit); +MODULE_DESCRIPTION("hsic control xport driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/usb/gadget/u_data_hsic.c b/drivers/usb/gadget/u_data_hsic.c new file mode 100644 index 00000000000..6f5e7b3c8eb --- /dev/null +++ b/drivers/usb/gadget/u_data_hsic.c @@ -0,0 +1,962 @@ +/* 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 + +static unsigned int no_data_ports; + +static const char *data_bridge_names[] = { + "dun_data_hsic0", + "rmnet_data_hsic0" +}; + +#define DATA_BRIDGE_NAME_MAX_LEN 20 + +#define GHSIC_DATA_RMNET_RX_Q_SIZE 50 +#define GHSIC_DATA_RMNET_TX_Q_SIZE 300 +#define GHSIC_DATA_SERIAL_RX_Q_SIZE 2 +#define GHSIC_DATA_SERIAL_TX_Q_SIZE 2 +#define GHSIC_DATA_RX_REQ_SIZE 2048 + +static unsigned int ghsic_data_rmnet_tx_q_size = GHSIC_DATA_RMNET_TX_Q_SIZE; +module_param(ghsic_data_rmnet_tx_q_size, uint, S_IRUGO | S_IWUSR); + +static unsigned int ghsic_data_rmnet_rx_q_size = GHSIC_DATA_RMNET_RX_Q_SIZE; +module_param(ghsic_data_rmnet_rx_q_size, uint, S_IRUGO | S_IWUSR); + +static unsigned int ghsic_data_serial_tx_q_size = GHSIC_DATA_SERIAL_TX_Q_SIZE; +module_param(ghsic_data_serial_tx_q_size, uint, S_IRUGO | S_IWUSR); + +static unsigned int ghsic_data_serial_rx_q_size = GHSIC_DATA_SERIAL_RX_Q_SIZE; +module_param(ghsic_data_serial_rx_q_size, uint, S_IRUGO | S_IWUSR); + +static unsigned int ghsic_data_rx_req_size = GHSIC_DATA_RX_REQ_SIZE; +module_param(ghsic_data_rx_req_size, uint, S_IRUGO | S_IWUSR); + +/*flow ctrl*/ +#define GHSIC_DATA_FLOW_CTRL_EN_THRESHOLD 500 +#define GHSIC_DATA_FLOW_CTRL_DISABLE 300 +#define GHSIC_DATA_FLOW_CTRL_SUPPORT 1 +#define GHSIC_DATA_PENDLIMIT_WITH_BRIDGE 500 + +static unsigned int ghsic_data_fctrl_support = GHSIC_DATA_FLOW_CTRL_SUPPORT; +module_param(ghsic_data_fctrl_support, uint, S_IRUGO | S_IWUSR); + +static unsigned int ghsic_data_fctrl_en_thld = + GHSIC_DATA_FLOW_CTRL_EN_THRESHOLD; +module_param(ghsic_data_fctrl_en_thld, uint, S_IRUGO | S_IWUSR); + +static unsigned int ghsic_data_fctrl_dis_thld = GHSIC_DATA_FLOW_CTRL_DISABLE; +module_param(ghsic_data_fctrl_dis_thld, uint, S_IRUGO | S_IWUSR); + +static unsigned int ghsic_data_pend_limit_with_bridge = + GHSIC_DATA_PENDLIMIT_WITH_BRIDGE; +module_param(ghsic_data_pend_limit_with_bridge, uint, S_IRUGO | S_IWUSR); + +#define CH_OPENED 0 +#define CH_READY 1 + +struct gdata_port { + /* port */ + unsigned port_num; + + /* gadget */ + spinlock_t port_lock; + void *port_usb; + struct usb_ep *in; + struct usb_ep *out; + + enum gadget_type gtype; + + /* data transfer queues */ + unsigned int tx_q_size; + struct list_head tx_idle; + struct sk_buff_head tx_skb_q; + + unsigned int rx_q_size; + struct list_head rx_idle; + struct sk_buff_head rx_skb_q; + + /* work */ + struct workqueue_struct *wq; + struct work_struct connect_w; + struct work_struct disconnect_w; + struct work_struct write_tomdm_w; + struct work_struct write_tohost_w; + + struct bridge brdg; + + /*bridge status*/ + unsigned long bridge_sts; + + /*counters*/ + unsigned long to_modem; + unsigned long to_host; + unsigned int rx_throttled_cnt; + unsigned int rx_unthrottled_cnt; + unsigned int tx_throttled_cnt; + unsigned int tx_unthrottled_cnt; + unsigned int tomodem_drp_cnt; + unsigned int unthrottled_pnd_skbs; +}; + +static struct { + struct gdata_port *port; + struct platform_driver pdrv; +} gdata_ports[NUM_PORTS]; + +static void ghsic_data_start_rx(struct gdata_port *port); + +static void ghsic_data_free_requests(struct usb_ep *ep, struct list_head *head) +{ + struct usb_request *req; + + while (!list_empty(head)) { + req = list_entry(head->next, struct usb_request, list); + list_del(&req->list); + usb_ep_free_request(ep, req); + } +} + +static int ghsic_data_alloc_requests(struct usb_ep *ep, struct list_head *head, + int num, + void (*cb)(struct usb_ep *ep, struct usb_request *), + gfp_t flags) +{ + int i; + struct usb_request *req; + + pr_debug("%s: ep:%s head:%p num:%d cb:%p", __func__, + ep->name, head, num, cb); + + for (i = 0; i < num; i++) { + req = usb_ep_alloc_request(ep, flags); + if (!req) { + pr_debug("%s: req allocated:%d\n", __func__, i); + return list_empty(head) ? -ENOMEM : 0; + } + req->complete = cb; + list_add(&req->list, head); + } + + return 0; +} + +static void ghsic_data_unthrottle_tx(void *ctx) +{ + struct gdata_port *port = ctx; + unsigned long flags; + + if (!port) + return; + + spin_lock_irqsave(&port->port_lock, flags); + if (port->port_usb) { + port->tx_unthrottled_cnt++; + queue_work(port->wq, &port->write_tomdm_w); + pr_debug("%s: port num =%d unthrottled\n", __func__, + port->port_num); + } + spin_unlock_irqrestore(&port->port_lock, flags); +} + +static void ghsic_data_write_tohost(struct work_struct *w) +{ + unsigned long flags; + struct sk_buff *skb; + int ret; + struct usb_request *req; + struct usb_ep *ep; + struct gdata_port *port; + + port = container_of(w, struct gdata_port, write_tohost_w); + + spin_lock_irqsave(&port->port_lock, flags); + if (!port->port_usb) { + spin_unlock_irqrestore(&port->port_lock, flags); + return; + } + + ep = port->in; + + while (!list_empty(&port->tx_idle)) { + skb = __skb_dequeue(&port->tx_skb_q); + if (!skb) + break; + + req = list_first_entry(&port->tx_idle, struct usb_request, + list); + req->context = skb; + req->buf = skb->data; + req->length = skb->len; + + list_del(&req->list); + + spin_unlock_irqrestore(&port->port_lock, flags); + ret = usb_ep_queue(ep, req, GFP_KERNEL); + spin_lock_irqsave(&port->port_lock, flags); + if (ret) { + pr_err("%s: usb epIn failed\n", __func__); + list_add(&req->list, &port->tx_idle); + dev_kfree_skb_any(skb); + break; + } + port->to_host++; + if (ghsic_data_fctrl_support && + port->tx_skb_q.qlen <= ghsic_data_fctrl_dis_thld && + test_and_clear_bit(RX_THROTTLED, &port->brdg.flags)) { + port->rx_unthrottled_cnt++; + port->unthrottled_pnd_skbs = port->tx_skb_q.qlen; + pr_debug_ratelimited("%s: disable flow ctrl:" + " tx skbq len: %u\n", + __func__, port->tx_skb_q.qlen); + data_bridge_unthrottle_rx(port->brdg.ch_id); + } + } + spin_unlock_irqrestore(&port->port_lock, flags); +} + +static int ghsic_data_receive(void *p, void *data, size_t len) +{ + struct gdata_port *port = p; + unsigned long flags; + struct sk_buff *skb = data; + + if (!port) { + dev_kfree_skb_any(skb); + return -EINVAL; + } + + pr_debug("%s: p:%p#%d skb_len:%d\n", __func__, + port, port->port_num, skb->len); + + spin_lock_irqsave(&port->port_lock, flags); + if (!port->port_usb) { + spin_unlock_irqrestore(&port->port_lock, flags); + dev_kfree_skb_any(skb); + return -ENOTCONN; + } + + __skb_queue_tail(&port->tx_skb_q, skb); + + if (ghsic_data_fctrl_support && + port->tx_skb_q.qlen >= ghsic_data_fctrl_en_thld) { + set_bit(RX_THROTTLED, &port->brdg.flags); + port->rx_throttled_cnt++; + pr_debug_ratelimited("%s: flow ctrl enabled: tx skbq len: %u\n", + __func__, port->tx_skb_q.qlen); + spin_unlock_irqrestore(&port->port_lock, flags); + queue_work(port->wq, &port->write_tohost_w); + return -EBUSY; + } + + spin_unlock_irqrestore(&port->port_lock, flags); + + queue_work(port->wq, &port->write_tohost_w); + + return 0; +} + +static void ghsic_data_write_tomdm(struct work_struct *w) +{ + struct gdata_port *port; + struct sk_buff *skb; + unsigned long flags; + int ret; + + port = container_of(w, struct gdata_port, write_tomdm_w); + + spin_lock_irqsave(&port->port_lock, flags); + if (!port->port_usb) { + spin_unlock_irqrestore(&port->port_lock, flags); + return; + } + + if (test_bit(TX_THROTTLED, &port->brdg.flags)) { + spin_unlock_irqrestore(&port->port_lock, flags); + goto start_rx; + } + + while ((skb = __skb_dequeue(&port->rx_skb_q))) { + pr_debug("%s: port:%p tom:%lu pno:%d\n", __func__, + port, port->to_modem, port->port_num); + + spin_unlock_irqrestore(&port->port_lock, flags); + ret = data_bridge_write(port->brdg.ch_id, skb); + spin_lock_irqsave(&port->port_lock, flags); + if (ret < 0) { + if (ret == -EBUSY) { + /*flow control*/ + port->tx_throttled_cnt++; + break; + } + pr_err_ratelimited("%s: write error:%d\n", + __func__, ret); + port->tomodem_drp_cnt++; + dev_kfree_skb_any(skb); + break; + } + port->to_modem++; + } + spin_unlock_irqrestore(&port->port_lock, flags); +start_rx: + ghsic_data_start_rx(port); +} + +static void ghsic_data_epin_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct gdata_port *port = ep->driver_data; + struct sk_buff *skb = req->context; + int status = req->status; + + switch (status) { + case 0: + /* successful completion */ + break; + case -ECONNRESET: + case -ESHUTDOWN: + /* connection gone */ + dev_kfree_skb_any(skb); + req->buf = 0; + usb_ep_free_request(ep, req); + return; + default: + pr_err("%s: data tx ep error %d\n", __func__, status); + break; + } + + dev_kfree_skb_any(skb); + + spin_lock(&port->port_lock); + list_add_tail(&req->list, &port->tx_idle); + spin_unlock(&port->port_lock); + + queue_work(port->wq, &port->write_tohost_w); +} + +static void +ghsic_data_epout_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct gdata_port *port = ep->driver_data; + struct sk_buff *skb = req->context; + int status = req->status; + int queue = 0; + + switch (status) { + case 0: + skb_put(skb, req->actual); + queue = 1; + break; + case -ECONNRESET: + case -ESHUTDOWN: + /* cable disconnection */ + dev_kfree_skb_any(skb); + req->buf = 0; + usb_ep_free_request(ep, req); + return; + default: + pr_err_ratelimited("%s: %s response error %d, %d/%d\n", + __func__, ep->name, status, + req->actual, req->length); + dev_kfree_skb_any(skb); + break; + } + + spin_lock(&port->port_lock); + if (queue) { + __skb_queue_tail(&port->rx_skb_q, skb); + list_add_tail(&req->list, &port->rx_idle); + queue_work(port->wq, &port->write_tomdm_w); + } + spin_unlock(&port->port_lock); +} + +static void ghsic_data_start_rx(struct gdata_port *port) +{ + struct usb_request *req; + struct usb_ep *ep; + unsigned long flags; + int ret; + struct sk_buff *skb; + + pr_debug("%s: port:%p\n", __func__, port); + spin_lock_irqsave(&port->port_lock, flags); + if (!port->port_usb) { + spin_unlock_irqrestore(&port->port_lock, flags); + return; + } + + ep = port->out; + + while (port->port_usb && !list_empty(&port->rx_idle)) { + if (port->rx_skb_q.qlen > ghsic_data_pend_limit_with_bridge) + break; + + req = list_first_entry(&port->rx_idle, + struct usb_request, list); + + skb = alloc_skb(ghsic_data_rx_req_size, GFP_ATOMIC); + if (!skb) + break; + + list_del(&req->list); + req->buf = skb->data; + req->length = ghsic_data_rx_req_size; + req->context = skb; + + spin_unlock_irqrestore(&port->port_lock, flags); + ret = usb_ep_queue(ep, req, GFP_KERNEL); + spin_lock_irqsave(&port->port_lock, flags); + if (ret) { + dev_kfree_skb_any(skb); + + pr_err_ratelimited("%s: rx queue failed\n", __func__); + + if (port->port_usb) + list_add(&req->list, &port->rx_idle); + else + usb_ep_free_request(ep, req); + break; + } + } + spin_unlock_irqrestore(&port->port_lock, flags); +} + +static void ghsic_data_start_io(struct gdata_port *port) +{ + unsigned long flags; + struct usb_ep *ep; + int ret; + + pr_debug("%s: port:%p\n", __func__, port); + + spin_lock_irqsave(&port->port_lock, flags); + if (!port->port_usb) { + spin_unlock_irqrestore(&port->port_lock, flags); + return; + } + + ep = port->out; + ret = ghsic_data_alloc_requests(ep, &port->rx_idle, + port->rx_q_size, ghsic_data_epout_complete, GFP_ATOMIC); + if (ret) { + pr_err("%s: rx req allocation failed\n", __func__); + spin_unlock_irqrestore(&port->port_lock, flags); + return; + } + + ep = port->in; + ret = ghsic_data_alloc_requests(ep, &port->tx_idle, + port->tx_q_size, ghsic_data_epin_complete, GFP_ATOMIC); + if (ret) { + pr_err("%s: tx req allocation failed\n", __func__); + ghsic_data_free_requests(ep, &port->rx_idle); + spin_unlock_irqrestore(&port->port_lock, flags); + return; + } + + spin_unlock_irqrestore(&port->port_lock, flags); + + /* queue out requests */ + ghsic_data_start_rx(port); +} + +static void ghsic_data_connect_w(struct work_struct *w) +{ + struct gdata_port *port = + container_of(w, struct gdata_port, connect_w); + int ret; + + if (!port || !test_bit(CH_READY, &port->bridge_sts)) + return; + + pr_debug("%s: port:%p\n", __func__, port); + + ret = data_bridge_open(&port->brdg); + if (ret) { + pr_err("%s: unable open bridge ch:%d err:%d\n", + __func__, port->brdg.ch_id, ret); + return; + } + + set_bit(CH_OPENED, &port->bridge_sts); + + ghsic_data_start_io(port); +} + +static void ghsic_data_disconnect_w(struct work_struct *w) +{ + struct gdata_port *port = + container_of(w, struct gdata_port, disconnect_w); + + if (!test_bit(CH_OPENED, &port->bridge_sts)) + return; + + data_bridge_close(port->brdg.ch_id); + clear_bit(CH_OPENED, &port->bridge_sts); +} + +static void ghsic_data_free_buffers(struct gdata_port *port) +{ + struct sk_buff *skb; + unsigned long flags; + + spin_lock_irqsave(&port->port_lock, flags); + + if (!port || !port->port_usb) + goto free_buf_out; + + ghsic_data_free_requests(port->in, &port->tx_idle); + ghsic_data_free_requests(port->out, &port->rx_idle); + + while ((skb = __skb_dequeue(&port->tx_skb_q))) + dev_kfree_skb_any(skb); + + while ((skb = __skb_dequeue(&port->rx_skb_q))) + dev_kfree_skb_any(skb); + +free_buf_out: + spin_unlock_irqrestore(&port->port_lock, flags); +} + +static int ghsic_data_probe(struct platform_device *pdev) +{ + struct gdata_port *port; + unsigned long flags; + + pr_debug("%s: name:%s no_data_ports= %d\n", + __func__, pdev->name, no_data_ports); + + if (pdev->id >= no_data_ports) { + pr_err("%s: invalid port: %d\n", __func__, pdev->id); + return -EINVAL; + } + + port = gdata_ports[pdev->id].port; + set_bit(CH_READY, &port->bridge_sts); + + spin_lock_irqsave(&port->port_lock, flags); + /* if usb is online, try opening bridge */ + if (port->port_usb) + queue_work(port->wq, &port->connect_w); + spin_unlock_irqrestore(&port->port_lock, flags); + + return 0; +} + +/* mdm disconnect */ +static int ghsic_data_remove(struct platform_device *pdev) +{ + struct gdata_port *port; + struct usb_ep *ep_in = NULL; + struct usb_ep *ep_out = NULL; + unsigned long flags; + + pr_debug("%s: name:%s\n", __func__, pdev->name); + + if (pdev->id >= no_data_ports) { + pr_err("%s: invalid port: %d\n", __func__, pdev->id); + return -EINVAL; + } + + port = gdata_ports[pdev->id].port; + + spin_lock_irqsave(&port->port_lock, flags); + if (port->port_usb) { + ep_in = port->in; + ep_out = port->out; + } + spin_unlock_irqrestore(&port->port_lock, flags); + + if (ep_in) + usb_ep_fifo_flush(ep_in); + if (ep_out) + usb_ep_fifo_flush(ep_out); + + ghsic_data_free_buffers(port); + + data_bridge_close(port->brdg.ch_id); + + clear_bit(CH_READY, &port->bridge_sts); + clear_bit(CH_OPENED, &port->bridge_sts); + + return 0; +} + +static void ghsic_data_port_free(int portno) +{ + struct gdata_port *port = gdata_ports[portno].port; + struct platform_driver *pdrv = &gdata_ports[portno].pdrv; + + destroy_workqueue(port->wq); + kfree(port); + + if (pdrv) + platform_driver_unregister(pdrv); +} + +static int ghsic_data_port_alloc(unsigned port_num, enum gadget_type gtype) +{ + struct gdata_port *port; + struct platform_driver *pdrv; + + port = kzalloc(sizeof(struct gdata_port), GFP_KERNEL); + if (!port) + return -ENOMEM; + + port->wq = create_singlethread_workqueue(data_bridge_names[port_num]); + if (!port->wq) { + pr_err("%s: Unable to create workqueue:%s\n", + __func__, data_bridge_names[port_num]); + kfree(port); + return -ENOMEM; + } + port->port_num = port_num; + + /* port initialization */ + spin_lock_init(&port->port_lock); + + INIT_WORK(&port->connect_w, ghsic_data_connect_w); + INIT_WORK(&port->disconnect_w, ghsic_data_disconnect_w); + INIT_WORK(&port->write_tohost_w, ghsic_data_write_tohost); + INIT_WORK(&port->write_tomdm_w, ghsic_data_write_tomdm); + + INIT_LIST_HEAD(&port->tx_idle); + INIT_LIST_HEAD(&port->rx_idle); + + skb_queue_head_init(&port->tx_skb_q); + skb_queue_head_init(&port->rx_skb_q); + + port->gtype = gtype; + port->brdg.ch_id = port_num; + port->brdg.ctx = port; + port->brdg.ops.send_pkt = ghsic_data_receive; + port->brdg.ops.unthrottle_tx = ghsic_data_unthrottle_tx; + gdata_ports[port_num].port = port; + + pdrv = &gdata_ports[port_num].pdrv; + pdrv->probe = ghsic_data_probe; + pdrv->remove = ghsic_data_remove; + pdrv->driver.name = data_bridge_names[port_num]; + pdrv->driver.owner = THIS_MODULE; + + platform_driver_register(pdrv); + + pr_debug("%s: port:%p portno:%d\n", __func__, port, port_num); + + return 0; +} + +void ghsic_data_disconnect(void *gptr, int port_num) +{ + struct gdata_port *port; + unsigned long flags; + + pr_debug("%s: port#%d\n", __func__, port_num); + + port = gdata_ports[port_num].port; + + if (port_num > no_data_ports) { + pr_err("%s: invalid portno#%d\n", __func__, port_num); + return; + } + + if (!gptr || !port) { + pr_err("%s: port is null\n", __func__); + return; + } + + ghsic_data_free_buffers(port); + + /* disable endpoints */ + if (port->in) + usb_ep_disable(port->out); + + if (port->out) + usb_ep_disable(port->in); + + spin_lock_irqsave(&port->port_lock, flags); + port->port_usb = 0; + port->in = NULL; + port->out = NULL; + clear_bit(TX_THROTTLED, &port->brdg.flags); + clear_bit(RX_THROTTLED, &port->brdg.flags); + spin_unlock_irqrestore(&port->port_lock, flags); + + queue_work(port->wq, &port->disconnect_w); +} + +int ghsic_data_connect(void *gptr, int port_num) +{ + struct gdata_port *port; + struct gserial *gser; + struct grmnet *gr; + struct usb_endpoint_descriptor *in_desc; + struct usb_endpoint_descriptor *out_desc; + unsigned long flags; + int ret = 0; + + pr_debug("%s: port#%d\n", __func__, port_num); + + port = gdata_ports[port_num].port; + + if (port_num > no_data_ports) { + pr_err("%s: invalid portno#%d\n", __func__, port_num); + return -ENODEV; + } + + if (!gptr || !port) { + pr_err("%s: port is null\n", __func__); + return -ENODEV; + } + + if (port->gtype == USB_GADGET_SERIAL) { + gser = gptr; + port->in = gser->in; + port->out = gser->out; + port->tx_q_size = ghsic_data_serial_tx_q_size; + port->rx_q_size = ghsic_data_serial_rx_q_size; + gser->in->driver_data = port; + gser->out->driver_data = port; + in_desc = gser->in_desc; + out_desc = gser->out_desc; + } else { + gr = gptr; + port->in = gr->in; + port->out = gr->out; + port->tx_q_size = ghsic_data_rmnet_tx_q_size; + port->rx_q_size = ghsic_data_rmnet_rx_q_size; + gr->in->driver_data = port; + gr->out->driver_data = port; + in_desc = gr->in_desc; + out_desc = gr->out_desc; + } + + ret = usb_ep_enable(port->in, in_desc); + if (ret) { + pr_err("%s: usb_ep_enable failed eptype:IN ep:%p", + __func__, port->in); + goto fail; + } + + ret = usb_ep_enable(port->out, out_desc); + if (ret) { + pr_err("%s: usb_ep_enable failed eptype:OUT ep:%p", + __func__, port->out); + usb_ep_disable(port->in); + goto fail; + } + spin_lock_irqsave(&port->port_lock, flags); + port->port_usb = gptr; + port->to_host = 0; + port->to_modem = 0; + port->tomodem_drp_cnt = 0; + port->rx_throttled_cnt = 0; + port->rx_unthrottled_cnt = 0; + port->tx_throttled_cnt = 0; + port->tx_unthrottled_cnt = 0; + port->unthrottled_pnd_skbs = 0; + spin_unlock_irqrestore(&port->port_lock, flags); + + queue_work(port->wq, &port->connect_w); +fail: + return ret; +} + +#if defined(CONFIG_DEBUG_FS) +#define DEBUG_BUF_SIZE 1024 +static ssize_t ghsic_data_read_stats(struct file *file, + char __user *ubuf, size_t count, loff_t *ppos) +{ + struct gdata_port *port; + struct platform_driver *pdrv; + char *buf; + unsigned long flags; + int ret; + int i; + int temp = 0; + + buf = kzalloc(sizeof(char) * DEBUG_BUF_SIZE, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + for (i = 0; i < no_data_ports; i++) { + port = gdata_ports[i].port; + if (!port) + continue; + pdrv = &gdata_ports[i].pdrv; + spin_lock_irqsave(&port->port_lock, flags); + + temp += scnprintf(buf + temp, DEBUG_BUF_SIZE - temp, + "\nName: %s\n" + "#PORT:%d port#: %p\n" + "dpkts_to_usbhost: %lu\n" + "dpkts_to_modem: %lu\n" + "tomodem_drp_cnt: %u\n" + "tx_buf_len: %u\n" + "rx_buf_len: %u\n" + "rx thld cnt %u\n" + "rx unthld cnt %u\n" + "tx thld cnt %u\n" + "tx unthld cnt %u\n" + "uthld pnd skbs %u\n" + "RX_THROTTLED %d\n" + "TX_THROTTLED %d\n" + "data_ch_open: %d\n" + "data_ch_ready: %d\n", + pdrv->driver.name, + i, port, + port->to_host, port->to_modem, + port->tomodem_drp_cnt, + port->tx_skb_q.qlen, + port->rx_skb_q.qlen, + port->rx_throttled_cnt, + port->rx_unthrottled_cnt, + port->tx_throttled_cnt, + port->tx_unthrottled_cnt, + port->unthrottled_pnd_skbs, + test_bit(RX_THROTTLED, &port->brdg.flags), + test_bit(TX_THROTTLED, &port->brdg.flags), + test_bit(CH_OPENED, &port->bridge_sts), + test_bit(CH_READY, &port->bridge_sts)); + + spin_unlock_irqrestore(&port->port_lock, flags); + } + + ret = simple_read_from_buffer(ubuf, count, ppos, buf, temp); + + kfree(buf); + + return ret; +} + +static ssize_t ghsic_data_reset_stats(struct file *file, + const char __user *buf, size_t count, loff_t *ppos) +{ + struct gdata_port *port; + int i; + unsigned long flags; + + for (i = 0; i < no_data_ports; i++) { + port = gdata_ports[i].port; + if (!port) + continue; + + spin_lock_irqsave(&port->port_lock, flags); + port->to_host = 0; + port->to_modem = 0; + port->tomodem_drp_cnt = 0; + port->rx_throttled_cnt = 0; + port->rx_unthrottled_cnt = 0; + port->tx_throttled_cnt = 0; + port->tx_unthrottled_cnt = 0; + port->unthrottled_pnd_skbs = 0; + spin_unlock_irqrestore(&port->port_lock, flags); + } + return count; +} + +const struct file_operations ghsic_stats_ops = { + .read = ghsic_data_read_stats, + .write = ghsic_data_reset_stats, +}; + +static struct dentry *gdata_dent; +static struct dentry *gdata_dfile; + +static void ghsic_data_debugfs_init(void) +{ + gdata_dent = debugfs_create_dir("ghsic_data_xport", 0); + if (IS_ERR(gdata_dent)) + return; + + gdata_dfile = debugfs_create_file("status", 0444, gdata_dent, 0, + &ghsic_stats_ops); + if (!gdata_dfile || IS_ERR(gdata_dfile)) + debugfs_remove(gdata_dent); +} + +static void ghsic_data_debugfs_exit(void) +{ + debugfs_remove(gdata_dfile); + debugfs_remove(gdata_dent); +} + +#else +static void ghsic_data_debugfs_init(void) { } +static void ghsic_data_debugfs_exit(void) { } + +#endif + +int ghsic_data_setup(unsigned num_ports, enum gadget_type gtype) +{ + int first_port_id = no_data_ports; + int total_num_ports = num_ports + no_data_ports; + int ret = 0; + int i; + + if (!num_ports || total_num_ports > NUM_PORTS) { + pr_err("%s: Invalid num of ports count:%d\n", + __func__, num_ports); + return -EINVAL; + } + pr_debug("%s: count: %d\n", __func__, num_ports); + + for (i = first_port_id; i < (num_ports + first_port_id); i++) { + + /*probe can be called while port_alloc,so update no_data_ports*/ + no_data_ports++; + ret = ghsic_data_port_alloc(i, gtype); + if (ret) { + no_data_ports--; + pr_err("%s: Unable to alloc port:%d\n", __func__, i); + goto free_ports; + } + } + + /*return the starting index*/ + return first_port_id; + +free_ports: + for (i = first_port_id; i < no_data_ports; i++) + ghsic_data_port_free(i); + no_data_ports = first_port_id; + + return ret; +} + +static int __init ghsic_data_init(void) +{ + ghsic_data_debugfs_init(); + + return 0; +} +module_init(ghsic_data_init); + +static void __exit ghsic_data_exit(void) +{ + ghsic_data_debugfs_exit(); +} +module_exit(ghsic_data_exit); +MODULE_DESCRIPTION("hsic data xport driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/usb/gadget/u_ether.c b/drivers/usb/gadget/u_ether.c index a3be709984c..3a9c4b86a5f 100644 --- a/drivers/usb/gadget/u_ether.c +++ b/drivers/usb/gadget/u_ether.c @@ -67,7 +67,12 @@ struct eth_dev { spinlock_t req_lock; /* guard {rx,tx}_reqs */ struct list_head tx_reqs, rx_reqs; - atomic_t tx_qlen; + unsigned tx_qlen; +/* Minimum number of TX USB request queued to UDC */ +#define TX_REQ_THRESHOLD 5 + int no_tx_req_used; + int tx_skb_hold_count; + u32 tx_req_bufsize; struct sk_buff_head rx_frames; @@ -340,8 +345,7 @@ static void rx_complete(struct usb_ep *ep, struct usb_request *req) DBG(dev, "rx %s reset\n", ep->name); defer_kevent(dev, WORK_RX_MEMORY); quiesce: - if (skb) - dev_kfree_skb_any(skb); + dev_kfree_skb_any(skb); goto clean; /* data overrun */ @@ -466,6 +470,11 @@ static void tx_complete(struct usb_ep *ep, struct usb_request *req) { struct sk_buff *skb = req->context; struct eth_dev *dev = ep->driver_data; + struct net_device *net = dev->net; + struct usb_request *new_req; + struct usb_ep *in; + int length; + int retval; switch (req->status) { default: @@ -476,16 +485,74 @@ static void tx_complete(struct usb_ep *ep, struct usb_request *req) case -ESHUTDOWN: /* disconnect etc */ break; case 0: - dev->net->stats.tx_bytes += skb->len; + if (!req->zero) + dev->net->stats.tx_bytes += req->length-1; + else + dev->net->stats.tx_bytes += req->length; } dev->net->stats.tx_packets++; spin_lock(&dev->req_lock); - list_add(&req->list, &dev->tx_reqs); - spin_unlock(&dev->req_lock); - dev_kfree_skb_any(skb); + list_add_tail(&req->list, &dev->tx_reqs); + + if (dev->port_usb->multi_pkt_xfer) { + dev->no_tx_req_used--; + req->length = 0; + in = dev->port_usb->in_ep; + + if (!list_empty(&dev->tx_reqs)) { + new_req = container_of(dev->tx_reqs.next, + struct usb_request, list); + list_del(&new_req->list); + spin_unlock(&dev->req_lock); + if (new_req->length > 0) { + length = new_req->length; + + /* NCM requires no zlp if transfer is + * dwNtbInMaxSize */ + if (dev->port_usb->is_fixed && + length == dev->port_usb->fixed_in_len && + (length % in->maxpacket) == 0) + new_req->zero = 0; + else + new_req->zero = 1; + + /* use zlp framing on tx for strict CDC-Ether + * conformance, though any robust network rx + * path ignores extra padding. and some hardware + * doesn't like to write zlps. + */ + if (new_req->zero && !dev->zlp && + (length % in->maxpacket) == 0) { + new_req->zero = 0; + length++; + } + + new_req->length = length; + retval = usb_ep_queue(in, new_req, GFP_ATOMIC); + switch (retval) { + default: + DBG(dev, "tx queue err %d\n", retval); + break; + case 0: + spin_lock(&dev->req_lock); + dev->no_tx_req_used++; + spin_unlock(&dev->req_lock); + net->trans_start = jiffies; + } + } else { + spin_lock(&dev->req_lock); + list_add(&new_req->list, &dev->tx_reqs); + spin_unlock(&dev->req_lock); + } + } else { + spin_unlock(&dev->req_lock); + } + } else { + spin_unlock(&dev->req_lock); + dev_kfree_skb_any(skb); + } - atomic_dec(&dev->tx_qlen); if (netif_carrier_ok(dev->net)) netif_wake_queue(dev->net); } @@ -495,6 +562,26 @@ static inline int is_promisc(u16 cdc_filter) return cdc_filter & USB_CDC_PACKET_TYPE_PROMISCUOUS; } +static void alloc_tx_buffer(struct eth_dev *dev) +{ + struct list_head *act; + struct usb_request *req; + + dev->tx_req_bufsize = (TX_SKB_HOLD_THRESHOLD * + (dev->net->mtu + + sizeof(struct ethhdr) + /* size of rndis_packet_msg_type */ + + 44 + + 22)); + + list_for_each(act, &dev->tx_reqs) { + req = container_of(act, struct usb_request, list); + if (!req->buf) + req->buf = kmalloc(dev->tx_req_bufsize, + GFP_ATOMIC); + } +} + static netdev_tx_t eth_start_xmit(struct sk_buff *skb, struct net_device *net) { @@ -521,6 +608,10 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb, return NETDEV_TX_OK; } + /* Allocate memory for tx_reqs to support multi packet transfer */ + if (dev->port_usb->multi_pkt_xfer && !dev->tx_req_bufsize) + alloc_tx_buffer(dev); + /* apply outgoing CDC or RNDIS filters */ if (!is_promisc(cdc_filter)) { u8 *dest = skb->data; @@ -575,11 +666,39 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb, spin_unlock_irqrestore(&dev->lock, flags); if (!skb) goto drop; + } + + spin_lock_irqsave(&dev->req_lock, flags); + dev->tx_skb_hold_count++; + spin_unlock_irqrestore(&dev->req_lock, flags); + + if (dev->port_usb->multi_pkt_xfer) { + memcpy(req->buf + req->length, skb->data, skb->len); + req->length = req->length + skb->len; + length = req->length; + dev_kfree_skb_any(skb); + spin_lock_irqsave(&dev->req_lock, flags); + if (dev->tx_skb_hold_count < TX_SKB_HOLD_THRESHOLD) { + if (dev->no_tx_req_used > TX_REQ_THRESHOLD) { + list_add(&req->list, &dev->tx_reqs); + spin_unlock_irqrestore(&dev->req_lock, flags); + goto success; + } + } + + dev->no_tx_req_used++; + spin_unlock_irqrestore(&dev->req_lock, flags); + + spin_lock_irqsave(&dev->lock, flags); + dev->tx_skb_hold_count = 0; + spin_unlock_irqrestore(&dev->lock, flags); + } else { length = skb->len; + req->buf = skb->data; + req->context = skb; } - req->buf = skb->data; - req->context = skb; + req->complete = tx_complete; /* NCM requires no zlp if transfer is dwNtbInMaxSize */ @@ -594,16 +713,26 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb, * though any robust network rx path ignores extra padding. * and some hardware doesn't like to write zlps. */ - if (req->zero && !dev->zlp && (length % in->maxpacket) == 0) + if (req->zero && !dev->zlp && (length % in->maxpacket) == 0) { + req->zero = 0; length++; + } req->length = length; /* throttle highspeed IRQ rate back slightly */ - if (gadget_is_dualspeed(dev->gadget)) - req->no_interrupt = (dev->gadget->speed == USB_SPEED_HIGH) - ? ((atomic_read(&dev->tx_qlen) % qmult) != 0) - : 0; + if (gadget_is_dualspeed(dev->gadget) && + (dev->gadget->speed == USB_SPEED_HIGH)) { + dev->tx_qlen++; + if (dev->tx_qlen == (qmult/2)) { + req->no_interrupt = 0; + dev->tx_qlen = 0; + } else { + req->no_interrupt = 1; + } + } else { + req->no_interrupt = 0; + } retval = usb_ep_queue(in, req, GFP_ATOMIC); switch (retval) { @@ -612,11 +741,11 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb, break; case 0: net->trans_start = jiffies; - atomic_inc(&dev->tx_qlen); } if (retval) { - dev_kfree_skb_any(skb); + if (!dev->port_usb->multi_pkt_xfer) + dev_kfree_skb_any(skb); drop: dev->net->stats.tx_dropped++; spin_lock_irqsave(&dev->req_lock, flags); @@ -625,6 +754,7 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb, list_add(&req->list, &dev->tx_reqs); spin_unlock_irqrestore(&dev->req_lock, flags); } +success: return NETDEV_TX_OK; } @@ -638,7 +768,7 @@ static void eth_start(struct eth_dev *dev, gfp_t gfp_flags) rx_fill(dev, gfp_flags); /* and open the tx floodgates */ - atomic_set(&dev->tx_qlen, 0); + dev->tx_qlen = 0; netif_wake_queue(dev->net); } @@ -693,7 +823,7 @@ static int eth_stop(struct net_device *net) usb_ep_disable(link->in_ep); usb_ep_disable(link->out_ep); if (netif_carrier_ok(net)) { - ERROR(dev, "host still using in/out endpoints\n"); + DBG(dev, "host still using in/out endpoints\n"); usb_ep_enable(link->in_ep, link->in); usb_ep_enable(link->out_ep, link->out); } @@ -790,10 +920,8 @@ int gether_setup_name(struct usb_gadget *g, u8 ethaddr[ETH_ALEN], struct net_device *net; int status; - if (the_dev) { - memcpy(ethaddr, the_dev->host_mac, ETH_ALEN); - return 0; - } + if (the_dev) + return -EBUSY; net = alloc_etherdev(sizeof *dev); if (!net) @@ -921,6 +1049,9 @@ struct net_device *gether_connect(struct gether *link) dev->wrap = link->wrap; spin_lock(&dev->lock); + dev->tx_skb_hold_count = 0; + dev->no_tx_req_used = 0; + dev->tx_req_bufsize = 0; dev->port_usb = link; link->ioport = dev; if (netif_running(dev->net)) { @@ -986,6 +1117,8 @@ void gether_disconnect(struct gether *link) list_del(&req->list); spin_unlock(&dev->req_lock); + if (link->multi_pkt_xfer) + kfree(req->buf); usb_ep_free_request(link->in_ep, req); spin_lock(&dev->req_lock); } diff --git a/drivers/usb/gadget/u_ether.h b/drivers/usb/gadget/u_ether.h index 64b65f92168..09b6cccbac7 100644 --- a/drivers/usb/gadget/u_ether.h +++ b/drivers/usb/gadget/u_ether.h @@ -66,6 +66,9 @@ struct gether { bool is_fixed; u32 fixed_out_len; u32 fixed_in_len; +/* Max number of SKB packets to be used to create Multi Packet RNDIS */ +#define TX_SKB_HOLD_THRESHOLD 3 + bool multi_pkt_xfer; struct sk_buff *(*wrap)(struct gether *port, struct sk_buff *skb); int (*unwrap)(struct gether *port, diff --git a/drivers/usb/gadget/u_rmnet.h b/drivers/usb/gadget/u_rmnet.h index 3c21316d71c..fd1e124fa89 100644 --- a/drivers/usb/gadget/u_rmnet.h +++ b/drivers/usb/gadget/u_rmnet.h @@ -35,28 +35,23 @@ struct grmnet { /* to usb host, aka laptop, windows pc etc. Will * be filled by usb driver of rmnet functionality */ - int (*send_cpkt_response)(struct grmnet *g, - struct rmnet_ctrl_pkt *pkt); + int (*send_cpkt_response)(void *g, void *buf, size_t len); /* to modem, and to be filled by driver implementing * control function */ - int (*send_cpkt_request)(struct grmnet *g, - u8 port_num, - struct rmnet_ctrl_pkt *pkt); + int (*send_encap_cmd)(u8 port_num, void *buf, size_t len); - void (*send_cbits_tomodem)(struct grmnet *g, - u8 port_num, - int cbits); + void (*notify_modem)(void *g, u8 port_num, int cbits); void (*disconnect)(struct grmnet *g); void (*connect)(struct grmnet *g); }; -int gbam_setup(unsigned int count); -int gbam_connect(struct grmnet *, u8 port_num); -void gbam_disconnect(struct grmnet *, u8 port_num); - +int gbam_setup(unsigned int no_bam_port, unsigned int no_bam2bam_port); +int gbam_connect(struct grmnet *gr, u8 port_num, + enum transport_type trans, u8 connection_idx); +void gbam_disconnect(struct grmnet *gr, u8 port_num, enum transport_type trans); int gsmd_ctrl_connect(struct grmnet *gr, int port_num); void gsmd_ctrl_disconnect(struct grmnet *gr, u8 port_num); int gsmd_ctrl_setup(unsigned int count); diff --git a/drivers/usb/gadget/u_rmnet_ctrl_smd.c b/drivers/usb/gadget/u_rmnet_ctrl_smd.c index 68b7bb8a547..d42ac935029 100644 --- a/drivers/usb/gadget/u_rmnet_ctrl_smd.c +++ b/drivers/usb/gadget/u_rmnet_ctrl_smd.c @@ -1,5 +1,5 @@ /* - * 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 @@ -59,7 +59,7 @@ struct rmnet_ctrl_port { struct grmnet *port_usb; spinlock_t port_lock; - struct work_struct connect_w; + struct delayed_work connect_w; }; static struct rmnet_ctrl_ports { @@ -83,6 +83,7 @@ static struct rmnet_ctrl_pkt *alloc_rmnet_ctrl_pkt(unsigned len, gfp_t flags) kfree(pkt); return ERR_PTR(-ENOMEM); } + pkt->len = len; return pkt; @@ -103,35 +104,37 @@ static void grmnet_ctrl_smd_read_w(struct work_struct *w) struct smd_ch_info *c = container_of(w, struct smd_ch_info, read_w); struct rmnet_ctrl_port *port = c->port; int sz; - struct rmnet_ctrl_pkt *cpkt; + size_t len; + void *buf; unsigned long flags; - while (1) { + spin_lock_irqsave(&port->port_lock, flags); + while (c->ch) { sz = smd_cur_packet_size(c->ch); - if (sz == 0) + if (sz <= 0) break; if (smd_read_avail(c->ch) < sz) break; - cpkt = alloc_rmnet_ctrl_pkt(sz, GFP_KERNEL); - if (IS_ERR(cpkt)) { - pr_err("%s: unable to allocate rmnet control pkt\n", - __func__); + spin_unlock_irqrestore(&port->port_lock, flags); + + buf = kmalloc(sz, GFP_KERNEL); + if (!buf) return; - } - cpkt->len = smd_read(c->ch, cpkt->buf, sz); + + len = smd_read(c->ch, buf, sz); /* send it to USB here */ spin_lock_irqsave(&port->port_lock, flags); if (port->port_usb && port->port_usb->send_cpkt_response) { - port->port_usb->send_cpkt_response( - port->port_usb, - cpkt); + port->port_usb->send_cpkt_response(port->port_usb, + buf, len); c->to_host++; } - spin_unlock_irqrestore(&port->port_lock, flags); + kfree(buf); } + spin_unlock_irqrestore(&port->port_lock, flags); } static void grmnet_ctrl_smd_write_w(struct work_struct *w) @@ -143,7 +146,7 @@ static void grmnet_ctrl_smd_write_w(struct work_struct *w) int ret; spin_lock_irqsave(&port->port_lock, flags); - while (1) { + while (c->ch) { if (list_empty(&c->tx_q)) break; @@ -157,8 +160,7 @@ static void grmnet_ctrl_smd_write_w(struct work_struct *w) ret = smd_write(c->ch, cpkt->buf, cpkt->len); spin_lock_irqsave(&port->port_lock, flags); if (ret != cpkt->len) { - pr_err("%s: smd_write failed err:%d\n", - __func__, ret); + pr_err("%s: smd_write failed err:%d\n", __func__, ret); free_rmnet_ctrl_pkt(cpkt); break; } @@ -169,24 +171,29 @@ static void grmnet_ctrl_smd_write_w(struct work_struct *w) } static int -grmnet_ctrl_smd_send_cpkt_tomodem(struct grmnet *gr, u8 portno, - struct rmnet_ctrl_pkt *cpkt) +grmnet_ctrl_smd_send_cpkt_tomodem(u8 portno, + void *buf, size_t len) { unsigned long flags; struct rmnet_ctrl_port *port; struct smd_ch_info *c; + struct rmnet_ctrl_pkt *cpkt; if (portno >= n_rmnet_ctrl_ports) { pr_err("%s: Invalid portno#%d\n", __func__, portno); return -ENODEV; } - if (!gr) { - pr_err("%s: grmnet is null\n", __func__); - return -ENODEV; + port = ctrl_smd_ports[portno].port; + + cpkt = alloc_rmnet_ctrl_pkt(len, GFP_ATOMIC); + if (IS_ERR(cpkt)) { + pr_err("%s: Unable to allocate ctrl pkt\n", __func__); + return -ENOMEM; } - port = ctrl_smd_ports[portno].port; + memcpy(cpkt->buf, buf, len); + cpkt->len = len; spin_lock_irqsave(&port->port_lock, flags); c = &port->ctrl_ch; @@ -207,7 +214,7 @@ grmnet_ctrl_smd_send_cpkt_tomodem(struct grmnet *gr, u8 portno, #define RMNET_CTRL_DTR 0x01 static void -gsmd_ctrl_send_cbits_tomodem(struct grmnet *gr, u8 portno, int cbits) +gsmd_ctrl_send_cbits_tomodem(void *gptr, u8 portno, int cbits) { struct rmnet_ctrl_port *port; struct smd_ch_info *c; @@ -220,7 +227,7 @@ gsmd_ctrl_send_cbits_tomodem(struct grmnet *gr, u8 portno, int cbits) return; } - if (!gr) { + if (!gptr) { pr_err("%s: grmnet is null\n", __func__); return; } @@ -316,7 +323,7 @@ static void grmnet_ctrl_smd_notify(void *p, unsigned event) static void grmnet_ctrl_smd_connect_w(struct work_struct *w) { struct rmnet_ctrl_port *port = - container_of(w, struct rmnet_ctrl_port, connect_w); + container_of(w, struct rmnet_ctrl_port, connect_w.work); struct smd_ch_info *c = &port->ctrl_ch; unsigned long flags; int ret; @@ -328,17 +335,22 @@ static void grmnet_ctrl_smd_connect_w(struct work_struct *w) ret = smd_open(c->name, &c->ch, port, grmnet_ctrl_smd_notify); if (ret) { - pr_err("%s: Unable to open smd ch:%s err:%d\n", - __func__, c->name, ret); + if (ret == -EAGAIN) { + /* port not ready - retry */ + pr_debug("%s: SMD port not ready - rescheduling:%s err:%d\n", + __func__, c->name, ret); + queue_delayed_work(grmnet_ctrl_wq, &port->connect_w, + msecs_to_jiffies(250)); + } else { + pr_err("%s: unable to open smd port:%s err:%d\n", + __func__, c->name, ret); + } return; } spin_lock_irqsave(&port->port_lock, flags); - if (port->port_usb) { - pr_warning("%s: SMD notify closing before open\n", __func__); - c->cbits_tomodem |= TIOCM_RTS; + if (port->port_usb) smd_tiocmset(c->ch, c->cbits_tomodem, ~c->cbits_tomodem); - } spin_unlock_irqrestore(&port->port_lock, flags); } @@ -365,11 +377,11 @@ int gsmd_ctrl_connect(struct grmnet *gr, int port_num) spin_lock_irqsave(&port->port_lock, flags); port->port_usb = gr; - gr->send_cpkt_request = grmnet_ctrl_smd_send_cpkt_tomodem; - gr->send_cbits_tomodem = gsmd_ctrl_send_cbits_tomodem; + gr->send_encap_cmd = grmnet_ctrl_smd_send_cpkt_tomodem; + gr->notify_modem = gsmd_ctrl_send_cbits_tomodem; spin_unlock_irqrestore(&port->port_lock, flags); - queue_work(grmnet_ctrl_wq, &port->connect_w); + queue_delayed_work(grmnet_ctrl_wq, &port->connect_w, 0); return 0; } @@ -398,8 +410,8 @@ void gsmd_ctrl_disconnect(struct grmnet *gr, u8 port_num) spin_lock_irqsave(&port->port_lock, flags); port->port_usb = 0; - gr->send_cpkt_request = 0; - gr->send_cbits_tomodem = 0; + gr->send_encap_cmd = 0; + gr->notify_modem = 0; c->cbits_tomodem = 0; while (!list_empty(&c->tx_q)) { @@ -411,10 +423,13 @@ void gsmd_ctrl_disconnect(struct grmnet *gr, u8 port_num) spin_unlock_irqrestore(&port->port_lock, flags); - if (test_bit(CH_OPENED, &c->flags)) { - /* this should send the dtr zero */ + if (test_and_clear_bit(CH_OPENED, &c->flags)) + /* send dtr zero */ + smd_tiocmset(c->ch, c->cbits_tomodem, ~c->cbits_tomodem); + + if (c->ch) { smd_close(c->ch); - clear_bit(CH_OPENED, &c->flags); + c->ch = NULL; } } @@ -438,7 +453,8 @@ static int grmnet_ctrl_smd_ch_probe(struct platform_device *pdev) /* if usb is online, try opening smd_ch */ spin_lock_irqsave(&port->port_lock, flags); if (port->port_usb) - queue_work(grmnet_ctrl_wq, &port->connect_w); + queue_delayed_work(grmnet_ctrl_wq, + &port->connect_w, 0); spin_unlock_irqrestore(&port->port_lock, flags); break; @@ -463,7 +479,10 @@ static int grmnet_ctrl_smd_ch_remove(struct platform_device *pdev) if (!strncmp(c->name, pdev->name, SMD_CH_MAX_LEN)) { clear_bit(CH_READY, &c->flags); clear_bit(CH_OPENED, &c->flags); - smd_close(c->ch); + if (c->ch) { + smd_close(c->ch); + c->ch = NULL; + } break; } } @@ -496,7 +515,7 @@ static int grmnet_ctrl_smd_port_alloc(int portno) port->port_num = portno; spin_lock_init(&port->port_lock); - INIT_WORK(&port->connect_w, grmnet_ctrl_smd_connect_w); + INIT_DELAYED_WORK(&port->connect_w, grmnet_ctrl_smd_connect_w); c = &port->ctrl_ch; c->name = rmnet_ctrl_names[portno]; @@ -543,12 +562,13 @@ int gsmd_ctrl_setup(unsigned int count) } for (i = 0; i < count; i++) { + n_rmnet_ctrl_ports++; ret = grmnet_ctrl_smd_port_alloc(i); if (ret) { pr_err("%s: Unable to alloc port:%d\n", __func__, i); + n_rmnet_ctrl_ports--; goto free_ctrl_smd_ports; } - n_rmnet_ctrl_ports++; } return 0; @@ -601,8 +621,8 @@ static ssize_t gsmd_ctrl_read_stats(struct file *file, char __user *ubuf, c->cbits_tomodem ? "HIGH" : "LOW", test_bit(CH_OPENED, &c->flags), test_bit(CH_READY, &c->flags), - smd_read_avail(c->ch), - smd_write_avail(c->ch)); + c->ch ? smd_read_avail(c->ch) : 0, + c->ch ? smd_write_avail(c->ch) : 0); spin_unlock_irqrestore(&port->port_lock, flags); } diff --git a/drivers/usb/gadget/u_sdio.c b/drivers/usb/gadget/u_sdio.c index 056920aeaf4..8ff4c4bd461 100644 --- a/drivers/usb/gadget/u_sdio.c +++ b/drivers/usb/gadget/u_sdio.c @@ -232,7 +232,7 @@ int gsdio_write(struct gsdio_port *port, struct usb_request *req) { unsigned avail; char *packet; - unsigned size; + unsigned size = req->actual; unsigned n; int ret = 0; @@ -248,8 +248,6 @@ int gsdio_write(struct gsdio_port *port, struct usb_request *req) return -ENODEV; } - size = req->actual; - packet = req->buf; pr_debug("%s: port:%p port#%d req:%p actual:%d n_read:%d\n", __func__, port, port->port_num, req, req->actual, port->n_read); @@ -536,20 +534,6 @@ void gsdio_tx_pull(struct work_struct *w) goto tx_pull_end; } - /* Do not send data if DTR is not set */ - if (!(port->cbits_to_modem & TIOCM_DTR)) { - pr_info("%s: DTR low. flush %d bytes.", __func__, avail); - /* check if usb is still active */ - if (!port->port_usb) { - gsdio_free_req(in, req); - } else { - list_add(&req->list, pool); - port->wp_len++; - } - goto tx_pull_end; - } - - req->length = avail; spin_unlock_irq(&port->port_lock); @@ -655,10 +639,11 @@ void gsdio_ctrl_wq(struct work_struct *w) port->cbits_to_modem, ~(port->cbits_to_modem)); } -void gsdio_ctrl_notify_modem(struct gserial *gser, u8 portno, int ctrl_bits) +void gsdio_ctrl_notify_modem(void *gptr, u8 portno, int ctrl_bits) { struct gsdio_port *port; int temp; + struct gserial *gser = gptr; if (portno >= n_sdio_ports) { pr_err("%s: invalid portno#%d\n", __func__, portno); @@ -1146,8 +1131,8 @@ int gsdio_setup(struct usb_gadget *g, unsigned count) for (i = 0; i < count; i++) { mutex_init(&sdio_ports[i].lock); - n_sdio_ports++; ret = gsdio_port_alloc(i, &coding, sport_info + i); + n_sdio_ports++; if (ret) { n_sdio_ports--; pr_err("%s: sdio logical port allocation failed\n", diff --git a/drivers/usb/gadget/u_serial.c b/drivers/usb/gadget/u_serial.c index bde6a3f322b..c9da4bd78c0 100644 --- a/drivers/usb/gadget/u_serial.c +++ b/drivers/usb/gadget/u_serial.c @@ -55,7 +55,7 @@ * is managed in userspace ... OBEX, PTP, and MTP have been mentioned. */ -#define PREFIX "ttyHSUSB" +#define PREFIX "ttyGS" /* * gserial is the lifecycle interface, used by USB functions @@ -371,16 +371,11 @@ __acquires(&port->port_lock) */ { struct list_head *pool = &port->write_pool; - struct usb_ep *in; + struct usb_ep *in = port->port_usb->in; int status = 0; static long prev_len; bool do_tty_wake = false; - if (port->port_usb) - in = port->port_usb->in; - else - return 0; - while (!list_empty(pool)) { struct usb_request *req; int len; @@ -391,7 +386,9 @@ __acquires(&port->port_lock) req = list_entry(pool->next, struct usb_request, list); len = gs_send_packet(port, req->buf, TX_BUF_SIZE); if (len == 0) { - /* Queue zero length packet */ + /* Queue zero length packet explicitly to make it + * work with UDCs which don't support req->zero flag + */ if (prev_len && (prev_len % in->maxpacket == 0)) { req->length = 0; list_del(&req->list); @@ -416,7 +413,6 @@ __acquires(&port->port_lock) req->length = len; list_del(&req->list); - req->zero = (gs_buf_data_avail(&port->port_write_buf) == 0); pr_vdebug(PREFIX "%d: tx len=%d, 0x%02x 0x%02x 0x%02x ...\n", port->port_num, len, *((u8 *)req->buf), @@ -468,14 +464,9 @@ __acquires(&port->port_lock) */ { struct list_head *pool = &port->read_pool; - struct usb_ep *out; + struct usb_ep *out = port->port_usb->out; unsigned started = 0; - if (port->port_usb) - out = port->port_usb->out; - else - return 0; - while (!list_empty(pool)) { struct usb_request *req; int status; @@ -736,15 +727,10 @@ static int gs_alloc_requests(struct usb_ep *ep, struct list_head *head, static int gs_start_io(struct gs_port *port) { struct list_head *head = &port->read_pool; - struct usb_ep *ep; + struct usb_ep *ep = port->port_usb->out; int status; unsigned started; - if (port->port_usb) - ep = port->port_usb->out; - else - return -EIO; - /* Allocate RX and TX I/O buffers. We can't easily do this much * earlier (with GFP_KERNEL) because the requests are coupled to * endpoints, as are the packet sizes we'll be using. Different @@ -1361,9 +1347,6 @@ int gserial_setup(struct usb_gadget *g, unsigned count) if (count == 0 || count > N_PORTS) return -EINVAL; - if (gs_tty_driver) - return 0; - gs_tty_driver = alloc_tty_driver(count); if (!gs_tty_driver) return -ENOMEM; @@ -1388,10 +1371,6 @@ int gserial_setup(struct usb_gadget *g, unsigned count) gs_tty_driver->init_termios.c_ispeed = 9600; gs_tty_driver->init_termios.c_ospeed = 9600; - gs_tty_driver->init_termios.c_lflag = 0; - gs_tty_driver->init_termios.c_iflag = 0; - gs_tty_driver->init_termios.c_oflag = 0; - coding.dwDTERate = cpu_to_le32(9600); coding.bCharFormat = 8; coding.bParityType = USB_CDC_NO_PARITY; diff --git a/drivers/usb/gadget/u_serial.h b/drivers/usb/gadget/u_serial.h index fea53d8cee5..c9370068a7a 100644 --- a/drivers/usb/gadget/u_serial.h +++ b/drivers/usb/gadget/u_serial.h @@ -55,7 +55,7 @@ struct gserial { int (*send_modem_ctrl_bits)(struct gserial *p, int ctrl_bits); /* notification changes to modem */ - void (*notify_modem)(struct gserial *gser, u8 portno, int ctrl_bits); + void (*notify_modem)(void *gser, u8 portno, int ctrl_bits); }; /* utilities to allocate/free request and buffer */ diff --git a/drivers/usb/gadget/u_smd.c b/drivers/usb/gadget/u_smd.c index fe55363a8c1..3de82b37fe6 100644 --- a/drivers/usb/gadget/u_smd.c +++ b/drivers/usb/gadget/u_smd.c @@ -71,7 +71,7 @@ struct gsmd_port { struct gserial *port_usb; struct smd_port_info *pi; - struct work_struct connect_work; + struct delayed_work connect_work; /* At present, smd does not notify * control bit change info from modem @@ -209,6 +209,7 @@ static void gsmd_start_rx(struct gsmd_port *port) static void gsmd_rx_push(struct work_struct *w) { struct gsmd_port *port = container_of(w, struct gsmd_port, push); + struct smd_port_info *pi = port->pi; struct list_head *q; pr_debug("%s: port:%p port#%d", __func__, port, port->port_num); @@ -216,10 +217,9 @@ static void gsmd_rx_push(struct work_struct *w) spin_lock_irq(&port->port_lock); q = &port->read_queue; - while (!list_empty(q)) { + while (pi->ch && !list_empty(q)) { struct usb_request *req; int avail; - struct smd_port_info *pi = port->pi; req = list_first_entry(q, struct usb_request, list); @@ -245,7 +245,7 @@ static void gsmd_rx_push(struct work_struct *w) char *packet = req->buf; unsigned size = req->actual; unsigned n; - int count; + int count; n = port->n_read; if (n) { @@ -296,21 +296,24 @@ static void gsmd_tx_pull(struct work_struct *w) { struct gsmd_port *port = container_of(w, struct gsmd_port, pull); struct list_head *pool = &port->write_pool; + struct smd_port_info *pi = port->pi; + struct usb_ep *in; pr_debug("%s: port:%p port#%d pool:%p\n", __func__, port, port->port_num, pool); + spin_lock_irq(&port->port_lock); + if (!port->port_usb) { pr_debug("%s: usb is disconnected\n", __func__); + spin_unlock_irq(&port->port_lock); gsmd_read_pending(port); return; } - spin_lock_irq(&port->port_lock); - while (!list_empty(pool)) { + in = port->port_usb->in; + while (pi->ch && !list_empty(pool)) { struct usb_request *req; - struct usb_ep *in = port->port_usb->in; - struct smd_port_info *pi = port->pi; int avail; int ret; @@ -562,7 +565,7 @@ static void gsmd_connect_work(struct work_struct *w) struct smd_port_info *pi; int ret; - port = container_of(w, struct gsmd_port, connect_work); + port = container_of(w, struct gsmd_port, connect_work.work); pi = port->pi; pr_debug("%s: port:%p port#%d\n", __func__, port, port->port_num); @@ -573,16 +576,24 @@ static void gsmd_connect_work(struct work_struct *w) ret = smd_named_open_on_edge(pi->name, SMD_APPS_MODEM, &pi->ch, port, gsmd_notify); if (ret) { - pr_err("%s: unable to open smd port:%s err:%d\n", - __func__, pi->name, ret); - return; + if (ret == -EAGAIN) { + /* port not ready - retry */ + pr_debug("%s: SMD port not ready - rescheduling:%s err:%d\n", + __func__, pi->name, ret); + queue_delayed_work(gsmd_wq, &port->connect_work, + msecs_to_jiffies(250)); + } else { + pr_err("%s: unable to open smd port:%s err:%d\n", + __func__, pi->name, ret); + } } } -static void gsmd_notify_modem(struct gserial *gser, u8 portno, int ctrl_bits) +static void gsmd_notify_modem(void *gptr, u8 portno, int ctrl_bits) { struct gsmd_port *port; int temp; + struct gserial *gser = gptr; if (portno >= n_smd_ports) { pr_err("%s: invalid portno#%d\n", __func__, portno); @@ -671,7 +682,7 @@ int gsmd_connect(struct gserial *gser, u8 portno) } gser->out->driver_data = port; - queue_work(gsmd_wq, &port->connect_work); + queue_delayed_work(gsmd_wq, &port->connect_work, msecs_to_jiffies(0)); return 0; } @@ -710,17 +721,18 @@ void gsmd_disconnect(struct gserial *gser, u8 portno) port->n_read = 0; spin_unlock_irqrestore(&port->port_lock, flags); - if (!test_bit(CH_OPENED, &port->pi->flags)) - return; - - /* lower the dtr */ - port->cbits_to_modem = 0; - smd_tiocmset(port->pi->ch, - port->cbits_to_modem, - ~port->cbits_to_modem); + if (test_and_clear_bit(CH_OPENED, &port->pi->flags)) { + /* lower the dtr */ + port->cbits_to_modem = 0; + smd_tiocmset(port->pi->ch, + port->cbits_to_modem, + ~port->cbits_to_modem); + } - smd_close(port->pi->ch); - clear_bit(CH_OPENED, &port->pi->flags); + if (port->pi->ch) { + smd_close(port->pi->ch); + port->pi->ch = NULL; + } } #define SMD_CH_MAX_LEN 20 @@ -741,7 +753,8 @@ static int gsmd_ch_probe(struct platform_device *pdev) set_bit(CH_READY, &pi->flags); spin_lock_irqsave(&port->port_lock, flags); if (port->port_usb) - queue_work(gsmd_wq, &port->connect_work); + queue_delayed_work(gsmd_wq, &port->connect_work, + msecs_to_jiffies(0)); spin_unlock_irqrestore(&port->port_lock, flags); break; } @@ -764,7 +777,10 @@ static int gsmd_ch_remove(struct platform_device *pdev) if (!strncmp(pi->name, pdev->name, SMD_CH_MAX_LEN)) { clear_bit(CH_READY, &pi->flags); clear_bit(CH_OPENED, &pi->flags); - smd_close(pi->ch); + if (pi->ch) { + smd_close(pi->ch); + pi->ch = NULL; + } break; } } @@ -800,7 +816,7 @@ static int gsmd_port_alloc(int portno, struct usb_cdc_line_coding *coding) INIT_LIST_HEAD(&port->write_pool); INIT_WORK(&port->pull, gsmd_tx_pull); - INIT_WORK(&port->connect_work, gsmd_connect_work); + INIT_DELAYED_WORK(&port->connect_work, gsmd_connect_work); smd_ports[portno].port = port; pdrv = &smd_ports[portno].pdrv; @@ -820,6 +836,7 @@ static ssize_t debug_smd_read_stats(struct file *file, char __user *ubuf, size_t count, loff_t *ppos) { struct gsmd_port *port; + struct smd_port_info *pi; char *buf; unsigned long flags; int temp = 0; @@ -832,6 +849,7 @@ static ssize_t debug_smd_read_stats(struct file *file, char __user *ubuf, for (i = 0; i < n_smd_ports; i++) { port = smd_ports[i].port; + pi = port->pi; spin_lock_irqsave(&port->port_lock, flags); temp += scnprintf(buf + temp, 512 - temp, "###PORT:%d###\n" @@ -847,10 +865,10 @@ static ssize_t debug_smd_read_stats(struct file *file, char __user *ubuf, i, port->nbytes_tolaptop, port->nbytes_tomodem, port->cbits_to_modem, port->cbits_to_laptop, port->n_read, - smd_read_avail(port->pi->ch), - smd_write_avail(port->pi->ch), - test_bit(CH_OPENED, &port->pi->flags), - test_bit(CH_READY, &port->pi->flags)); + pi->ch ? smd_read_avail(pi->ch) : 0, + pi->ch ? smd_write_avail(pi->ch) : 0, + test_bit(CH_OPENED, &pi->flags), + test_bit(CH_READY, &pi->flags)); spin_unlock_irqrestore(&port->port_lock, flags); } diff --git a/include/linux/diagchar.h b/include/linux/diagchar.h index 3f75445e531..bd6671dd885 100644 --- a/include/linux/diagchar.h +++ b/include/linux/diagchar.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 @@ -19,7 +19,6 @@ #define PKT_TYPE 8 #define DEINIT_TYPE 16 #define USER_SPACE_LOG_TYPE 32 -#define USERMODE_DIAGFWD 64 #define USB_MODE 1 #define MEMORY_DEVICE_MODE 2 #define NO_LOGGING_MODE 3 @@ -41,8 +40,21 @@ #define APQ8060_MACHINE_ID 86 #define AO8960_MACHINE_ID 87 #define MSM8660_MACHINE_ID 71 +#define MDM9615_MACHINE_ID 104 +#define APQ8064_MACHINE_ID 109 +#define MSM8930_MACHINE_ID 116 +#define MSM8630_MACHINE_ID 117 +#define MSM8230_MACHINE_ID 118 +#define APQ8030_MACHINE_ID 119 +#define MSM8627_MACHINE_ID 120 +#define MSM8227_MACHINE_ID 121 +#define MSM8260A_MACHINE_ID 123 +#define MSM8974_MACHINE_ID 126 #define APQ8060_TOOLS_ID 4062 #define AO8960_TOOLS_ID 4064 +#define APQ8064_TOOLS_ID 4072 +#define MSM8930_TOOLS_ID 4072 +#define MSM8974_TOOLS_ID 4072 #define MSG_MASK_0 (0x00000001) #define MSG_MASK_1 (0x00000002) @@ -97,11 +109,11 @@ the appropriate macros. */ /* This needs to be modified manually now, when we add a new RANGE of SSIDs to the msg_mask_tbl */ -#define MSG_MASK_TBL_CNT 19 +#define MSG_MASK_TBL_CNT 23 #define EVENT_LAST_ID 0x083F #define MSG_SSID_0 0 -#define MSG_SSID_0_LAST 68 +#define MSG_SSID_0_LAST 90 #define MSG_SSID_1 500 #define MSG_SSID_1_LAST 506 #define MSG_SSID_2 1000 @@ -109,19 +121,19 @@ the appropriate macros. */ #define MSG_SSID_3 2000 #define MSG_SSID_3_LAST 2008 #define MSG_SSID_4 3000 -#define MSG_SSID_4_LAST 3012 +#define MSG_SSID_4_LAST 3014 #define MSG_SSID_5 4000 #define MSG_SSID_5_LAST 4010 #define MSG_SSID_6 4500 #define MSG_SSID_6_LAST 4526 #define MSG_SSID_7 4600 -#define MSG_SSID_7_LAST 4611 +#define MSG_SSID_7_LAST 4612 #define MSG_SSID_8 5000 -#define MSG_SSID_8_LAST 5024 +#define MSG_SSID_8_LAST 5029 #define MSG_SSID_9 5500 -#define MSG_SSID_9_LAST 5514 +#define MSG_SSID_9_LAST 5516 #define MSG_SSID_10 6000 -#define MSG_SSID_10_LAST 6050 +#define MSG_SSID_10_LAST 6072 #define MSG_SSID_11 6500 #define MSG_SSID_11_LAST 6521 #define MSG_SSID_12 7000 @@ -138,6 +150,14 @@ the appropriate macros. */ #define MSG_SSID_17_LAST 9008 #define MSG_SSID_18 9500 #define MSG_SSID_18_LAST 9509 +#define MSG_SSID_19 10200 +#define MSG_SSID_19_LAST 10210 +#define MSG_SSID_20 10251 +#define MSG_SSID_20_LAST 10255 +#define MSG_SSID_21 10300 +#define MSG_SSID_21_LAST 10300 +#define MSG_SSID_22 10350 +#define MSG_SSID_22_LAST 10361 struct diagpkt_delay_params { void *rsp_ptr; @@ -232,6 +252,28 @@ static const uint32_t msg_bld_masks_0[] = { MSG_LVL_MED, MSG_LVL_LOW, MSG_LVL_LOW, + MSG_LVL_LOW, + MSG_LVL_HIGH, + MSG_LVL_HIGH, + MSG_LVL_LOW, + MSG_LVL_LOW, + MSG_LVL_LOW, + MSG_LVL_LOW, + MSG_LVL_LOW, + MSG_LVL_HIGH, + MSG_LVL_HIGH, + MSG_LVL_LOW, + MSG_LVL_LOW, + MSG_LVL_LOW, + MSG_LVL_LOW, + MSG_LVL_LOW, + MSG_LVL_LOW, + MSG_LVL_LOW|MSG_LVL_MED|MSG_LVL_HIGH|MSG_LVL_ERROR|MSG_LVL_FATAL, + MSG_LVL_MED, + MSG_LVL_LOW|MSG_LVL_MED|MSG_LVL_HIGH|MSG_LVL_ERROR|MSG_LVL_FATAL, + MSG_LVL_LOW, + MSG_LVL_MED, + MSG_LVL_LOW }; static const uint32_t msg_bld_masks_1[] = { @@ -241,7 +283,7 @@ static const uint32_t msg_bld_masks_1[] = { MSG_LVL_LOW, MSG_LVL_HIGH, MSG_LVL_HIGH, - MSG_LVL_HIGH, + MSG_LVL_HIGH }; static const uint32_t msg_bld_masks_2[] = { @@ -280,7 +322,9 @@ static const uint32_t msg_bld_masks_4[] = { MSG_LVL_HIGH, MSG_LVL_HIGH, MSG_LVL_HIGH, - MSG_LVL_HIGH + MSG_LVL_HIGH, + MSG_LVL_LOW, + MSG_LVL_LOW }; static const uint32_t msg_bld_masks_5[] = { @@ -293,7 +337,8 @@ static const uint32_t msg_bld_masks_5[] = { MSG_LVL_MED, MSG_LVL_MED, MSG_LVL_MED, - MSG_LVL_MED, + MSG_LVL_MED|MSG_LVL_MED|MSG_MASK_5|MSG_MASK_6|MSG_MASK_7| \ + MSG_MASK_8|MSG_MASK_9, MSG_LVL_MED }; @@ -340,6 +385,7 @@ static const uint32_t msg_bld_masks_7[] = { MSG_LVL_MED, MSG_LVL_MED, MSG_LVL_MED, + MSG_LVL_LOW }; static const uint32_t msg_bld_masks_8[] = { @@ -368,6 +414,11 @@ static const uint32_t msg_bld_masks_8[] = { MSG_LVL_MED, MSG_LVL_MED, MSG_LVL_MED, + MSG_LVL_MED, + MSG_LVL_MED, + MSG_LVL_MED, + MSG_LVL_MED, + MSG_LVL_MED }; static const uint32_t msg_bld_masks_9[] = { @@ -386,6 +437,8 @@ static const uint32_t msg_bld_masks_9[] = { MSG_LVL_MED|MSG_MASK_5, MSG_LVL_MED|MSG_MASK_5, MSG_LVL_MED|MSG_MASK_5, + MSG_LVL_MED|MSG_MASK_5, + MSG_LVL_MED|MSG_MASK_5 }; static const uint32_t msg_bld_masks_10[] = { @@ -445,6 +498,28 @@ static const uint32_t msg_bld_masks_10[] = { MSG_LVL_LOW, MSG_LVL_LOW, MSG_LVL_LOW, + MSG_LVL_LOW, + MSG_LVL_MED, + MSG_LVL_MED, + MSG_LVL_MED, + MSG_LVL_MED, + MSG_LVL_MED, + MSG_LVL_MED, + MSG_LVL_MED, + MSG_LVL_MED, + MSG_LVL_MED, + MSG_LVL_MED, + MSG_LVL_MED, + MSG_LVL_MED, + MSG_LVL_MED, + MSG_LVL_MED, + MSG_LVL_MED, + MSG_LVL_MED, + MSG_LVL_MED, + MSG_LVL_MED, + MSG_LVL_MED, + MSG_LVL_MED, + MSG_LVL_LOW }; static const uint32_t msg_bld_masks_11[] = { @@ -567,10 +642,51 @@ static const uint32_t msg_bld_masks_18[] = { MSG_LVL_LOW }; +static const uint32_t msg_bld_masks_19[] = { + MSG_LVL_LOW, + MSG_LVL_LOW, + MSG_LVL_LOW, + MSG_LVL_LOW, + MSG_LVL_LOW, + MSG_LVL_LOW, + MSG_LVL_LOW, + MSG_LVL_LOW, + MSG_LVL_LOW, + MSG_LVL_LOW, + MSG_LVL_LOW +}; + +static const uint32_t msg_bld_masks_20[] = { + MSG_LVL_LOW, + MSG_LVL_LOW, + MSG_LVL_LOW, + MSG_LVL_LOW, + MSG_LVL_LOW +}; + +static const uint32_t msg_bld_masks_21[] = { + MSG_LVL_HIGH +}; + +static const uint32_t msg_bld_masks_22[] = { + MSG_LVL_HIGH, + MSG_LVL_HIGH, + MSG_LVL_HIGH, + MSG_LVL_HIGH, + MSG_LVL_HIGH, + MSG_LVL_HIGH, + MSG_LVL_HIGH, + MSG_LVL_HIGH, + MSG_LVL_HIGH, + MSG_LVL_HIGH, + MSG_LVL_HIGH, + MSG_LVL_HIGH +}; + /* LOG CODES */ #define LOG_0 0x0 -#define LOG_1 0x1520 +#define LOG_1 0x15A7 #define LOG_2 0x0 #define LOG_3 0x0 #define LOG_4 0x4910 diff --git a/include/linux/usb/android.h b/include/linux/usb/android.h new file mode 100644 index 00000000000..9d7e4a84802 --- /dev/null +++ b/include/linux/usb/android.h @@ -0,0 +1,24 @@ +/* + * Platform data for Android USB + * + * Copyright (C) 2008 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_ANDROID_H +#define __LINUX_USB_ANDROID_H + +struct android_usb_platform_data { + int (*update_pid_and_serial_num)(uint32_t, const char *); +}; + +#endif /* __LINUX_USB_ANDROID_H */ diff --git a/include/linux/usb/ccid_desc.h b/include/linux/usb/ccid_desc.h new file mode 100644 index 00000000000..2d1ae741641 --- /dev/null +++ b/include/linux/usb/ccid_desc.h @@ -0,0 +1,112 @@ +/* + * 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_USB_CCID_DESC_H +#define __LINUX_USB_CCID_DESC_H + +/*CCID specification version 1.10*/ +#define CCID1_10 0x0110 + +#define SMART_CARD_DEVICE_CLASS 0x0B +/* Smart Card Device Class Descriptor Type */ +#define CCID_DECRIPTOR_TYPE 0x21 + +/* Table 5.3-1 Summary of CCID Class Specific Request */ +#define CCIDGENERICREQ_ABORT 0x01 +#define CCIDGENERICREQ_GET_CLOCK_FREQUENCIES 0x02 +#define CCIDGENERICREQ_GET_DATA_RATES 0x03 + +/* 6.1 Command Pipe, Bulk-OUT Messages */ +#define PC_TO_RDR_ICCPOWERON 0x62 +#define PC_TO_RDR_ICCPOWEROFF 0x63 +#define PC_TO_RDR_GETSLOTSTATUS 0x65 +#define PC_TO_RDR_XFRBLOCK 0x6F +#define PC_TO_RDR_GETPARAMETERS 0x6C +#define PC_TO_RDR_RESETPARAMETERS 0x6D +#define PC_TO_RDR_SETPARAMETERS 0x61 +#define PC_TO_RDR_ESCAPE 0x6B +#define PC_TO_RDR_ICCCLOCK 0x6E +#define PC_TO_RDR_T0APDU 0x6A +#define PC_TO_RDR_SECURE 0x69 +#define PC_TO_RDR_MECHANICAL 0x71 +#define PC_TO_RDR_ABORT 0x72 +#define PC_TO_RDR_SETDATARATEANDCLOCKFREQUENCY 0x73 + +/* 6.2 Response Pipe, Bulk-IN Messages */ +#define RDR_TO_PC_DATABLOCK 0x80 +#define RDR_TO_PC_SLOTSTATUS 0x81 +#define RDR_TO_PC_PARAMETERS 0x82 +#define RDR_TO_PC_ESCAPE 0x83 +#define RDR_TO_PC_DATARATEANDCLOCKFREQUENCY 0x84 + +/* 6.3 Interrupt-IN Messages */ +#define RDR_TO_PC_NOTIFYSLOTCHANGE 0x50 +#define RDR_TO_PC_HARDWAREERROR 0x51 + +/* Table 6.2-2 Slot error register when bmCommandStatus = 1 */ +#define CMD_ABORTED 0xFF +#define ICC_MUTE 0xFE +#define XFR_PARITY_ERROR 0xFD +#define XFR_OVERRUN 0xFC +#define HW_ERROR 0xFB +#define BAD_ATR_TS 0xF8 +#define BAD_ATR_TCK 0xF7 +#define ICC_PROTOCOL_NOT_SUPPORTED 0xF6 +#define ICC_CLASS_NOT_SUPPORTED 0xF5 +#define PROCEDURE_BYTE_CONFLICT 0xF4 +#define DEACTIVATED_PROTOCOL 0xF3 +#define BUSY_WITH_AUTO_SEQUENCE 0xF2 +#define PIN_TIMEOUT 0xF0 +#define PIN_CANCELLED 0xEF +#define CMD_SLOT_BUSY 0xE0 + +/* CCID rev 1.1, p.27 */ +#define VOLTS_AUTO 0x00 +#define VOLTS_5_0 0x01 +#define VOLTS_3_0 0x02 +#define VOLTS_1_8 0x03 + +/* 6.3.1 RDR_to_PC_NotifySlotChange */ +#define ICC_NOT_PRESENT 0x00 +#define ICC_PRESENT 0x01 +#define ICC_CHANGE 0x02 +#define ICC_INSERTED_EVENT (ICC_PRESENT+ICC_CHANGE) + +/* Identifies the length of type of subordinate descriptors of a CCID device + * Table 5.1-1 Smart Card Device Class descriptors + */ +struct usb_ccid_class_descriptor { + unsigned char bLength; + unsigned char bDescriptorType; + unsigned short bcdCCID; + unsigned char bMaxSlotIndex; + unsigned char bVoltageSupport; + unsigned long dwProtocols; + unsigned long dwDefaultClock; + unsigned long dwMaximumClock; + unsigned char bNumClockSupported; + unsigned long dwDataRate; + unsigned long dwMaxDataRate; + unsigned char bNumDataRatesSupported; + unsigned long dwMaxIFSD; + unsigned long dwSynchProtocols; + unsigned long dwMechanical; + unsigned long dwFeatures; + unsigned long dwMaxCCIDMessageLength; + unsigned char bClassGetResponse; + unsigned char bClassEnvelope; + unsigned short wLcdLayout; + unsigned char bPINSupport; + unsigned char bMaxCCIDBusySlots; +} __packed; +#endif diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h index 47d319bd728..66a29a91899 100644 --- a/include/linux/usb/composite.h +++ b/include/linux/usb/composite.h @@ -36,7 +36,6 @@ #include #include -#include /* * USB function drivers should return USB_GADGET_DELAYED_STATUS if they @@ -362,13 +361,6 @@ struct usb_composite_dev { /* protects deactivations and delayed_status counts*/ spinlock_t lock; - - /* switch indicating Connect_to_PC App only */ - struct switch_dev sw_connect2pc; - /* current connected state for sw_connected */ - bool connected; - - struct work_struct switch_work; }; extern int usb_string_id(struct usb_composite_dev *c); diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h index 2f2e3dbf042..47e84270d31 100644 --- a/include/linux/usb/gadget.h +++ b/include/linux/usb/gadget.h @@ -57,6 +57,7 @@ struct usb_ep; * Note that for writes (IN transfers) some data bytes may still * reside in a device-side FIFO when the request is reported as * complete. + *@udc_priv: Vendor private data in usage by the UDC. * * These are allocated/freed through the endpoint they're used with. The * hardware's driver can add extra per-request data to the memory it returns, @@ -92,6 +93,7 @@ struct usb_request { int status; unsigned actual; + unsigned udc_priv; }; /*-------------------------------------------------------------------------*/ @@ -111,7 +113,6 @@ struct usb_ep_ops { struct usb_request *(*alloc_request) (struct usb_ep *ep, gfp_t gfp_flags); void (*free_request) (struct usb_ep *ep, struct usb_request *req); - int (*queue) (struct usb_ep *ep, struct usb_request *req, gfp_t gfp_flags); int (*dequeue) (struct usb_ep *ep, struct usb_request *req); @@ -774,7 +775,6 @@ struct usb_gadget_driver { int (*setup)(struct usb_gadget *, const struct usb_ctrlrequest *); void (*disconnect)(struct usb_gadget *); - void (*mute_disconnect)(struct usb_gadget *); void (*suspend)(struct usb_gadget *); void (*resume)(struct usb_gadget *); From cff54ddd0239e041a03fdefddd788f81131a731e Mon Sep 17 00:00:00 2001 From: David Hays Date: Wed, 29 May 2013 01:28:26 -0500 Subject: [PATCH 016/111] msm: vigor: bulk update USB gadget CAF Change-Id: I05998d85acb0e9c2729da1a7dbbec38da035bbd3 --- arch/arm/configs/vigor_aosp_defconfig | 26 +- drivers/usb/gadget/Kconfig | 13 + drivers/usb/gadget/android.c | 47 +- drivers/usb/gadget/ci13xxx_udc.c | 14 +- drivers/usb/gadget/f_adb.c | 2 + drivers/usb/gadget/f_projector.c | 889 ++++++++++++++++++++------ drivers/usb/gadget/f_usbnet.c | 840 ------------------------ drivers/usb/gadget/htc_attr.c | 628 +----------------- drivers/usb/gadget/u_xpst.c | 5 +- include/linux/usb/android.h | 1 + 10 files changed, 770 insertions(+), 1695 deletions(-) delete mode 100644 drivers/usb/gadget/f_usbnet.c diff --git a/arch/arm/configs/vigor_aosp_defconfig b/arch/arm/configs/vigor_aosp_defconfig index bb85202f838..57b08ce9ca1 100644 --- a/arch/arm/configs/vigor_aosp_defconfig +++ b/arch/arm/configs/vigor_aosp_defconfig @@ -2245,40 +2245,32 @@ CONFIG_USB_GADGET_DUALSPEED=y # CONFIG_USB_MIDI_GADGET is not set # CONFIG_USB_G_PRINTER is not set CONFIG_USB_G_ANDROID=y -# CONFIG_USB_ANDROID_ACM is not set -CONFIG_USB_ANDROID_ADB=y -CONFIG_USB_ANDROID_DIAG=y -CONFIG_USB_ANDROID_MDM9K_DIAG=y -CONFIG_USB_ANDROID_MDM9K_MODEM=y -CONFIG_USB_ANDROID_MASS_STORAGE=y -CONFIG_USB_ANDROID_MTP=y -CONFIG_USB_ANDROID_RNDIS=y -# CONFIG_USB_ANDROID_RMNET 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 +CONFIG_USB_CSW_HACK=y +# CONFIG_USB_MSC_PROFILING is not set +CONFIG_MODEM_SUPPORT=y CONFIG_RMNET_SMD_CTL_CHANNEL="" CONFIG_RMNET_SMD_DATA_CHANNEL="" -# CONFIG_USB_ANDROID_RMNET_SDIO is not set CONFIG_RMNET_SDIO_CTL_CHANNEL=8 CONFIG_RMNET_SDIO_DATA_CHANNEL=8 -CONFIG_USB_ANDROID_RMNET_SMD_SDIO=y CONFIG_RMNET_SMD_SDIO_CTL_CHANNEL=8 CONFIG_RMNET_SMD_SDIO_DATA_CHANNEL=8 CONFIG_RMNET_SDIO_SMD_DATA_CHANNEL="DATA40" # CONFIG_USB_ANDROID_RMNET_CTRL_SMD is not set # CONFIG_USB_F_SERIAL is not set -CONFIG_MODEM_SUPPORT=y -CONFIG_USB_ANDROID_SERIAL=y -CONFIG_USB_ANDROID_PROJECTOR=y -CONFIG_USB_ANDROID_ECM=y # CONFIG_USB_F_SERIAL_SDIO is not set # CONFIG_USB_F_SERIAL_SMD is not set -CONFIG_USB_ANDROID_USBNET=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_BY_ADC is not set -# CONFIG_USB_CSW_HACK is not set +CONFIG_USB_CSW_HACK=y CONFIG_USB_GADGET_VERIZON_PRODUCT_ID=y +CONFIG_USB_HTC_SWITCH_STUB=y # # OTG and related infrastructure diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 117d3bf9b69..ac0e7f4133b 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -1089,6 +1089,12 @@ config USB_CSW_HACK This csw hack feature is for increasing the performance of the mass storage +config USB_MSC_PROFILING + bool "USB MSC performance profiling" + help + If you say Y here, support will be added for collecting + Mass-storage performance numbers at the VFS level. + config MODEM_SUPPORT boolean "modem support in generic serial function driver" depends on USB_G_ANDROID @@ -1163,3 +1169,10 @@ config USB_ANDROID_RMNET_CTRL_SMD Data interface used is BAM. endif # USB_GADGET + +config USB_HTC_SWITCH_STUB + depends on USB_G_ANDROID + bool "USB HTC function switch stub" + default n + help + Provide dummy HTC USB function switch sysfs device for libhtc_ril. diff --git a/drivers/usb/gadget/android.c b/drivers/usb/gadget/android.c index 403c373714b..f24e74d8df4 100644 --- a/drivers/usb/gadget/android.c +++ b/drivers/usb/gadget/android.c @@ -2,6 +2,7 @@ * Gadget Driver for Android * * Copyright (C) 2008 Google, Inc. + * Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. * Author: Mike Lockwood * * This software is licensed under the terms of the GNU General Public @@ -878,14 +879,28 @@ static int mass_storage_function_init(struct android_usb_function *f, struct mass_storage_function_config *config; struct fsg_common *common; int err; + struct android_dev *dev = _android_dev; + int i; config = kzalloc(sizeof(struct mass_storage_function_config), GFP_KERNEL); if (!config) return -ENOMEM; - config->fsg.nluns = 1; - config->fsg.luns[0].removable = 1; + if (dev->pdata->nluns) { + config->fsg.nluns = dev->pdata->nluns; + if (config->fsg.nluns > FSG_MAX_LUNS) + config->fsg.nluns = FSG_MAX_LUNS; + for (i = 0; i < config->fsg.nluns; i++) { + config->fsg.luns[i].cdrom = 0; + config->fsg.luns[i].removable = 1; + config->fsg.luns[i].ro = 0; + } + } else { + /* default value */ + config->fsg.nluns = 1; + config->fsg.luns[0].removable = 1; + } common = fsg_common_init(NULL, cdev, &config->fsg); if (IS_ERR(common)) { @@ -893,13 +908,15 @@ static int mass_storage_function_init(struct android_usb_function *f, return PTR_ERR(common); } - err = sysfs_create_link(&f->dev->kobj, - &common->luns[0].dev.kobj, - "lun"); - if (err) { - fsg_common_release(&common->ref); - kfree(config); - return err; + for (i = 0; i < config->fsg.nluns; i++) { + err = sysfs_create_link(&f->dev->kobj, + &common->luns[i].dev.kobj, + common->luns[i].dev.kobj.name); + if (err) { + fsg_common_release(&common->ref); + kfree(config); + return err; + } } config->common = common; @@ -1323,6 +1340,10 @@ static struct device_attribute *android_usb_attributes[] = { NULL }; +#ifdef CONFIG_USB_HTC_SWITCH_STUB +#include "htc_attr.c" +#endif + /*-------------------------------------------------------------------------*/ /* Composite driver */ @@ -1519,6 +1540,14 @@ static int __devinit android_probe(struct platform_device *pdev) struct android_usb_platform_data *pdata = pdev->dev.platform_data; struct android_dev *dev = _android_dev; +#ifdef CONFIG_USB_HTC_SWITCH_STUB + int err; + err = sysfs_create_group(&pdev->dev.kobj, &htc_attr_group); + if (err) { + pr_err("%s: failed to create HTC USB devices\n", __func__); + } +#endif + dev->pdata = pdata; return 0; diff --git a/drivers/usb/gadget/ci13xxx_udc.c b/drivers/usb/gadget/ci13xxx_udc.c index e97602d7642..287c9933873 100644 --- a/drivers/usb/gadget/ci13xxx_udc.c +++ b/drivers/usb/gadget/ci13xxx_udc.c @@ -878,7 +878,7 @@ static void dbg_print(u8 addr, const char *name, int status, const char *extra) stamp = stamp * 1000000 + tval.tv_usec; scnprintf(dbg_data.buf[dbg_data.idx], DBG_DATA_MSG, - "%04X\t» %02X %-7.7s %4i «\t%s\n", + "%04X\t» %02X %-7.7s %4i «\t%s\n", stamp, addr, name, status, extra); dbg_inc(&dbg_data.idx); @@ -886,7 +886,7 @@ static void dbg_print(u8 addr, const char *name, int status, const char *extra) write_unlock_irqrestore(&dbg_data.lck, flags); if (dbg_data.tty != 0) - pr_notice("%04X\t» %02X %-7.7s %4i «\t%s\n", + pr_notice("%04X\t» %02X %-7.7s %4i «\t%s\n", stamp, addr, name, status, extra); } @@ -1046,15 +1046,15 @@ static ssize_t show_inters(struct device *dev, struct device_attribute *attr, n += scnprintf(buf + n, PAGE_SIZE - n, "*test = %d\n", isr_statistics.test); - n += scnprintf(buf + n, PAGE_SIZE - n, "» ui = %d\n", + n += scnprintf(buf + n, PAGE_SIZE - n, "» ui = %d\n", isr_statistics.ui); - n += scnprintf(buf + n, PAGE_SIZE - n, "» uei = %d\n", + n += scnprintf(buf + n, PAGE_SIZE - n, "» uei = %d\n", isr_statistics.uei); - n += scnprintf(buf + n, PAGE_SIZE - n, "» pci = %d\n", + n += scnprintf(buf + n, PAGE_SIZE - n, "» pci = %d\n", isr_statistics.pci); - n += scnprintf(buf + n, PAGE_SIZE - n, "» uri = %d\n", + n += scnprintf(buf + n, PAGE_SIZE - n, "» uri = %d\n", isr_statistics.uri); - n += scnprintf(buf + n, PAGE_SIZE - n, "» sli = %d\n", + n += scnprintf(buf + n, PAGE_SIZE - n, "» sli = %d\n", isr_statistics.sli); n += scnprintf(buf + n, PAGE_SIZE - n, "*none = %d\n", isr_statistics.none); diff --git a/drivers/usb/gadget/f_adb.c b/drivers/usb/gadget/f_adb.c index b85805c04a3..cae01368c20 100644 --- a/drivers/usb/gadget/f_adb.c +++ b/drivers/usb/gadget/f_adb.c @@ -446,6 +446,8 @@ static struct miscdevice adb_device = { }; + + static int adb_function_bind(struct usb_configuration *c, struct usb_function *f) { diff --git a/drivers/usb/gadget/f_projector.c b/drivers/usb/gadget/f_projector.c index 557f9f5a188..c1f14051b1c 100644 --- a/drivers/usb/gadget/f_projector.c +++ b/drivers/usb/gadget/f_projector.c @@ -21,9 +21,18 @@ #include #include #include +#include +#include +#include #include - #include +#include +#include + +#ifdef DUMMY_DISPLAY_MODE +#include "f_projector_debug.h" +#endif + #ifdef DBG #undef DBG #endif @@ -34,25 +43,41 @@ #define DBG(x...) printk(KERN_INFO x) #endif +#ifdef VDBG +#undef VDBG +#endif + +#if 1 +#define VDBG(x...) do {} while (0) +#else +#define VDBG(x...) printk(KERN_INFO x) +#endif + + /*16KB*/ #define TXN_MAX 16384 #define RXN_MAX 4096 -/* number of rx and tx requests to allocate */ +/* number of rx requests to allocate */ #define PROJ_RX_REQ_MAX 4 -#if 0 -#define PROJ_TX_REQ_MAX 115 /*for resolution 1280*736*2 / 16k */ -#define PROJ_TX_REQ_MAX 75 /*for resolution 1024*600*2 / 16k */ -#define PROJ_TX_REQ_MAX 56 /*for 8k resolution 480*800*2 / 16k */ -#endif + +#define DEFAULT_PROJ_WIDTH 480 +#define DEFAULT_PROJ_HEIGHT 800 + +#define TOUCH_WIDTH 480 +#define TOUCH_HEIGHT 800 #define BITSPIXEL 16 #define PROJECTOR_FUNCTION_NAME "projector" +#define htc_mode_info(fmt, args...) \ + printk(KERN_INFO "[htc_mode] " pr_fmt(fmt), ## args) + static struct wake_lock prj_idle_wake_lock; static int keypad_code[] = {KEY_WAKEUP, 0, 0, 0, KEY_HOME, KEY_MENU, KEY_BACK}; -static const char shortname[] = "android_projector"; +static const char cand_shortname[] = "htc_cand"; +static const char htcmode_shortname[] = "htcmode"; struct projector_dev { struct usb_function function; @@ -62,6 +87,9 @@ struct projector_dev { struct usb_ep *ep_in; struct usb_ep *ep_out; + struct usb_endpoint_descriptor *in; + struct usb_endpoint_descriptor *out; + int online; int error; @@ -82,7 +110,20 @@ struct projector_dev { struct input_dev *keypad_input; struct input_dev *touch_input; char *fbaddr; - struct platform_device *pdev; + + atomic_t cand_online; + struct switch_dev cand_sdev; + struct switch_dev htcmode_sdev; + struct work_struct notifier_work; + struct work_struct htcmode_notifier_work; + + struct workqueue_struct *wq_display; + struct work_struct send_fb_work; + int start_send_fb; + + /* HTC Mode Protocol Info */ + struct htcmode_protocol *htcmode_proto; + u8 is_htcmode; }; static struct usb_interface_descriptor projector_interface_desc = { @@ -155,8 +196,31 @@ static struct usb_gadget_strings *projector_strings[] = { &projector_string_table, NULL, }; -static struct projector_dev _projector_dev; -struct device prj_dev; + +static struct projector_dev *projector_dev = NULL; + +struct size { + int w; + int h; +}; + +enum { + NOT_ON_AUTOBOT, + DOCK_ON_AUTOBOT, + HTC_MODE_RUNNING +}; +/* the value of htc_mode_status should be one of above status */ +static atomic_t htc_mode_status = ATOMIC_INIT(0); + +static void usb_setup_andriod_projector(struct work_struct *work); +static DECLARE_WORK(conf_usb_work, usb_setup_andriod_projector); + + +static void usb_setup_andriod_projector(struct work_struct *work) +{ + android_switch_htc_mode(); + htc_mode_enable(1); +} static inline struct projector_dev *proj_func_to_dev(struct usb_function *f) { @@ -188,21 +252,6 @@ static void projector_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 proj_req_put(struct projector_dev *dev, struct list_head *head, struct usb_request *req) @@ -231,20 +280,20 @@ static struct usb_request *proj_req_get(struct projector_dev *dev, struct list_h return req; } -static void projector_queue_out(struct projector_dev *ctxt) +static void projector_queue_out(struct projector_dev *dev) { int ret; struct usb_request *req; /* if we have idle read requests, get them queued */ - while ((req = proj_req_get(ctxt, &ctxt->rx_idle))) { + while ((req = proj_req_get(dev, &dev->rx_idle))) { req->length = RXN_MAX; - DBG("%s: queue %p\n", __func__, req); - ret = usb_ep_queue(ctxt->ep_out, req, GFP_ATOMIC); + VDBG("%s: queue %p\n", __func__, req); + ret = usb_ep_queue(dev->ep_out, req, GFP_ATOMIC); if (ret < 0) { - DBG("projector: failed to queue out req (%d)\n", ret); - ctxt->error = 1; - proj_req_put(ctxt, &ctxt->rx_idle, req); + VDBG("projector: failed to queue out req (%d)\n", ret); + dev->error = 1; + proj_req_put(dev, &dev->rx_idle, req); break; } } @@ -305,10 +354,10 @@ static void projector_send_touch_event(struct projector_dev *dev, } /* key code: 4 -> home, 5-> menu, 6 -> back, 0 -> system wake */ -static void projector_send_Key_event(struct projector_dev *ctxt, +static void projector_send_Key_event(struct projector_dev *dev, int iKeycode) { - struct input_dev *kdev = ctxt->keypad_input; + struct input_dev *kdev = dev->keypad_input; printk(KERN_INFO "%s keycode %d\n", __func__, iKeycode); /* ics will use default Generic.kl to translate linux keycode WAKEUP @@ -325,60 +374,146 @@ static void projector_send_Key_event(struct projector_dev *ctxt, input_sync(kdev); } -static void send_fb(struct projector_dev *ctxt) +#if defined(CONFIG_ARCH_MSM7X30) || defined(CONFIG_ARCH_MSM8X60) +extern char *get_fb_addr(void); +#endif + +static void send_fb(struct projector_dev *dev) { struct usb_request *req; - char *frame; int xfer; - int count = ctxt->framesize; + int count = dev->framesize; +#ifdef DUMMY_DISPLAY_MODE + unsigned short *frame; +#else + char *frame; +#endif - if (msmfb_get_fb_area()) - frame = (ctxt->fbaddr + ctxt->framesize); - else - frame = ctxt->fbaddr; + +#ifdef DUMMY_DISPLAY_MODE + frame = test_frame; +#elif defined(CONFIG_ARCH_MSM7X30) || defined(CONFIG_ARCH_MSM8X60) + frame = get_fb_addr(); +#else + if (msmfb_get_fb_area()) + frame = (dev->fbaddr + dev->framesize); + else + frame = dev->fbaddr; +#endif + if (frame == NULL) + return; while (count > 0) { - req = proj_req_get(ctxt, &ctxt->tx_idle); + req = proj_req_get(dev, &dev->tx_idle); if (req) { xfer = count > TXN_MAX? TXN_MAX : count; req->length = xfer; memcpy(req->buf, frame, xfer); - if (usb_ep_queue(ctxt->ep_in, req, GFP_ATOMIC) < 0) { - proj_req_put(ctxt, &ctxt->tx_idle, req); + if (usb_ep_queue(dev->ep_in, req, GFP_ATOMIC) < 0) { + proj_req_put(dev, &dev->tx_idle, req); printk(KERN_WARNING "%s: failed to queue req %p\n", __func__, req); break; } + + count -= xfer; +#ifdef DUMMY_DISPLAY_MODE + frame += xfer/2; +#else + frame += xfer; +#endif + } else { + printk(KERN_ERR "send_fb: no req to send\n"); + break; + } + } +} + +static void send_fb2(struct projector_dev *dev) +{ + struct usb_request *req; + int xfer; + +#ifdef DUMMY_DISPLAY_MODE + unsigned short *frame; + int count = dev->framesize; +#else + char *frame; + int count = dev->htcmode_proto->server_info.width * + dev->htcmode_proto->server_info.height * (BITSPIXEL / 8); +#endif + +#ifdef DUMMY_DISPLAY_MODE + frame = test_frame; +#elif defined(CONFIG_ARCH_MSM7X30) || defined(CONFIG_ARCH_MSM8X60) + frame = get_fb_addr(); +#else + if (msmfb_get_fb_area()) + frame = (dev->fbaddr + dev->framesize); + else + frame = dev->fbaddr; +#endif + if (frame == NULL) + return; + + while (count > 0 && dev->online) { + + while (!(req = proj_req_get(dev, &dev->tx_idle))) { + msleep(1); + + if (!dev->online) + break; + } + + if (req) { + xfer = count > TXN_MAX? TXN_MAX : count; + req->length = xfer; + memcpy(req->buf, frame, xfer); + if (usb_ep_queue(dev->ep_in, req, GFP_ATOMIC) < 0) { + proj_req_put(dev, &dev->tx_idle, req); + printk(KERN_WARNING "%s: failed to queue req" + " %p\n", __func__, req); + break; + } count -= xfer; +#ifdef DUMMY_DISPLAY_MODE + frame += xfer/2; +#else frame += xfer; +#endif } else { printk(KERN_ERR "send_fb: no req to send\n"); break; } } } -static void send_info(struct projector_dev *ctxt) + +void send_fb_do_work(struct work_struct *work) +{ + struct projector_dev *dev = projector_dev; + while (dev->start_send_fb) { + send_fb2(dev); + msleep(1); + } +} + + + +static void send_info(struct projector_dev *dev) { struct usb_request *req; - req = proj_req_get(ctxt, &ctxt->tx_idle); + req = proj_req_get(dev, &dev->tx_idle); if (req) { req->length = 20; memcpy(req->buf, "okay", 4); - memcpy(req->buf + 4, &ctxt->bitsPixel, 4); - #if defined(CONFIG_MACH_PARADISE) - if (machine_is_paradise()) { - ctxt->framesize = 320 * 480 * 2; - printk(KERN_INFO "send_info: framesize %d\n", - ctxt->framesize); - } - #endif - memcpy(req->buf + 8, &ctxt->framesize, 4); - memcpy(req->buf + 12, &ctxt->width, 4); - memcpy(req->buf + 16, &ctxt->height, 4); - if (usb_ep_queue(ctxt->ep_in, req, GFP_ATOMIC) < 0) { - proj_req_put(ctxt, &ctxt->tx_idle, req); + memcpy(req->buf + 4, &dev->bitsPixel, 4); + memcpy(req->buf + 8, &dev->framesize, 4); + memcpy(req->buf + 12, &dev->width, 4); + memcpy(req->buf + 16, &dev->height, 4); + if (usb_ep_queue(dev->ep_in, req, GFP_ATOMIC) < 0) { + proj_req_put(dev, &dev->tx_idle, req); printk(KERN_WARNING "%s: failed to queue req %p\n", __func__, req); } @@ -386,81 +521,263 @@ static void send_info(struct projector_dev *ctxt) printk(KERN_INFO "%s: no req to send\n", __func__); } -static void projector_get_msmfb(struct projector_dev *ctxt) + +static void send_server_info(struct projector_dev *dev) +{ + struct usb_request *req; + + req = proj_req_get(dev, &dev->tx_idle); + if (req) { + req->length = sizeof(struct msm_server_info); + memcpy(req->buf, &dev->htcmode_proto->server_info, req->length); + if (usb_ep_queue(dev->ep_in, req, GFP_ATOMIC) < 0) { + proj_req_put(dev, &dev->tx_idle, req); + printk(KERN_WARNING "%s: failed to queue req %p\n", + __func__, req); + } + } else { + printk(KERN_INFO "%s: no req to send\n", __func__); + } +} + +static void send_server_nonce(struct projector_dev *dev) +{ + struct usb_request *req; + int nonce[NONCE_SIZE]; + int i = 0; + + req = proj_req_get(dev, &dev->tx_idle); + if (req) { + req->length = NONCE_SIZE * sizeof(int); + for (i = 0; i < NONCE_SIZE; i++) + nonce[i] = get_random_int(); + memcpy(req->buf, nonce, req->length); + if (usb_ep_queue(dev->ep_in, req, GFP_ATOMIC) < 0) { + proj_req_put(dev, &dev->tx_idle, req); + printk(KERN_WARNING "%s: failed to queue req %p\n", + __func__, req); + } + } else { + printk(KERN_INFO "%s: no req to send\n", __func__); + } +} + +struct size rotate(struct size v) +{ + struct size r; + r.w = v.h; + r.h = v.w; + return r; +} + +static struct size get_projection_size(struct projector_dev *dev, struct msm_client_info *client_info) +{ + int server_width = 0; + int server_height = 0; + struct size client; + struct size server; + struct size ret; + int perserve_aspect_ratio = client_info->display_conf & (1 << 0); + int server_orientation = 0; + int client_orientation = (client_info->width > client_info->height); + int align_w = 0; + + server_width = dev->width; + server_height = dev->height; + + server_orientation = (server_width > server_height); + + printk(KERN_INFO "%s(): perserve_aspect_ratio= %d\n", __func__, perserve_aspect_ratio); + + client.w = client_info->width; + client.h = client_info->height; + server.w = server_width; + server.h = server_height; + + if (server_orientation != client_orientation) + client = rotate(client); + + align_w = client.h * server.w > server.h * client.w; + + if (perserve_aspect_ratio) { + if (align_w) { + ret.w = client.w; + ret.h = (client.w * server.h) / server.w; + } else { + ret.w = (client.h * server.w) / server.h; + ret.h = client.h; + } + + ret.w = round_down(ret.w, 32); + } else { + ret = client; + } + + printk(KERN_INFO "projector size(w=%d, h=%d)\n", ret.w, ret.h); + + return ret; +} + + +static void projector_get_msmfb(struct projector_dev *dev) { struct msm_fb_info fb_info; msmfb_get_var(&fb_info); - ctxt->bitsPixel = BITSPIXEL; - ctxt->width = fb_info.xres; - ctxt->height = fb_info.yres; - ctxt->fbaddr = fb_info.fb_addr; - printk(KERN_INFO "projector: width %d, height %d\n", - fb_info.xres, fb_info.yres); + dev->bitsPixel = BITSPIXEL; + dev->width = fb_info.xres; + dev->height = fb_info.yres; +#if defined(CONFIG_ARCH_MSM7X30) || defined(CONFIG_ARCH_MSM8X60) + dev->fbaddr = get_fb_addr(); +#else + dev->fbaddr = fb_info.fb_addr; +#endif + dev->framesize = dev->width * dev->height * (dev->bitsPixel / 8); + printk(KERN_INFO "projector: width %d, height %d framesize %d, %p\n", + fb_info.xres, fb_info.yres, dev->framesize, dev->fbaddr); +} + +/* + * Handle HTC Mode specific messages and return 1 if message has been handled + */ +static int projector_handle_htcmode_msg(struct projector_dev *dev, struct usb_request *req) +{ + unsigned char *data = req->buf; + int handled = 1; + struct size projector_size; + + if ((data[0] == CLIENT_INFO_MESGID) && (req->actual == sizeof(struct msm_client_info))) { + memcpy(&dev->htcmode_proto->client_info, req->buf, sizeof(struct msm_client_info)); + + projector_size = get_projection_size(dev, &dev->htcmode_proto->client_info); + projector_get_msmfb(dev); + + dev->htcmode_proto->server_info.mesg_id = SERVER_INFO_MESGID; + dev->htcmode_proto->server_info.width = projector_size.w; + dev->htcmode_proto->server_info.height = projector_size.h; + dev->htcmode_proto->server_info.pixel_format = PIXEL_FORMAT_RGB565; + dev->htcmode_proto->server_info.ctrl_conf = CTRL_CONF_TOUCH_EVENT_SUPPORTED | + CTRL_CONF_NUM_SIMULTANEOUS_TOUCH; + send_server_info(dev); - ctxt->framesize = (ctxt->width)*(ctxt->height)*2; - printk(KERN_INFO "projector: width %d, height %d %d\n", - fb_info.xres, fb_info.yres, ctxt->framesize); + if (dev->htcmode_proto->version >= 0x0005) + send_server_nonce(dev); + } else if (dev->htcmode_proto->version >= 0x0005 && + data[0] == AUTH_CLIENT_NONCE_MESGID) { + /* TODO: Future extension */ + } else if (!strncmp("startfb", data, 7)) { + dev->start_send_fb = true; + queue_work(dev->wq_display, &dev->send_fb_work); + + dev->frame_count++; + + if (atomic_inc_return(&htc_mode_status) != HTC_MODE_RUNNING) + atomic_dec(&htc_mode_status); + + htc_mode_info("startfb current htc_mode_status = %d\n", + atomic_read(&htc_mode_status)); + schedule_work(&dev->htcmode_notifier_work); + + /* 30s send system wake code */ + if (dev->frame_count == 30 * 30) { + projector_send_Key_event(dev, 0); + dev->frame_count = 0; + } + } else if (!strncmp("endfb", data, 5)) { + dev->start_send_fb = false; + if (atomic_dec_return(&htc_mode_status) != DOCK_ON_AUTOBOT) + atomic_inc(&htc_mode_status); + htc_mode_info("endfb current htc_mode_status = %d\n", + atomic_read(&htc_mode_status)); + schedule_work(&dev->htcmode_notifier_work); + } else if (!strncmp("startcand", data, 9)) { + atomic_set(&dev->cand_online, 1); + htc_mode_info("startcand %d\n", atomic_read(&dev->cand_online)); + + schedule_work(&dev->notifier_work); + } else if (!strncmp("endcand", data, 7)) { + atomic_set(&dev->cand_online, 0); + htc_mode_info("endcand %d\n", atomic_read(&dev->cand_online)); + + schedule_work(&dev->notifier_work); + } else { + handled = 0; + } + + return handled; } static void projector_complete_in(struct usb_ep *ep, struct usb_request *req) { - struct projector_dev *dev = &_projector_dev; + struct projector_dev *dev = projector_dev; proj_req_put(dev, &dev->tx_idle, req); } static void projector_complete_out(struct usb_ep *ep, struct usb_request *req) { - struct projector_dev *ctxt = &_projector_dev; + struct projector_dev *dev = projector_dev; unsigned char *data = req->buf; int mouse_data[3]; int i; - DBG("%s: status %d, %d bytes\n", __func__, + int handled = 0; + VDBG("%s: status %d, %d bytes\n", __func__, req->status, req->actual); if (req->status != 0) { - ctxt->error = 1; - proj_req_put(ctxt, &ctxt->rx_idle, req); + dev->error = 1; + proj_req_put(dev, &dev->rx_idle, req); return ; } - /* for mouse event type, 1 :move, 2:down, 3:up */ - mouse_data[0] = *((int *)(req->buf)); - - if (!strncmp("init", data, 4)) { - if (!ctxt->init_done) { - projector_get_msmfb(ctxt); - ctxt->init_done = 1; - } - send_info(ctxt); - /* system wake code */ - projector_send_Key_event(ctxt, 0); - } else if (*data == ' ') { - send_fb(ctxt); - ctxt->frame_count++; - /* 30s send system wake code */ - if (ctxt->frame_count == 30 * 30) { - projector_send_Key_event(ctxt, 0); - ctxt->frame_count = 0; - } - } else if (mouse_data[0] > 0) { - if (mouse_data[0] < 4) { - for (i = 0; i < 3; i++) - mouse_data[i] = *(((int *)(req->buf))+i); - projector_send_touch_event(ctxt, - mouse_data[0], mouse_data[1], mouse_data[2]); - } else { - projector_send_Key_event(ctxt, mouse_data[0]); - printk(KERN_INFO "projector: Key command data %02x, keycode %d\n", - *((char *)(req->buf)), mouse_data[0]); - } - } else if (mouse_data[0] != 0) - printk(KERN_ERR "projector: Unknow command data %02x, mouse %d,%d,%d\n", - *((char *)(req->buf)), mouse_data[0], mouse_data[1], mouse_data[2]); - - proj_req_put(ctxt, &ctxt->rx_idle, req); - projector_queue_out(ctxt); + if (dev->is_htcmode) + handled = projector_handle_htcmode_msg(dev, req); + + if (!handled) { + /* for mouse event type, 1 :move, 2:down, 3:up */ + mouse_data[0] = *((int *)(req->buf)); + + if (!strncmp("init", data, 4)) { + + dev->init_done = 1; + dev->bitsPixel = BITSPIXEL; + dev->width = DEFAULT_PROJ_WIDTH; + dev->height = DEFAULT_PROJ_HEIGHT; + dev->framesize = dev->width * dev->height * (BITSPIXEL / 8); + + send_info(dev); + /* system wake code */ + projector_send_Key_event(dev, 0); + + atomic_set( &htc_mode_status, HTC_MODE_RUNNING); + htc_mode_info("init current htc_mode_status = %d\n", + atomic_read(&htc_mode_status)); + schedule_work(&dev->htcmode_notifier_work); + } else if (*data == ' ') { + send_fb(dev); + dev->frame_count++; + /* 30s send system wake code */ + if (dev->frame_count == 30 * 30) { + projector_send_Key_event(dev, 0); + dev->frame_count = 0; + } + } else if (mouse_data[0] > 0) { + if (mouse_data[0] < 4) { + for (i = 0; i < 3; i++) + mouse_data[i] = *(((int *)(req->buf))+i); + projector_send_touch_event(dev, + mouse_data[0], mouse_data[1], mouse_data[2]); + } else { + projector_send_Key_event(dev, mouse_data[0]); + printk(KERN_INFO "projector: Key command data %02x, keycode %d\n", + *((char *)(req->buf)), mouse_data[0]); + } + } else if (mouse_data[0] != 0) + printk(KERN_ERR "projector: Unknow command data %02x, mouse %d,%d,%d\n", + *((char *)(req->buf)), mouse_data[0], mouse_data[1], mouse_data[2]); + } + proj_req_put(dev, &dev->rx_idle, req); + projector_queue_out(dev); wake_lock_timeout(&prj_idle_wake_lock, HZ / 2); } @@ -529,7 +846,7 @@ projector_function_bind(struct usb_configuration *c, struct usb_function *f) int ret; dev->cdev = cdev; - DBG("projector_function_bind dev: %p\n", dev); + DBG("%s\n", __func__); /* allocate interface ID(s) */ id = usb_interface_id(c, f); @@ -557,70 +874,53 @@ projector_function_bind(struct usb_configuration *c, struct usb_function *f) return 0; } -static void -projector_function_unbind(struct usb_configuration *c, struct usb_function *f) -{ - struct projector_dev *dev = proj_func_to_dev(f); - struct usb_request *req; - - while ((req = proj_req_get(dev, &dev->tx_idle))) - projector_request_free(req, dev->ep_in); - while ((req = proj_req_get(dev, &dev->rx_idle))) - projector_request_free(req, dev->ep_out); - - dev->online = 0; - dev->error = 1; - if (dev->touch_input) - input_unregister_device(dev->touch_input); - if (dev->keypad_input) - input_unregister_device(dev->keypad_input); -} static int projector_function_set_alt(struct usb_function *f, unsigned intf, unsigned alt) { - struct projector_dev *dev = proj_func_to_dev(f); + struct projector_dev *dev = proj_func_to_dev(f); struct usb_composite_dev *cdev = f->config->cdev; + struct android_dev *adev = _android_dev; + struct android_usb_function *af; int ret; DBG("%s intf: %d alt: %d\n", __func__, intf, alt); - ret = usb_ep_enable(dev->ep_in, - ep_choose(cdev->gadget, + + dev->in = ep_choose(cdev->gadget, &projector_highspeed_in_desc, - &projector_fullspeed_in_desc)); + &projector_fullspeed_in_desc); + + dev->out = ep_choose(cdev->gadget, + &projector_highspeed_out_desc, + &projector_fullspeed_out_desc); + + ret = usb_ep_enable(dev->ep_in, dev->in); if (ret) return ret; - ret = usb_ep_enable(dev->ep_out, - ep_choose(cdev->gadget, - &projector_highspeed_out_desc, - &projector_fullspeed_out_desc)); + + ret = usb_ep_enable(dev->ep_out,dev->out); if (ret) { usb_ep_disable(dev->ep_in); return ret; } - dev->online = 1; + + dev->online = 0; + list_for_each_entry(af, &adev->enabled_functions, enabled_list) { + if (!strcmp(af->name, f->name)) { + dev->online = 1; + break; + } + } projector_queue_out(dev); return 0; } -static void projector_function_disable(struct usb_function *f) -{ - struct projector_dev *dev = proj_func_to_dev(f); - - DBG("projector_function_disable\n"); - dev->online = 0; - dev->error = 1; - usb_ep_disable(dev->ep_in); - usb_ep_disable(dev->ep_out); - - VDBG(dev->cdev, "%s disabled\n", dev->function.name); -} static int projector_touch_init(struct projector_dev *dev) { - int x = dev->width; - int y = dev->height; + int x = TOUCH_WIDTH; + int y = TOUCH_HEIGHT; int ret = 0; struct input_dev *tdev = dev->touch_input; @@ -639,33 +939,6 @@ static int projector_touch_init(struct projector_dev *dev) set_bit(BTN_2, tdev->keybit); set_bit(EV_ABS, tdev->evbit); - if (x == 0) { - printk(KERN_ERR "%s: x=0\n", __func__); - #if defined(CONFIG_ARCH_QSD8X50) - x = 480; - #elif defined(CONFIG_MACH_PARADISE) - if (machine_is_paradise()) - x = 240; - else - x = 320; - #else - x = 320; - #endif - } - - if (y == 0) { - printk(KERN_ERR "%s: y=0\n", __func__); - #if defined(CONFIG_ARCH_QSD8X50) - y = 800; - #elif defined(CONFIG_MACH_PARADISE) - if (machine_is_paradise()) - y = 400; - else - y = 480; - #else - y = 480; - #endif - } /* Set input parameters boundary. */ input_set_abs_params(tdev, ABS_X, 0, x, 0, 0); input_set_abs_params(tdev, ABS_Y, 0, y, 0, 0); @@ -750,13 +1023,128 @@ static ssize_t show_enable(struct device *dev, struct device_attribute *attr, static DEVICE_ATTR(enable, 0664, show_enable, store_enable); #endif -static int projector_bind_config(struct usb_configuration *c) + +static void cand_online_notify(struct work_struct *w) +{ + struct projector_dev *dev = container_of(w, + struct projector_dev, notifier_work); + DBG("%s\n", __func__); + switch_set_state(&dev->cand_sdev, atomic_read(&dev->cand_online)); +} + +static void htcmode_status_notify(struct work_struct *w) +{ + struct projector_dev *dev = container_of(w, + struct projector_dev, htcmode_notifier_work); + DBG("%s\n", __func__); + switch_set_state(&dev->htcmode_sdev, atomic_read(&htc_mode_status)); +} + +/* + * 1: enable; 0: disable + */ +void htc_mode_enable(int enable) +{ + htc_mode_info("%s = %d, current htc_mode_status = %d\n", + __func__, enable, atomic_read(&htc_mode_status)); + + if (enable) + atomic_set(&htc_mode_status, DOCK_ON_AUTOBOT); + else + atomic_set(&htc_mode_status, NOT_ON_AUTOBOT); + + htcmode_status_notify(&projector_dev->htcmode_notifier_work); +} + +int check_htc_mode_status(void) +{ + return atomic_read(&htc_mode_status); +} + +static ssize_t print_cand_switch_name(struct switch_dev *sdev, char *buf) +{ + return sprintf(buf, "%s\n", cand_shortname); +} + +static ssize_t print_cand_switch_state(struct switch_dev *cand_sdev, char *buf) +{ + struct projector_dev *dev = container_of(cand_sdev, + struct projector_dev, cand_sdev); + return sprintf(buf, "%s\n", (atomic_read(&dev->cand_online) ? + "online" : "offline")); +} + +static ssize_t print_htcmode_switch_name(struct switch_dev *sdev, char *buf) +{ + return sprintf(buf, "%s\n", htcmode_shortname); +} + +static ssize_t print_htcmode_switch_state(struct switch_dev *htcmode_sdev, char *buf) +{ + return sprintf(buf, "%s\n", (atomic_read(&htc_mode_status)==HTC_MODE_RUNNING ? + "projecting" : (atomic_read(&htc_mode_status)==DOCK_ON_AUTOBOT ? "online" : "offline"))); +} + + +static void projector_function_disable(struct usb_function *f) +{ + struct projector_dev *dev = proj_func_to_dev(f); + + DBG("%s\n", __func__); + + dev->start_send_fb = false; + dev->online = 0; + dev->error = 1; + usb_ep_disable(dev->ep_in); + usb_ep_disable(dev->ep_out); + + atomic_set(&dev->cand_online, 0); + schedule_work(&dev->notifier_work); + + VDBG(dev->cdev, "%s disabled\n", dev->function.name); +} + + +static void +projector_function_unbind(struct usb_configuration *c, struct usb_function *f) +{ + struct projector_dev *dev = proj_func_to_dev(f); + struct usb_request *req; + + DBG("%s\n", __func__); + + destroy_workqueue(dev->wq_display); + + while ((req = proj_req_get(dev, &dev->tx_idle))) + projector_request_free(req, dev->ep_in); + while ((req = proj_req_get(dev, &dev->rx_idle))) + projector_request_free(req, dev->ep_out); + + dev->online = 0; + dev->error = 1; + dev->is_htcmode = 0; + + if (dev->touch_input) { + input_unregister_device(dev->touch_input); + input_free_device(dev->touch_input); + } + if (dev->keypad_input) { + input_unregister_device(dev->keypad_input); + input_free_device(dev->keypad_input); + } + +} + + +static int projector_bind_config(struct usb_configuration *c, + struct htcmode_protocol *config) { - struct projector_dev *dev = &_projector_dev; + struct projector_dev *dev; struct msm_fb_info fb_info; int ret = 0; - printk(KERN_INFO "projector_bind_config\n"); + DBG("%s\n", __func__); + dev = projector_dev; if (projector_string_defs[0].id == 0) { ret = usb_string_id(c->cdev); @@ -780,40 +1168,147 @@ static int projector_bind_config(struct usb_configuration *c) dev->bitsPixel = BITSPIXEL; dev->width = fb_info.xres; dev->height = fb_info.yres; +#if defined(CONFIG_ARCH_MSM7X30) || defined(CONFIG_ARCH_MSM8X60) + dev->fbaddr = get_fb_addr(); +#else dev->fbaddr = fb_info.fb_addr; - +#endif dev->rx_req_count = PROJ_RX_REQ_MAX; dev->tx_req_count = (dev->width * dev->height * 2 / TXN_MAX) + 1; printk(KERN_INFO "[USB][Projector]resolution: %u*%u" ", rx_cnt: %u, tx_cnt:%u\n", dev->width, dev->height, dev->rx_req_count, dev->tx_req_count); - if (projector_touch_init(dev) < 0) - goto err; + goto err_free; if (projector_keypad_init(dev) < 0) - goto err; + goto err_free; + spin_lock_init(&dev->lock); + INIT_LIST_HEAD(&dev->rx_idle); + INIT_LIST_HEAD(&dev->tx_idle); ret = usb_add_function(c, &dev->function); if (ret) - goto err; + goto err_free; + + dev->wq_display = create_singlethread_workqueue("projector_mode"); + if (!dev->wq_display) + goto err_free_wq; + + workqueue_set_max_active(dev->wq_display,1); + + INIT_WORK(&dev->send_fb_work, send_fb_do_work); + + dev->init_done = 0; + dev->frame_count = 0; + dev->is_htcmode = 0; + dev->htcmode_proto = config; + dev->htcmode_proto->server_info.height = DEFAULT_PROJ_HEIGHT; + dev->htcmode_proto->server_info.width = DEFAULT_PROJ_WIDTH; + dev->htcmode_proto->client_info.display_conf = 0; return 0; -err: - printk(KERN_ERR "projector gadget driver failed to initialize\n"); + +err_free_wq: + destroy_workqueue(dev->wq_display); +err_free: + printk(KERN_ERR "projector gadget driver failed to initialize, err=%d\n", ret); return ret; } static int projector_setup(void) { - struct projector_dev *dev = &_projector_dev; - dev->init_done = 0; - dev->frame_count = 0; - wake_lock_init(&prj_idle_wake_lock, WAKE_LOCK_IDLE, "prj_idle_lock"); + struct projector_dev *dev; + int ret = 0; - spin_lock_init(&dev->lock); - INIT_LIST_HEAD(&dev->rx_idle); - INIT_LIST_HEAD(&dev->tx_idle); + DBG("%s\n", __func__); + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + projector_dev = dev; + + INIT_WORK(&dev->notifier_work, cand_online_notify); + INIT_WORK(&dev->htcmode_notifier_work, htcmode_status_notify); + + dev->cand_sdev.name = cand_shortname; + dev->cand_sdev.print_name = print_cand_switch_name; + dev->cand_sdev.print_state = print_cand_switch_state; + ret = switch_dev_register(&dev->cand_sdev); + if (ret < 0) { + printk(KERN_ERR "usb cand_sdev switch_dev_register register fail\n"); + goto err_free; + } + + dev->htcmode_sdev.name = htcmode_shortname; + dev->htcmode_sdev.print_name = print_htcmode_switch_name; + dev->htcmode_sdev.print_state = print_htcmode_switch_state; + ret = switch_dev_register(&dev->htcmode_sdev); + if (ret < 0) { + printk(KERN_ERR "usb htcmode_sdev switch_dev_register register fail\n"); + goto err_unregister_cand; + } + + wake_lock_init(&prj_idle_wake_lock, WAKE_LOCK_IDLE, "prj_idle_lock"); return 0; + +err_unregister_cand: + switch_dev_unregister(&dev->cand_sdev); +err_free: + kfree(dev); + printk(KERN_ERR "projector gadget driver failed to initialize, err=%d\n", ret); + return ret; + } +static void projector_cleanup(void) +{ + struct projector_dev *dev; + + dev = projector_dev; + + switch_dev_unregister(&dev->cand_sdev); + switch_dev_unregister(&dev->htcmode_sdev); + + kfree(dev); +} + +#ifdef CONFIG_USB_ANDROID_PROJECTOR_HTC_MODE +static int projector_ctrlrequest(struct usb_composite_dev *cdev, + const struct usb_ctrlrequest *ctrl) +{ + int value = -EOPNOTSUPP; + + if (((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_VENDOR) && + (ctrl->bRequest == HTC_MODE_CONTROL_REQ)) { + if (check_htc_mode_status() == NOT_ON_AUTOBOT) + schedule_work(&conf_usb_work); + else { + if (projector_dev) { + projector_dev->htcmode_proto->version = le16_to_cpu(ctrl->wValue); + /* + * 0x0034 is for Autobot. It is not a correct HTC mode version. + */ + if (projector_dev->htcmode_proto->version == 0x0034) + projector_dev->htcmode_proto->version = 0x0003; + projector_dev->is_htcmode = 1; + printk(KERN_INFO "HTC Mode version = 0x%04X\n", projector_dev->htcmode_proto->version); + } else { + printk(KERN_ERR "%s: projector_dev is NULL!!", __func__); + } + } + value = 0; + } + + 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) + printk(KERN_ERR "%s setup response queue error\n", + __func__); + } + + return value; +} +#endif diff --git a/drivers/usb/gadget/f_usbnet.c b/drivers/usb/gadget/f_usbnet.c deleted file mode 100644 index 1945b61c0de..00000000000 --- a/drivers/usb/gadget/f_usbnet.c +++ /dev/null @@ -1,840 +0,0 @@ -/* - * Gadget Driver for Motorola USBNet - * - * Copyright (C) 2009 Motorola, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -/* - * Macro Defines - */ - -#define EP0_BUFSIZE 256 -#define USBNET_FUNCTION_NAME "usbnet" - -/* Vendor Request to config IP */ -#define USBNET_SET_IP_ADDRESS 0x05 -#define USBNET_SET_SUBNET_MASK 0x06 -#define USBNET_SET_HOST_IP 0x07 - -/* Linux Network Interface */ -#define USB_MTU 1536 -#define MAX_BULK_TX_REQ_NUM 8 -#define MAX_BULK_RX_REQ_NUM 8 -#define MAX_INTR_RX_REQ_NUM 8 -#define STRING_INTERFACE 0 - -struct usbnet_context { - spinlock_t lock; /* For RX/TX list */ - struct net_device *dev; - - struct usb_gadget *gadget; - - struct usb_ep *bulk_in; - struct usb_ep *bulk_out; - struct usb_ep *intr_out; - u16 config; /* current USB config w_value */ - - struct list_head rx_reqs; - struct list_head tx_reqs; - - struct net_device_stats stats; - struct work_struct usbnet_config_wq; - u32 ip_addr; - u32 subnet_mask; - u32 router_ip; - u32 iff_flag; -}; - - -struct usbnet_device { - struct usb_function function; - struct usb_composite_dev *cdev; - struct usbnet_context *net_ctxt; -}; - -/* static strings, in UTF-8 */ -static struct usb_string usbnet_string_defs[] = { - [STRING_INTERFACE].s = "HTC Test Command", - { /* ZEROES END LIST */ }, -}; - -static struct usb_gadget_strings usbnet_string_table = { - .language = 0x0409, /* en-us */ - .strings = usbnet_string_defs, -}; - -static struct usb_gadget_strings *usbnet_strings[] = { - &usbnet_string_table, - NULL, -}; - - - -/* There is only one interface. */ - -static struct usb_interface_descriptor usbnet_intf_desc = { - .bLength = sizeof usbnet_intf_desc, - .bDescriptorType = USB_DT_INTERFACE, - - .bNumEndpoints = 3, - .bInterfaceClass = 0x02, - .bInterfaceSubClass = 0x0a, - .bInterfaceProtocol = 0x01, -}; - - -static struct usb_endpoint_descriptor usbnet_fs_bulk_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 usbnet_fs_bulk_out_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = USB_DIR_OUT, - .bmAttributes = USB_ENDPOINT_XFER_BULK, -}; - -static struct usb_endpoint_descriptor fs_intr_out_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = USB_DIR_OUT, - .bmAttributes = USB_ENDPOINT_XFER_INT, - .bInterval = 1, -}; - -static struct usb_descriptor_header *fs_function[] = { - (struct usb_descriptor_header *) &usbnet_intf_desc, - (struct usb_descriptor_header *) &usbnet_fs_bulk_in_desc, - (struct usb_descriptor_header *) &usbnet_fs_bulk_out_desc, - (struct usb_descriptor_header *) &fs_intr_out_desc, - NULL, -}; - -static struct usb_endpoint_descriptor usbnet_hs_bulk_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), - .bInterval = 0, -}; - -static struct usb_endpoint_descriptor usbnet_hs_bulk_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), - .bInterval = 0, -}; - -static struct usb_endpoint_descriptor hs_intr_out_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = USB_DIR_OUT, - .bmAttributes = USB_ENDPOINT_XFER_INT, - .wMaxPacketSize = __constant_cpu_to_le16(64), - .bInterval = 1, -}; - -static struct usb_descriptor_header *hs_function[] = { - (struct usb_descriptor_header *) &usbnet_intf_desc, - (struct usb_descriptor_header *) &usbnet_hs_bulk_in_desc, - (struct usb_descriptor_header *) &usbnet_hs_bulk_out_desc, - (struct usb_descriptor_header *) &hs_intr_out_desc, - NULL, -}; - -#define DO_NOT_STOP_QUEUE 0 -#define STOP_QUEUE 1 - -#define USBNETDBG(context, fmt, args...) \ - do { \ - if (context && (context->gadget)) \ - dev_dbg(&(context->gadget->dev) , fmt , ## args); \ - } while (0) - -struct usbnet_device *_usbnet_dev; - -static inline struct usbnet_device *usbnet_func_to_dev(struct usb_function *f) -{ - return container_of(f, struct usbnet_device, function); -} - - -static int ether_queue_out(struct usb_request *req , - struct usbnet_context *context) -{ - unsigned long flags; - struct sk_buff *skb; - int ret; - - skb = alloc_skb(USB_MTU + NET_IP_ALIGN, GFP_ATOMIC); - if (!skb) { - USBNETDBG(context, "%s: failed to alloc skb\n", __func__); - ret = -ENOMEM; - goto fail; - } - - skb_reserve(skb, NET_IP_ALIGN); - - req->buf = skb->data; - req->length = USB_MTU; - req->context = skb; - - ret = usb_ep_queue(context->bulk_out, req, GFP_KERNEL); - if (ret == 0) - return 0; - else - kfree_skb(skb); -fail: - spin_lock_irqsave(&context->lock, flags); - list_add_tail(&req->list, &context->rx_reqs); - spin_unlock_irqrestore(&context->lock, flags); - - return ret; -} - -struct usb_request *usb_get_recv_request(struct usbnet_context *context) -{ - unsigned long flags; - struct usb_request *req; - - spin_lock_irqsave(&context->lock, flags); - if (list_empty(&context->rx_reqs)) { - req = NULL; - } else { - req = list_first_entry(&context->rx_reqs, - struct usb_request, list); - list_del(&req->list); - } - spin_unlock_irqrestore(&context->lock, flags); - - return req; -} - -struct usb_request *usb_get_xmit_request(int stop_flag, struct net_device *dev) -{ - struct usbnet_context *context = netdev_priv(dev); - unsigned long flags; - struct usb_request *req; - - spin_lock_irqsave(&context->lock, flags); - if (list_empty(&context->tx_reqs)) { - req = NULL; - } else { - req = list_first_entry(&context->tx_reqs, - struct usb_request, list); - list_del(&req->list); - if (stop_flag == STOP_QUEUE && - list_empty(&context->tx_reqs)) - netif_stop_queue(dev); - } - spin_unlock_irqrestore(&context->lock, flags); - return req; -} - -static int usb_ether_xmit(struct sk_buff *skb, struct net_device *dev) -{ - struct usbnet_context *context = netdev_priv(dev); - struct usb_request *req; - unsigned long flags; - unsigned len; - int rc; - - req = usb_get_xmit_request(STOP_QUEUE, dev); - - if (!req) { - USBNETDBG(context, "%s: could not obtain tx request\n", - __func__); - return 1; - } - - /* Add 4 bytes CRC */ - skb->len += 4; - - /* ensure that we end with a short packet */ - len = skb->len; - if (!(len & 63) || !(len & 511)) - len++; - - req->context = skb; - req->buf = skb->data; - req->length = len; - - rc = usb_ep_queue(context->bulk_in, req, GFP_KERNEL); - if (rc != 0) { - spin_lock_irqsave(&context->lock, flags); - list_add_tail(&req->list, &context->tx_reqs); - spin_unlock_irqrestore(&context->lock, flags); - - dev_kfree_skb_any(skb); - context->stats.tx_dropped++; - - USBNETDBG(context, - "%s: could not queue tx request\n", __func__); - } - - return 0; -} - -static void usb_ether_tx_timeout(struct net_device *dev) -{ - struct usbnet_context *context = netdev_priv(dev); - USBNETDBG(context, "%s\n", __func__); -} - -static int usb_ether_open(struct net_device *dev) -{ - struct usbnet_context *context = netdev_priv(dev); - USBNETDBG(context, "%s\n", __func__); - return 0; -} - -static int usb_ether_stop(struct net_device *dev) -{ - struct usbnet_context *context = netdev_priv(dev); - USBNETDBG(context, "%s\n", __func__); - return 0; -} - -static struct net_device_stats *usb_ether_get_stats(struct net_device *dev) -{ - struct usbnet_context *context = netdev_priv(dev); - USBNETDBG(context, "%s\n", __func__); - return &context->stats; -} - -static void usbnet_if_config(struct work_struct *work) -{ - struct ifreq ifr; - mm_segment_t saved_fs; - unsigned err; - struct sockaddr_in *sin; - struct usbnet_context *context = container_of(work, - struct usbnet_context, usbnet_config_wq); - - memset(&ifr, 0, sizeof(ifr)); - sin = (void *) &(ifr.ifr_ifru.ifru_addr); - strncpy(ifr.ifr_ifrn.ifrn_name, context->dev->name, - sizeof(ifr.ifr_ifrn.ifrn_name)); - sin->sin_family = AF_INET; - - sin->sin_addr.s_addr = context->ip_addr; - saved_fs = get_fs(); - set_fs(get_ds()); - err = devinet_ioctl(dev_net(context->dev), SIOCSIFADDR, &ifr); - if (err) - USBNETDBG(context, "%s: Error in SIOCSIFADDR\n", __func__); - - sin->sin_addr.s_addr = context->subnet_mask; - err = devinet_ioctl(dev_net(context->dev), SIOCSIFNETMASK, &ifr); - if (err) - USBNETDBG(context, "%s: Error in SIOCSIFNETMASK\n", __func__); - - sin->sin_addr.s_addr = context->ip_addr | ~(context->subnet_mask); - err = devinet_ioctl(dev_net(context->dev), SIOCSIFBRDADDR, &ifr); - if (err) - USBNETDBG(context, "%s: Error in SIOCSIFBRDADDR\n", __func__); - - memset(&ifr, 0, sizeof(ifr)); - strncpy(ifr.ifr_ifrn.ifrn_name, context->dev->name, - sizeof(ifr.ifr_ifrn.ifrn_name)); - ifr.ifr_flags = ((context->dev->flags) | context->iff_flag); - err = devinet_ioctl(dev_net(context->dev), SIOCSIFFLAGS, &ifr); - if (err) - USBNETDBG(context, "%s: Error in SIOCSIFFLAGS\n", __func__); - - set_fs(saved_fs); -} - -static const struct net_device_ops usb_netdev_ops = { - .ndo_open = usb_ether_open, - .ndo_stop = usb_ether_stop, - .ndo_start_xmit = usb_ether_xmit, - .ndo_validate_addr = eth_validate_addr, - .ndo_tx_timeout = usb_ether_tx_timeout, - .ndo_get_stats = usb_ether_get_stats, -}; - -static void usb_ether_setup(struct net_device *dev) -{ - struct usbnet_context *context = netdev_priv(dev); - INIT_LIST_HEAD(&context->rx_reqs); - INIT_LIST_HEAD(&context->tx_reqs); - - spin_lock_init(&context->lock); - context->dev = dev; - dev->netdev_ops = &usb_netdev_ops; - ether_setup(dev); - - random_ether_addr(dev->dev_addr); -} - -/*-------------------------------------------------------------------------*/ -static void usbnet_cleanup(struct usbnet_device *dev) -{ - struct usbnet_context *context = dev->net_ctxt; - if (context) { - unregister_netdev(context->dev); - free_netdev(context->dev); - dev->net_ctxt = NULL; - } -} - -static void usbnet_unbind(struct usb_configuration *c, struct usb_function *f) -{ - struct usbnet_device *dev = usbnet_func_to_dev(f); - struct usb_composite_dev *cdev = c->cdev; - struct usbnet_context *context = dev->net_ctxt; - struct usb_request *req; - - dev->cdev = cdev; - - usb_ep_disable(context->bulk_in); - usb_ep_disable(context->bulk_out); - - /* Free BULK OUT Requests */ - while ((req = usb_get_recv_request(context))) - usb_ep_free_request(context->bulk_out, req); - - /* Free BULK IN Requests */ - while ((req = usb_get_xmit_request(DO_NOT_STOP_QUEUE, - context->dev))) { - usb_ep_free_request(context->bulk_in, req); - } - - context->config = 0; - - usbnet_cleanup(dev); -} - -static void ether_out_complete(struct usb_ep *ep, struct usb_request *req) -{ - struct sk_buff *skb = req->context; - struct usbnet_context *context = ep->driver_data; - - if (req->status == 0) { - dmac_inv_range((void *)req->buf, (void *)(req->buf + - req->actual)); - skb_put(skb, req->actual); - skb->protocol = eth_type_trans(skb, context->dev); - context->stats.rx_packets++; - context->stats.rx_bytes += req->actual; - netif_rx(skb); - } else { - dev_kfree_skb_any(skb); - context->stats.rx_errors++; - } - - /* don't bother requeuing if we just went offline */ - if ((req->status == -ENODEV) || (req->status == -ESHUTDOWN)) { - unsigned long flags; - spin_lock_irqsave(&context->lock, flags); - list_add_tail(&req->list, &context->rx_reqs); - spin_unlock_irqrestore(&context->lock, flags); - } else { - if (ether_queue_out(req, context)) - USBNETDBG(context, "ether_out: cannot requeue\n"); - } -} - -static void ether_in_complete(struct usb_ep *ep, struct usb_request *req) -{ - unsigned long flags; - struct sk_buff *skb = req->context; - struct usbnet_context *context = ep->driver_data; - - if (req->status == 0) { - context->stats.tx_packets++; - context->stats.tx_bytes += req->actual; - } else { - context->stats.tx_errors++; - } - - dev_kfree_skb_any(skb); - - spin_lock_irqsave(&context->lock, flags); - if (list_empty(&context->tx_reqs)) - netif_start_queue(context->dev); - - list_add_tail(&req->list, &context->tx_reqs); - spin_unlock_irqrestore(&context->lock, flags); -} - -static int usbnet_bind(struct usb_configuration *c, - struct usb_function *f) -{ - struct usb_composite_dev *cdev = c->cdev; - struct usbnet_device *dev = usbnet_func_to_dev(f); - struct usbnet_context *context = dev->net_ctxt; - int n, rc, id; - struct usb_ep *ep; - struct usb_request *req; - unsigned long flags; - - dev->cdev = cdev; - - id = usb_interface_id(c, f); - if (id < 0) - return id; - usbnet_intf_desc.bInterfaceNumber = id; - context->gadget = cdev->gadget; - - /* Find all the endpoints we will use */ - ep = usb_ep_autoconfig(cdev->gadget, &usbnet_fs_bulk_in_desc); - if (!ep) { - USBNETDBG(context, "%s auto-configure usbnet_hs_bulk_in_desc error\n", - __func__); - goto autoconf_fail; - } - ep->driver_data = context; - context->bulk_in = ep; - - ep = usb_ep_autoconfig(cdev->gadget, &usbnet_fs_bulk_out_desc); - if (!ep) { - USBNETDBG(context, "%s auto-configure usbnet_hs_bulk_out_desc error\n", - __func__); - goto autoconf_fail; - } - ep->driver_data = context; - context->bulk_out = ep; - - - ep = usb_ep_autoconfig(cdev->gadget, &fs_intr_out_desc); - if (!ep) { - USBNETDBG(context, "%s auto-configure hs_intr_out_desc error\n", - __func__); - goto autoconf_fail; - } - ep->driver_data = context; - context->intr_out = ep; - - if (gadget_is_dualspeed(cdev->gadget)) { - - /* Assume endpoint addresses are the same for both speeds */ - usbnet_hs_bulk_in_desc.bEndpointAddress = - usbnet_fs_bulk_in_desc.bEndpointAddress; - usbnet_hs_bulk_out_desc.bEndpointAddress = - usbnet_fs_bulk_out_desc.bEndpointAddress; - hs_intr_out_desc.bEndpointAddress = - fs_intr_out_desc.bEndpointAddress; - } - - - rc = -ENOMEM; - - for (n = 0; n < MAX_BULK_RX_REQ_NUM; n++) { - req = usb_ep_alloc_request(context->bulk_out, - GFP_KERNEL); - if (!req) { - USBNETDBG(context, "%s: alloc request bulk_out fail\n", - __func__); - break; - } - req->complete = ether_out_complete; - spin_lock_irqsave(&context->lock, flags); - list_add_tail(&req->list, &context->rx_reqs); - spin_unlock_irqrestore(&context->lock, flags); - } - for (n = 0; n < MAX_BULK_TX_REQ_NUM; n++) { - req = usb_ep_alloc_request(context->bulk_in, - GFP_KERNEL); - if (!req) { - USBNETDBG(context, "%s: alloc request bulk_in fail\n", - __func__); - break; - } - req->complete = ether_in_complete; - spin_lock_irqsave(&context->lock, flags); - list_add_tail(&req->list, &context->tx_reqs); - spin_unlock_irqrestore(&context->lock, flags); - } - - return 0; - -autoconf_fail: - rc = -ENOTSUPP; - usbnet_unbind(c, f); - return rc; -} - - - - -static void do_set_config(struct usb_function *f, u16 new_config) -{ - struct usbnet_device *dev = usbnet_func_to_dev(f); - struct usbnet_context *context = dev->net_ctxt; - int result = 0; - struct usb_request *req; - int high_speed_flag = 0; - - if (context->config == new_config) /* Config did not change */ - return; - - context->config = new_config; - - if (new_config == 1) { /* Enable End points */ - if (gadget_is_dualspeed(context->gadget) - && context->gadget->speed == USB_SPEED_HIGH) - high_speed_flag = 1; - - if (high_speed_flag) - result = usb_ep_enable(context->bulk_in, - &usbnet_hs_bulk_in_desc); - else - result = usb_ep_enable(context->bulk_in, - &usbnet_fs_bulk_in_desc); - - if (result != 0) { - USBNETDBG(context, - "%s: failed to enable BULK_IN EP ret=%d\n", - __func__, result); - } - - context->bulk_in->driver_data = context; - - if (high_speed_flag) - result = usb_ep_enable(context->bulk_out, - &usbnet_hs_bulk_out_desc); - else - result = usb_ep_enable(context->bulk_out, - &usbnet_fs_bulk_out_desc); - - if (result != 0) { - USBNETDBG(context, - "%s: failed to enable BULK_OUT EP ret = %d\n", - __func__, result); - } - - context->bulk_out->driver_data = context; - - if (high_speed_flag) - result = usb_ep_enable(context->intr_out, - &hs_intr_out_desc); - else - result = usb_ep_enable(context->intr_out, - &fs_intr_out_desc); - - if (result != 0) { - USBNETDBG(context, - "%s: failed to enable INTR_OUT EP ret = %d\n", - __func__, result); - } - - context->intr_out->driver_data = context; - - /* we're online -- get all rx requests queued */ - while ((req = usb_get_recv_request(context))) { - if (ether_queue_out(req, context)) { - USBNETDBG(context, - "%s: ether_queue_out failed\n", - __func__); - break; - } - } - - } else {/* Disable Endpoints */ - if (context->bulk_in) - usb_ep_disable(context->bulk_in); - if (context->bulk_out) - usb_ep_disable(context->bulk_out); - } -} - - -static int usbnet_set_alt(struct usb_function *f, - unsigned intf, unsigned alt) -{ - struct usbnet_device *dev = usbnet_func_to_dev(f); - struct usbnet_context *context = dev->net_ctxt; - USBNETDBG(context, "usbnet_set_alt intf: %d alt: %d\n", intf, alt); - do_set_config(f, 1); - return 0; -} - -static int usbnet_ctrlrequest(struct usb_composite_dev *cdev, - const struct usb_ctrlrequest *ctrl) -{ - struct usbnet_device *dev = _usbnet_dev; - struct usbnet_context *context = dev->net_ctxt; - int rc = -EOPNOTSUPP; - int wIndex = le16_to_cpu(ctrl->wIndex); - int wValue = le16_to_cpu(ctrl->wValue); - int wLength = le16_to_cpu(ctrl->wLength); - struct usb_request *req = cdev->req; - - USBNETDBG(context, "usbnet_ctrlrequest " - "%02x.%02x v%04x i%04x l%u\n", - ctrl->bRequestType, ctrl->bRequest, - wValue, wIndex, wLength); - - if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_VENDOR) { - switch (ctrl->bRequest) { - case USBNET_SET_IP_ADDRESS: - context->ip_addr = (wValue << 16) | wIndex; - rc = 0; - break; - case USBNET_SET_SUBNET_MASK: - context->subnet_mask = (wValue << 16) | wIndex; - rc = 0; - break; - case USBNET_SET_HOST_IP: - context->router_ip = (wValue << 16) | wIndex; - rc = 0; - break; - default: - break; - } - - if (context->ip_addr && context->subnet_mask - && context->router_ip) { - context->iff_flag = IFF_UP; - /* schedule a work queue to do this because we - need to be able to sleep */ - schedule_work(&context->usbnet_config_wq); - } - } - - /* respond with data transfer or status phase? */ - if (rc >= 0) { - req->zero = rc < wLength; - req->length = rc; - rc = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC); - if (rc < 0) - USBNETDBG(context, "usbnet setup response error\n"); - } - return rc; -} - -static void usbnet_disable(struct usb_function *f) -{ - struct usbnet_device *dev = usbnet_func_to_dev(f); - struct usbnet_context *context = dev->net_ctxt; - USBNETDBG(context, "%s\n", __func__); - do_set_config(f, 0); -} - -static void usbnet_suspend(struct usb_function *f) -{ - struct usbnet_device *dev = usbnet_func_to_dev(f); - struct usbnet_context *context = dev->net_ctxt; - USBNETDBG(context, "%s\n", __func__); -} - -static void usbnet_resume(struct usb_function *f) -{ - struct usbnet_device *dev = usbnet_func_to_dev(f); - struct usbnet_context *context = dev->net_ctxt; - USBNETDBG(context, "%s\n", __func__); -} - -static int usbnet_bind_config(struct usb_configuration *c) -{ - struct usbnet_device *dev = _usbnet_dev; - struct usbnet_context *context; - struct net_device *net_dev; - int ret, status; - - net_dev = alloc_netdev(sizeof(struct usbnet_context), - "usb%d", usb_ether_setup); - if (!net_dev) { - pr_err("%s: alloc_netdev error\n", __func__); - return -EINVAL; - } - net_dev->netdev_ops = &usb_netdev_ops; - - ret = register_netdev(net_dev); - if (ret) { - pr_err("%s: register_netdev error\n", __func__); - free_netdev(net_dev); - return -EINVAL; - } - context = netdev_priv(net_dev); - INIT_WORK(&context->usbnet_config_wq, usbnet_if_config); - - status = usb_string_id(c->cdev); - if (status >= 0) { - usbnet_string_defs[STRING_INTERFACE].id = status; - usbnet_intf_desc.iInterface = status; - } - - context->config = 0; - dev->net_ctxt = context; - dev->cdev = c->cdev; - dev->function.name = USBNET_FUNCTION_NAME; - dev->function.descriptors = fs_function; - dev->function.hs_descriptors = hs_function; - dev->function.bind = usbnet_bind; - dev->function.unbind = usbnet_unbind; - dev->function.set_alt = usbnet_set_alt; - dev->function.disable = usbnet_disable; - dev->function.suspend = usbnet_suspend; - dev->function.resume = usbnet_resume; - dev->function.strings = usbnet_strings; - - ret = usb_add_function(c, &dev->function); - if (ret) - goto err1; - - pr_info("%s\n", __func__); - - return 0; - -err1: - kfree(dev); - pr_err("usbnet gadget driver failed to initialize\n"); - usbnet_cleanup(dev); - return ret; -} - -static int usbnet_setup(void) -{ - struct usbnet_device *dev = _usbnet_dev; - - dev = kzalloc(sizeof(*dev), GFP_KERNEL); - if (!dev) - return -ENOMEM; - - _usbnet_dev = dev; - - return 0; -} diff --git a/drivers/usb/gadget/htc_attr.c b/drivers/usb/gadget/htc_attr.c index 4247ab53e31..072564f96e2 100644 --- a/drivers/usb/gadget/htc_attr.c +++ b/drivers/usb/gadget/htc_attr.c @@ -12,647 +12,27 @@ * */ -#include -#include - -enum { - USB_FUNCTION_UMS = 0, - USB_FUNCTION_ADB = 1, - USB_FUNCTION_RNDIS, - USB_FUNCTION_DIAG, - USB_FUNCTION_SERIAL, - USB_FUNCTION_PROJECTOR, - USB_FUNCTION_FSYNC, - USB_FUNCTION_MTP, - USB_FUNCTION_MODEM, /* 8 */ - USB_FUNCTION_ECM, - USB_FUNCTION_ACM, - USB_FUNCTION_DIAG_MDM, /* 11 */ - USB_FUNCTION_RMNET, - USB_FUNCTION_ACCESSORY, - USB_FUNCTION_MODEM_MDM, /* 14 */ - USB_FUNCTION_MTP36, - USB_FUNCTION_USBNET, - USB_FUNCTION_RNDIS_IPT = 31, -}; - -struct usb_string_node{ - u32 usb_function_flag; - char *name; -}; - -static struct usb_string_node usb_string_array[] = { - { - .usb_function_flag = 1 << USB_FUNCTION_UMS, - .name = "mass_storage", - }, - { - .usb_function_flag = 1 << USB_FUNCTION_ADB, - .name = "adb", - }, - { - .usb_function_flag = 1 << USB_FUNCTION_RNDIS, - .name = "rndis", - }, - { - .usb_function_flag = 1 << USB_FUNCTION_DIAG, - .name = "diag", - }, - { - .usb_function_flag = 1 << USB_FUNCTION_SERIAL, - .name = "serial", - }, - { - .usb_function_flag = 1 << USB_FUNCTION_PROJECTOR, - .name = "projector", - }, - { - .usb_function_flag = 1 << USB_FUNCTION_MODEM, - .name = "modem", - }, - { - .usb_function_flag = 1 << USB_FUNCTION_ECM, - .name = "cdc_ethernet", - }, - { - .usb_function_flag = 1 << USB_FUNCTION_ACM, - .name = "acm", - }, - { - .usb_function_flag = 1 << USB_FUNCTION_DIAG_MDM, - .name = "diag_mdm", - }, - { - .usb_function_flag = 1 << USB_FUNCTION_RMNET, - .name = "rmnet", - }, - { - .usb_function_flag = 1 << USB_FUNCTION_ACCESSORY, - .name = "accessory", - }, - { - .usb_function_flag = 1 << USB_FUNCTION_MODEM_MDM, - .name = "modem_mdm", - }, - { - .usb_function_flag = 1 << USB_FUNCTION_MTP, - .name = "mtp", - }, - -}; - -static int use_mfg_serialno; -static char mfg_df_serialno[16]; -static int intrsharing; - -#define PID_RNDIS 0x0ffe -#define PID_ECM 0x0ff8 -#define PID_ACM 0x0ff4 -#define PID_USBNET 0x0fcd - -/* for htc in-house device attribute, htc_usb_attr.c */ -void android_force_reset(void) -{ - if (_android_dev && _android_dev->cdev) - usb_composite_force_reset(_android_dev->cdev); -} - -static bool isFunctionDisabled(struct android_usb_function *function) -{ - struct android_usb_function *f; - struct list_head *list = &_android_dev->enabled_functions; - - list_for_each_entry(f, list, enabled_list) { - if (!strcmp(function->name, f->name)) - return false; - } - return true; -} - -static int product_has_function(struct android_usb_product *p, - struct android_usb_function *f) -{ - char **functions = p->functions; - int count = p->num_functions; - const char *name = f->name; - int i; - - for (i = 0; i < count; i++) { - if (!strncmp(name, functions[i], strlen(name))) - return 1; - } - return 0; -} - -static int product_matches_functions(struct android_usb_product *p, - struct list_head *list) -{ - int count = 0; - struct android_usb_function *f; - list_for_each_entry(f, list, enabled_list) { - count++; - if (product_has_function(p, f) == isFunctionDisabled(f)) - return 0; - } - - if (count == p->num_functions) - return 1; - else - return 0; -} - -static int get_product_id(struct android_dev *dev, struct list_head *list) -{ - struct android_usb_product *p = dev->products; - int count = dev->num_products; - int i; - - if (p) { - for (i = 0; i < count; i++, p++) { - if (product_matches_functions(p, list)) - return p->product_id; - } - } - /* use default product ID */ - return dev->pdata->product_id; -} - -static struct android_usb_product *get_product(struct android_dev *dev, struct list_head *list) -{ - struct android_usb_product *p = dev->products; - int count = dev->num_products; - int i; - - if (p) { - for (i = 0; i < count; i++, p++) { - if (product_matches_functions(p, list)) - return p; - } - } - return NULL; -} - - -static unsigned int htc_usb_get_func_combine_value(void) -{ - struct android_dev *dev = _android_dev; - struct android_usb_function *f; - int i; - unsigned int val = 0; - - list_for_each_entry(f, &dev->enabled_functions, enabled_list) { - for (i = 0; i < ARRAY_SIZE(usb_string_array); i++) - if (!strcmp(usb_string_array[i].name, f->name)) { - val |= usb_string_array[i].usb_function_flag; - break; - } - } - return val; -} -static DEFINE_MUTEX(function_bind_sem); -int htc_usb_enable_function(char *name, int ebl) -{ - int i; - unsigned val; - - mutex_lock(&function_bind_sem); - - val = htc_usb_get_func_combine_value(); - - for (i = 0; i < ARRAY_SIZE(usb_string_array); i++) { - if (!strcmp(usb_string_array[i].name, name)) { - if (ebl) { - if (val & usb_string_array[i].usb_function_flag) { - pr_info("%s: '%s' is already enabled\n", __func__, name); - mutex_unlock(&function_bind_sem); - return 0; - } - val |= usb_string_array[i].usb_function_flag; - } else { - if (!(val & usb_string_array[i].usb_function_flag)) { - pr_info("%s: '%s' is already disabled\n", __func__, name); - mutex_unlock(&function_bind_sem); - return 0; - } - - val &= ~usb_string_array[i].usb_function_flag; - } - break; - } - } - mutex_unlock(&function_bind_sem); - return android_switch_function(val); -} - - -int android_show_function(char *buf) -{ - unsigned length = 0; - struct android_dev *dev = _android_dev; - struct android_usb_function *f; - char *ebl_str[2] = {"disable", "enable"}; - char *p; - int i; - - for (i = 0; dev->functions[i] != NULL; i++) { - - p = ebl_str[0]; - list_for_each_entry(f, &dev->enabled_functions, enabled_list) { - if (!strcmp(dev->functions[i]->name, f->name)) { - p = ebl_str[1]; - break; - } - } - - length += sprintf(buf + length, "%s:%s\n", - dev->functions[i]->name, p); - - } - return length; -} - - -int android_switch_function(unsigned func) -{ - struct android_dev *dev = _android_dev; - struct android_usb_function **functions = dev->functions; - struct android_usb_function *f; - struct android_usb_product *product; - int product_id, vendor_id; - unsigned val; - - /* framework may try to enable adb before android_usb_init_work is done.*/ - if (dev->enabled != true) { - pr_info("%s: USB driver is not initialize\n", __func__); - return 0; - } - - mutex_lock(&function_bind_sem); - - val = htc_usb_get_func_combine_value(); - - pr_info("%s: %u, before %u\n", __func__, func, val); - - if (func == val) { - pr_info("%s: SKIP due the function is the same ,%u\n" - , __func__, func); - mutex_unlock(&function_bind_sem); - return 0; - } - - usb_gadget_disconnect(dev->cdev->gadget); - usb_remove_config(dev->cdev, &android_config_driver); - - INIT_LIST_HEAD(&dev->enabled_functions); - - while ((f = *functions++)) { - if ((func & (1 << USB_FUNCTION_UMS)) && - !strcmp(f->name, "mass_storage")) - list_add_tail(&f->enabled_list, &dev->enabled_functions); - else if ((func & (1 << USB_FUNCTION_ADB)) && - !strcmp(f->name, "adb")) - list_add_tail(&f->enabled_list, &dev->enabled_functions); - else if ((func & (1 << USB_FUNCTION_ECM)) && - !strcmp(f->name, "cdc_ethernet")) - list_add_tail(&f->enabled_list, &dev->enabled_functions); - else if ((func & (1 << USB_FUNCTION_ACM)) && - !strcmp(f->name, "acm")) - list_add_tail(&f->enabled_list, &dev->enabled_functions); - else if ((func & (1 << USB_FUNCTION_RNDIS)) && - !strcmp(f->name, "rndis")) { - list_add_tail(&f->enabled_list, &dev->enabled_functions); - intrsharing = !((func >> USB_FUNCTION_RNDIS_IPT) & 1); - } else if ((func & (1 << USB_FUNCTION_DIAG)) && - !strcmp(f->name, "diag")) { - list_add_tail(&f->enabled_list, &dev->enabled_functions); -#ifdef CONFIG_USB_ANDROID_MDM9K_DIAG - func |= 1 << USB_FUNCTION_DIAG_MDM; -#endif - } else if ((func & (1 << USB_FUNCTION_MODEM)) && - !strcmp(f->name, "modem")) { - list_add_tail(&f->enabled_list, &dev->enabled_functions); -#ifdef CONFIG_USB_ANDROID_MDM9K_MODEM - func |= 1 << USB_FUNCTION_MODEM_MDM; -#endif - } else if ((func & (1 << USB_FUNCTION_SERIAL)) && - !strcmp(f->name, "serial")) - list_add_tail(&f->enabled_list, &dev->enabled_functions); - else if ((func & (1 << USB_FUNCTION_MTP)) && - !strcmp(f->name, "mtp")) - list_add_tail(&f->enabled_list, &dev->enabled_functions); - else if ((func & (1 << USB_FUNCTION_ACCESSORY)) && - !strcmp(f->name, "accessory")) - list_add_tail(&f->enabled_list, &dev->enabled_functions); - else if ((func & (1 << USB_FUNCTION_PROJECTOR)) && - !strcmp(f->name, "projector")) - list_add_tail(&f->enabled_list, &dev->enabled_functions); -#ifdef CONFIG_USB_ANDROID_MDM9K_DIAG - else if ((func & (1 << USB_FUNCTION_DIAG_MDM)) && - !strcmp(f->name, "diag_mdm")) { - if (func & (1 << USB_FUNCTION_DIAG)) - list_add_tail(&f->enabled_list, &dev->enabled_functions); - else - func &= ~(1 << USB_FUNCTION_DIAG_MDM); - } -#endif - else if ((func & (1 << USB_FUNCTION_RMNET)) && - !strcmp(f->name, "rmnet")) - list_add_tail(&f->enabled_list, &dev->enabled_functions); -#ifdef CONFIG_USB_ANDROID_MDM9K_MODEM - else if ((func & (1 << USB_FUNCTION_MODEM_MDM)) && - !strcmp(f->name, "modem_mdm")) { - if (func & (1 << USB_FUNCTION_MODEM)) - list_add_tail(&f->enabled_list, &dev->enabled_functions); - else - func &= ~(1 << USB_FUNCTION_MODEM_MDM); - } -#endif -#ifdef CONFIG_USB_ANDROID_USBNET - else if ((func & (1 << USB_FUNCTION_USBNET)) && - !strcmp(f->name, "usbnet")) - list_add_tail(&f->enabled_list, &dev->enabled_functions); -#endif - } - - list_for_each_entry(f, &dev->enabled_functions, enabled_list) - pr_debug("# %s\n", f->name); - - product = get_product(dev, &dev->enabled_functions); - - if (product) { - vendor_id = product->vendor_id ? product->vendor_id : dev->pdata->vendor_id; - product_id = product->product_id; - } else { - vendor_id = dev->pdata->vendor_id; - product_id = dev->pdata->product_id; - } - - /* We need to specify the COMM class in the device descriptor - * if we are using RNDIS. - */ - if (product_id == PID_RNDIS || product_id == PID_ECM - || product_id == PID_ACM || product_id == PID_USBNET) - dev->cdev->desc.bDeviceClass = USB_CLASS_COMM; - else - dev->cdev->desc.bDeviceClass = USB_CLASS_PER_INTERFACE; - - if (dev->match) - product_id = dev->match(product_id, intrsharing); - - pr_info("%s: vendor_id=0x%x, product_id=0x%x\n", - __func__, vendor_id, product_id); - - device_desc.idVendor = __constant_cpu_to_le16(vendor_id); - device_desc.idProduct = __constant_cpu_to_le16(product_id); - - dev->cdev->desc.idVendor = device_desc.idVendor; - dev->cdev->desc.idProduct = device_desc.idProduct; - - device_desc.bDeviceClass = dev->cdev->desc.bDeviceClass; - - usb_add_config(dev->cdev, &android_config_driver, android_bind_config); - - mdelay(100); - usb_gadget_connect(dev->cdev->gadget); - dev->enabled = true; - - mutex_unlock(&function_bind_sem); - return 0; -} - -void android_set_serialno(char *serialno) -{ - strings_dev[STRING_SERIAL_IDX].s = serialno; -} - -void init_mfg_serialno(void) -{ - char *serialno = "000000000000"; - - use_mfg_serialno = (board_mfg_mode() == 1) ? 1 : 0; - strncpy(mfg_df_serialno, serialno, strlen(serialno)); -} - -static ssize_t show_usb_cable_connect(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned length; - - length = sprintf(buf, "%d", - (usb_get_connect_type() == CONNECT_TYPE_USB)?1:0); - return length; -} static ssize_t show_usb_function_switch(struct device *dev, struct device_attribute *attr, char *buf) { - return android_show_function(buf); + return 0; } 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; - - ret = strict_strtoul(buf, 10, (unsigned long *)&u); - if (ret < 0) { - USB_ERR("%s: %d\n", __func__, ret); - return 0; - } - - ret = android_switch_function(u); - - if (ret == 0) - return count; - else - return 0; -} - -static ssize_t show_USB_ID_status(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct android_usb_platform_data *pdata = dev->platform_data; - int value = 1; - unsigned length; - printk(KERN_INFO "[USB] id pin: %d\n", pdata->usb_id_pin_gpio); - - if (pdata->usb_id_pin_gpio != 0) { - value = gpio_get_value(pdata->usb_id_pin_gpio); - printk(KERN_INFO"[USB] id pin status %d\n", value); - } - - length = sprintf(buf, "%d", value); - return length; -} - -static ssize_t show_usb_serial_number(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned length; - struct android_usb_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 android_usb_platform_data *pdata = dev->platform_data; - char *serialno = "000000000000"; - - if (buf[0] == '0' || buf[0] == '1') { - memset(mfg_df_serialno, 0x0, sizeof(mfg_df_serialno)); - if (buf[0] == '0') { - strncpy(mfg_df_serialno, serialno, strlen(serialno)); - 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 */ - android_force_reset(); - } - - return count; -} - -static ssize_t show_dummy_usb_serial_number(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned length; - struct android_usb_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_INFO "%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 */ - android_force_reset(); - - return count; -} - -static ssize_t -show_usb_car_kit_enable(struct device *dev, struct device_attribute *attr, - char *buf) -{ - unsigned length; - int value = 0; -#ifdef CONFIG_CABLE_DETECT_ACCESSORY -#include - value = (cable_get_accessory_type() == DOCK_STATE_UNDOCKED) ? 0 : 1; - printk(KERN_INFO "USB_car_kit_enable %d\n", cable_get_accessory_type()); -#else - value = 0; - printk(KERN_INFO "USB_car_kit_enable: CABLE_DETECT_ACCESSORY was not defined\n"); -#endif - - length = sprintf(buf, "%d", value); - return length; -} - -static ssize_t show_usb_phy_setting(struct device *dev, - struct device_attribute *attr, char *buf) -{ - return otg_show_usb_phy_setting(buf); -} -static ssize_t store_usb_phy_setting(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - return otg_store_usb_phy_setting(buf, count); -} - -#if (defined(CONFIG_USB_OTG) && defined(CONFIG_USB_OTG_HOST)) -void msm_otg_set_id_state(int id); -static ssize_t store_usb_host_mode(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - unsigned u, enable; - ssize_t ret; - - ret = strict_strtoul(buf, 10, (unsigned long *)&u); - if (ret < 0) { - USB_ERR("%s: %d\n", __func__, ret); - return 0; - } - - enable = u ? 1 : 0; - msm_otg_set_id_state(!enable); - - USB_INFO("%s USB host\n", enable ? "Enable" : "Disable"); - - return count; + return 0; } -static DEVICE_ATTR(host_mode, 0220, - NULL, store_usb_host_mode); -#endif -static DEVICE_ATTR(usb_cable_connect, 0444, show_usb_cable_connect, NULL); static DEVICE_ATTR(usb_function_switch, 0664, show_usb_function_switch, store_usb_function_switch); -static DEVICE_ATTR(USB_ID_status, 0444, show_USB_ID_status, NULL); -static DEVICE_ATTR(usb_serial_number, 0644, - show_usb_serial_number, store_usb_serial_number); -static DEVICE_ATTR(dummy_usb_serial_number, 0644, - show_dummy_usb_serial_number, store_dummy_usb_serial_number); -static DEVICE_ATTR(usb_car_kit_enable, 0444, show_usb_car_kit_enable, NULL); -static DEVICE_ATTR(usb_phy_setting, 0664, - show_usb_phy_setting, store_usb_phy_setting); static struct attribute *android_htc_usb_attributes[] = { - &dev_attr_usb_cable_connect.attr, &dev_attr_usb_function_switch.attr, - &dev_attr_USB_ID_status.attr, /* for MFG */ - &dev_attr_usb_serial_number.attr, /* for MFG */ - &dev_attr_dummy_usb_serial_number.attr, /* for MFG */ - &dev_attr_usb_car_kit_enable.attr, - &dev_attr_usb_phy_setting.attr, -#if (defined(CONFIG_USB_OTG) && defined(CONFIG_USB_OTG_HOST)) - &dev_attr_host_mode.attr, -#endif - NULL + NULL, }; -static const struct attribute_group android_usb_attr_group = { +static const struct attribute_group htc_attr_group = { .attrs = android_htc_usb_attributes, }; - diff --git a/drivers/usb/gadget/u_xpst.c b/drivers/usb/gadget/u_xpst.c index 7df4ab6eeb9..66027741f1d 100644 --- a/drivers/usb/gadget/u_xpst.c +++ b/drivers/usb/gadget/u_xpst.c @@ -11,6 +11,9 @@ * GNU General Public License for more details. * */ +#if defined(CONFIG_MACH_MECHA) +#include +#endif struct diag_context _context; static struct usb_diag_ch *legacych; @@ -367,7 +370,7 @@ static long htc_diag_ioctl(struct file *file, unsigned int cmd, unsigned long ar diag_smd_enable(driver->ch, "diag_ioctl", tmp_value); #if defined(CONFIG_MACH_MECHA) /* internal hub*/ - /*smsc251x_mdm_port_sw(tmp_value);*/ + smsc251x_mdm_port_sw(tmp_value); #endif /* force diag_read to return error when disable diag */ if (tmp_value == 0) diff --git a/include/linux/usb/android.h b/include/linux/usb/android.h index 9d7e4a84802..6736a085f32 100644 --- a/include/linux/usb/android.h +++ b/include/linux/usb/android.h @@ -19,6 +19,7 @@ struct android_usb_platform_data { int (*update_pid_and_serial_num)(uint32_t, const char *); + int nluns; }; #endif /* __LINUX_USB_ANDROID_H */ From 8957fb757d2f6aeb6bf6c01d22b2bc718321fea7 Mon Sep 17 00:00:00 2001 From: Ethan Chen Date: Sat, 1 Dec 2012 21:42:48 -0800 Subject: [PATCH 017/111] msm_fb: display: fix default MDP colorspace --- drivers/video/msm/msm_fb_def.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/video/msm/msm_fb_def.h b/drivers/video/msm/msm_fb_def.h index 733e80c204b..1eb326bc02c 100644 --- a/drivers/video/msm/msm_fb_def.h +++ b/drivers/video/msm/msm_fb_def.h @@ -102,11 +102,11 @@ typedef unsigned int boolean; #define MSM_FB_ENABLE_DBGFS #define FEATURE_MDDI -#if defined(CONFIG_FB_MSM_8X60_DEFAULT_DEPTH_RGB565) +#if defined(CONFIG_FB_MSM_DEFAULT_DEPTH_RGB565) #define MSMFB_DEFAULT_TYPE MDP_RGB_565 -#elif defined(CONFIG_FB_MSM_8X60_DEFAULT_DEPTH_ARGB8888) +#elif defined(CONFIG_FB_MSM_DEFAULT_DEPTH_ARGB8888) #define MSMFB_DEFAULT_TYPE MDP_ARGB_8888 -#elif defined(CONFIG_FB_MSM_8X60_DEFAULT_DEPTH_RGBA8888) +#elif defined(CONFIG_FB_MSM_DEFAULT_DEPTH_RGBA8888) #define MSMFB_DEFAULT_TYPE MDP_RGBA_8888 #else #define MSMFB_DEFAULT_TYPE MDP_RGB_565 From fb7f03a524b77db0169d0335199970f4fd4806c9 Mon Sep 17 00:00:00 2001 From: jared waters Date: Thu, 29 Nov 2012 20:10:54 -0800 Subject: [PATCH 018/111] power: fbearlysuspend: add sleep hack for CRT-off animation HTC panels don't seem to work correctly with the Android CRT-off animation. Add a short sleep period to let userspace finish drawing the animation. --- kernel/power/fbearlysuspend.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/kernel/power/fbearlysuspend.c b/kernel/power/fbearlysuspend.c index 15137650149..660800943f3 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(350); + spin_lock_irqsave(&fb_state_lock, irq_flags); fb_state = FB_STATE_REQUEST_STOP_DRAWING; spin_unlock_irqrestore(&fb_state_lock, irq_flags); From 90db5c8b99a3feae4837c5c0b46636eb2cfd1460 Mon Sep 17 00:00:00 2001 From: David Hays Date: Sun, 2 Jun 2013 14:21:12 -0500 Subject: [PATCH 019/111] vigor: update board to CAF USB gadget Change-Id: I388dcbc074dece7615f5b5d1a37a85643e3fd037 --- arch/arm/mach-msm/board-vigor.c | 31 +------------------------------ 1 file changed, 1 insertion(+), 30 deletions(-) diff --git a/arch/arm/mach-msm/board-vigor.c b/arch/arm/mach-msm/board-vigor.c index bae59260f3e..bc80022ccd0 100644 --- a/arch/arm/mach-msm/board-vigor.c +++ b/arch/arm/mach-msm/board-vigor.c @@ -84,7 +84,6 @@ #include #include #endif -#include #include #ifdef CONFIG_MSM_DSPS #include @@ -100,7 +99,7 @@ #include #include #ifdef CONFIG_USB_G_ANDROID -#include +#include #include #endif #include @@ -1654,19 +1653,7 @@ static int vigor_usb_product_id_match(int product_id, int intrsharing) #endif static struct android_usb_platform_data android_usb_pdata = { - .vendor_id = 0x0BB4, - .product_id = 0x0ccd, - .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, - .enable_fast_charge = NULL, .update_pid_and_serial_num = usb_diag_update_pid_and_serial_num, - .usb_id_pin_gpio = VIGOR_GPIO_USB_ID, - .fserial_init_string = "smd:modem,sdio:modem_mdm,tty,tty,tty:serial", #ifdef CONFIG_USB_GADGET_VERIZON_PRODUCT_ID .match = vigor_usb_product_id_match, #endif @@ -1683,7 +1670,6 @@ static struct platform_device android_usb_device = { static int __init board_serialno_setup(char *serialno) { - android_usb_pdata.serial_number = serialno; return 1; } __setup("androidboot.serialno=", board_serialno_setup); @@ -1691,24 +1677,9 @@ __setup("androidboot.serialno=", board_serialno_setup); static void vigor_add_usb_devices(void) { printk(KERN_INFO "%s rev: %d\n", __func__, system_rev); - android_usb_pdata.products[0].product_id = - android_usb_pdata.product_id; config_vigor_mhl_gpios(); - /* diag bit set */ - if (get_radio_flag() & 0x20000) { - android_usb_pdata.diag_init = 1; - android_usb_pdata.modem_init = 1; - android_usb_pdata.rmnet_init = 1; - } - - /* add cdrom support in normal mode */ - if (board_mfg_mode() == 0) { - android_usb_pdata.nluns = 3; - android_usb_pdata.cdrom_lun = 0x4; - } - msm_device_gadget_peripheral.dev.parent = &msm_device_otg.dev; platform_device_register(&msm_device_gadget_peripheral); platform_device_register(&android_usb_device); From 3c31d58b096cf997a17e71b9cf25107e48bebafb Mon Sep 17 00:00:00 2001 From: Sultanxda Date: Sat, 4 May 2013 22:38:53 -0700 Subject: [PATCH 020/111] Threw in some optimization flags for the GPU driver --- drivers/gpu/Makefile | 2 ++ drivers/gpu/ion/Makefile | 1 + drivers/gpu/ion/msm/Makefile | 1 + 3 files changed, 4 insertions(+) diff --git a/drivers/gpu/Makefile b/drivers/gpu/Makefile index 066748bf51c..49b39e7135c 100644 --- a/drivers/gpu/Makefile +++ b/drivers/gpu/Makefile @@ -1,2 +1,4 @@ +ccflags-y := -Ofast -ffast-math -fgcse-lm -fgcse-sm -fsched-spec-load -fforce-addr -fsingle-precision-constant -mtune=cortex-a9 -marm -march=armv7-a -mfpu=neon -ftree-vectorize -mvectorize-with-neon-double + obj-y += drm/ vga/ stub/ ion/ obj-$(CONFIG_MSM_KGSL) += msm/ diff --git a/drivers/gpu/ion/Makefile b/drivers/gpu/ion/Makefile index c9e8a944052..3a13b158aef 100644 --- a/drivers/gpu/ion/Makefile +++ b/drivers/gpu/ion/Makefile @@ -1,3 +1,4 @@ +ccflags-y := -Ofast -ffast-math -fgcse-lm -fgcse-sm -fsched-spec-load -fforce-addr -fsingle-precision-constant -mtune=cortex-a9 -marm -march=armv7-a -mfpu=neon -ftree-vectorize -mvectorize-with-neon-double 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/msm/Makefile b/drivers/gpu/ion/msm/Makefile index bedd8d22779..05660070d6b 100644 --- a/drivers/gpu/ion/msm/Makefile +++ b/drivers/gpu/ion/msm/Makefile @@ -1 +1,2 @@ +ccflags-y := -Ofast -ffast-math -fgcse-lm -fgcse-sm -fsched-spec-load -fforce-addr -fsingle-precision-constant -mtune=cortex-a9 -marm -march=armv7-a -mfpu=neon -ftree-vectorize -mvectorize-with-neon-double obj-y += msm_ion.o From bff6d3dfec9a1fd78eec9695282d39ce3bf9673e Mon Sep 17 00:00:00 2001 From: Ethan Chen Date: Sun, 2 Sep 2012 17:34:26 -0700 Subject: [PATCH 021/111] Linux 3.0.17 Change-Id: I0b2ccbbdc5207af4a8ff919f76e07b70eee7b0e6 --- Documentation/HOWTO | 4 +- Documentation/development-process/5.Posting | 8 +-- Documentation/usb/usbmon.txt | 14 ++-- MAINTAINERS | 2 +- Makefile | 2 +- arch/powerpc/include/asm/time.h | 2 + arch/powerpc/kernel/irq.c | 15 ++-- arch/powerpc/kernel/time.c | 9 +++ arch/powerpc/platforms/pseries/hvCall_inst.c | 4 +- arch/powerpc/platforms/pseries/lpar.c | 2 + drivers/base/firmware_class.c | 14 ++-- drivers/infiniband/hw/qib/qib_iba6120.c | 4 +- drivers/infiniband/hw/qib/qib_iba7220.c | 4 +- drivers/infiniband/hw/qib/qib_iba7322.c | 6 +- drivers/net/bonding/bond_main.c | 9 ++- drivers/net/usb/asix.c | 6 +- drivers/net/wireless/rt2x00/rt2800usb.c | 2 +- drivers/net/wireless/wl12xx/boot.c | 14 ++++ drivers/net/wireless/wl12xx/cmd.c | 22 ++++++ drivers/scsi/mpt2sas/mpt2sas_scsih.c | 1 + drivers/tty/serial/atmel_serial.c | 5 +- drivers/usb/class/cdc-acm.c | 16 ++++- drivers/usb/core/hcd.c | 5 +- drivers/usb/host/ehci-q.c | 2 +- drivers/usb/host/ohci-hcd.c | 15 ++-- drivers/usb/host/ohci-pci.c | 26 ------- drivers/usb/host/ohci.h | 1 - drivers/usb/host/pci-quirks.c | 50 ++++++------- drivers/usb/host/uhci-q.c | 2 +- drivers/usb/host/whci/qset.c | 4 +- drivers/usb/host/xhci-ring.c | 4 +- drivers/usb/host/xhci.c | 4 +- drivers/usb/host/xhci.h | 1 - drivers/usb/misc/isight_firmware.c | 6 +- drivers/usb/musb/musb_core.c | 2 - drivers/usb/serial/cp210x.c | 1 + drivers/usb/serial/omninet.c | 2 +- drivers/usb/serial/option.c | 10 +++ drivers/usb/storage/usb.c | 1 + drivers/video/offb.c | 52 +++++++------- fs/ext3/inode.c | 24 ++++++- fs/reiserfs/super.c | 27 ++++--- fs/udf/file.c | 6 +- fs/udf/inode.c | 21 +++++- fs/xfs/linux-2.6/xfs_acl.c | 2 +- include/linux/usb.h | 1 + include/linux/usb/ch9.h | 20 +++++- kernel/cgroup.c | 6 +- kernel/cpu.c | 74 ++++++++++++++++++++ net/ipv4/igmp.c | 2 + tools/perf/util/trace-event-parse.c | 2 + 51 files changed, 360 insertions(+), 178 deletions(-) diff --git a/Documentation/HOWTO b/Documentation/HOWTO index 81bc1a9ab9d..f7ade3b3b40 100644 --- a/Documentation/HOWTO +++ b/Documentation/HOWTO @@ -275,8 +275,8 @@ versions. If no 2.6.x.y kernel is available, then the highest numbered 2.6.x kernel is the current stable kernel. -2.6.x.y are maintained by the "stable" team , and are -released as needs dictate. The normal release period is approximately +2.6.x.y are maintained by the "stable" team , and +are released as needs dictate. The normal release period is approximately two weeks, but it can be longer if there are no pressing problems. A security-related problem, instead, can cause a release to happen almost instantly. diff --git a/Documentation/development-process/5.Posting b/Documentation/development-process/5.Posting index 903a2546f13..8a48c9b6286 100644 --- a/Documentation/development-process/5.Posting +++ b/Documentation/development-process/5.Posting @@ -271,10 +271,10 @@ copies should go to: the linux-kernel list. - If you are fixing a bug, think about whether the fix should go into the - next stable update. If so, stable@kernel.org should get a copy of the - patch. Also add a "Cc: stable@kernel.org" to the tags within the patch - itself; that will cause the stable team to get a notification when your - fix goes into the mainline. + next stable update. If so, stable@vger.kernel.org should get a copy of + the patch. Also add a "Cc: stable@vger.kernel.org" to the tags within + the patch itself; that will cause the stable team to get a notification + when your fix goes into the mainline. When selecting recipients for a patch, it is good to have an idea of who you think will eventually accept the patch and get it merged. While it diff --git a/Documentation/usb/usbmon.txt b/Documentation/usb/usbmon.txt index a4efa0462f0..5335fa8b06e 100644 --- a/Documentation/usb/usbmon.txt +++ b/Documentation/usb/usbmon.txt @@ -47,10 +47,11 @@ This allows to filter away annoying devices that talk continuously. 2. Find which bus connects to the desired device -Run "cat /proc/bus/usb/devices", and find the T-line which corresponds to -the device. Usually you do it by looking for the vendor string. If you have -many similar devices, unplug one and compare two /proc/bus/usb/devices outputs. -The T-line will have a bus number. Example: +Run "cat /sys/kernel/debug/usb/devices", and find the T-line which corresponds +to the device. Usually you do it by looking for the vendor string. If you have +many similar devices, unplug one and compare the two +/sys/kernel/debug/usb/devices outputs. The T-line will have a bus number. +Example: T: Bus=03 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#= 2 Spd=12 MxCh= 0 D: Ver= 1.10 Cls=00(>ifc ) Sub=00 Prot=00 MxPS= 8 #Cfgs= 1 @@ -58,7 +59,10 @@ P: Vendor=0557 ProdID=2004 Rev= 1.00 S: Manufacturer=ATEN S: Product=UC100KM V2.00 -Bus=03 means it's bus 3. +"Bus=03" means it's bus 3. Alternatively, you can look at the output from +"lsusb" and get the bus number from the appropriate line. Example: + +Bus 003 Device 002: ID 0557:2004 ATEN UC100KM V2.00 3. Start 'cat' diff --git a/MAINTAINERS b/MAINTAINERS index 34e24186584..de85391c021 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6039,7 +6039,7 @@ F: arch/alpha/kernel/srm_env.c STABLE BRANCH M: Greg Kroah-Hartman -L: stable@kernel.org +L: stable@vger.kernel.org S: Maintained STAGING SUBSYSTEM diff --git a/Makefile b/Makefile index 6d356e7dca4..5041de04f6b 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ VERSION = 3 PATCHLEVEL = 0 -SUBLEVEL = 16 +SUBLEVEL = 17 EXTRAVERSION = NAME = Sneaky Weasel diff --git a/arch/powerpc/include/asm/time.h b/arch/powerpc/include/asm/time.h index fe6f7c2c9c6..bc3c745cb90 100644 --- a/arch/powerpc/include/asm/time.h +++ b/arch/powerpc/include/asm/time.h @@ -219,5 +219,7 @@ DECLARE_PER_CPU(struct cpu_usage, cpu_usage_array); extern void secondary_cpu_time_init(void); extern void iSeries_time_init_early(void); +extern void decrementer_check_overflow(void); + #endif /* __KERNEL__ */ #endif /* __POWERPC_TIME_H */ diff --git a/arch/powerpc/kernel/irq.c b/arch/powerpc/kernel/irq.c index 5b428e30866..ca2987d939f 100644 --- a/arch/powerpc/kernel/irq.c +++ b/arch/powerpc/kernel/irq.c @@ -170,16 +170,13 @@ notrace void arch_local_irq_restore(unsigned long en) */ local_paca->hard_enabled = en; -#ifndef CONFIG_BOOKE - /* On server, re-trigger the decrementer if it went negative since - * some processors only trigger on edge transitions of the sign bit. - * - * BookE has a level sensitive decrementer (latches in TSR) so we - * don't need that + /* + * Trigger the decrementer if we have a pending event. Some processors + * only trigger on edge transitions of the sign bit. We might also + * have disabled interrupts long enough that the decrementer wrapped + * to positive. */ - if ((int)mfspr(SPRN_DEC) < 0) - mtspr(SPRN_DEC, 1); -#endif /* CONFIG_BOOKE */ + decrementer_check_overflow(); /* * Force the delivery of pending soft-disabled interrupts on PS3. diff --git a/arch/powerpc/kernel/time.c b/arch/powerpc/kernel/time.c index 03b29a6759a..2de304af07a 100644 --- a/arch/powerpc/kernel/time.c +++ b/arch/powerpc/kernel/time.c @@ -889,6 +889,15 @@ static void __init clocksource_init(void) clock->name, clock->mult, clock->shift); } +void decrementer_check_overflow(void) +{ + u64 now = get_tb_or_rtc(); + struct decrementer_clock *decrementer = &__get_cpu_var(decrementers); + + if (now >= decrementer->next_tb) + set_dec(1); +} + static int decrementer_set_next_event(unsigned long evt, struct clock_event_device *dev) { diff --git a/arch/powerpc/platforms/pseries/hvCall_inst.c b/arch/powerpc/platforms/pseries/hvCall_inst.c index f106662f438..c9311cfdfca 100644 --- a/arch/powerpc/platforms/pseries/hvCall_inst.c +++ b/arch/powerpc/platforms/pseries/hvCall_inst.c @@ -109,7 +109,7 @@ static void probe_hcall_entry(void *ignored, unsigned long opcode, unsigned long if (opcode > MAX_HCALL_OPCODE) return; - h = &get_cpu_var(hcall_stats)[opcode / 4]; + h = &__get_cpu_var(hcall_stats)[opcode / 4]; h->tb_start = mftb(); h->purr_start = mfspr(SPRN_PURR); } @@ -126,8 +126,6 @@ static void probe_hcall_exit(void *ignored, unsigned long opcode, unsigned long h->num_calls++; h->tb_total += mftb() - h->tb_start; h->purr_total += mfspr(SPRN_PURR) - h->purr_start; - - put_cpu_var(hcall_stats); } static int __init hcall_inst_init(void) diff --git a/arch/powerpc/platforms/pseries/lpar.c b/arch/powerpc/platforms/pseries/lpar.c index ed96b376537..81e30d96f83 100644 --- a/arch/powerpc/platforms/pseries/lpar.c +++ b/arch/powerpc/platforms/pseries/lpar.c @@ -745,6 +745,7 @@ void __trace_hcall_entry(unsigned long opcode, unsigned long *args) goto out; (*depth)++; + preempt_disable(); trace_hcall_entry(opcode, args); (*depth)--; @@ -767,6 +768,7 @@ void __trace_hcall_exit(long opcode, unsigned long retval, (*depth)++; trace_hcall_exit(opcode, retval, retbuf); + preempt_enable(); (*depth)--; out: diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index 06ed6b4e7df..3719c94be19 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -226,13 +226,13 @@ static ssize_t firmware_loading_store(struct device *dev, int loading = simple_strtol(buf, NULL, 10); int i; + mutex_lock(&fw_lock); + + if (!fw_priv->fw) + goto out; + switch (loading) { case 1: - mutex_lock(&fw_lock); - if (!fw_priv->fw) { - mutex_unlock(&fw_lock); - break; - } firmware_free_data(fw_priv->fw); memset(fw_priv->fw, 0, sizeof(struct firmware)); /* If the pages are not owned by 'struct firmware' */ @@ -243,7 +243,6 @@ static ssize_t firmware_loading_store(struct device *dev, fw_priv->page_array_size = 0; fw_priv->nr_pages = 0; set_bit(FW_STATUS_LOADING, &fw_priv->status); - mutex_unlock(&fw_lock); break; case 0: if (test_bit(FW_STATUS_LOADING, &fw_priv->status)) { @@ -274,7 +273,8 @@ static ssize_t firmware_loading_store(struct device *dev, fw_load_abort(fw_priv); break; } - +out: + mutex_unlock(&fw_lock); return count; } diff --git a/drivers/infiniband/hw/qib/qib_iba6120.c b/drivers/infiniband/hw/qib/qib_iba6120.c index d8ca0a0b970..65df26ce538 100644 --- a/drivers/infiniband/hw/qib/qib_iba6120.c +++ b/drivers/infiniband/hw/qib/qib_iba6120.c @@ -2076,9 +2076,11 @@ static void qib_6120_config_ctxts(struct qib_devdata *dd) static void qib_update_6120_usrhead(struct qib_ctxtdata *rcd, u64 hd, u32 updegr, u32 egrhd, u32 npkts) { - qib_write_ureg(rcd->dd, ur_rcvhdrhead, hd, rcd->ctxt); if (updegr) qib_write_ureg(rcd->dd, ur_rcvegrindexhead, egrhd, rcd->ctxt); + mmiowb(); + qib_write_ureg(rcd->dd, ur_rcvhdrhead, hd, rcd->ctxt); + mmiowb(); } static u32 qib_6120_hdrqempty(struct qib_ctxtdata *rcd) diff --git a/drivers/infiniband/hw/qib/qib_iba7220.c b/drivers/infiniband/hw/qib/qib_iba7220.c index c765a2eb04c..759bb63bb3b 100644 --- a/drivers/infiniband/hw/qib/qib_iba7220.c +++ b/drivers/infiniband/hw/qib/qib_iba7220.c @@ -2704,9 +2704,11 @@ static int qib_7220_set_loopback(struct qib_pportdata *ppd, const char *what) static void qib_update_7220_usrhead(struct qib_ctxtdata *rcd, u64 hd, u32 updegr, u32 egrhd, u32 npkts) { - qib_write_ureg(rcd->dd, ur_rcvhdrhead, hd, rcd->ctxt); if (updegr) qib_write_ureg(rcd->dd, ur_rcvegrindexhead, egrhd, rcd->ctxt); + mmiowb(); + qib_write_ureg(rcd->dd, ur_rcvhdrhead, hd, rcd->ctxt); + mmiowb(); } static u32 qib_7220_hdrqempty(struct qib_ctxtdata *rcd) diff --git a/drivers/infiniband/hw/qib/qib_iba7322.c b/drivers/infiniband/hw/qib/qib_iba7322.c index 8ec5237031a..49e4a589479 100644 --- a/drivers/infiniband/hw/qib/qib_iba7322.c +++ b/drivers/infiniband/hw/qib/qib_iba7322.c @@ -4060,10 +4060,12 @@ static void qib_update_7322_usrhead(struct qib_ctxtdata *rcd, u64 hd, */ if (hd >> IBA7322_HDRHEAD_PKTINT_SHIFT) adjust_rcv_timeout(rcd, npkts); - qib_write_ureg(rcd->dd, ur_rcvhdrhead, hd, rcd->ctxt); - qib_write_ureg(rcd->dd, ur_rcvhdrhead, hd, rcd->ctxt); if (updegr) qib_write_ureg(rcd->dd, ur_rcvegrindexhead, egrhd, rcd->ctxt); + mmiowb(); + qib_write_ureg(rcd->dd, ur_rcvhdrhead, hd, rcd->ctxt); + qib_write_ureg(rcd->dd, ur_rcvhdrhead, hd, rcd->ctxt); + mmiowb(); } static u32 qib_7322_hdrqempty(struct qib_ctxtdata *rcd) diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 2065cb4002b..0b65c5fccc3 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -1905,7 +1905,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) "but new slave device does not support netpoll.\n", bond_dev->name); res = -EBUSY; - goto err_close; + goto err_detach; } } #endif @@ -1914,7 +1914,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) res = bond_create_slave_symlinks(bond_dev, slave_dev); if (res) - goto err_close; + goto err_detach; res = netdev_rx_handler_register(slave_dev, bond_handle_frame, new_slave); @@ -1935,6 +1935,11 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) err_dest_symlinks: bond_destroy_slave_symlinks(bond_dev, slave_dev); +err_detach: + write_lock_bh(&bond->lock); + bond_detach_slave(bond, new_slave); + write_unlock_bh(&bond->lock); + err_close: dev_close(slave_dev); diff --git a/drivers/net/usb/asix.c b/drivers/net/usb/asix.c index c5c4b4def7f..71055770009 100644 --- a/drivers/net/usb/asix.c +++ b/drivers/net/usb/asix.c @@ -371,7 +371,7 @@ static int asix_rx_fixup(struct usbnet *dev, struct sk_buff *skb) skb_pull(skb, (size + 1) & 0xfffe); - if (skb->len == 0) + if (skb->len < sizeof(header)) break; head = (u8 *) skb->data; @@ -1560,6 +1560,10 @@ static const struct usb_device_id products [] = { // ASIX 88772a USB_DEVICE(0x0db0, 0xa877), .driver_info = (unsigned long) &ax88772_info, +}, { + // Asus USB Ethernet Adapter + USB_DEVICE (0x0b95, 0x7e2b), + .driver_info = (unsigned long) &ax88772_info, }, { }, // END }; diff --git a/drivers/net/wireless/rt2x00/rt2800usb.c b/drivers/net/wireless/rt2x00/rt2800usb.c index 6e7fe941b95..d4e9eac5815 100644 --- a/drivers/net/wireless/rt2x00/rt2800usb.c +++ b/drivers/net/wireless/rt2x00/rt2800usb.c @@ -878,6 +878,7 @@ static struct usb_device_id rt2800usb_device_table[] = { { USB_DEVICE(0x13b1, 0x0031) }, { USB_DEVICE(0x1737, 0x0070) }, { USB_DEVICE(0x1737, 0x0071) }, + { USB_DEVICE(0x1737, 0x0077) }, /* Logitec */ { USB_DEVICE(0x0789, 0x0162) }, { USB_DEVICE(0x0789, 0x0163) }, @@ -1069,7 +1070,6 @@ static struct usb_device_id rt2800usb_device_table[] = { { USB_DEVICE(0x1740, 0x0605) }, { USB_DEVICE(0x1740, 0x0615) }, /* Linksys */ - { USB_DEVICE(0x1737, 0x0077) }, { USB_DEVICE(0x1737, 0x0078) }, /* Logitec */ { USB_DEVICE(0x0789, 0x0168) }, diff --git a/drivers/net/wireless/wl12xx/boot.c b/drivers/net/wireless/wl12xx/boot.c index b07f8b7e5f1..e0e16888fe8 100644 --- a/drivers/net/wireless/wl12xx/boot.c +++ b/drivers/net/wireless/wl12xx/boot.c @@ -328,6 +328,9 @@ static int wl1271_boot_upload_nvs(struct wl1271 *wl) nvs_ptr += 3; for (i = 0; i < burst_len; i++) { + if (nvs_ptr + 3 >= (u8 *) wl->nvs + nvs_len) + goto out_badnvs; + val = (nvs_ptr[0] | (nvs_ptr[1] << 8) | (nvs_ptr[2] << 16) | (nvs_ptr[3] << 24)); @@ -339,6 +342,9 @@ static int wl1271_boot_upload_nvs(struct wl1271 *wl) nvs_ptr += 4; dest_addr += 4; } + + if (nvs_ptr >= (u8 *) wl->nvs + nvs_len) + goto out_badnvs; } /* @@ -350,6 +356,10 @@ static int wl1271_boot_upload_nvs(struct wl1271 *wl) */ nvs_ptr = (u8 *)wl->nvs + ALIGN(nvs_ptr - (u8 *)wl->nvs + 7, 4); + + if (nvs_ptr >= (u8 *) wl->nvs + nvs_len) + goto out_badnvs; + nvs_len -= nvs_ptr - (u8 *)wl->nvs; /* Now we must set the partition correctly */ @@ -365,6 +375,10 @@ static int wl1271_boot_upload_nvs(struct wl1271 *wl) kfree(nvs_aligned); return 0; + +out_badnvs: + wl1271_error("nvs data is malformed"); + return -EILSEQ; } static void wl1271_boot_enable_interrupts(struct wl1271 *wl) diff --git a/drivers/net/wireless/wl12xx/cmd.c b/drivers/net/wireless/wl12xx/cmd.c index 42935ac7266..b8ec8cd69b0 100644 --- a/drivers/net/wireless/wl12xx/cmd.c +++ b/drivers/net/wireless/wl12xx/cmd.c @@ -121,6 +121,11 @@ int wl1271_cmd_general_parms(struct wl1271 *wl) if (!wl->nvs) return -ENODEV; + if (gp->tx_bip_fem_manufacturer >= WL1271_INI_FEM_MODULE_COUNT) { + wl1271_warning("FEM index from INI out of bounds"); + return -EINVAL; + } + gen_parms = kzalloc(sizeof(*gen_parms), GFP_KERNEL); if (!gen_parms) return -ENOMEM; @@ -144,6 +149,12 @@ int wl1271_cmd_general_parms(struct wl1271 *wl) gp->tx_bip_fem_manufacturer = gen_parms->general_params.tx_bip_fem_manufacturer; + if (gp->tx_bip_fem_manufacturer >= WL1271_INI_FEM_MODULE_COUNT) { + wl1271_warning("FEM index from FW out of bounds"); + ret = -EINVAL; + goto out; + } + wl1271_debug(DEBUG_CMD, "FEM autodetect: %s, manufacturer: %d\n", answer ? "auto" : "manual", gp->tx_bip_fem_manufacturer); @@ -163,6 +174,11 @@ int wl128x_cmd_general_parms(struct wl1271 *wl) if (!wl->nvs) return -ENODEV; + if (gp->tx_bip_fem_manufacturer >= WL1271_INI_FEM_MODULE_COUNT) { + wl1271_warning("FEM index from ini out of bounds"); + return -EINVAL; + } + gen_parms = kzalloc(sizeof(*gen_parms), GFP_KERNEL); if (!gen_parms) return -ENOMEM; @@ -187,6 +203,12 @@ int wl128x_cmd_general_parms(struct wl1271 *wl) gp->tx_bip_fem_manufacturer = gen_parms->general_params.tx_bip_fem_manufacturer; + if (gp->tx_bip_fem_manufacturer >= WL1271_INI_FEM_MODULE_COUNT) { + wl1271_warning("FEM index from FW out of bounds"); + ret = -EINVAL; + goto out; + } + wl1271_debug(DEBUG_CMD, "FEM autodetect: %s, manufacturer: %d\n", answer ? "auto" : "manual", gp->tx_bip_fem_manufacturer); diff --git a/drivers/scsi/mpt2sas/mpt2sas_scsih.c b/drivers/scsi/mpt2sas/mpt2sas_scsih.c index f88e52a1a39..c79857e439f 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_scsih.c +++ b/drivers/scsi/mpt2sas/mpt2sas_scsih.c @@ -7211,6 +7211,7 @@ _scsih_remove(struct pci_dev *pdev) } sas_remove_host(shost); + mpt2sas_base_detach(ioc); list_del(&ioc->list); scsi_remove_host(shost); scsi_host_put(shost); diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index af9b7814965..b989495c763 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -199,8 +199,9 @@ void atmel_config_rs485(struct uart_port *port, struct serial_rs485 *rs485conf) { struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); unsigned int mode; + unsigned long flags; - spin_lock(&port->lock); + spin_lock_irqsave(&port->lock, flags); /* Disable interrupts */ UART_PUT_IDR(port, atmel_port->tx_done_mask); @@ -231,7 +232,7 @@ void atmel_config_rs485(struct uart_port *port, struct serial_rs485 *rs485conf) /* Enable interrupts */ UART_PUT_IER(port, atmel_port->tx_done_mask); - spin_unlock(&port->lock); + spin_unlock_irqrestore(&port->lock, flags); } diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 8faa23cd74f..158f6312143 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -554,10 +554,18 @@ static void acm_port_down(struct acm *acm) static void acm_tty_hangup(struct tty_struct *tty) { - struct acm *acm = tty->driver_data; - tty_port_hangup(&acm->port); + struct acm *acm; + mutex_lock(&open_mutex); + acm = tty->driver_data; + + if (!acm) + goto out; + + tty_port_hangup(&acm->port); acm_port_down(acm); + +out: mutex_unlock(&open_mutex); } @@ -1183,6 +1191,8 @@ static int acm_probe(struct usb_interface *intf, i = device_create_file(&intf->dev, &dev_attr_wCountryCodes); if (i < 0) { kfree(acm->country_codes); + acm->country_codes = NULL; + acm->country_code_size = 0; goto skip_countries; } @@ -1191,6 +1201,8 @@ static int acm_probe(struct usb_interface *intf, if (i < 0) { device_remove_file(&intf->dev, &dev_attr_wCountryCodes); kfree(acm->country_codes); + acm->country_codes = NULL; + acm->country_code_size = 0; goto skip_countries; } } diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 54338fcc464..145d2ea234b 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -1395,11 +1395,10 @@ int usb_hcd_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb, ret = -EAGAIN; else urb->transfer_flags |= URB_DMA_MAP_SG; - if (n != urb->num_sgs) { - urb->num_sgs = n; + urb->num_mapped_sgs = n; + if (n != urb->num_sgs) urb->transfer_flags |= URB_DMA_SG_COMBINED; - } } else if (urb->sg) { struct scatterlist *sg = urb->sg; urb->transfer_dma = dma_map_page( diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index 9c24ff441d5..57ab83e2da4 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -649,7 +649,7 @@ qh_urb_transaction ( /* * data transfer stage: buffer setup */ - i = urb->num_sgs; + i = urb->num_mapped_sgs; if (len > 0 && i > 0) { sg = urb->sg; buf = sg_dma_address(sg); diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index f9cf3f04b74..23107e23053 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -389,17 +389,14 @@ ohci_shutdown (struct usb_hcd *hcd) struct ohci_hcd *ohci; ohci = hcd_to_ohci (hcd); - ohci_writel (ohci, OHCI_INTR_MIE, &ohci->regs->intrdisable); - ohci->hc_control = ohci_readl(ohci, &ohci->regs->control); + ohci_writel(ohci, (u32) ~0, &ohci->regs->intrdisable); - /* If the SHUTDOWN quirk is set, don't put the controller in RESET */ - ohci->hc_control &= (ohci->flags & OHCI_QUIRK_SHUTDOWN ? - OHCI_CTRL_RWC | OHCI_CTRL_HCFS : - OHCI_CTRL_RWC); - ohci_writel(ohci, ohci->hc_control, &ohci->regs->control); + /* Software reset, after which the controller goes into SUSPEND */ + ohci_writel(ohci, OHCI_HCR, &ohci->regs->cmdstatus); + ohci_readl(ohci, &ohci->regs->cmdstatus); /* flush the writes */ + udelay(10); - /* flush the writes */ - (void) ohci_readl (ohci, &ohci->regs->control); + ohci_writel(ohci, ohci->fminterval, &ohci->regs->fminterval); } static int check_ed(struct ohci_hcd *ohci, struct ed *ed) diff --git a/drivers/usb/host/ohci-pci.c b/drivers/usb/host/ohci-pci.c index ad8166c681e..bc01b064585 100644 --- a/drivers/usb/host/ohci-pci.c +++ b/drivers/usb/host/ohci-pci.c @@ -175,28 +175,6 @@ static int ohci_quirk_amd700(struct usb_hcd *hcd) return 0; } -/* nVidia controllers continue to drive Reset signalling on the bus - * even after system shutdown, wasting power. This flag tells the - * shutdown routine to leave the controller OPERATIONAL instead of RESET. - */ -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); - - /* 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; -} - static void sb800_prefetch(struct ohci_hcd *ohci, int on) { struct pci_dev *pdev; @@ -260,10 +238,6 @@ static const struct pci_device_id ohci_pci_quirks[] = { PCI_DEVICE(PCI_VENDOR_ID_ATI, 0x4399), .driver_data = (unsigned long)ohci_quirk_amd700, }, - { - PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID), - .driver_data = (unsigned long) ohci_quirk_nvidia_shutdown, - }, /* FIXME for some of the early AMD 760 southbridges, OHCI * won't work at all. blacklist them. diff --git a/drivers/usb/host/ohci.h b/drivers/usb/host/ohci.h index 35e5fd640ce..0795b934d00 100644 --- a/drivers/usb/host/ohci.h +++ b/drivers/usb/host/ohci.h @@ -403,7 +403,6 @@ struct ohci_hcd { #define OHCI_QUIRK_HUB_POWER 0x100 /* distrust firmware power/oc setup */ #define OHCI_QUIRK_AMD_PLL 0x200 /* AMD PLL quirk*/ #define OHCI_QUIRK_AMD_PREFETCH 0x400 /* pre-fetch for ISO transfer */ -#define OHCI_QUIRK_SHUTDOWN 0x800 /* nVidia power bug */ // there are also chip quirks/bugs in init logic struct work_struct nec_work; /* Worker for NEC quirk */ diff --git a/drivers/usb/host/pci-quirks.c b/drivers/usb/host/pci-quirks.c index a495d489918..23e04fb038b 100644 --- a/drivers/usb/host/pci-quirks.c +++ b/drivers/usb/host/pci-quirks.c @@ -36,6 +36,7 @@ #define OHCI_INTRENABLE 0x10 #define OHCI_INTRDISABLE 0x14 #define OHCI_FMINTERVAL 0x34 +#define OHCI_HCFS (3 << 6) /* hc functional state */ #define OHCI_HCR (1 << 0) /* host controller reset */ #define OHCI_OCR (1 << 3) /* ownership change request */ #define OHCI_CTRL_RWC (1 << 9) /* remote wakeup connected */ @@ -465,6 +466,8 @@ static void __devinit quirk_usb_handoff_ohci(struct pci_dev *pdev) { void __iomem *base; u32 control; + u32 fminterval; + int cnt; if (!mmio_resource_enabled(pdev, 0)) return; @@ -497,41 +500,32 @@ static void __devinit quirk_usb_handoff_ohci(struct pci_dev *pdev) } #endif - /* reset controller, preserving RWC (and possibly IR) */ - writel(control & OHCI_CTRL_MASK, base + OHCI_CONTROL); - readl(base + OHCI_CONTROL); + /* disable interrupts */ + writel((u32) ~0, base + OHCI_INTRDISABLE); - /* Some NVIDIA controllers stop working if kept in RESET for too long */ - if (pdev->vendor == PCI_VENDOR_ID_NVIDIA) { - u32 fminterval; - int cnt; + /* Reset the USB bus, if the controller isn't already in RESET */ + if (control & OHCI_HCFS) { + /* Go into RESET, preserving RWC (and possibly IR) */ + writel(control & OHCI_CTRL_MASK, base + OHCI_CONTROL); + readl(base + OHCI_CONTROL); - /* drive reset for at least 50 ms (7.1.7.5) */ + /* drive bus reset for at least 50 ms (7.1.7.5) */ msleep(50); + } - /* software reset of the controller, preserving HcFmInterval */ - fminterval = readl(base + OHCI_FMINTERVAL); - writel(OHCI_HCR, base + OHCI_CMDSTATUS); + /* software reset of the controller, preserving HcFmInterval */ + fminterval = readl(base + OHCI_FMINTERVAL); + writel(OHCI_HCR, base + OHCI_CMDSTATUS); - /* reset requires max 10 us delay */ - for (cnt = 30; cnt > 0; --cnt) { /* ... allow extra time */ - if ((readl(base + OHCI_CMDSTATUS) & OHCI_HCR) == 0) - break; - udelay(1); - } - writel(fminterval, base + OHCI_FMINTERVAL); - - /* Now we're in the SUSPEND state with all devices reset - * and wakeups and interrupts disabled - */ + /* reset requires max 10 us delay */ + for (cnt = 30; cnt > 0; --cnt) { /* ... allow extra time */ + if ((readl(base + OHCI_CMDSTATUS) & OHCI_HCR) == 0) + break; + udelay(1); } + writel(fminterval, base + OHCI_FMINTERVAL); - /* - * disable interrupts - */ - writel(~(u32)0, base + OHCI_INTRDISABLE); - writel(~(u32)0, base + OHCI_INTRSTATUS); - + /* Now the controller is safely in SUSPEND and nothing can wake it up */ iounmap(base); } diff --git a/drivers/usb/host/uhci-q.c b/drivers/usb/host/uhci-q.c index 84ed28b34f9..82539913ad8 100644 --- a/drivers/usb/host/uhci-q.c +++ b/drivers/usb/host/uhci-q.c @@ -943,7 +943,7 @@ static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb, if (usb_pipein(urb->pipe)) status |= TD_CTRL_SPD; - i = urb->num_sgs; + i = urb->num_mapped_sgs; if (len > 0 && i > 0) { sg = urb->sg; data = sg_dma_address(sg); diff --git a/drivers/usb/host/whci/qset.c b/drivers/usb/host/whci/qset.c index a403b53e86b..76083ae9213 100644 --- a/drivers/usb/host/whci/qset.c +++ b/drivers/usb/host/whci/qset.c @@ -443,7 +443,7 @@ static int qset_add_urb_sg(struct whc *whc, struct whc_qset *qset, struct urb *u remaining = urb->transfer_buffer_length; - for_each_sg(urb->sg, sg, urb->num_sgs, i) { + for_each_sg(urb->sg, sg, urb->num_mapped_sgs, i) { dma_addr_t dma_addr; size_t dma_remaining; dma_addr_t sp, ep; @@ -561,7 +561,7 @@ static int qset_add_urb_sg_linearize(struct whc *whc, struct whc_qset *qset, remaining = urb->transfer_buffer_length; - for_each_sg(urb->sg, sg, urb->num_sgs, i) { + for_each_sg(urb->sg, sg, urb->num_mapped_sgs, i) { size_t len; size_t sg_remaining; void *orig; diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index b4b06910f68..c0c5d6c7cb6 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -2570,7 +2570,7 @@ static unsigned int count_sg_trbs_needed(struct xhci_hcd *xhci, struct urb *urb) struct scatterlist *sg; sg = NULL; - num_sgs = urb->num_sgs; + num_sgs = urb->num_mapped_sgs; temp = urb->transfer_buffer_length; xhci_dbg(xhci, "count sg list trbs: \n"); @@ -2754,7 +2754,7 @@ static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags, return -EINVAL; num_trbs = count_sg_trbs_needed(xhci, urb); - num_sgs = urb->num_sgs; + num_sgs = urb->num_mapped_sgs; total_packet_count = roundup(urb->transfer_buffer_length, le16_to_cpu(urb->ep->desc.wMaxPacketSize)); diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 221f14e1fdd..107438eca2b 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -1568,6 +1568,7 @@ static int xhci_configure_endpoint_result(struct xhci_hcd *xhci, /* FIXME: can we allocate more resources for the HC? */ break; case COMP_BW_ERR: + case COMP_2ND_BW_ERR: dev_warn(&udev->dev, "Not enough bandwidth " "for new device state.\n"); ret = -ENOSPC; @@ -2183,8 +2184,7 @@ static int xhci_calculate_streams_and_bitmask(struct xhci_hcd *xhci, if (ret < 0) return ret; - max_streams = USB_SS_MAX_STREAMS( - eps[i]->ss_ep_comp.bmAttributes); + max_streams = usb_ss_max_streams(&eps[i]->ss_ep_comp); if (max_streams < (*num_streams - 1)) { xhci_dbg(xhci, "Ep 0x%x only supports %u stream IDs.\n", eps[i]->desc.bEndpointAddress, diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 49ce76c6b41..3e7c3a6c0fb 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -900,7 +900,6 @@ struct xhci_transfer_event { /* Invalid Stream ID Error */ #define COMP_STRID_ERR 34 /* Secondary Bandwidth Error - may be returned by a Configure Endpoint cmd */ -/* FIXME - check for this */ #define COMP_2ND_BW_ERR 35 /* Split Transaction Error */ #define COMP_SPLIT_ERR 36 diff --git a/drivers/usb/misc/isight_firmware.c b/drivers/usb/misc/isight_firmware.c index fe1d44319d0..8f725f65191 100644 --- a/drivers/usb/misc/isight_firmware.c +++ b/drivers/usb/misc/isight_firmware.c @@ -55,8 +55,9 @@ static int isight_firmware_load(struct usb_interface *intf, ptr = firmware->data; + buf[0] = 0x01; if (usb_control_msg - (dev, usb_sndctrlpipe(dev, 0), 0xa0, 0x40, 0xe600, 0, "\1", 1, + (dev, usb_sndctrlpipe(dev, 0), 0xa0, 0x40, 0xe600, 0, buf, 1, 300) != 1) { printk(KERN_ERR "Failed to initialise isight firmware loader\n"); @@ -100,8 +101,9 @@ static int isight_firmware_load(struct usb_interface *intf, } } + buf[0] = 0x00; if (usb_control_msg - (dev, usb_sndctrlpipe(dev, 0), 0xa0, 0x40, 0xe600, 0, "\0", 1, + (dev, usb_sndctrlpipe(dev, 0), 0xa0, 0x40, 0xe600, 0, buf, 1, 300) != 1) { printk(KERN_ERR "isight firmware loading completion failed\n"); ret = -ENODEV; diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index dce7182e1df..a0232a77c05 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -2078,8 +2078,6 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl) if (status < 0) goto fail3; - pm_runtime_put(musb->controller); - status = musb_init_debugfs(musb); if (status < 0) goto fail4; diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c index fd67cc53545..a1a324b30d2 100644 --- a/drivers/usb/serial/cp210x.c +++ b/drivers/usb/serial/cp210x.c @@ -92,6 +92,7 @@ static const struct usb_device_id id_table[] = { { USB_DEVICE(0x10C4, 0x818B) }, /* AVIT Research USB to TTL */ { USB_DEVICE(0x10C4, 0x819F) }, /* MJS USB Toslink Switcher */ { USB_DEVICE(0x10C4, 0x81A6) }, /* ThinkOptics WavIt */ + { USB_DEVICE(0x10C4, 0x81A9) }, /* Multiplex RC Interface */ { USB_DEVICE(0x10C4, 0x81AC) }, /* MSD Dash Hawk */ { USB_DEVICE(0x10C4, 0x81AD) }, /* INSYS USB Modem */ { USB_DEVICE(0x10C4, 0x81C8) }, /* Lipowsky Industrie Elektronik GmbH, Baby-JTAG */ diff --git a/drivers/usb/serial/omninet.c b/drivers/usb/serial/omninet.c index 60f38d5e64f..0a8c1e64b24 100644 --- a/drivers/usb/serial/omninet.c +++ b/drivers/usb/serial/omninet.c @@ -315,7 +315,7 @@ static int omninet_write_room(struct tty_struct *tty) int room = 0; /* Default: no room */ /* FIXME: no consistent locking for write_urb_busy */ - if (wport->write_urb_busy) + if (!wport->write_urb_busy) room = wport->bulk_out_size - OMNINET_HEADERLEN; dbg("%s - returns %d", __func__, room); diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index d2becb9eb60..c96b6b6509f 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -472,6 +472,14 @@ static void option_instat_callback(struct urb *urb); #define YUGA_PRODUCT_CLU528 0x260D #define YUGA_PRODUCT_CLU526 0x260F +/* Viettel products */ +#define VIETTEL_VENDOR_ID 0x2262 +#define VIETTEL_PRODUCT_VT1000 0x0002 + +/* ZD Incorporated */ +#define ZD_VENDOR_ID 0x0685 +#define ZD_PRODUCT_7000 0x7000 + /* some devices interfaces need special handling due to a number of reasons */ enum option_blacklist_reason { OPTION_BLACKLIST_NONE = 0, @@ -1173,6 +1181,8 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE(YUGA_VENDOR_ID, YUGA_PRODUCT_CLU516) }, { USB_DEVICE(YUGA_VENDOR_ID, YUGA_PRODUCT_CLU528) }, { USB_DEVICE(YUGA_VENDOR_ID, YUGA_PRODUCT_CLU526) }, + { USB_DEVICE_AND_INTERFACE_INFO(VIETTEL_VENDOR_ID, VIETTEL_PRODUCT_VT1000, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZD_VENDOR_ID, ZD_PRODUCT_7000, 0xff, 0xff, 0xff) }, { } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, option_ids); diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c index c325e69415a..9e069efeefe 100644 --- a/drivers/usb/storage/usb.c +++ b/drivers/usb/storage/usb.c @@ -1073,6 +1073,7 @@ static struct usb_driver usb_storage_driver = { .id_table = usb_storage_usb_ids, .supports_autosuspend = 1, .soft_unbind = 1, + .no_dynamic_id = 1, }; static int __init usb_stor_init(void) diff --git a/drivers/video/offb.c b/drivers/video/offb.c index cb163a5397b..3251a0236d5 100644 --- a/drivers/video/offb.c +++ b/drivers/video/offb.c @@ -100,36 +100,32 @@ static int offb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, u_int transp, struct fb_info *info) { struct offb_par *par = (struct offb_par *) info->par; - int i, depth; - u32 *pal = info->pseudo_palette; - - depth = info->var.bits_per_pixel; - if (depth == 16) - depth = (info->var.green.length == 5) ? 15 : 16; - - if (regno > 255 || - (depth == 16 && regno > 63) || - (depth == 15 && regno > 31)) - return 1; - - if (regno < 16) { - switch (depth) { - case 15: - pal[regno] = (regno << 10) | (regno << 5) | regno; - break; - case 16: - pal[regno] = (regno << 11) | (regno << 5) | regno; - break; - case 24: - pal[regno] = (regno << 16) | (regno << 8) | regno; - break; - case 32: - i = (regno << 8) | regno; - pal[regno] = (i << 16) | i; - break; + + if (info->fix.visual == FB_VISUAL_TRUECOLOR) { + u32 *pal = info->pseudo_palette; + u32 cr = red >> (16 - info->var.red.length); + u32 cg = green >> (16 - info->var.green.length); + u32 cb = blue >> (16 - info->var.blue.length); + u32 value; + + if (regno >= 16) + return -EINVAL; + + value = (cr << info->var.red.offset) | + (cg << info->var.green.offset) | + (cb << info->var.blue.offset); + if (info->var.transp.length > 0) { + u32 mask = (1 << info->var.transp.length) - 1; + mask <<= info->var.transp.offset; + value |= mask; } + pal[regno] = value; + return 0; } + if (regno > 255) + return -EINVAL; + red >>= 8; green >>= 8; blue >>= 8; @@ -381,7 +377,7 @@ static void __init offb_init_fb(const char *name, const char *full_name, int pitch, unsigned long address, int foreign_endian, struct device_node *dp) { - unsigned long res_size = pitch * height * (depth + 7) / 8; + unsigned long res_size = pitch * height; struct offb_par *par = &default_par; unsigned long res_start = address; struct fb_fix_screeninfo *fix; diff --git a/fs/ext3/inode.c b/fs/ext3/inode.c index 3451d23c3ba..db9ba1a3f7f 100644 --- a/fs/ext3/inode.c +++ b/fs/ext3/inode.c @@ -1568,7 +1568,13 @@ static int ext3_ordered_writepage(struct page *page, int err; J_ASSERT(PageLocked(page)); - WARN_ON_ONCE(IS_RDONLY(inode)); + /* + * We don't want to warn for emergency remount. The condition is + * ordered to avoid dereferencing inode->i_sb in non-error case to + * avoid slow-downs. + */ + WARN_ON_ONCE(IS_RDONLY(inode) && + !(EXT3_SB(inode->i_sb)->s_mount_state & EXT3_ERROR_FS)); /* * We give up here if we're reentered, because it might be for a @@ -1642,7 +1648,13 @@ static int ext3_writeback_writepage(struct page *page, int err; J_ASSERT(PageLocked(page)); - WARN_ON_ONCE(IS_RDONLY(inode)); + /* + * We don't want to warn for emergency remount. The condition is + * ordered to avoid dereferencing inode->i_sb in non-error case to + * avoid slow-downs. + */ + WARN_ON_ONCE(IS_RDONLY(inode) && + !(EXT3_SB(inode->i_sb)->s_mount_state & EXT3_ERROR_FS)); if (ext3_journal_current_handle()) goto out_fail; @@ -1684,7 +1696,13 @@ static int ext3_journalled_writepage(struct page *page, int err; J_ASSERT(PageLocked(page)); - WARN_ON_ONCE(IS_RDONLY(inode)); + /* + * We don't want to warn for emergency remount. The condition is + * ordered to avoid dereferencing inode->i_sb in non-error case to + * avoid slow-downs. + */ + WARN_ON_ONCE(IS_RDONLY(inode) && + !(EXT3_SB(inode->i_sb)->s_mount_state & EXT3_ERROR_FS)); if (ext3_journal_current_handle()) goto no_write; diff --git a/fs/reiserfs/super.c b/fs/reiserfs/super.c index aa91089162c..f19dfbf6000 100644 --- a/fs/reiserfs/super.c +++ b/fs/reiserfs/super.c @@ -453,16 +453,20 @@ int remove_save_link(struct inode *inode, int truncate) static void reiserfs_kill_sb(struct super_block *s) { if (REISERFS_SB(s)) { - if (REISERFS_SB(s)->xattr_root) { - d_invalidate(REISERFS_SB(s)->xattr_root); - dput(REISERFS_SB(s)->xattr_root); - REISERFS_SB(s)->xattr_root = NULL; - } - if (REISERFS_SB(s)->priv_root) { - d_invalidate(REISERFS_SB(s)->priv_root); - dput(REISERFS_SB(s)->priv_root); - REISERFS_SB(s)->priv_root = NULL; - } + /* + * Force any pending inode evictions to occur now. Any + * inodes to be removed that have extended attributes + * associated with them need to clean them up before + * we can release the extended attribute root dentries. + * shrink_dcache_for_umount will BUG if we don't release + * those before it's called so ->put_super is too late. + */ + shrink_dcache_sb(s); + + dput(REISERFS_SB(s)->xattr_root); + REISERFS_SB(s)->xattr_root = NULL; + dput(REISERFS_SB(s)->priv_root); + REISERFS_SB(s)->priv_root = NULL; } kill_block_super(s); @@ -1164,7 +1168,8 @@ static void handle_quota_files(struct super_block *s, char **qf_names, kfree(REISERFS_SB(s)->s_qf_names[i]); REISERFS_SB(s)->s_qf_names[i] = qf_names[i]; } - REISERFS_SB(s)->s_jquota_fmt = *qfmt; + if (*qfmt) + REISERFS_SB(s)->s_jquota_fmt = *qfmt; } #endif diff --git a/fs/udf/file.c b/fs/udf/file.c index 2a346bb1d9f..0c0c9d33dbc 100644 --- a/fs/udf/file.c +++ b/fs/udf/file.c @@ -125,7 +125,6 @@ static ssize_t udf_file_aio_write(struct kiocb *iocb, const struct iovec *iov, err = udf_expand_file_adinicb(inode); if (err) { udf_debug("udf_expand_adinicb: err=%d\n", err); - up_write(&iinfo->i_data_sem); return err; } } else { @@ -133,9 +132,10 @@ static ssize_t udf_file_aio_write(struct kiocb *iocb, const struct iovec *iov, iinfo->i_lenAlloc = pos + count; else iinfo->i_lenAlloc = inode->i_size; + up_write(&iinfo->i_data_sem); } - } - up_write(&iinfo->i_data_sem); + } else + up_write(&iinfo->i_data_sem); retval = generic_file_aio_write(iocb, iov, nr_segs, ppos); if (retval > 0) diff --git a/fs/udf/inode.c b/fs/udf/inode.c index 1d1358ed80c..262050f2eb6 100644 --- a/fs/udf/inode.c +++ b/fs/udf/inode.c @@ -145,6 +145,12 @@ const struct address_space_operations udf_aops = { .bmap = udf_bmap, }; +/* + * Expand file stored in ICB to a normal one-block-file + * + * This function requires i_data_sem for writing and releases it. + * This function requires i_mutex held + */ int udf_expand_file_adinicb(struct inode *inode) { struct page *page; @@ -163,9 +169,15 @@ int udf_expand_file_adinicb(struct inode *inode) iinfo->i_alloc_type = ICBTAG_FLAG_AD_LONG; /* from now on we have normal address_space methods */ inode->i_data.a_ops = &udf_aops; + up_write(&iinfo->i_data_sem); mark_inode_dirty(inode); return 0; } + /* + * Release i_data_sem so that we can lock a page - page lock ranks + * above i_data_sem. i_mutex still protects us against file changes. + */ + up_write(&iinfo->i_data_sem); page = find_or_create_page(inode->i_mapping, 0, GFP_NOFS); if (!page) @@ -181,6 +193,7 @@ int udf_expand_file_adinicb(struct inode *inode) SetPageUptodate(page); kunmap(page); } + down_write(&iinfo->i_data_sem); memset(iinfo->i_ext.i_data + iinfo->i_lenEAttr, 0x00, iinfo->i_lenAlloc); iinfo->i_lenAlloc = 0; @@ -190,17 +203,20 @@ int udf_expand_file_adinicb(struct inode *inode) iinfo->i_alloc_type = ICBTAG_FLAG_AD_LONG; /* from now on we have normal address_space methods */ inode->i_data.a_ops = &udf_aops; + up_write(&iinfo->i_data_sem); err = inode->i_data.a_ops->writepage(page, &udf_wbc); if (err) { /* Restore everything back so that we don't lose data... */ lock_page(page); kaddr = kmap(page); + down_write(&iinfo->i_data_sem); memcpy(iinfo->i_ext.i_data + iinfo->i_lenEAttr, kaddr, inode->i_size); kunmap(page); unlock_page(page); iinfo->i_alloc_type = ICBTAG_FLAG_AD_IN_ICB; inode->i_data.a_ops = &udf_adinicb_aops; + up_write(&iinfo->i_data_sem); } page_cache_release(page); mark_inode_dirty(inode); @@ -1105,10 +1121,9 @@ int udf_setsize(struct inode *inode, loff_t newsize) if (bsize < (udf_file_entry_alloc_offset(inode) + newsize)) { err = udf_expand_file_adinicb(inode); - if (err) { - up_write(&iinfo->i_data_sem); + if (err) return err; - } + down_write(&iinfo->i_data_sem); } else iinfo->i_lenAlloc = newsize; } diff --git a/fs/xfs/linux-2.6/xfs_acl.c b/fs/xfs/linux-2.6/xfs_acl.c index 4b9fb915d44..f86e0348786 100644 --- a/fs/xfs/linux-2.6/xfs_acl.c +++ b/fs/xfs/linux-2.6/xfs_acl.c @@ -39,7 +39,7 @@ xfs_acl_from_disk(struct xfs_acl *aclp) struct posix_acl_entry *acl_e; struct posix_acl *acl; struct xfs_acl_entry *ace; - int count, i; + unsigned int count, i; count = be32_to_cpu(aclp->acl_cnt); if (count > XFS_ACL_MAX_ENTRIES) diff --git a/include/linux/usb.h b/include/linux/usb.h index 583ceb890ca..7e3decbdf34 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -1226,6 +1226,7 @@ struct urb { void *transfer_buffer; /* (in) associated data buffer */ dma_addr_t transfer_dma; /* (in) dma addr for transfer_buffer */ struct scatterlist *sg; /* (in) scatter gather buffer list */ + int num_mapped_sgs; /* (internal) mapped sg entries */ int num_sgs; /* (in) number of entries in the sg list */ u32 transfer_buffer_length; /* (in) data buffer length */ u32 actual_length; /* (return) actual transfer length */ diff --git a/include/linux/usb/ch9.h b/include/linux/usb/ch9.h index 736203bb9ee..da974d9dcf2 100644 --- a/include/linux/usb/ch9.h +++ b/include/linux/usb/ch9.h @@ -587,8 +587,26 @@ struct usb_ss_ep_comp_descriptor { } __attribute__ ((packed)); #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)) +static inline int +usb_ss_max_streams(const struct usb_ss_ep_comp_descriptor *comp) +{ + int max_streams; + + if (!comp) + return 0; + + max_streams = comp->bmAttributes & 0x1f; + + if (!max_streams) + return 0; + + max_streams = 1 << max_streams; + + return max_streams; +} + /* Bits 1:0 of bmAttributes if this is an isoc endpoint */ #define USB_SS_MULT(p) (1 + ((p) & 0x3)) diff --git a/kernel/cgroup.c b/kernel/cgroup.c index 51767a005eb..d25134ee07b 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -1176,10 +1176,10 @@ static int parse_cgroupfs_options(char *data, struct cgroup_sb_opts *opts) /* * If the 'all' option was specified select all the subsystems, - * otherwise 'all, 'none' and a subsystem name options were not - * specified, let's default to 'all' + * otherwise if 'none', 'name=' and a subsystem name options + * were not specified, let's default to 'all' */ - if (all_ss || (!all_ss && !one_ss && !opts->none)) { + if (all_ss || (!one_ss && !opts->none && !opts->name)) { for (i = 0; i < CGROUP_SUBSYS_COUNT; i++) { struct cgroup_subsys *ss = subsys[i]; if (ss == NULL) diff --git a/kernel/cpu.c b/kernel/cpu.c index e0ce243ea20..6ce19c96a27 100644 --- a/kernel/cpu.c +++ b/kernel/cpu.c @@ -15,6 +15,7 @@ #include #include #include +#include #ifdef CONFIG_SMP /* Serializes the updates to cpu_online_mask, cpu_present_mask */ @@ -484,6 +485,79 @@ static int alloc_frozen_cpus(void) return 0; } core_initcall(alloc_frozen_cpus); + +/* + * Prevent regular CPU hotplug from racing with the freezer, by disabling CPU + * hotplug when tasks are about to be frozen. Also, don't allow the freezer + * to continue until any currently running CPU hotplug operation gets + * completed. + * To modify the 'cpu_hotplug_disabled' flag, we need to acquire the + * 'cpu_add_remove_lock'. And this same lock is also taken by the regular + * CPU hotplug path and released only after it is complete. Thus, we + * (and hence the freezer) will block here until any currently running CPU + * hotplug operation gets completed. + */ +void cpu_hotplug_disable_before_freeze(void) +{ + cpu_maps_update_begin(); + cpu_hotplug_disabled = 1; + cpu_maps_update_done(); +} + + +/* + * When tasks have been thawed, re-enable regular CPU hotplug (which had been + * disabled while beginning to freeze tasks). + */ +void cpu_hotplug_enable_after_thaw(void) +{ + cpu_maps_update_begin(); + cpu_hotplug_disabled = 0; + cpu_maps_update_done(); +} + +/* + * When callbacks for CPU hotplug notifications are being executed, we must + * ensure that the state of the system with respect to the tasks being frozen + * or not, as reported by the notification, remains unchanged *throughout the + * duration* of the execution of the callbacks. + * Hence we need to prevent the freezer from racing with regular CPU hotplug. + * + * This synchronization is implemented by mutually excluding regular CPU + * hotplug and Suspend/Hibernate call paths by hooking onto the Suspend/ + * Hibernate notifications. + */ +static int +cpu_hotplug_pm_callback(struct notifier_block *nb, + unsigned long action, void *ptr) +{ + switch (action) { + + case PM_SUSPEND_PREPARE: + case PM_HIBERNATION_PREPARE: + cpu_hotplug_disable_before_freeze(); + break; + + case PM_POST_SUSPEND: + case PM_POST_HIBERNATION: + cpu_hotplug_enable_after_thaw(); + break; + + default: + return NOTIFY_DONE; + } + + return NOTIFY_OK; +} + + +int cpu_hotplug_pm_sync_init(void) +{ + pm_notifier(cpu_hotplug_pm_callback, 0); + return 0; +} +core_initcall(cpu_hotplug_pm_sync_init); + #endif /* CONFIG_PM_SLEEP_SMP */ /** diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c index d577199eabd..e0d42dbb33f 100644 --- a/net/ipv4/igmp.c +++ b/net/ipv4/igmp.c @@ -875,6 +875,8 @@ static void igmp_heard_query(struct in_device *in_dev, struct sk_buff *skb, * to be intended in a v3 query. */ max_delay = IGMPV3_MRC(ih3->code)*(HZ/IGMP_TIMER_SCALE); + if (!max_delay) + max_delay = 1; /* can't mod w/ 0 */ } else { /* v3 */ if (!pskb_may_pull(skb, sizeof(struct igmpv3_query))) return; diff --git a/tools/perf/util/trace-event-parse.c b/tools/perf/util/trace-event-parse.c index 6c164dc9ee9..bf54c48871d 100644 --- a/tools/perf/util/trace-event-parse.c +++ b/tools/perf/util/trace-event-parse.c @@ -1582,6 +1582,8 @@ process_symbols(struct event *event, struct print_arg *arg, char **tok) field = malloc_or_die(sizeof(*field)); type = process_arg(event, field, &token); + while (type == EVENT_OP) + type = process_op(event, field, &token); if (test_type_token(type, token, EVENT_DELIM, ",")) goto out_free; From f86f1b50936dba51080e866ec990d02e2ea07aa2 Mon Sep 17 00:00:00 2001 From: Ethan Chen Date: Sun, 2 Sep 2012 17:38:31 -0700 Subject: [PATCH 022/111] Linux 3.0.18 --- Makefile | 2 +- arch/ia64/kernel/acpi.c | 10 +- arch/score/kernel/entry.S | 2 +- arch/x86/include/asm/amd_nb.h | 2 + arch/x86/kernel/amd_nb.c | 31 ++++ arch/x86/kernel/apic/x2apic_uv_x.c | 7 +- arch/x86/mm/mmap.c | 4 +- arch/x86/mm/srat.c | 4 + arch/x86/pci/Makefile | 3 +- arch/x86/pci/acpi.c | 18 ++- arch/x86/pci/amd_bus.c | 42 ++---- arch/x86/platform/uv/tlb_uv.c | 13 +- block/scsi_ioctl.c | 52 +++++++ drivers/acpi/acpica/dsargs.c | 24 +++ drivers/acpi/numa.c | 6 + drivers/acpi/processor_core.c | 26 +++- drivers/block/cciss.c | 6 +- drivers/block/ub.c | 3 +- drivers/block/virtio_blk.c | 4 +- drivers/cdrom/cdrom.c | 3 +- drivers/gpu/drm/radeon/r100.c | 5 +- drivers/gpu/drm/radeon/r600_hdmi.c | 7 + drivers/gpu/drm/radeon/radeon_device.c | 5 +- drivers/gpu/drm/radeon/rs600.c | 4 +- drivers/hid/hid-core.c | 14 +- drivers/hid/hid-ids.h | 14 +- drivers/hid/hid-multitouch.c | 32 ++-- drivers/i2c/busses/i2c-ali1535.c | 11 +- drivers/i2c/busses/i2c-eg20t.c | 2 +- drivers/i2c/busses/i2c-nforce2.c | 2 +- drivers/i2c/busses/i2c-omap.c | 2 +- drivers/i2c/busses/i2c-sis5595.c | 4 +- drivers/i2c/busses/i2c-sis630.c | 6 +- drivers/i2c/busses/i2c-viapro.c | 7 +- drivers/ide/ide-floppy_ioctl.c | 3 +- drivers/idle/intel_idle.c | 9 +- drivers/md/dm-flakey.c | 11 +- drivers/md/dm-linear.c | 12 +- drivers/md/dm-mpath.c | 6 + drivers/media/video/uvc/uvc_v4l2.c | 9 ++ drivers/media/video/uvc/uvcvideo.h | 1 + drivers/media/video/v4l2-ioctl.c | 4 + drivers/mmc/host/sdhci.c | 6 +- drivers/mtd/mtd_blkdevs.c | 3 +- drivers/mtd/mtdoops.c | 5 +- drivers/mtd/tests/mtd_stresstest.c | 7 + drivers/mtd/ubi/cdev.c | 3 + drivers/mtd/ubi/debug.h | 5 +- drivers/mtd/ubi/eba.c | 6 +- drivers/mtd/ubi/ubi.h | 2 + drivers/mtd/ubi/wl.c | 12 +- drivers/net/phy/mdio-gpio.c | 2 +- drivers/net/wireless/iwlegacy/iwl3945-base.c | 9 +- drivers/net/wireless/iwlwifi/iwl-agn-lib.c | 1 + drivers/net/wireless/iwlwifi/iwl-agn-rxon.c | 3 + drivers/net/wireless/rt2x00/rt2800pci.c | 28 +--- drivers/net/wireless/rtlwifi/rtl8192se/fw.c | 4 + drivers/pci/msi.c | 10 ++ drivers/pnp/quirks.c | 42 ++++++ drivers/rtc/interface.c | 4 +- drivers/scsi/mpt2sas/mpt2sas_base.c | 83 ++++------- drivers/scsi/mpt2sas/mpt2sas_scsih.c | 11 +- drivers/scsi/sd.c | 13 +- drivers/scsi/sym53c8xx_2/sym_glue.c | 4 + drivers/target/target_core_cdb.c | 12 ++ drivers/target/target_core_transport.c | 14 ++ drivers/xen/xenbus/xenbus_xs.c | 6 + fs/dcache.c | 14 +- fs/ext4/super.c | 7 +- fs/nfs/callback_proc.c | 2 +- fs/nfs/objlayout/objio_osd.c | 3 +- fs/nfs/objlayout/objlayout.c | 10 +- fs/nfs/pnfs.c | 12 ++ fs/nfs/pnfs.h | 1 + fs/nfs/super.c | 43 +++--- fs/nfsd/export.c | 2 +- fs/notify/mark.c | 8 +- fs/proc/base.c | 145 +++++-------------- fs/proc/task_mmu.c | 3 + fs/proc/uptime.c | 9 +- fs/ubifs/debug.h | 17 ++- include/acpi/acpi_numa.h | 1 + include/linux/blkdev.h | 3 + include/linux/dcache.h | 1 + include/linux/memcontrol.h | 6 + include/linux/pci_regs.h | 2 +- include/linux/sunrpc/svcsock.h | 2 +- include/linux/videodev2.h | 1 + include/target/target_core_base.h | 1 + include/xen/interface/io/xs_wire.h | 3 + init/do_mounts.c | 35 ++++- kernel/kprobes.c | 2 +- mm/filemap.c | 18 +-- mm/memcontrol.c | 44 ++++++ mm/page_alloc.c | 11 ++ net/ipv4/ah4.c | 6 +- net/ipv6/ah6.c | 6 +- net/mac80211/wpa.c | 2 +- net/sunrpc/svc.c | 20 ++- net/sunrpc/svc_xprt.c | 53 ++++--- scripts/kconfig/streamline_config.pl | 52 +++++-- scripts/recordmcount.h | 2 +- security/integrity/ima/ima_api.c | 4 +- security/integrity/ima/ima_queue.c | 17 ++- sound/pci/hda/hda_local.h | 7 +- sound/pci/hda/hda_proc.c | 2 + sound/pci/hda/patch_sigmatel.c | 2 +- sound/pci/ice1712/amp.c | 7 +- sound/pci/oxygen/xonar_wm87x6.c | 1 + sound/usb/usx2y/usb_stream.c | 6 +- 110 files changed, 868 insertions(+), 452 deletions(-) diff --git a/Makefile b/Makefile index 5041de04f6b..ccc9f39b2fc 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ VERSION = 3 PATCHLEVEL = 0 -SUBLEVEL = 17 +SUBLEVEL = 18 EXTRAVERSION = NAME = Sneaky Weasel diff --git a/arch/ia64/kernel/acpi.c b/arch/ia64/kernel/acpi.c index 3be485a300b..f19de9f7f5f 100644 --- a/arch/ia64/kernel/acpi.c +++ b/arch/ia64/kernel/acpi.c @@ -429,22 +429,24 @@ static u32 __devinitdata pxm_flag[PXM_FLAG_LEN]; static struct acpi_table_slit __initdata *slit_table; cpumask_t early_cpu_possible_map = CPU_MASK_NONE; -static int get_processor_proximity_domain(struct acpi_srat_cpu_affinity *pa) +static int __init +get_processor_proximity_domain(struct acpi_srat_cpu_affinity *pa) { int pxm; pxm = pa->proximity_domain_lo; - if (ia64_platform_is("sn2")) + if (ia64_platform_is("sn2") || acpi_srat_revision >= 2) pxm += pa->proximity_domain_hi[0] << 8; return pxm; } -static int get_memory_proximity_domain(struct acpi_srat_mem_affinity *ma) +static int __init +get_memory_proximity_domain(struct acpi_srat_mem_affinity *ma) { int pxm; pxm = ma->proximity_domain; - if (!ia64_platform_is("sn2")) + if (!ia64_platform_is("sn2") && acpi_srat_revision <= 1) pxm &= 0xff; return pxm; diff --git a/arch/score/kernel/entry.S b/arch/score/kernel/entry.S index 577abba3fac..83bb96079c4 100644 --- a/arch/score/kernel/entry.S +++ b/arch/score/kernel/entry.S @@ -408,7 +408,7 @@ ENTRY(handle_sys) sw r9, [r0, PT_EPC] cmpi.c r27, __NR_syscalls # check syscall number - bgtu illegal_syscall + bgeu illegal_syscall slli r8, r27, 2 # get syscall routine la r11, sys_call_table diff --git a/arch/x86/include/asm/amd_nb.h b/arch/x86/include/asm/amd_nb.h index 67f87f25761..78a1eff7422 100644 --- a/arch/x86/include/asm/amd_nb.h +++ b/arch/x86/include/asm/amd_nb.h @@ -1,6 +1,7 @@ #ifndef _ASM_X86_AMD_NB_H #define _ASM_X86_AMD_NB_H +#include #include struct amd_nb_bus_dev_range { @@ -13,6 +14,7 @@ extern const struct pci_device_id amd_nb_misc_ids[]; extern const struct amd_nb_bus_dev_range amd_nb_bus_dev_ranges[]; extern bool early_is_amd_nb(u32 value); +extern struct resource *amd_get_mmconfig_range(struct resource *res); extern int amd_cache_northbridges(void); extern void amd_flush_garts(void); extern int amd_numa_init(void); diff --git a/arch/x86/kernel/amd_nb.c b/arch/x86/kernel/amd_nb.c index 4c39baa8fac..bae1efe6d51 100644 --- a/arch/x86/kernel/amd_nb.c +++ b/arch/x86/kernel/amd_nb.c @@ -119,6 +119,37 @@ bool __init early_is_amd_nb(u32 device) return false; } +struct resource *amd_get_mmconfig_range(struct resource *res) +{ + u32 address; + u64 base, msr; + unsigned segn_busn_bits; + + if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD) + return NULL; + + /* assume all cpus from fam10h have mmconfig */ + if (boot_cpu_data.x86 < 0x10) + return NULL; + + address = MSR_FAM10H_MMIO_CONF_BASE; + rdmsrl(address, msr); + + /* mmconfig is not enabled */ + if (!(msr & FAM10H_MMIO_CONF_ENABLE)) + return NULL; + + base = msr & (FAM10H_MMIO_CONF_BASE_MASK<> FAM10H_MMIO_CONF_BUSRANGE_SHIFT) & + FAM10H_MMIO_CONF_BUSRANGE_MASK; + + res->flags = IORESOURCE_MEM; + res->start = base; + res->end = base + (1ULL<<(segn_busn_bits + 20)) - 1; + return res; +} + int amd_get_subcaches(int cpu) { struct pci_dev *link = node_to_amd_nb(amd_get_nb_id(cpu))->link; diff --git a/arch/x86/kernel/apic/x2apic_uv_x.c b/arch/x86/kernel/apic/x2apic_uv_x.c index cfeb978f49f..874c2087714 100644 --- a/arch/x86/kernel/apic/x2apic_uv_x.c +++ b/arch/x86/kernel/apic/x2apic_uv_x.c @@ -779,7 +779,12 @@ void __init uv_system_init(void) for(i = 0; i < UVH_NODE_PRESENT_TABLE_DEPTH; i++) uv_possible_blades += hweight64(uv_read_local_mmr( UVH_NODE_PRESENT_TABLE + i * 8)); - printk(KERN_DEBUG "UV: Found %d blades\n", uv_num_possible_blades()); + + /* uv_num_possible_blades() is really the hub count */ + printk(KERN_INFO "UV: Found %d blades, %d hubs\n", + is_uv1_hub() ? uv_num_possible_blades() : + (uv_num_possible_blades() + 1) / 2, + uv_num_possible_blades()); bytes = sizeof(struct uv_blade_info) * uv_num_possible_blades(); uv_blade_info = kzalloc(bytes, GFP_KERNEL); diff --git a/arch/x86/mm/mmap.c b/arch/x86/mm/mmap.c index 1dab5194fd9..f927429d07c 100644 --- a/arch/x86/mm/mmap.c +++ b/arch/x86/mm/mmap.c @@ -87,9 +87,9 @@ static unsigned long mmap_rnd(void) */ if (current->flags & PF_RANDOMIZE) { if (mmap_is_ia32()) - rnd = (long)get_random_int() % (1<<8); + rnd = get_random_int() % (1<<8); else - rnd = (long)(get_random_int() % (1<<28)); + rnd = get_random_int() % (1<<28); } return rnd << PAGE_SHIFT; } diff --git a/arch/x86/mm/srat.c b/arch/x86/mm/srat.c index 81dbfdeb080..7efd0c615d5 100644 --- a/arch/x86/mm/srat.c +++ b/arch/x86/mm/srat.c @@ -104,6 +104,8 @@ acpi_numa_processor_affinity_init(struct acpi_srat_cpu_affinity *pa) if ((pa->flags & ACPI_SRAT_CPU_ENABLED) == 0) return; pxm = pa->proximity_domain_lo; + if (acpi_srat_revision >= 2) + pxm |= *((unsigned int*)pa->proximity_domain_hi) << 8; node = setup_node(pxm); if (node < 0) { printk(KERN_ERR "SRAT: Too many proximity domains %x\n", pxm); @@ -155,6 +157,8 @@ acpi_numa_memory_affinity_init(struct acpi_srat_mem_affinity *ma) start = ma->base_address; end = start + ma->length; pxm = ma->proximity_domain; + if (acpi_srat_revision <= 1) + pxm &= 0xff; node = setup_node(pxm); if (node < 0) { printk(KERN_ERR "SRAT: Too many proximity domains.\n"); diff --git a/arch/x86/pci/Makefile b/arch/x86/pci/Makefile index 6b8759f7634..d24d3da7292 100644 --- a/arch/x86/pci/Makefile +++ b/arch/x86/pci/Makefile @@ -18,8 +18,9 @@ obj-$(CONFIG_X86_NUMAQ) += numaq_32.o obj-$(CONFIG_X86_MRST) += mrst.o obj-y += common.o early.o -obj-y += amd_bus.o bus_numa.o +obj-y += bus_numa.o +obj-$(CONFIG_AMD_NB) += amd_bus.o obj-$(CONFIG_PCI_CNB20LE_QUIRK) += broadcom_bus.o ifeq ($(CONFIG_PCI_DEBUG),y) diff --git a/arch/x86/pci/acpi.c b/arch/x86/pci/acpi.c index 50b3f14c59a..53f9e684c81 100644 --- a/arch/x86/pci/acpi.c +++ b/arch/x86/pci/acpi.c @@ -149,7 +149,7 @@ setup_resource(struct acpi_resource *acpi_res, void *data) struct acpi_resource_address64 addr; acpi_status status; unsigned long flags; - u64 start, end; + u64 start, orig_end, end; status = resource_to_addr(acpi_res, &addr); if (!ACPI_SUCCESS(status)) @@ -165,7 +165,21 @@ setup_resource(struct acpi_resource *acpi_res, void *data) return AE_OK; start = addr.minimum + addr.translation_offset; - end = addr.maximum + addr.translation_offset; + orig_end = end = addr.maximum + addr.translation_offset; + + /* Exclude non-addressable range or non-addressable portion of range */ + end = min(end, (u64)iomem_resource.end); + if (end <= start) { + dev_info(&info->bridge->dev, + "host bridge window [%#llx-%#llx] " + "(ignored, not CPU addressable)\n", start, orig_end); + return AE_OK; + } else if (orig_end != end) { + dev_info(&info->bridge->dev, + "host bridge window [%#llx-%#llx] " + "([%#llx-%#llx] ignored, not CPU addressable)\n", + start, orig_end, end + 1, orig_end); + } res = &info->res[info->res_num]; res->name = info->name; diff --git a/arch/x86/pci/amd_bus.c b/arch/x86/pci/amd_bus.c index 026e4931d16..385a940b542 100644 --- a/arch/x86/pci/amd_bus.c +++ b/arch/x86/pci/amd_bus.c @@ -30,34 +30,6 @@ static struct pci_hostbridge_probe pci_probes[] __initdata = { { 0, 0x18, PCI_VENDOR_ID_AMD, 0x1300 }, }; -static u64 __initdata fam10h_mmconf_start; -static u64 __initdata fam10h_mmconf_end; -static void __init get_pci_mmcfg_amd_fam10h_range(void) -{ - u32 address; - u64 base, msr; - unsigned segn_busn_bits; - - /* assume all cpus from fam10h have mmconf */ - if (boot_cpu_data.x86 < 0x10) - return; - - address = MSR_FAM10H_MMIO_CONF_BASE; - rdmsrl(address, msr); - - /* mmconfig is not enable */ - if (!(msr & FAM10H_MMIO_CONF_ENABLE)) - return; - - base = msr & (FAM10H_MMIO_CONF_BASE_MASK<> FAM10H_MMIO_CONF_BUSRANGE_SHIFT) & - FAM10H_MMIO_CONF_BUSRANGE_MASK; - - fam10h_mmconf_start = base; - fam10h_mmconf_end = base + (1ULL<<(segn_busn_bits + 20)) - 1; -} - #define RANGE_NUM 16 /** @@ -85,6 +57,9 @@ static int __init early_fill_mp_bus_info(void) u64 val; u32 address; bool found; + struct resource fam10h_mmconf_res, *fam10h_mmconf; + u64 fam10h_mmconf_start; + u64 fam10h_mmconf_end; if (!early_pci_allowed()) return -1; @@ -211,12 +186,17 @@ static int __init early_fill_mp_bus_info(void) subtract_range(range, RANGE_NUM, 0, end); /* get mmconfig */ - get_pci_mmcfg_amd_fam10h_range(); + fam10h_mmconf = amd_get_mmconfig_range(&fam10h_mmconf_res); /* need to take out mmconf range */ - if (fam10h_mmconf_end) { - printk(KERN_DEBUG "Fam 10h mmconf [%llx, %llx]\n", fam10h_mmconf_start, fam10h_mmconf_end); + if (fam10h_mmconf) { + printk(KERN_DEBUG "Fam 10h mmconf %pR\n", fam10h_mmconf); + fam10h_mmconf_start = fam10h_mmconf->start; + fam10h_mmconf_end = fam10h_mmconf->end; subtract_range(range, RANGE_NUM, fam10h_mmconf_start, fam10h_mmconf_end + 1); + } else { + fam10h_mmconf_start = 0; + fam10h_mmconf_end = 0; } /* mmio resource */ diff --git a/arch/x86/platform/uv/tlb_uv.c b/arch/x86/platform/uv/tlb_uv.c index 82cff4a25f4..edf435b74e8 100644 --- a/arch/x86/platform/uv/tlb_uv.c +++ b/arch/x86/platform/uv/tlb_uv.c @@ -1575,14 +1575,14 @@ static int calculate_destination_timeout(void) ts_ns = base * mult1 * mult2; ret = ts_ns / 1000; } else { - /* 4 bits 0/1 for 10/80us, 3 bits of multiplier */ - mmr_image = uv_read_local_mmr(UVH_AGING_PRESCALE_SEL); + /* 4 bits 0/1 for 10/80us base, 3 bits of multiplier */ + mmr_image = uv_read_local_mmr(UVH_LB_BAU_MISC_CONTROL); mmr_image = (mmr_image & UV_SA_MASK) >> UV_SA_SHFT; if (mmr_image & (1L << UV2_ACK_UNITS_SHFT)) - mult1 = 80; + base = 80; else - mult1 = 10; - base = mmr_image & UV2_ACK_MASK; + base = 10; + mult1 = mmr_image & UV2_ACK_MASK; ret = mult1 * base; } return ret; @@ -1820,6 +1820,8 @@ static int __init uv_bau_init(void) uv_base_pnode = uv_blade_to_pnode(uvhub); } + enable_timeouts(); + if (init_per_cpu(nuvhubs, uv_base_pnode)) { nobau = 1; return 0; @@ -1830,7 +1832,6 @@ static int __init uv_bau_init(void) if (uv_blade_nr_possible_cpus(uvhub)) init_uvhub(uvhub, vector, uv_base_pnode); - enable_timeouts(); alloc_intr_gate(vector, uv_bau_message_intr1); for_each_possible_blade(uvhub) { diff --git a/block/scsi_ioctl.c b/block/scsi_ioctl.c index 4f4230b79bb..5ef1f4c17e6 100644 --- a/block/scsi_ioctl.c +++ b/block/scsi_ioctl.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -691,6 +692,57 @@ int scsi_cmd_ioctl(struct request_queue *q, struct gendisk *bd_disk, fmode_t mod } EXPORT_SYMBOL(scsi_cmd_ioctl); +int scsi_verify_blk_ioctl(struct block_device *bd, unsigned int cmd) +{ + if (bd && bd == bd->bd_contains) + return 0; + + /* Actually none of these is particularly useful on a partition, + * but they are safe. + */ + switch (cmd) { + case SCSI_IOCTL_GET_IDLUN: + case SCSI_IOCTL_GET_BUS_NUMBER: + case SCSI_IOCTL_GET_PCI: + case SCSI_IOCTL_PROBE_HOST: + case SG_GET_VERSION_NUM: + case SG_SET_TIMEOUT: + case SG_GET_TIMEOUT: + case SG_GET_RESERVED_SIZE: + case SG_SET_RESERVED_SIZE: + case SG_EMULATED_HOST: + return 0; + case CDROM_GET_CAPABILITY: + /* Keep this until we remove the printk below. udev sends it + * and we do not want to spam dmesg about it. CD-ROMs do + * not have partitions, so we get here only for disks. + */ + return -ENOTTY; + default: + break; + } + + /* In particular, rule out all resets and host-specific ioctls. */ + printk_ratelimited(KERN_WARNING + "%s: sending ioctl %x to a partition!\n", current->comm, cmd); + + return capable(CAP_SYS_RAWIO) ? 0 : -ENOTTY; +} +EXPORT_SYMBOL(scsi_verify_blk_ioctl); + +int scsi_cmd_blk_ioctl(struct block_device *bd, fmode_t mode, + unsigned int cmd, void __user *arg) +{ + int ret; + + ret = scsi_verify_blk_ioctl(bd, cmd); + if (ret < 0) + return ret; + + return scsi_cmd_ioctl(bd->bd_disk->queue, bd->bd_disk, mode, cmd, arg); +} +EXPORT_SYMBOL(scsi_cmd_blk_ioctl); + static int __init blk_scsi_ioctl_init(void) { blk_set_cmd_filter_defaults(&blk_default_cmd_filter); diff --git a/drivers/acpi/acpica/dsargs.c b/drivers/acpi/acpica/dsargs.c index 8c7b99728aa..42163d8db50 100644 --- a/drivers/acpi/acpica/dsargs.c +++ b/drivers/acpi/acpica/dsargs.c @@ -387,5 +387,29 @@ acpi_status acpi_ds_get_region_arguments(union acpi_operand_object *obj_desc) status = acpi_ds_execute_arguments(node, node->parent, extra_desc->extra.aml_length, extra_desc->extra.aml_start); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* Validate the region address/length via the host OS */ + + status = acpi_os_validate_address(obj_desc->region.space_id, + obj_desc->region.address, + (acpi_size) obj_desc->region.length, + acpi_ut_get_node_name(node)); + + if (ACPI_FAILURE(status)) { + /* + * Invalid address/length. We will emit an error message and mark + * the region as invalid, so that it will cause an additional error if + * it is ever used. Then return AE_OK. + */ + ACPI_EXCEPTION((AE_INFO, status, + "During address validation of OpRegion [%4.4s]", + node->name.ascii)); + obj_desc->common.flags |= AOPOBJ_INVALID; + status = AE_OK; + } + return_ACPI_STATUS(status); } diff --git a/drivers/acpi/numa.c b/drivers/acpi/numa.c index 3b5c3189fd9..e56f3be7b07 100644 --- a/drivers/acpi/numa.c +++ b/drivers/acpi/numa.c @@ -45,6 +45,8 @@ static int pxm_to_node_map[MAX_PXM_DOMAINS] static int node_to_pxm_map[MAX_NUMNODES] = { [0 ... MAX_NUMNODES - 1] = PXM_INVAL }; +unsigned char acpi_srat_revision __initdata; + int pxm_to_node(int pxm) { if (pxm < 0) @@ -255,9 +257,13 @@ acpi_parse_memory_affinity(struct acpi_subtable_header * header, static int __init acpi_parse_srat(struct acpi_table_header *table) { + struct acpi_table_srat *srat; if (!table) return -EINVAL; + srat = (struct acpi_table_srat *)table; + acpi_srat_revision = srat->header.revision; + /* Real work done in acpi_table_parse_srat below. */ return 0; diff --git a/drivers/acpi/processor_core.c b/drivers/acpi/processor_core.c index 02d2a4c9084..0c0669fb1cc 100644 --- a/drivers/acpi/processor_core.c +++ b/drivers/acpi/processor_core.c @@ -172,8 +172,30 @@ int acpi_get_cpuid(acpi_handle handle, int type, u32 acpi_id) apic_id = map_mat_entry(handle, type, acpi_id); if (apic_id == -1) apic_id = map_madt_entry(type, acpi_id); - if (apic_id == -1) - return apic_id; + if (apic_id == -1) { + /* + * On UP processor, there is no _MAT or MADT table. + * So above apic_id is always set to -1. + * + * BIOS may define multiple CPU handles even for UP processor. + * For example, + * + * Scope (_PR) + * { + * Processor (CPU0, 0x00, 0x00000410, 0x06) {} + * Processor (CPU1, 0x01, 0x00000410, 0x06) {} + * Processor (CPU2, 0x02, 0x00000410, 0x06) {} + * Processor (CPU3, 0x03, 0x00000410, 0x06) {} + * } + * + * Ignores apic_id and always return 0 for CPU0's handle. + * Return -1 for other CPU's handle. + */ + if (acpi_id == 0) + return acpi_id; + else + return apic_id; + } #ifdef CONFIG_SMP for_each_possible_cpu(i) { diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c index c2f9b3e3dec..1dab802d82b 100644 --- a/drivers/block/cciss.c +++ b/drivers/block/cciss.c @@ -1716,7 +1716,7 @@ static int cciss_ioctl(struct block_device *bdev, fmode_t mode, case CCISS_BIG_PASSTHRU: return cciss_bigpassthru(h, argp); - /* scsi_cmd_ioctl handles these, below, though some are not */ + /* scsi_cmd_blk_ioctl handles these, below, though some are not */ /* very meaningful for cciss. SG_IO is the main one people want. */ case SG_GET_VERSION_NUM: @@ -1727,9 +1727,9 @@ static int cciss_ioctl(struct block_device *bdev, fmode_t mode, case SG_EMULATED_HOST: case SG_IO: case SCSI_IOCTL_SEND_COMMAND: - return scsi_cmd_ioctl(disk->queue, disk, mode, cmd, argp); + return scsi_cmd_blk_ioctl(bdev, mode, cmd, argp); - /* scsi_cmd_ioctl would normally handle these, below, but */ + /* scsi_cmd_blk_ioctl would normally handle these, below, but */ /* they aren't a good fit for cciss, as CD-ROMs are */ /* not supported, and we don't have any bus/target/lun */ /* which we present to the kernel. */ diff --git a/drivers/block/ub.c b/drivers/block/ub.c index 0e376d46bdd..7333b9e4441 100644 --- a/drivers/block/ub.c +++ b/drivers/block/ub.c @@ -1744,12 +1744,11 @@ static int ub_bd_release(struct gendisk *disk, fmode_t mode) static int ub_bd_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd, unsigned long arg) { - struct gendisk *disk = bdev->bd_disk; void __user *usermem = (void __user *) arg; int ret; mutex_lock(&ub_mutex); - ret = scsi_cmd_ioctl(disk->queue, disk, mode, cmd, usermem); + ret = scsi_cmd_blk_ioctl(bdev, mode, cmd, usermem); mutex_unlock(&ub_mutex); return ret; diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c index 079c08808d8..5d7a9340363 100644 --- a/drivers/block/virtio_blk.c +++ b/drivers/block/virtio_blk.c @@ -236,8 +236,8 @@ static int virtblk_ioctl(struct block_device *bdev, fmode_t mode, if (!virtio_has_feature(vblk->vdev, VIRTIO_BLK_F_SCSI)) return -ENOTTY; - return scsi_cmd_ioctl(disk->queue, disk, mode, cmd, - (void __user *)data); + return scsi_cmd_blk_ioctl(bdev, mode, cmd, + (void __user *)data); } /* We provide getgeo only to please some old bootloader/partitioning tools */ diff --git a/drivers/cdrom/cdrom.c b/drivers/cdrom/cdrom.c index 75fb965b8f7..b693cbdb421 100644 --- a/drivers/cdrom/cdrom.c +++ b/drivers/cdrom/cdrom.c @@ -2741,12 +2741,11 @@ int cdrom_ioctl(struct cdrom_device_info *cdi, struct block_device *bdev, { void __user *argp = (void __user *)arg; int ret; - struct gendisk *disk = bdev->bd_disk; /* * Try the generic SCSI command ioctl's first. */ - ret = scsi_cmd_ioctl(disk->queue, disk, mode, cmd, argp); + ret = scsi_cmd_blk_ioctl(bdev, mode, cmd, argp); if (ret != -ENOTTY) return ret; diff --git a/drivers/gpu/drm/radeon/r100.c b/drivers/gpu/drm/radeon/r100.c index b94d871487e..764249587f1 100644 --- a/drivers/gpu/drm/radeon/r100.c +++ b/drivers/gpu/drm/radeon/r100.c @@ -2069,6 +2069,7 @@ bool r100_gpu_is_lockup(struct radeon_device *rdev) void r100_bm_disable(struct radeon_device *rdev) { u32 tmp; + u16 tmp16; /* disable bus mastering */ tmp = RREG32(R_000030_BUS_CNTL); @@ -2079,8 +2080,8 @@ void r100_bm_disable(struct radeon_device *rdev) WREG32(R_000030_BUS_CNTL, (tmp & 0xFFFFFFFF) | 0x00000040); tmp = RREG32(RADEON_BUS_CNTL); mdelay(1); - pci_read_config_word(rdev->pdev, 0x4, (u16*)&tmp); - pci_write_config_word(rdev->pdev, 0x4, tmp & 0xFFFB); + pci_read_config_word(rdev->pdev, 0x4, &tmp16); + pci_write_config_word(rdev->pdev, 0x4, tmp16 & 0xFFFB); mdelay(1); } diff --git a/drivers/gpu/drm/radeon/r600_hdmi.c b/drivers/gpu/drm/radeon/r600_hdmi.c index f5ac7e788d8..c45d92191fd 100644 --- a/drivers/gpu/drm/radeon/r600_hdmi.c +++ b/drivers/gpu/drm/radeon/r600_hdmi.c @@ -196,6 +196,13 @@ static void r600_hdmi_videoinfoframe( frame[0xD] = (right_bar >> 8); r600_hdmi_infoframe_checksum(0x82, 0x02, 0x0D, frame); + /* Our header values (type, version, length) should be alright, Intel + * is using the same. Checksum function also seems to be OK, it works + * fine for audio infoframe. However calculated value is always lower + * by 2 in comparison to fglrx. It breaks displaying anything in case + * of TVs that strictly check the checksum. Hack it manually here to + * workaround this issue. */ + frame[0x0] += 2; WREG32(offset+R600_HDMI_VIDEOINFOFRAME_0, frame[0x0] | (frame[0x1] << 8) | (frame[0x2] << 16) | (frame[0x3] << 24)); diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c index 440e6ecccc4..5d0c1236dd4 100644 --- a/drivers/gpu/drm/radeon/radeon_device.c +++ b/drivers/gpu/drm/radeon/radeon_device.c @@ -223,8 +223,11 @@ int radeon_wb_init(struct radeon_device *rdev) if (radeon_no_wb == 1) rdev->wb.enabled = false; else { - /* often unreliable on AGP */ if (rdev->flags & RADEON_IS_AGP) { + /* often unreliable on AGP */ + rdev->wb.enabled = false; + } else if (rdev->family < CHIP_R300) { + /* often unreliable on pre-r300 */ rdev->wb.enabled = false; } else { rdev->wb.enabled = true; diff --git a/drivers/gpu/drm/radeon/rs600.c b/drivers/gpu/drm/radeon/rs600.c index a37a1efdd22..21acfb5449a 100644 --- a/drivers/gpu/drm/radeon/rs600.c +++ b/drivers/gpu/drm/radeon/rs600.c @@ -324,10 +324,10 @@ void rs600_hpd_fini(struct radeon_device *rdev) void rs600_bm_disable(struct radeon_device *rdev) { - u32 tmp; + u16 tmp; /* disable bus mastering */ - pci_read_config_word(rdev->pdev, 0x4, (u16*)&tmp); + pci_read_config_word(rdev->pdev, 0x4, &tmp); pci_write_config_word(rdev->pdev, 0x4, tmp & 0xFFFB); mdelay(1); } diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 6c2a31593a2..606fecb8c76 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -361,7 +361,7 @@ static int hid_parser_global(struct hid_parser *parser, struct hid_item *item) case HID_GLOBAL_ITEM_TAG_REPORT_SIZE: parser->global.report_size = item_udata(item); - if (parser->global.report_size > 32) { + if (parser->global.report_size > 96) { dbg_hid("invalid report_size %d\n", parser->global.report_size); return -1; @@ -1382,11 +1382,13 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_TRUETOUCH) }, { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, 0x0006) }, { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, 0x0011) }, - { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH) }, - { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH1) }, - { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH2) }, - { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH3) }, - { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH4) }, + { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_480D) }, + { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_480E) }, + { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_720C) }, + { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_726B) }, + { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72A1) }, + { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7302) }, + { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_A001) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_BM084) }, { HID_USB_DEVICE(USB_VENDOR_ID_ELO, USB_DEVICE_ID_ELO_TS2515) }, { HID_USB_DEVICE(USB_VENDOR_ID_EMS, USB_DEVICE_ID_EMS_TRIO_LINKER_PLUS_II) }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 206f75021d5..e0a28ade952 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -21,6 +21,7 @@ #define USB_VENDOR_ID_3M 0x0596 #define USB_DEVICE_ID_3M1968 0x0500 #define USB_DEVICE_ID_3M2256 0x0502 +#define USB_DEVICE_ID_3M3266 0x0506 #define USB_VENDOR_ID_A4TECH 0x09da #define USB_DEVICE_ID_A4TECH_WCP32PU 0x0006 @@ -230,11 +231,14 @@ #define USB_VENDOR_ID_DWAV 0x0eef #define USB_DEVICE_ID_EGALAX_TOUCHCONTROLLER 0x0001 -#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH 0x480d -#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH1 0x720c -#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH2 0x72a1 -#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH3 0x480e -#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH4 0x726b +#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_480D 0x480d +#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_480E 0x480e +#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_720C 0x720c +#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_726B 0x726b +#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72A1 0x72a1 +#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72FA 0x72fa +#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7302 0x7302 +#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_A001 0xa001 #define USB_VENDOR_ID_ELECOM 0x056e #define USB_DEVICE_ID_ELECOM_BM084 0x0061 diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 685d8e42587..1308703afdb 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -593,6 +593,9 @@ static const struct hid_device_id mt_devices[] = { { .driver_data = MT_CLS_3M, HID_USB_DEVICE(USB_VENDOR_ID_3M, USB_DEVICE_ID_3M2256) }, + { .driver_data = MT_CLS_3M, + HID_USB_DEVICE(USB_VENDOR_ID_3M, + USB_DEVICE_ID_3M3266) }, /* ActionStar panels */ { .driver_data = MT_CLS_DEFAULT, @@ -629,23 +632,32 @@ static const struct hid_device_id mt_devices[] = { USB_DEVICE_ID_CYPRESS_TRUETOUCH) }, /* eGalax devices (resistive) */ - { .driver_data = MT_CLS_EGALAX, + { .driver_data = MT_CLS_EGALAX, HID_USB_DEVICE(USB_VENDOR_ID_DWAV, - USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH) }, - { .driver_data = MT_CLS_EGALAX, + USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_480D) }, + { .driver_data = MT_CLS_EGALAX, HID_USB_DEVICE(USB_VENDOR_ID_DWAV, - USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH3) }, + USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_480E) }, /* eGalax devices (capacitive) */ - { .driver_data = MT_CLS_EGALAX, + { .driver_data = MT_CLS_EGALAX, + HID_USB_DEVICE(USB_VENDOR_ID_DWAV, + USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_720C) }, + { .driver_data = MT_CLS_EGALAX, + HID_USB_DEVICE(USB_VENDOR_ID_DWAV, + USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_726B) }, + { .driver_data = MT_CLS_EGALAX, + HID_USB_DEVICE(USB_VENDOR_ID_DWAV, + USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72A1) }, + { .driver_data = MT_CLS_EGALAX, HID_USB_DEVICE(USB_VENDOR_ID_DWAV, - USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH1) }, - { .driver_data = MT_CLS_EGALAX, + USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72FA) }, + { .driver_data = MT_CLS_EGALAX, HID_USB_DEVICE(USB_VENDOR_ID_DWAV, - USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH2) }, - { .driver_data = MT_CLS_EGALAX, + USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7302) }, + { .driver_data = MT_CLS_EGALAX, HID_USB_DEVICE(USB_VENDOR_ID_DWAV, - USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH4) }, + USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_A001) }, /* Elo TouchSystems IntelliTouch Plus panel */ { .driver_data = MT_CLS_DUAL_NSMU_CONTACTID, diff --git a/drivers/i2c/busses/i2c-ali1535.c b/drivers/i2c/busses/i2c-ali1535.c index dd364171f9c..cd7ac5c6783 100644 --- a/drivers/i2c/busses/i2c-ali1535.c +++ b/drivers/i2c/busses/i2c-ali1535.c @@ -140,7 +140,7 @@ static unsigned short ali1535_smba; defined to make the transition easier. */ static int __devinit ali1535_setup(struct pci_dev *dev) { - int retval = -ENODEV; + int retval; unsigned char temp; /* Check the following things: @@ -155,6 +155,7 @@ static int __devinit ali1535_setup(struct pci_dev *dev) if (ali1535_smba == 0) { dev_warn(&dev->dev, "ALI1535_smb region uninitialized - upgrade BIOS?\n"); + retval = -ENODEV; goto exit; } @@ -167,6 +168,7 @@ static int __devinit ali1535_setup(struct pci_dev *dev) ali1535_driver.name)) { dev_err(&dev->dev, "ALI1535_smb region 0x%x already in use!\n", ali1535_smba); + retval = -EBUSY; goto exit; } @@ -174,6 +176,7 @@ static int __devinit ali1535_setup(struct pci_dev *dev) pci_read_config_byte(dev, SMBCFG, &temp); if ((temp & ALI1535_SMBIO_EN) == 0) { dev_err(&dev->dev, "SMB device not enabled - upgrade BIOS?\n"); + retval = -ENODEV; goto exit_free; } @@ -181,6 +184,7 @@ static int __devinit ali1535_setup(struct pci_dev *dev) pci_read_config_byte(dev, SMBHSTCFG, &temp); if ((temp & 1) == 0) { dev_err(&dev->dev, "SMBus controller not enabled - upgrade BIOS?\n"); + retval = -ENODEV; goto exit_free; } @@ -198,12 +202,11 @@ static int __devinit ali1535_setup(struct pci_dev *dev) dev_dbg(&dev->dev, "SMBREV = 0x%X\n", temp); dev_dbg(&dev->dev, "ALI1535_smba = 0x%X\n", ali1535_smba); - retval = 0; -exit: - return retval; + return 0; exit_free: release_region(ali1535_smba, ALI1535_SMB_IOSIZE); +exit: return retval; } diff --git a/drivers/i2c/busses/i2c-eg20t.c b/drivers/i2c/busses/i2c-eg20t.c index 8abfa4a03ce..656b028d981 100644 --- a/drivers/i2c/busses/i2c-eg20t.c +++ b/drivers/i2c/busses/i2c-eg20t.c @@ -242,7 +242,7 @@ static void pch_i2c_init(struct i2c_algo_pch_data *adap) if (pch_clk > PCH_MAX_CLK) pch_clk = 62500; - pch_i2cbc = (pch_clk + (pch_i2c_speed * 4)) / pch_i2c_speed * 8; + pch_i2cbc = (pch_clk + (pch_i2c_speed * 4)) / (pch_i2c_speed * 8); /* Set transfer speed in I2CBC */ iowrite32(pch_i2cbc, p + PCH_I2CBC); diff --git a/drivers/i2c/busses/i2c-nforce2.c b/drivers/i2c/busses/i2c-nforce2.c index ff1e127dfea..4853b52a40a 100644 --- a/drivers/i2c/busses/i2c-nforce2.c +++ b/drivers/i2c/busses/i2c-nforce2.c @@ -356,7 +356,7 @@ static int __devinit nforce2_probe_smb (struct pci_dev *dev, int bar, error = acpi_check_region(smbus->base, smbus->size, nforce2_driver.name); if (error) - return -1; + return error; if (!request_region(smbus->base, smbus->size, nforce2_driver.name)) { dev_err(&smbus->adapter.dev, "Error requesting region %02x .. %02X for %s\n", diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c index 58a58c7eaa1..137e1a3bfad 100644 --- a/drivers/i2c/busses/i2c-omap.c +++ b/drivers/i2c/busses/i2c-omap.c @@ -235,7 +235,7 @@ const static u8 omap4_reg_map[] = { [OMAP_I2C_BUF_REG] = 0x94, [OMAP_I2C_CNT_REG] = 0x98, [OMAP_I2C_DATA_REG] = 0x9c, - [OMAP_I2C_SYSC_REG] = 0x20, + [OMAP_I2C_SYSC_REG] = 0x10, [OMAP_I2C_CON_REG] = 0xa4, [OMAP_I2C_OA_REG] = 0xa8, [OMAP_I2C_SA_REG] = 0xac, diff --git a/drivers/i2c/busses/i2c-sis5595.c b/drivers/i2c/busses/i2c-sis5595.c index 437586611d4..6d60284cc04 100644 --- a/drivers/i2c/busses/i2c-sis5595.c +++ b/drivers/i2c/busses/i2c-sis5595.c @@ -147,7 +147,7 @@ static int __devinit sis5595_setup(struct pci_dev *SIS5595_dev) u16 a; u8 val; int *i; - int retval = -ENODEV; + int retval; /* Look for imposters */ for (i = blacklist; *i != 0; i++) { @@ -223,7 +223,7 @@ static int __devinit sis5595_setup(struct pci_dev *SIS5595_dev) error: release_region(sis5595_base + SMB_INDEX, 2); - return retval; + return -ENODEV; } static int sis5595_transaction(struct i2c_adapter *adap) diff --git a/drivers/i2c/busses/i2c-sis630.c b/drivers/i2c/busses/i2c-sis630.c index e6f539e26f6..b617fd068ac 100644 --- a/drivers/i2c/busses/i2c-sis630.c +++ b/drivers/i2c/busses/i2c-sis630.c @@ -393,7 +393,7 @@ static int __devinit sis630_setup(struct pci_dev *sis630_dev) { unsigned char b; struct pci_dev *dummy = NULL; - int retval = -ENODEV, i; + int retval, i; /* check for supported SiS devices */ for (i=0; supported[i] > 0 ; i++) { @@ -418,18 +418,21 @@ static int __devinit sis630_setup(struct pci_dev *sis630_dev) */ if (pci_read_config_byte(sis630_dev, SIS630_BIOS_CTL_REG,&b)) { dev_err(&sis630_dev->dev, "Error: Can't read bios ctl reg\n"); + retval = -ENODEV; goto exit; } /* if ACPI already enabled , do nothing */ if (!(b & 0x80) && pci_write_config_byte(sis630_dev, SIS630_BIOS_CTL_REG, b | 0x80)) { dev_err(&sis630_dev->dev, "Error: Can't enable ACPI\n"); + retval = -ENODEV; goto exit; } /* Determine the ACPI base address */ if (pci_read_config_word(sis630_dev,SIS630_ACPI_BASE_REG,&acpi_base)) { dev_err(&sis630_dev->dev, "Error: Can't determine ACPI base address\n"); + retval = -ENODEV; goto exit; } @@ -445,6 +448,7 @@ static int __devinit sis630_setup(struct pci_dev *sis630_dev) sis630_driver.name)) { dev_err(&sis630_dev->dev, "SMBus registers 0x%04x-0x%04x already " "in use!\n", acpi_base + SMB_STS, acpi_base + SMB_SAA); + retval = -EBUSY; goto exit; } diff --git a/drivers/i2c/busses/i2c-viapro.c b/drivers/i2c/busses/i2c-viapro.c index 0b012f1f8ac..58261d4725b 100644 --- a/drivers/i2c/busses/i2c-viapro.c +++ b/drivers/i2c/busses/i2c-viapro.c @@ -324,7 +324,7 @@ static int __devinit vt596_probe(struct pci_dev *pdev, const struct pci_device_id *id) { unsigned char temp; - int error = -ENODEV; + int error; /* Determine the address of the SMBus areas */ if (force_addr) { @@ -390,6 +390,7 @@ static int __devinit vt596_probe(struct pci_dev *pdev, dev_err(&pdev->dev, "SMBUS: Error: Host SMBus " "controller not enabled! - upgrade BIOS or " "use force=1\n"); + error = -ENODEV; goto release_region; } } @@ -422,9 +423,11 @@ static int __devinit vt596_probe(struct pci_dev *pdev, "SMBus Via Pro adapter at %04x", vt596_smba); vt596_pdev = pci_dev_get(pdev); - if (i2c_add_adapter(&vt596_adapter)) { + error = i2c_add_adapter(&vt596_adapter); + if (error) { pci_dev_put(vt596_pdev); vt596_pdev = NULL; + goto release_region; } /* Always return failure here. This is to allow other drivers to bind diff --git a/drivers/ide/ide-floppy_ioctl.c b/drivers/ide/ide-floppy_ioctl.c index d267b7affad..a22ca846701 100644 --- a/drivers/ide/ide-floppy_ioctl.c +++ b/drivers/ide/ide-floppy_ioctl.c @@ -292,8 +292,7 @@ int ide_floppy_ioctl(ide_drive_t *drive, struct block_device *bdev, * and CDROM_SEND_PACKET (legacy) ioctls */ if (cmd != CDROM_SEND_PACKET && cmd != SCSI_IOCTL_SEND_COMMAND) - err = scsi_cmd_ioctl(bdev->bd_disk->queue, bdev->bd_disk, - mode, cmd, argp); + err = scsi_cmd_blk_ioctl(bdev, mode, cmd, argp); if (err == -ENOTTY) err = generic_ide_ioctl(drive, bdev, cmd, arg); diff --git a/drivers/idle/intel_idle.c b/drivers/idle/intel_idle.c index a46dddf6107..026f9aa789e 100644 --- a/drivers/idle/intel_idle.c +++ b/drivers/idle/intel_idle.c @@ -321,7 +321,8 @@ static int intel_idle_probe(void) cpuid(CPUID_MWAIT_LEAF, &eax, &ebx, &ecx, &mwait_substates); if (!(ecx & CPUID5_ECX_EXTENSIONS_SUPPORTED) || - !(ecx & CPUID5_ECX_INTERRUPT_BREAK)) + !(ecx & CPUID5_ECX_INTERRUPT_BREAK) || + !mwait_substates) return -ENODEV; pr_debug(PREFIX "MWAIT substates: 0x%x\n", mwait_substates); @@ -367,7 +368,7 @@ static int intel_idle_probe(void) if (boot_cpu_has(X86_FEATURE_ARAT)) /* Always Reliable APIC Timer */ lapic_timer_reliable_states = LAPIC_TIMER_ALWAYS_RELIABLE; else { - smp_call_function(__setup_broadcast_timer, (void *)true, 1); + on_each_cpu(__setup_broadcast_timer, (void *)true, 1); register_cpu_notifier(&setup_broadcast_notifier); } @@ -459,7 +460,7 @@ static int intel_idle_cpuidle_devices_init(void) } } if (auto_demotion_disable_flags) - smp_call_function(auto_demotion_disable, NULL, 1); + on_each_cpu(auto_demotion_disable, NULL, 1); return 0; } @@ -499,7 +500,7 @@ static void __exit intel_idle_exit(void) cpuidle_unregister_driver(&intel_idle_driver); if (lapic_timer_reliable_states != LAPIC_TIMER_ALWAYS_RELIABLE) { - smp_call_function(__setup_broadcast_timer, (void *)false, 1); + on_each_cpu(__setup_broadcast_timer, (void *)false, 1); unregister_cpu_notifier(&setup_broadcast_notifier); } diff --git a/drivers/md/dm-flakey.c b/drivers/md/dm-flakey.c index ea790623c30..3e90b8014f9 100644 --- a/drivers/md/dm-flakey.c +++ b/drivers/md/dm-flakey.c @@ -149,8 +149,17 @@ static int flakey_status(struct dm_target *ti, status_type_t type, static int flakey_ioctl(struct dm_target *ti, unsigned int cmd, unsigned long arg) { struct flakey_c *fc = ti->private; + struct dm_dev *dev = fc->dev; + int r = 0; - return __blkdev_driver_ioctl(fc->dev->bdev, fc->dev->mode, cmd, arg); + /* + * Only pass ioctls through if the device sizes match exactly. + */ + if (fc->start || + ti->len != i_size_read(dev->bdev->bd_inode) >> SECTOR_SHIFT) + r = scsi_verify_blk_ioctl(NULL, cmd); + + return r ? : __blkdev_driver_ioctl(dev->bdev, dev->mode, cmd, arg); } static int flakey_merge(struct dm_target *ti, struct bvec_merge_data *bvm, diff --git a/drivers/md/dm-linear.c b/drivers/md/dm-linear.c index 3921e3bb43c..9728839f844 100644 --- a/drivers/md/dm-linear.c +++ b/drivers/md/dm-linear.c @@ -116,7 +116,17 @@ static int linear_ioctl(struct dm_target *ti, unsigned int cmd, unsigned long arg) { struct linear_c *lc = (struct linear_c *) ti->private; - return __blkdev_driver_ioctl(lc->dev->bdev, lc->dev->mode, cmd, arg); + struct dm_dev *dev = lc->dev; + int r = 0; + + /* + * Only pass ioctls through if the device sizes match exactly. + */ + if (lc->start || + ti->len != i_size_read(dev->bdev->bd_inode) >> SECTOR_SHIFT) + r = scsi_verify_blk_ioctl(NULL, cmd); + + return r ? : __blkdev_driver_ioctl(dev->bdev, dev->mode, cmd, arg); } static int linear_merge(struct dm_target *ti, struct bvec_merge_data *bvm, diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c index 209991bebd3..70373bfa20b 100644 --- a/drivers/md/dm-mpath.c +++ b/drivers/md/dm-mpath.c @@ -1584,6 +1584,12 @@ static int multipath_ioctl(struct dm_target *ti, unsigned int cmd, spin_unlock_irqrestore(&m->lock, flags); + /* + * Only pass ioctls through if the device sizes match exactly. + */ + if (!r && ti->len != i_size_read(bdev->bd_inode) >> SECTOR_SHIFT) + r = scsi_verify_blk_ioctl(NULL, cmd); + return r ? : __blkdev_driver_ioctl(bdev, mode, cmd, arg); } diff --git a/drivers/media/video/uvc/uvc_v4l2.c b/drivers/media/video/uvc/uvc_v4l2.c index 543a80395b7..dbefdb09c10 100644 --- a/drivers/media/video/uvc/uvc_v4l2.c +++ b/drivers/media/video/uvc/uvc_v4l2.c @@ -65,6 +65,15 @@ static int uvc_ioctl_ctrl_map(struct uvc_video_chain *chain, goto done; } + /* Prevent excessive memory consumption, as well as integer + * overflows. + */ + if (xmap->menu_count == 0 || + xmap->menu_count > UVC_MAX_CONTROL_MENU_ENTRIES) { + ret = -EINVAL; + goto done; + } + size = xmap->menu_count * sizeof(*map->menu_info); map->menu_info = kmalloc(size, GFP_KERNEL); if (map->menu_info == NULL) { diff --git a/drivers/media/video/uvc/uvcvideo.h b/drivers/media/video/uvc/uvcvideo.h index 2a38d5e5535..cf2401a041a 100644 --- a/drivers/media/video/uvc/uvcvideo.h +++ b/drivers/media/video/uvc/uvcvideo.h @@ -200,6 +200,7 @@ struct uvc_xu_control { /* Maximum allowed number of control mappings per device */ #define UVC_MAX_CONTROL_MAPPINGS 1024 +#define UVC_MAX_CONTROL_MENU_ENTRIES 32 /* Devices quirks */ #define UVC_QUIRK_STATUS_INTERVAL 0x00000001 diff --git a/drivers/media/video/v4l2-ioctl.c b/drivers/media/video/v4l2-ioctl.c index 69e8c6ffcc4..bda252f04e8 100644 --- a/drivers/media/video/v4l2-ioctl.c +++ b/drivers/media/video/v4l2-ioctl.c @@ -2289,6 +2289,10 @@ static int check_array_args(unsigned int cmd, void *parg, size_t *array_size, struct v4l2_ext_controls *ctrls = parg; if (ctrls->count != 0) { + if (ctrls->count > V4L2_CID_MAX_CTRLS) { + ret = -EINVAL; + break; + } *user_ptr = (void __user *)ctrls->controls; *kernel_ptr = (void **)&ctrls->controls; *array_size = sizeof(struct v4l2_ext_control) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 6d3de0888e7..153008fff54 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1340,8 +1340,7 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) if ((ios->timing == MMC_TIMING_UHS_SDR50) || (ios->timing == MMC_TIMING_UHS_SDR104) || (ios->timing == MMC_TIMING_UHS_DDR50) || - (ios->timing == MMC_TIMING_UHS_SDR25) || - (ios->timing == MMC_TIMING_UHS_SDR12)) + (ios->timing == MMC_TIMING_UHS_SDR25)) ctrl |= SDHCI_CTRL_HISPD; ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2); @@ -2227,9 +2226,8 @@ int sdhci_suspend_host(struct sdhci_host *host, pm_message_t state) /* Disable tuning since we are suspending */ if (host->version >= SDHCI_SPEC_300 && host->tuning_count && host->tuning_mode == SDHCI_TUNING_MODE_1) { + del_timer_sync(&host->tuning_timer); host->flags &= ~SDHCI_NEEDS_RETUNING; - mod_timer(&host->tuning_timer, jiffies + - host->tuning_count * HZ); } ret = mmc_suspend_host(host->mmc); diff --git a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c index ca385697446..bff8d4671ad 100644 --- a/drivers/mtd/mtd_blkdevs.c +++ b/drivers/mtd/mtd_blkdevs.c @@ -215,7 +215,7 @@ static int blktrans_open(struct block_device *bdev, fmode_t mode) mutex_lock(&dev->lock); - if (dev->open++) + if (dev->open) goto unlock; kref_get(&dev->ref); @@ -235,6 +235,7 @@ static int blktrans_open(struct block_device *bdev, fmode_t mode) goto error_release; unlock: + dev->open++; mutex_unlock(&dev->lock); blktrans_dev_put(dev); return ret; diff --git a/drivers/mtd/mtdoops.c b/drivers/mtd/mtdoops.c index e3e40f44032..43130e8acea 100644 --- a/drivers/mtd/mtdoops.c +++ b/drivers/mtd/mtdoops.c @@ -253,6 +253,9 @@ static void find_next_position(struct mtdoops_context *cxt) size_t retlen; for (page = 0; page < cxt->oops_pages; page++) { + if (mtd->block_isbad && + mtd->block_isbad(mtd, page * record_size)) + continue; /* Assume the page is used */ mark_page_used(cxt, page); ret = mtd->read(mtd, page * record_size, MTDOOPS_HEADER_SIZE, @@ -369,7 +372,7 @@ static void mtdoops_notify_add(struct mtd_info *mtd) /* oops_page_used is a bit field */ cxt->oops_page_used = vmalloc(DIV_ROUND_UP(mtdoops_pages, - BITS_PER_LONG)); + BITS_PER_LONG) * sizeof(unsigned long)); if (!cxt->oops_page_used) { printk(KERN_ERR "mtdoops: could not allocate page array\n"); return; diff --git a/drivers/mtd/tests/mtd_stresstest.c b/drivers/mtd/tests/mtd_stresstest.c index 531625fc925..129bad2e408 100644 --- a/drivers/mtd/tests/mtd_stresstest.c +++ b/drivers/mtd/tests/mtd_stresstest.c @@ -277,6 +277,12 @@ static int __init mtd_stresstest_init(void) (unsigned long long)mtd->size, mtd->erasesize, pgsize, ebcnt, pgcnt, mtd->oobsize); + if (ebcnt < 2) { + printk(PRINT_PREF "error: need at least 2 eraseblocks\n"); + err = -ENOSPC; + goto out_put_mtd; + } + /* Read or write up 2 eraseblocks at a time */ bufsize = mtd->erasesize * 2; @@ -315,6 +321,7 @@ static int __init mtd_stresstest_init(void) kfree(bbt); vfree(writebuf); vfree(readbuf); +out_put_mtd: put_mtd_device(mtd); if (err) printk(PRINT_PREF "error %d occurred\n", err); diff --git a/drivers/mtd/ubi/cdev.c b/drivers/mtd/ubi/cdev.c index 191f3bb3c41..cdea6692dea 100644 --- a/drivers/mtd/ubi/cdev.c +++ b/drivers/mtd/ubi/cdev.c @@ -628,6 +628,9 @@ static int verify_mkvol_req(const struct ubi_device *ubi, if (req->alignment != 1 && n) goto bad; + if (!req->name[0] || !req->name_len) + goto bad; + if (req->name_len > UBI_VOL_NAME_MAX) { err = -ENAMETOOLONG; goto bad; diff --git a/drivers/mtd/ubi/debug.h b/drivers/mtd/ubi/debug.h index 3f1a09c5c43..5f0e4c2d9cd 100644 --- a/drivers/mtd/ubi/debug.h +++ b/drivers/mtd/ubi/debug.h @@ -51,7 +51,10 @@ struct ubi_mkvol_req; pr_debug("UBI DBG " type ": " fmt "\n", ##__VA_ARGS__) /* Just a debugging messages not related to any specific UBI subsystem */ -#define dbg_msg(fmt, ...) ubi_dbg_msg("msg", fmt, ##__VA_ARGS__) +#define dbg_msg(fmt, ...) \ + printk(KERN_DEBUG "UBI DBG (pid %d): %s: " fmt "\n", \ + current->pid, __func__, ##__VA_ARGS__) + /* General debugging messages */ #define dbg_gen(fmt, ...) ubi_dbg_msg("gen", fmt, ##__VA_ARGS__) /* Messages from the eraseblock association sub-system */ diff --git a/drivers/mtd/ubi/eba.c b/drivers/mtd/ubi/eba.c index 4be67181501..c696c9481c9 100644 --- a/drivers/mtd/ubi/eba.c +++ b/drivers/mtd/ubi/eba.c @@ -1028,12 +1028,14 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, * 'ubi_wl_put_peb()' function on the @ubi->move_mutex. In turn, we are * holding @ubi->move_mutex and go sleep on the LEB lock. So, if the * LEB is already locked, we just do not move it and return - * %MOVE_CANCEL_RACE, which means that UBI will re-try, but later. + * %MOVE_RETRY. Note, we do not return %MOVE_CANCEL_RACE here because + * we do not know the reasons of the contention - it may be just a + * normal I/O on this LEB, so we want to re-try. */ err = leb_write_trylock(ubi, vol_id, lnum); if (err) { dbg_wl("contention on LEB %d:%d, cancel", vol_id, lnum); - return MOVE_CANCEL_RACE; + return MOVE_RETRY; } /* diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h index c6c22295898..bbfa88d459e 100644 --- a/drivers/mtd/ubi/ubi.h +++ b/drivers/mtd/ubi/ubi.h @@ -121,6 +121,7 @@ enum { * PEB * MOVE_CANCEL_BITFLIPS: canceled because a bit-flip was detected in the * target PEB + * MOVE_RETRY: retry scrubbing the PEB */ enum { MOVE_CANCEL_RACE = 1, @@ -128,6 +129,7 @@ enum { MOVE_TARGET_RD_ERR, MOVE_TARGET_WR_ERR, MOVE_CANCEL_BITFLIPS, + MOVE_RETRY, }; /** diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c index ff2c4956eef..12e44c90b32 100644 --- a/drivers/mtd/ubi/wl.c +++ b/drivers/mtd/ubi/wl.c @@ -792,7 +792,10 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, protect = 1; goto out_not_moved; } - + if (err == MOVE_RETRY) { + scrubbing = 1; + goto out_not_moved; + } if (err == MOVE_CANCEL_BITFLIPS || err == MOVE_TARGET_WR_ERR || err == MOVE_TARGET_RD_ERR) { /* @@ -1046,7 +1049,6 @@ static int erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk, ubi_err("failed to erase PEB %d, error %d", pnum, err); kfree(wl_wrk); - kmem_cache_free(ubi_wl_entry_slab, e); if (err == -EINTR || err == -ENOMEM || err == -EAGAIN || err == -EBUSY) { @@ -1059,14 +1061,16 @@ static int erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk, goto out_ro; } return err; - } else if (err != -EIO) { + } + + kmem_cache_free(ubi_wl_entry_slab, e); + if (err != -EIO) /* * If this is not %-EIO, we have no idea what to do. Scheduling * this physical eraseblock for erasure again would cause * errors again and again. Well, lets switch to R/O mode. */ goto out_ro; - } /* It is %-EIO, the PEB went bad */ diff --git a/drivers/net/phy/mdio-gpio.c b/drivers/net/phy/mdio-gpio.c index 47c8339a035..2843c90f712 100644 --- a/drivers/net/phy/mdio-gpio.c +++ b/drivers/net/phy/mdio-gpio.c @@ -241,7 +241,7 @@ MODULE_DEVICE_TABLE(of, mdio_ofgpio_match); static struct platform_driver mdio_ofgpio_driver = { .driver = { - .name = "mdio-gpio", + .name = "mdio-ofgpio", .owner = THIS_MODULE, .of_match_table = mdio_ofgpio_match, }, diff --git a/drivers/net/wireless/iwlegacy/iwl3945-base.c b/drivers/net/wireless/iwlegacy/iwl3945-base.c index 421d5c8b8e3..a9355856e2e 100644 --- a/drivers/net/wireless/iwlegacy/iwl3945-base.c +++ b/drivers/net/wireless/iwlegacy/iwl3945-base.c @@ -2910,14 +2910,13 @@ int iwl3945_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif) IWL_WARN(priv, "Invalid scan band\n"); return -EIO; } - /* - * If active scaning is requested but a certain channel - * is marked passive, we can do active scanning if we - * detect transmissions. + * If active scaning is requested but a certain channel is marked + * passive, we can do active scanning if we detect transmissions. For + * passive only scanning disable switching to active on any channel. */ scan->good_CRC_th = is_active ? IWL_GOOD_CRC_TH_DEFAULT : - IWL_GOOD_CRC_TH_DISABLED; + IWL_GOOD_CRC_TH_NEVER; if (!priv->is_internal_short_scan) { scan->tx_cmd.len = cpu_to_le16( diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-lib.c b/drivers/net/wireless/iwlwifi/iwl-agn-lib.c index f803fb62f8b..857cf613092 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-lib.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-lib.c @@ -2023,6 +2023,7 @@ static int iwl_get_idle_rx_chain_count(struct iwl_priv *priv, int active_cnt) case IEEE80211_SMPS_STATIC: case IEEE80211_SMPS_DYNAMIC: return IWL_NUM_IDLE_CHAINS_SINGLE; + case IEEE80211_SMPS_AUTOMATIC: case IEEE80211_SMPS_OFF: return active_cnt; default: diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-rxon.c b/drivers/net/wireless/iwlwifi/iwl-agn-rxon.c index 39a3c9c235f..272bcdfe53a 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-rxon.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-rxon.c @@ -442,6 +442,9 @@ int iwlagn_mac_config(struct ieee80211_hw *hw, u32 changed) mutex_lock(&priv->mutex); + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + goto out; + if (unlikely(test_bit(STATUS_SCANNING, &priv->status))) { IWL_DEBUG_MAC80211(priv, "leave - scanning\n"); goto out; diff --git a/drivers/net/wireless/rt2x00/rt2800pci.c b/drivers/net/wireless/rt2x00/rt2800pci.c index 55cd3e1f75b..dab7dc16a6c 100644 --- a/drivers/net/wireless/rt2x00/rt2800pci.c +++ b/drivers/net/wireless/rt2x00/rt2800pci.c @@ -426,7 +426,6 @@ static int rt2800pci_init_queues(struct rt2x00_dev *rt2x00dev) static void rt2800pci_toggle_irq(struct rt2x00_dev *rt2x00dev, enum dev_state state) { - int mask = (state == STATE_RADIO_IRQ_ON); u32 reg; unsigned long flags; @@ -448,25 +447,14 @@ static void rt2800pci_toggle_irq(struct rt2x00_dev *rt2x00dev, } spin_lock_irqsave(&rt2x00dev->irqmask_lock, flags); - rt2x00pci_register_read(rt2x00dev, INT_MASK_CSR, ®); - rt2x00_set_field32(®, INT_MASK_CSR_RXDELAYINT, 0); - rt2x00_set_field32(®, INT_MASK_CSR_TXDELAYINT, 0); - rt2x00_set_field32(®, INT_MASK_CSR_RX_DONE, mask); - rt2x00_set_field32(®, INT_MASK_CSR_AC0_DMA_DONE, 0); - rt2x00_set_field32(®, INT_MASK_CSR_AC1_DMA_DONE, 0); - rt2x00_set_field32(®, INT_MASK_CSR_AC2_DMA_DONE, 0); - rt2x00_set_field32(®, INT_MASK_CSR_AC3_DMA_DONE, 0); - rt2x00_set_field32(®, INT_MASK_CSR_HCCA_DMA_DONE, 0); - rt2x00_set_field32(®, INT_MASK_CSR_MGMT_DMA_DONE, 0); - rt2x00_set_field32(®, INT_MASK_CSR_MCU_COMMAND, 0); - rt2x00_set_field32(®, INT_MASK_CSR_RXTX_COHERENT, 0); - rt2x00_set_field32(®, INT_MASK_CSR_TBTT, mask); - rt2x00_set_field32(®, INT_MASK_CSR_PRE_TBTT, mask); - rt2x00_set_field32(®, INT_MASK_CSR_TX_FIFO_STATUS, mask); - rt2x00_set_field32(®, INT_MASK_CSR_AUTO_WAKEUP, mask); - rt2x00_set_field32(®, INT_MASK_CSR_GPTIMER, 0); - rt2x00_set_field32(®, INT_MASK_CSR_RX_COHERENT, 0); - rt2x00_set_field32(®, INT_MASK_CSR_TX_COHERENT, 0); + reg = 0; + if (state == STATE_RADIO_IRQ_ON) { + rt2x00_set_field32(®, INT_MASK_CSR_RX_DONE, 1); + rt2x00_set_field32(®, INT_MASK_CSR_TBTT, 1); + rt2x00_set_field32(®, INT_MASK_CSR_PRE_TBTT, 1); + rt2x00_set_field32(®, INT_MASK_CSR_TX_FIFO_STATUS, 1); + rt2x00_set_field32(®, INT_MASK_CSR_AUTO_WAKEUP, 1); + } rt2x00pci_register_write(rt2x00dev, INT_MASK_CSR, reg); spin_unlock_irqrestore(&rt2x00dev->irqmask_lock, flags); diff --git a/drivers/net/wireless/rtlwifi/rtl8192se/fw.c b/drivers/net/wireless/rtlwifi/rtl8192se/fw.c index 3b5af0113d7..0c77a14a382 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192se/fw.c +++ b/drivers/net/wireless/rtlwifi/rtl8192se/fw.c @@ -196,6 +196,8 @@ static bool _rtl92s_firmware_downloadcode(struct ieee80211_hw *hw, /* Allocate skb buffer to contain firmware */ /* info and tx descriptor info. */ skb = dev_alloc_skb(frag_length); + if (!skb) + return false; skb_reserve(skb, extra_descoffset); seg_ptr = (u8 *)skb_put(skb, (u32)(frag_length - extra_descoffset)); @@ -575,6 +577,8 @@ static bool _rtl92s_firmware_set_h2c_cmd(struct ieee80211_hw *hw, u8 h2c_cmd, len = _rtl92s_get_h2c_cmdlen(MAX_TRANSMIT_BUFFER_SIZE, 1, &cmd_len); skb = dev_alloc_skb(len); + if (!skb) + return false; cb_desc = (struct rtl_tcb_desc *)(skb->cb); cb_desc->queue_index = TXCMD_QUEUE; cb_desc->cmd_or_init = DESC_PACKET_TYPE_NORMAL; diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index 2f10328bf66..e1749825008 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -869,5 +869,15 @@ EXPORT_SYMBOL(pci_msi_enabled); void pci_msi_init_pci_dev(struct pci_dev *dev) { + int pos; INIT_LIST_HEAD(&dev->msi_list); + + /* Disable the msi hardware to avoid screaming interrupts + * during boot. This is the power on reset default so + * usually this should be a noop. + */ + pos = pci_find_capability(dev, PCI_CAP_ID_MSI); + if (pos) + msi_set_enable(dev, pos, 0); + msix_set_enable(dev, 0); } diff --git a/drivers/pnp/quirks.c b/drivers/pnp/quirks.c index dfbd5a6cc58..258fef272ea 100644 --- a/drivers/pnp/quirks.c +++ b/drivers/pnp/quirks.c @@ -295,6 +295,45 @@ static void quirk_system_pci_resources(struct pnp_dev *dev) } } +#ifdef CONFIG_AMD_NB + +#include + +static void quirk_amd_mmconfig_area(struct pnp_dev *dev) +{ + resource_size_t start, end; + struct pnp_resource *pnp_res; + struct resource *res; + struct resource mmconfig_res, *mmconfig; + + mmconfig = amd_get_mmconfig_range(&mmconfig_res); + if (!mmconfig) + return; + + list_for_each_entry(pnp_res, &dev->resources, list) { + res = &pnp_res->res; + if (res->end < mmconfig->start || res->start > mmconfig->end || + (res->start == mmconfig->start && res->end == mmconfig->end)) + continue; + + dev_info(&dev->dev, FW_BUG + "%pR covers only part of AMD MMCONFIG area %pR; adding more reservations\n", + res, mmconfig); + if (mmconfig->start < res->start) { + start = mmconfig->start; + end = res->start - 1; + pnp_add_mem_resource(dev, start, end, 0); + } + if (mmconfig->end > res->end) { + start = res->end + 1; + end = mmconfig->end; + pnp_add_mem_resource(dev, start, end, 0); + } + break; + } +} +#endif + /* * PnP Quirks * Cards or devices that need some tweaking due to incomplete resource info @@ -322,6 +361,9 @@ static struct pnp_fixup pnp_fixups[] = { /* PnP resources that might overlap PCI BARs */ {"PNP0c01", quirk_system_pci_resources}, {"PNP0c02", quirk_system_pci_resources}, +#ifdef CONFIG_AMD_NB + {"PNP0c01", quirk_amd_mmconfig_area}, +#endif {""} }; diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c index eb4c88316a1..38d1dc74b2c 100644 --- a/drivers/rtc/interface.c +++ b/drivers/rtc/interface.c @@ -227,11 +227,11 @@ int __rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) alarm->time.tm_hour = now.tm_hour; /* For simplicity, only support date rollover for now */ - if (alarm->time.tm_mday == -1) { + if (alarm->time.tm_mday < 1 || alarm->time.tm_mday > 31) { alarm->time.tm_mday = now.tm_mday; missing = day; } - if (alarm->time.tm_mon == -1) { + if ((unsigned)alarm->time.tm_mon >= 12) { alarm->time.tm_mon = now.tm_mon; if (missing == none) missing = month; diff --git a/drivers/scsi/mpt2sas/mpt2sas_base.c b/drivers/scsi/mpt2sas/mpt2sas_base.c index 39e81cd567a..10f16a306e5 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_base.c +++ b/drivers/scsi/mpt2sas/mpt2sas_base.c @@ -66,6 +66,8 @@ static MPT_CALLBACK mpt_callbacks[MPT_MAX_CALLBACKS]; #define FAULT_POLLING_INTERVAL 1000 /* in milliseconds */ +#define MAX_HBA_QUEUE_DEPTH 30000 +#define MAX_CHAIN_DEPTH 100000 static int max_queue_depth = -1; module_param(max_queue_depth, int, 0); MODULE_PARM_DESC(max_queue_depth, " max controller queue depth "); @@ -2098,8 +2100,6 @@ _base_release_memory_pools(struct MPT2SAS_ADAPTER *ioc) } if (ioc->chain_dma_pool) pci_pool_destroy(ioc->chain_dma_pool); - } - if (ioc->chain_lookup) { free_pages((ulong)ioc->chain_lookup, ioc->chain_pages); ioc->chain_lookup = NULL; } @@ -2117,9 +2117,7 @@ static int _base_allocate_memory_pools(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) { struct mpt2sas_facts *facts; - u32 queue_size, queue_diff; u16 max_sge_elements; - u16 num_of_reply_frames; u16 chains_needed_per_io; u32 sz, total_sz; u32 retry_sz; @@ -2146,7 +2144,8 @@ _base_allocate_memory_pools(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) max_request_credit = (max_queue_depth < facts->RequestCredit) ? max_queue_depth : facts->RequestCredit; else - max_request_credit = facts->RequestCredit; + max_request_credit = min_t(u16, facts->RequestCredit, + MAX_HBA_QUEUE_DEPTH); ioc->hba_queue_depth = max_request_credit; ioc->hi_priority_depth = facts->HighPriorityCredit; @@ -2187,50 +2186,25 @@ _base_allocate_memory_pools(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) } ioc->chains_needed_per_io = chains_needed_per_io; - /* reply free queue sizing - taking into account for events */ - num_of_reply_frames = ioc->hba_queue_depth + 32; - - /* number of replies frames can't be a multiple of 16 */ - /* decrease number of reply frames by 1 */ - if (!(num_of_reply_frames % 16)) - num_of_reply_frames--; - - /* calculate number of reply free queue entries - * (must be multiple of 16) - */ - - /* (we know reply_free_queue_depth is not a multiple of 16) */ - queue_size = num_of_reply_frames; - queue_size += 16 - (queue_size % 16); - ioc->reply_free_queue_depth = queue_size; - - /* reply descriptor post queue sizing */ - /* this size should be the number of request frames + number of reply - * frames - */ - - queue_size = ioc->hba_queue_depth + num_of_reply_frames + 1; - /* round up to 16 byte boundary */ - if (queue_size % 16) - queue_size += 16 - (queue_size % 16); - - /* check against IOC maximum reply post queue depth */ - if (queue_size > facts->MaxReplyDescriptorPostQueueDepth) { - queue_diff = queue_size - - facts->MaxReplyDescriptorPostQueueDepth; + /* reply free queue sizing - taking into account for 64 FW events */ + ioc->reply_free_queue_depth = ioc->hba_queue_depth + 64; - /* round queue_diff up to multiple of 16 */ - if (queue_diff % 16) - queue_diff += 16 - (queue_diff % 16); - - /* adjust hba_queue_depth, reply_free_queue_depth, - * and queue_size - */ - ioc->hba_queue_depth -= (queue_diff / 2); - ioc->reply_free_queue_depth -= (queue_diff / 2); - queue_size = facts->MaxReplyDescriptorPostQueueDepth; + /* align the reply post queue on the next 16 count boundary */ + if (!ioc->reply_free_queue_depth % 16) + ioc->reply_post_queue_depth = ioc->reply_free_queue_depth + 16; + else + ioc->reply_post_queue_depth = ioc->reply_free_queue_depth + + 32 - (ioc->reply_free_queue_depth % 16); + if (ioc->reply_post_queue_depth > + facts->MaxReplyDescriptorPostQueueDepth) { + ioc->reply_post_queue_depth = min_t(u16, + (facts->MaxReplyDescriptorPostQueueDepth - + (facts->MaxReplyDescriptorPostQueueDepth % 16)), + (ioc->hba_queue_depth - (ioc->hba_queue_depth % 16))); + ioc->reply_free_queue_depth = ioc->reply_post_queue_depth - 16; + ioc->hba_queue_depth = ioc->reply_free_queue_depth - 64; } - ioc->reply_post_queue_depth = queue_size; + dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "scatter gather: " "sge_in_main_msg(%d), sge_per_chain(%d), sge_per_io(%d), " @@ -2316,15 +2290,12 @@ _base_allocate_memory_pools(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) "depth(%d)\n", ioc->name, ioc->request, ioc->scsiio_depth)); - /* loop till the allocation succeeds */ - do { - sz = ioc->chain_depth * sizeof(struct chain_tracker); - ioc->chain_pages = get_order(sz); - ioc->chain_lookup = (struct chain_tracker *)__get_free_pages( - GFP_KERNEL, ioc->chain_pages); - if (ioc->chain_lookup == NULL) - ioc->chain_depth -= 100; - } while (ioc->chain_lookup == NULL); + ioc->chain_depth = min_t(u32, ioc->chain_depth, MAX_CHAIN_DEPTH); + sz = ioc->chain_depth * sizeof(struct chain_tracker); + ioc->chain_pages = get_order(sz); + + ioc->chain_lookup = (struct chain_tracker *)__get_free_pages( + GFP_KERNEL, ioc->chain_pages); ioc->chain_dma_pool = pci_pool_create("chain pool", ioc->pdev, ioc->request_sz, 16, 0); if (!ioc->chain_dma_pool) { diff --git a/drivers/scsi/mpt2sas/mpt2sas_scsih.c b/drivers/scsi/mpt2sas/mpt2sas_scsih.c index c79857e439f..aa51195a731 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_scsih.c +++ b/drivers/scsi/mpt2sas/mpt2sas_scsih.c @@ -974,8 +974,8 @@ _scsih_get_chain_buffer_tracker(struct MPT2SAS_ADAPTER *ioc, u16 smid) spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); if (list_empty(&ioc->free_chain_list)) { spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); - printk(MPT2SAS_WARN_FMT "chain buffers not available\n", - ioc->name); + dfailprintk(ioc, printk(MPT2SAS_WARN_FMT "chain buffers not " + "available\n", ioc->name)); return NULL; } chain_req = list_entry(ioc->free_chain_list.next, @@ -6425,6 +6425,7 @@ _scsih_mark_responding_raid_device(struct MPT2SAS_ADAPTER *ioc, u64 wwid, } else sas_target_priv_data = NULL; raid_device->responding = 1; + spin_unlock_irqrestore(&ioc->raid_device_lock, flags); starget_printk(KERN_INFO, raid_device->starget, "handle(0x%04x), wwid(0x%016llx)\n", handle, (unsigned long long)raid_device->wwid); @@ -6435,16 +6436,16 @@ _scsih_mark_responding_raid_device(struct MPT2SAS_ADAPTER *ioc, u64 wwid, */ _scsih_init_warpdrive_properties(ioc, raid_device); if (raid_device->handle == handle) - goto out; + return; printk(KERN_INFO "\thandle changed from(0x%04x)!!!\n", raid_device->handle); raid_device->handle = handle; if (sas_target_priv_data) sas_target_priv_data->handle = handle; - goto out; + return; } } - out: + spin_unlock_irqrestore(&ioc->raid_device_lock, flags); } diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 953773cb26d..7d8b5d8d749 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -1073,6 +1073,10 @@ static int sd_ioctl(struct block_device *bdev, fmode_t mode, SCSI_LOG_IOCTL(1, printk("sd_ioctl: disk=%s, cmd=0x%x\n", disk->disk_name, cmd)); + error = scsi_verify_blk_ioctl(bdev, cmd); + if (error < 0) + return error; + /* * If we are in the middle of error recovery, don't let anyone * else try and use this device. Also, if error recovery fails, it @@ -1095,7 +1099,7 @@ static int sd_ioctl(struct block_device *bdev, fmode_t mode, error = scsi_ioctl(sdp, cmd, p); break; default: - error = scsi_cmd_ioctl(disk->queue, disk, mode, cmd, p); + error = scsi_cmd_blk_ioctl(bdev, mode, cmd, p); if (error != -ENOTTY) break; error = scsi_ioctl(sdp, cmd, p); @@ -1265,6 +1269,11 @@ static int sd_compat_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd, unsigned long arg) { struct scsi_device *sdev = scsi_disk(bdev->bd_disk)->device; + int ret; + + ret = scsi_verify_blk_ioctl(bdev, cmd); + if (ret < 0) + return -ENOIOCTLCMD; /* * If we are in the middle of error recovery, don't let anyone @@ -1276,8 +1285,6 @@ static int sd_compat_ioctl(struct block_device *bdev, fmode_t mode, return -ENODEV; if (sdev->host->hostt->compat_ioctl) { - int ret; - ret = sdev->host->hostt->compat_ioctl(sdev, cmd, (void __user *)arg); return ret; diff --git a/drivers/scsi/sym53c8xx_2/sym_glue.c b/drivers/scsi/sym53c8xx_2/sym_glue.c index b4543f575f4..36d1ed7817e 100644 --- a/drivers/scsi/sym53c8xx_2/sym_glue.c +++ b/drivers/scsi/sym53c8xx_2/sym_glue.c @@ -839,6 +839,10 @@ static void sym53c8xx_slave_destroy(struct scsi_device *sdev) struct sym_lcb *lp = sym_lp(tp, sdev->lun); unsigned long flags; + /* if slave_alloc returned before allocating a sym_lcb, return */ + if (!lp) + return; + spin_lock_irqsave(np->s.host->host_lock, flags); if (lp->busy_itlq || lp->busy_itl) { diff --git a/drivers/target/target_core_cdb.c b/drivers/target/target_core_cdb.c index 7f19c8b7b84..f044d45fc58 100644 --- a/drivers/target/target_core_cdb.c +++ b/drivers/target/target_core_cdb.c @@ -83,6 +83,18 @@ target_emulate_inquiry_std(struct se_cmd *cmd) buf[1] = 0x80; buf[2] = dev->transport->get_device_rev(dev); + /* + * NORMACA and HISUP = 0, RESPONSE DATA FORMAT = 2 + * + * SPC4 says: + * A RESPONSE DATA FORMAT field set to 2h indicates that the + * standard INQUIRY data is in the format defined in this + * standard. Response data format values less than 2h are + * obsolete. Response data format values greater than 2h are + * reserved. + */ + buf[3] = 2; + /* * Enable SCCS and TPGS fields for Emulated ALUA */ diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index 1340ffd7648..bb86655dd40 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -5668,6 +5668,8 @@ int transport_send_check_condition_and_sense( case TCM_SECTOR_COUNT_TOO_MANY: /* CURRENT ERROR */ buffer[offset] = 0x70; + buffer[offset+SPC_ADD_SENSE_LEN_OFFSET] = 10; + buffer[offset+SPC_ADD_SENSE_LEN_OFFSET] = 10; /* ILLEGAL REQUEST */ buffer[offset+SPC_SENSE_KEY_OFFSET] = ILLEGAL_REQUEST; /* INVALID COMMAND OPERATION CODE */ @@ -5676,6 +5678,7 @@ int transport_send_check_condition_and_sense( case TCM_UNKNOWN_MODE_PAGE: /* CURRENT ERROR */ buffer[offset] = 0x70; + buffer[offset+SPC_ADD_SENSE_LEN_OFFSET] = 10; /* ILLEGAL REQUEST */ buffer[offset+SPC_SENSE_KEY_OFFSET] = ILLEGAL_REQUEST; /* INVALID FIELD IN CDB */ @@ -5684,6 +5687,7 @@ int transport_send_check_condition_and_sense( case TCM_CHECK_CONDITION_ABORT_CMD: /* CURRENT ERROR */ buffer[offset] = 0x70; + buffer[offset+SPC_ADD_SENSE_LEN_OFFSET] = 10; /* ABORTED COMMAND */ buffer[offset+SPC_SENSE_KEY_OFFSET] = ABORTED_COMMAND; /* BUS DEVICE RESET FUNCTION OCCURRED */ @@ -5693,6 +5697,7 @@ int transport_send_check_condition_and_sense( case TCM_INCORRECT_AMOUNT_OF_DATA: /* CURRENT ERROR */ buffer[offset] = 0x70; + buffer[offset+SPC_ADD_SENSE_LEN_OFFSET] = 10; /* ABORTED COMMAND */ buffer[offset+SPC_SENSE_KEY_OFFSET] = ABORTED_COMMAND; /* WRITE ERROR */ @@ -5703,6 +5708,7 @@ int transport_send_check_condition_and_sense( case TCM_INVALID_CDB_FIELD: /* CURRENT ERROR */ buffer[offset] = 0x70; + buffer[offset+SPC_ADD_SENSE_LEN_OFFSET] = 10; /* ABORTED COMMAND */ buffer[offset+SPC_SENSE_KEY_OFFSET] = ABORTED_COMMAND; /* INVALID FIELD IN CDB */ @@ -5711,6 +5717,7 @@ int transport_send_check_condition_and_sense( case TCM_INVALID_PARAMETER_LIST: /* CURRENT ERROR */ buffer[offset] = 0x70; + buffer[offset+SPC_ADD_SENSE_LEN_OFFSET] = 10; /* ABORTED COMMAND */ buffer[offset+SPC_SENSE_KEY_OFFSET] = ABORTED_COMMAND; /* INVALID FIELD IN PARAMETER LIST */ @@ -5719,6 +5726,7 @@ int transport_send_check_condition_and_sense( case TCM_UNEXPECTED_UNSOLICITED_DATA: /* CURRENT ERROR */ buffer[offset] = 0x70; + buffer[offset+SPC_ADD_SENSE_LEN_OFFSET] = 10; /* ABORTED COMMAND */ buffer[offset+SPC_SENSE_KEY_OFFSET] = ABORTED_COMMAND; /* WRITE ERROR */ @@ -5729,6 +5737,7 @@ int transport_send_check_condition_and_sense( case TCM_SERVICE_CRC_ERROR: /* CURRENT ERROR */ buffer[offset] = 0x70; + buffer[offset+SPC_ADD_SENSE_LEN_OFFSET] = 10; /* ABORTED COMMAND */ buffer[offset+SPC_SENSE_KEY_OFFSET] = ABORTED_COMMAND; /* PROTOCOL SERVICE CRC ERROR */ @@ -5739,6 +5748,7 @@ int transport_send_check_condition_and_sense( case TCM_SNACK_REJECTED: /* CURRENT ERROR */ buffer[offset] = 0x70; + buffer[offset+SPC_ADD_SENSE_LEN_OFFSET] = 10; /* ABORTED COMMAND */ buffer[offset+SPC_SENSE_KEY_OFFSET] = ABORTED_COMMAND; /* READ ERROR */ @@ -5749,6 +5759,7 @@ int transport_send_check_condition_and_sense( case TCM_WRITE_PROTECTED: /* CURRENT ERROR */ buffer[offset] = 0x70; + buffer[offset+SPC_ADD_SENSE_LEN_OFFSET] = 10; /* DATA PROTECT */ buffer[offset+SPC_SENSE_KEY_OFFSET] = DATA_PROTECT; /* WRITE PROTECTED */ @@ -5757,6 +5768,7 @@ int transport_send_check_condition_and_sense( case TCM_CHECK_CONDITION_UNIT_ATTENTION: /* CURRENT ERROR */ buffer[offset] = 0x70; + buffer[offset+SPC_ADD_SENSE_LEN_OFFSET] = 10; /* UNIT ATTENTION */ buffer[offset+SPC_SENSE_KEY_OFFSET] = UNIT_ATTENTION; core_scsi3_ua_for_check_condition(cmd, &asc, &ascq); @@ -5766,6 +5778,7 @@ int transport_send_check_condition_and_sense( case TCM_CHECK_CONDITION_NOT_READY: /* CURRENT ERROR */ buffer[offset] = 0x70; + buffer[offset+SPC_ADD_SENSE_LEN_OFFSET] = 10; /* Not Ready */ buffer[offset+SPC_SENSE_KEY_OFFSET] = NOT_READY; transport_get_sense_codes(cmd, &asc, &ascq); @@ -5776,6 +5789,7 @@ int transport_send_check_condition_and_sense( default: /* CURRENT ERROR */ buffer[offset] = 0x70; + buffer[offset+SPC_ADD_SENSE_LEN_OFFSET] = 10; /* ILLEGAL REQUEST */ buffer[offset+SPC_SENSE_KEY_OFFSET] = ILLEGAL_REQUEST; /* LOGICAL UNIT COMMUNICATION FAILURE */ diff --git a/drivers/xen/xenbus/xenbus_xs.c b/drivers/xen/xenbus/xenbus_xs.c index 5534690075a..daee5db4bef 100644 --- a/drivers/xen/xenbus/xenbus_xs.c +++ b/drivers/xen/xenbus/xenbus_xs.c @@ -801,6 +801,12 @@ static int process_msg(void) goto out; } + if (msg->hdr.len > XENSTORE_PAYLOAD_MAX) { + kfree(msg); + err = -EINVAL; + goto out; + } + body = kmalloc(msg->hdr.len + 1, GFP_NOIO | __GFP_HIGH); if (body == NULL) { kfree(msg); diff --git a/fs/dcache.c b/fs/dcache.c index d2f8feb75da..f598b98c00d 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -241,6 +241,7 @@ static void dentry_lru_add(struct dentry *dentry) static void __dentry_lru_del(struct dentry *dentry) { list_del_init(&dentry->d_lru); + dentry->d_flags &= ~DCACHE_SHRINK_LIST; dentry->d_sb->s_nr_dentry_unused--; dentry_stat.nr_unused--; } @@ -753,6 +754,7 @@ static void __shrink_dcache_sb(struct super_block *sb, int *count, int flags) spin_unlock(&dentry->d_lock); } else { list_move_tail(&dentry->d_lru, &tmp); + dentry->d_flags |= DCACHE_SHRINK_LIST; spin_unlock(&dentry->d_lock); if (!--cnt) break; @@ -1144,14 +1146,18 @@ static int select_parent(struct dentry * parent) /* * move only zero ref count dentries to the end * of the unused list for prune_dcache + * + * Those which are presently on the shrink list, being processed + * by shrink_dentry_list(), shouldn't be moved. Otherwise the + * loop in shrink_dcache_parent() might not make any progress + * and loop forever. */ - if (!dentry->d_count) { + if (dentry->d_count) { + dentry_lru_del(dentry); + } else if (!(dentry->d_flags & DCACHE_SHRINK_LIST)) { dentry_lru_move_tail(dentry); found++; - } else { - dentry_lru_del(dentry); } - /* * We can return to the caller if we have found some (this * ensures forward progress). We'll be coming back to find diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 4b983b88aa0..75de43a46f3 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -2001,17 +2001,16 @@ static int ext4_fill_flex_info(struct super_block *sb) struct ext4_group_desc *gdp = NULL; ext4_group_t flex_group_count; ext4_group_t flex_group; - int groups_per_flex = 0; + unsigned int groups_per_flex = 0; size_t size; int i; sbi->s_log_groups_per_flex = sbi->s_es->s_log_groups_per_flex; - groups_per_flex = 1 << sbi->s_log_groups_per_flex; - - if (groups_per_flex < 2) { + if (sbi->s_log_groups_per_flex < 1 || sbi->s_log_groups_per_flex > 31) { sbi->s_log_groups_per_flex = 0; return 1; } + groups_per_flex = 1 << sbi->s_log_groups_per_flex; /* We allocate both existing and potentially added groups */ flex_group_count = ((sbi->s_groups_count + groups_per_flex - 1) + diff --git a/fs/nfs/callback_proc.c b/fs/nfs/callback_proc.c index aaa09e948a9..b5c826e17b6 100644 --- a/fs/nfs/callback_proc.c +++ b/fs/nfs/callback_proc.c @@ -324,7 +324,7 @@ validate_seqid(struct nfs4_slot_table *tbl, struct cb_sequenceargs * args) dprintk("%s enter. slotid %d seqid %d\n", __func__, args->csa_slotid, args->csa_sequenceid); - if (args->csa_slotid > NFS41_BC_MAX_CALLBACKS) + if (args->csa_slotid >= NFS41_BC_MAX_CALLBACKS) return htonl(NFS4ERR_BADSLOT); slot = tbl->slots + args->csa_slotid; diff --git a/fs/nfs/objlayout/objio_osd.c b/fs/nfs/objlayout/objio_osd.c index 1d1dc1ee394..75fe694d78d 100644 --- a/fs/nfs/objlayout/objio_osd.c +++ b/fs/nfs/objlayout/objio_osd.c @@ -1006,7 +1006,8 @@ static bool objio_pg_test(struct nfs_pageio_descriptor *pgio, static struct pnfs_layoutdriver_type objlayout_type = { .id = LAYOUT_OSD2_OBJECTS, .name = "LAYOUT_OSD2_OBJECTS", - .flags = PNFS_LAYOUTRET_ON_SETATTR, + .flags = PNFS_LAYOUTRET_ON_SETATTR | + PNFS_LAYOUTRET_ON_ERROR, .alloc_layout_hdr = objlayout_alloc_layout_hdr, .free_layout_hdr = objlayout_free_layout_hdr, diff --git a/fs/nfs/objlayout/objlayout.c b/fs/nfs/objlayout/objlayout.c index 1d06f8e2ade..fefa1224aff 100644 --- a/fs/nfs/objlayout/objlayout.c +++ b/fs/nfs/objlayout/objlayout.c @@ -294,9 +294,11 @@ objlayout_read_done(struct objlayout_io_state *state, ssize_t status, bool sync) dprintk("%s: Begin status=%zd eof=%d\n", __func__, status, eof); rdata = state->rpcdata; rdata->task.tk_status = status; - if (status >= 0) { + if (likely(status >= 0)) { rdata->res.count = status; rdata->res.eof = eof; + } else { + rdata->pnfs_error = status; } objlayout_iodone(state); /* must not use state after this point */ @@ -380,15 +382,17 @@ objlayout_write_done(struct objlayout_io_state *state, ssize_t status, wdata = state->rpcdata; state->status = status; wdata->task.tk_status = status; - if (status >= 0) { + if (likely(status >= 0)) { wdata->res.count = status; wdata->verf.committed = state->committed; dprintk("%s: Return status %d committed %d\n", __func__, wdata->task.tk_status, wdata->verf.committed); - } else + } else { + wdata->pnfs_error = status; dprintk("%s: Return status %d\n", __func__, wdata->task.tk_status); + } objlayout_iodone(state); /* must not use state after this point */ diff --git a/fs/nfs/pnfs.c b/fs/nfs/pnfs.c index 36d2a29bfbe..99518872f42 100644 --- a/fs/nfs/pnfs.c +++ b/fs/nfs/pnfs.c @@ -1119,6 +1119,14 @@ pnfs_ld_write_done(struct nfs_write_data *data) data->mds_ops->rpc_release(data); return 0; } + if (NFS_SERVER(data->inode)->pnfs_curr_ld->flags & + PNFS_LAYOUTRET_ON_ERROR) { + /* Don't lo_commit on error, Server will needs to + * preform a file recovery. + */ + clear_bit(NFS_INO_LAYOUTCOMMIT, &NFS_I(data->inode)->flags); + pnfs_return_layout(data->inode); + } dprintk("%s: pnfs_error=%d, retry via MDS\n", __func__, data->pnfs_error); @@ -1167,6 +1175,10 @@ pnfs_ld_read_done(struct nfs_read_data *data) return 0; } + if (NFS_SERVER(data->inode)->pnfs_curr_ld->flags & + PNFS_LAYOUTRET_ON_ERROR) + pnfs_return_layout(data->inode); + dprintk("%s: pnfs_error=%d, retry via MDS\n", __func__, data->pnfs_error); status = nfs_initiate_read(data, NFS_CLIENT(data->inode), diff --git a/fs/nfs/pnfs.h b/fs/nfs/pnfs.h index 9d147d963bd..bb8b3247f29 100644 --- a/fs/nfs/pnfs.h +++ b/fs/nfs/pnfs.h @@ -68,6 +68,7 @@ enum { enum layoutdriver_policy_flags { /* Should the pNFS client commit and return the layout upon a setattr */ PNFS_LAYOUTRET_ON_SETATTR = 1 << 0, + PNFS_LAYOUTRET_ON_ERROR = 1 << 1, }; struct nfs4_deviceid_node; diff --git a/fs/nfs/super.c b/fs/nfs/super.c index 858d31be29c..7e8b07d4fa0 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -904,10 +904,24 @@ static struct nfs_parsed_mount_data *nfs_alloc_parsed_mount_data(unsigned int ve data->auth_flavor_len = 1; data->version = version; data->minorversion = 0; + security_init_mnt_opts(&data->lsm_opts); } return data; } +static void nfs_free_parsed_mount_data(struct nfs_parsed_mount_data *data) +{ + if (data) { + kfree(data->client_address); + kfree(data->mount_server.hostname); + kfree(data->nfs_server.export_path); + kfree(data->nfs_server.hostname); + kfree(data->fscache_uniq); + security_free_mnt_opts(&data->lsm_opts); + kfree(data); + } +} + /* * Sanity-check a server address provided by the mount command. * @@ -2218,9 +2232,7 @@ static struct dentry *nfs_fs_mount(struct file_system_type *fs_type, data = nfs_alloc_parsed_mount_data(NFS_DEFAULT_VERSION); mntfh = nfs_alloc_fhandle(); if (data == NULL || mntfh == NULL) - goto out_free_fh; - - security_init_mnt_opts(&data->lsm_opts); + goto out; /* Validate the mount data */ error = nfs_validate_mount_data(raw_data, data, mntfh, dev_name); @@ -2232,8 +2244,6 @@ static struct dentry *nfs_fs_mount(struct file_system_type *fs_type, #ifdef CONFIG_NFS_V4 if (data->version == 4) { mntroot = nfs4_try_mount(flags, dev_name, data); - kfree(data->client_address); - kfree(data->nfs_server.export_path); goto out; } #endif /* CONFIG_NFS_V4 */ @@ -2284,13 +2294,8 @@ static struct dentry *nfs_fs_mount(struct file_system_type *fs_type, s->s_flags |= MS_ACTIVE; out: - kfree(data->nfs_server.hostname); - kfree(data->mount_server.hostname); - kfree(data->fscache_uniq); - security_free_mnt_opts(&data->lsm_opts); -out_free_fh: + nfs_free_parsed_mount_data(data); nfs_free_fhandle(mntfh); - kfree(data); return mntroot; out_err_nosb: @@ -2613,9 +2618,7 @@ nfs4_remote_mount(struct file_system_type *fs_type, int flags, mntfh = nfs_alloc_fhandle(); if (data == NULL || mntfh == NULL) - goto out_free_fh; - - security_init_mnt_opts(&data->lsm_opts); + goto out; /* Get a volume representation */ server = nfs4_create_server(data, mntfh); @@ -2663,13 +2666,10 @@ nfs4_remote_mount(struct file_system_type *fs_type, int flags, s->s_flags |= MS_ACTIVE; - security_free_mnt_opts(&data->lsm_opts); nfs_free_fhandle(mntfh); return mntroot; out: - security_free_mnt_opts(&data->lsm_opts); -out_free_fh: nfs_free_fhandle(mntfh); return ERR_PTR(error); @@ -2855,7 +2855,7 @@ static struct dentry *nfs4_mount(struct file_system_type *fs_type, data = nfs_alloc_parsed_mount_data(4); if (data == NULL) - goto out_free_data; + goto out; /* Validate the mount data */ error = nfs4_validate_mount_data(raw_data, data, dev_name); @@ -2869,12 +2869,7 @@ static struct dentry *nfs4_mount(struct file_system_type *fs_type, error = PTR_ERR(res); out: - kfree(data->client_address); - kfree(data->nfs_server.export_path); - kfree(data->nfs_server.hostname); - kfree(data->fscache_uniq); -out_free_data: - kfree(data); + nfs_free_parsed_mount_data(data); dprintk("<-- nfs4_mount() = %d%s\n", error, error != 0 ? " [error]" : ""); return res; diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c index b9566e46219..4b470f6043e 100644 --- a/fs/nfsd/export.c +++ b/fs/nfsd/export.c @@ -88,7 +88,7 @@ static int expkey_parse(struct cache_detail *cd, char *mesg, int mlen) struct svc_expkey key; struct svc_expkey *ek = NULL; - if (mesg[mlen-1] != '\n') + if (mlen < 1 || mesg[mlen-1] != '\n') return -EINVAL; mesg[mlen-1] = 0; diff --git a/fs/notify/mark.c b/fs/notify/mark.c index 252ab1f6452..42ed195771f 100644 --- a/fs/notify/mark.c +++ b/fs/notify/mark.c @@ -135,9 +135,6 @@ void fsnotify_destroy_mark(struct fsnotify_mark *mark) mark->flags &= ~FSNOTIFY_MARK_FLAG_ALIVE; - /* 1 from caller and 1 for being on i_list/g_list */ - BUG_ON(atomic_read(&mark->refcnt) < 2); - spin_lock(&group->mark_lock); if (mark->flags & FSNOTIFY_MARK_FLAG_INODE) { @@ -181,6 +178,11 @@ void fsnotify_destroy_mark(struct fsnotify_mark *mark) if (inode && (mark->flags & FSNOTIFY_MARK_FLAG_OBJECT_PINNED)) iput(inode); + /* + * We don't necessarily have a ref on mark from caller so the above iput + * may have already destroyed it. Don't touch from now on. + */ + /* * it's possible that this group tried to destroy itself, but this * this mark was simultaneously being freed by inode. If that's the diff --git a/fs/proc/base.c b/fs/proc/base.c index 845c1d210ef..d4b5e408dcf 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -200,65 +200,7 @@ static int proc_root_link(struct inode *inode, struct path *path) return result; } -static struct mm_struct *__check_mem_permission(struct task_struct *task) -{ - struct mm_struct *mm; - - mm = get_task_mm(task); - if (!mm) - return ERR_PTR(-EINVAL); - - /* - * A task can always look at itself, in case it chooses - * to use system calls instead of load instructions. - */ - if (task == current) - return mm; - - /* - * If current is actively ptrace'ing, and would also be - * permitted to freshly attach with ptrace now, permit it. - */ - if (task_is_stopped_or_traced(task)) { - int match; - rcu_read_lock(); - match = (tracehook_tracer_task(task) == current); - rcu_read_unlock(); - if (match && ptrace_may_access(task, PTRACE_MODE_ATTACH)) - return mm; - } - - /* - * No one else is allowed. - */ - mmput(mm); - return ERR_PTR(-EPERM); -} - -/* - * If current may access user memory in @task return a reference to the - * corresponding mm, otherwise ERR_PTR. - */ -static struct mm_struct *check_mem_permission(struct task_struct *task) -{ - struct mm_struct *mm; - int err; - - /* - * Avoid racing if task exec's as we might get a new mm but validate - * against old credentials. - */ - err = mutex_lock_killable(&task->signal->cred_guard_mutex); - if (err) - return ERR_PTR(err); - - mm = __check_mem_permission(task); - mutex_unlock(&task->signal->cred_guard_mutex); - - return mm; -} - -struct mm_struct *mm_for_maps(struct task_struct *task) +static struct mm_struct *mm_access(struct task_struct *task, unsigned int mode) { struct mm_struct *mm; int err; @@ -269,7 +211,7 @@ struct mm_struct *mm_for_maps(struct task_struct *task) mm = get_task_mm(task); if (mm && mm != current->mm && - !ptrace_may_access(task, PTRACE_MODE_READ) && + !ptrace_may_access(task, mode) && !capable(CAP_SYS_RESOURCE)) { mmput(mm); mm = ERR_PTR(-EACCES); @@ -279,6 +221,11 @@ struct mm_struct *mm_for_maps(struct task_struct *task) return mm; } +struct mm_struct *mm_for_maps(struct task_struct *task) +{ + return mm_access(task, PTRACE_MODE_READ); +} + static int proc_pid_cmdline(struct task_struct *task, char * buffer) { int res = 0; @@ -823,38 +770,39 @@ static const struct file_operations proc_single_file_operations = { static int mem_open(struct inode* inode, struct file* file) { - file->private_data = (void*)((long)current->self_exec_id); + struct task_struct *task = get_proc_task(file->f_path.dentry->d_inode); + struct mm_struct *mm; + + if (!task) + return -ESRCH; + + mm = mm_access(task, PTRACE_MODE_ATTACH); + put_task_struct(task); + + if (IS_ERR(mm)) + return PTR_ERR(mm); + /* OK to pass negative loff_t, we can catch out-of-range */ file->f_mode |= FMODE_UNSIGNED_OFFSET; + file->private_data = mm; + return 0; } static ssize_t mem_read(struct file * file, char __user * buf, size_t count, loff_t *ppos) { - struct task_struct *task = get_proc_task(file->f_path.dentry->d_inode); + int ret; char *page; unsigned long src = *ppos; - int ret = -ESRCH; - struct mm_struct *mm; + struct mm_struct *mm = file->private_data; - if (!task) - goto out_no_task; + if (!mm) + return 0; - ret = -ENOMEM; page = (char *)__get_free_page(GFP_TEMPORARY); if (!page) - goto out; - - mm = check_mem_permission(task); - ret = PTR_ERR(mm); - if (IS_ERR(mm)) - goto out_free; - - ret = -EIO; - - if (file->private_data != (void*)((long)current->self_exec_id)) - goto out_put; + return -ENOMEM; ret = 0; @@ -881,13 +829,7 @@ static ssize_t mem_read(struct file * file, char __user * buf, } *ppos = src; -out_put: - mmput(mm); -out_free: free_page((unsigned long) page); -out: - put_task_struct(task); -out_no_task: return ret; } @@ -900,27 +842,15 @@ static ssize_t mem_write(struct file * file, const char __user *buf, { int copied; char *page; - struct task_struct *task = get_proc_task(file->f_path.dentry->d_inode); unsigned long dst = *ppos; - struct mm_struct *mm; + struct mm_struct *mm = file->private_data; - copied = -ESRCH; - if (!task) - goto out_no_task; + if (!mm) + return 0; - copied = -ENOMEM; page = (char *)__get_free_page(GFP_TEMPORARY); if (!page) - goto out_task; - - mm = check_mem_permission(task); - copied = PTR_ERR(mm); - if (IS_ERR(mm)) - goto out_free; - - copied = -EIO; - if (file->private_data != (void *)((long)current->self_exec_id)) - goto out_mm; + return -ENOMEM; copied = 0; while (count > 0) { @@ -944,13 +874,7 @@ static ssize_t mem_write(struct file * file, const char __user *buf, } *ppos = dst; -out_mm: - mmput(mm); -out_free: free_page((unsigned long) page); -out_task: - put_task_struct(task); -out_no_task: return copied; } #endif @@ -971,11 +895,20 @@ loff_t mem_lseek(struct file *file, loff_t offset, int orig) return file->f_pos; } +static int mem_release(struct inode *inode, struct file *file) +{ + struct mm_struct *mm = file->private_data; + + mmput(mm); + return 0; +} + static const struct file_operations proc_mem_operations = { .llseek = mem_lseek, .read = mem_read, .write = mem_write, .open = mem_open, + .release = mem_release, }; static ssize_t environ_read(struct file *file, char __user *buf, diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index c7d4ee663f1..3487b066500 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c @@ -516,6 +516,9 @@ static int clear_refs_pte_range(pmd_t *pmd, unsigned long addr, if (!page) continue; + if (PageReserved(page)) + continue; + /* Clear accessed and referenced bits. */ ptep_test_and_clear_young(vma, addr, pte); ClearPageReferenced(page); diff --git a/fs/proc/uptime.c b/fs/proc/uptime.c index 766b1d45605..29166ecd03a 100644 --- a/fs/proc/uptime.c +++ b/fs/proc/uptime.c @@ -11,15 +11,20 @@ static int uptime_proc_show(struct seq_file *m, void *v) { struct timespec uptime; struct timespec idle; + cputime64_t idletime; + u64 nsec; + u32 rem; int i; - cputime_t idletime = cputime_zero; + idletime = 0; for_each_possible_cpu(i) idletime = cputime64_add(idletime, kstat_cpu(i).cpustat.idle); do_posix_clock_monotonic_gettime(&uptime); monotonic_to_bootbased(&uptime); - cputime_to_timespec(idletime, &idle); + nsec = cputime64_to_jiffies64(idletime) * TICK_NSEC; + idle.tv_sec = div_u64_rem(nsec, NSEC_PER_SEC, &rem); + idle.tv_nsec = rem; seq_printf(m, "%lu.%02lu %lu.%02lu\n", (unsigned long) uptime.tv_sec, (uptime.tv_nsec / (NSEC_PER_SEC / 100)), diff --git a/fs/ubifs/debug.h b/fs/ubifs/debug.h index a811ac4a26b..fd75b635dae 100644 --- a/fs/ubifs/debug.h +++ b/fs/ubifs/debug.h @@ -121,20 +121,21 @@ const char *dbg_key_str1(const struct ubifs_info *c, const union ubifs_key *key); /* - * DBGKEY macros require @dbg_lock to be held, which it is in the dbg message - * macros. + * TODO: these macros are now broken because there is no locking around them + * and we use a global buffer for the key string. This means that in case of + * concurrent execution we will end up with incorrect and messy key strings. */ #define DBGKEY(key) dbg_key_str0(c, (key)) #define DBGKEY1(key) dbg_key_str1(c, (key)) -#define ubifs_dbg_msg(type, fmt, ...) do { \ - spin_lock(&dbg_lock); \ - pr_debug("UBIFS DBG " type ": " fmt "\n", ##__VA_ARGS__); \ - spin_unlock(&dbg_lock); \ -} while (0) +#define ubifs_dbg_msg(type, fmt, ...) \ + pr_debug("UBIFS DBG " type ": " fmt "\n", ##__VA_ARGS__) /* Just a debugging messages not related to any specific UBIFS subsystem */ -#define dbg_msg(fmt, ...) ubifs_dbg_msg("msg", fmt, ##__VA_ARGS__) +#define dbg_msg(fmt, ...) \ + printk(KERN_DEBUG "UBIFS DBG (pid %d): %s: " fmt "\n", current->pid, \ + __func__, ##__VA_ARGS__) + /* General messages */ #define dbg_gen(fmt, ...) ubifs_dbg_msg("gen", fmt, ##__VA_ARGS__) /* Additional journal messages */ diff --git a/include/acpi/acpi_numa.h b/include/acpi/acpi_numa.h index 17397267217..451823cb883 100644 --- a/include/acpi/acpi_numa.h +++ b/include/acpi/acpi_numa.h @@ -15,6 +15,7 @@ extern int pxm_to_node(int); extern int node_to_pxm(int); extern void __acpi_map_pxm_to_node(int, int); extern int acpi_map_pxm_to_node(int); +extern unsigned char acpi_srat_revision; #endif /* CONFIG_ACPI_NUMA */ #endif /* __ACP_NUMA_H */ diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 5edc4108c9a..981ba5331f7 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -675,6 +675,9 @@ extern int blk_insert_cloned_request(struct request_queue *q, struct request *rq); extern void blk_delay_queue(struct request_queue *, unsigned long); extern void blk_recount_segments(struct request_queue *, struct bio *); +extern int scsi_verify_blk_ioctl(struct block_device *, unsigned int); +extern int scsi_cmd_blk_ioctl(struct block_device *, fmode_t, + unsigned int, void __user *); extern int scsi_cmd_ioctl(struct request_queue *, struct gendisk *, fmode_t, unsigned int, void __user *); extern int sg_scsi_ioctl(struct request_queue *, struct gendisk *, fmode_t, diff --git a/include/linux/dcache.h b/include/linux/dcache.h index 8f848e462b2..f13bb6dd156 100644 --- a/include/linux/dcache.h +++ b/include/linux/dcache.h @@ -207,6 +207,7 @@ struct dentry_operations { #define DCACHE_CANT_MOUNT 0x0100 #define DCACHE_GENOCIDE 0x0200 +#define DCACHE_SHRINK_LIST 0x0400 #define DCACHE_OP_HASH 0x1000 #define DCACHE_OP_COMPARE 0x2000 diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h index 043d06a69d7..4a8da84487b 100644 --- a/include/linux/memcontrol.h +++ b/include/linux/memcontrol.h @@ -120,6 +120,8 @@ struct zone_reclaim_stat* mem_cgroup_get_reclaim_stat_from_page(struct page *page); extern void mem_cgroup_print_oom_info(struct mem_cgroup *memcg, struct task_struct *p); +extern void mem_cgroup_replace_page_cache(struct page *oldpage, + struct page *newpage); #ifdef CONFIG_CGROUP_MEM_RES_CTLR_SWAP extern int do_swap_account; @@ -371,6 +373,10 @@ static inline void mem_cgroup_count_vm_event(struct mm_struct *mm, enum vm_event_item idx) { } +static inline void mem_cgroup_replace_page_cache(struct page *oldpage, + struct page *newpage) +{ +} #endif /* CONFIG_CGROUP_MEM_CONT */ #if !defined(CONFIG_CGROUP_MEM_RES_CTLR) || !defined(CONFIG_DEBUG_VM) diff --git a/include/linux/pci_regs.h b/include/linux/pci_regs.h index e8840964aca..dad7d9a4abc 100644 --- a/include/linux/pci_regs.h +++ b/include/linux/pci_regs.h @@ -392,7 +392,7 @@ #define PCI_EXP_TYPE_DOWNSTREAM 0x6 /* Downstream Port */ #define PCI_EXP_TYPE_PCI_BRIDGE 0x7 /* PCI/PCI-X Bridge */ #define PCI_EXP_TYPE_RC_END 0x9 /* Root Complex Integrated Endpoint */ -#define PCI_EXP_TYPE_RC_EC 0x10 /* Root Complex Event Collector */ +#define PCI_EXP_TYPE_RC_EC 0xa /* Root Complex Event Collector */ #define PCI_EXP_FLAGS_SLOT 0x0100 /* Slot implemented */ #define PCI_EXP_FLAGS_IRQ 0x3e00 /* Interrupt message number */ #define PCI_EXP_DEVCAP 4 /* Device capabilities */ diff --git a/include/linux/sunrpc/svcsock.h b/include/linux/sunrpc/svcsock.h index 85c50b40759..c84e9741cb2 100644 --- a/include/linux/sunrpc/svcsock.h +++ b/include/linux/sunrpc/svcsock.h @@ -34,7 +34,7 @@ struct svc_sock { /* * Function prototypes. */ -void svc_close_all(struct list_head *); +void svc_close_all(struct svc_serv *); int svc_recv(struct svc_rqst *, long); int svc_send(struct svc_rqst *); void svc_drop(struct svc_rqst *); diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index 8a4c309d234..eeeda13b4d5 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -1075,6 +1075,7 @@ struct v4l2_querymenu { #define V4L2_CTRL_FLAG_NEXT_CTRL 0x80000000 /* User-class control IDs defined by V4L2 */ +#define V4L2_CID_MAX_CTRLS 1024 #define V4L2_CID_BASE (V4L2_CTRL_CLASS_USER | 0x900) #define V4L2_CID_USER_BASE V4L2_CID_BASE /* IDs reserved for driver specific controls */ diff --git a/include/target/target_core_base.h b/include/target/target_core_base.h index 561ac99def5..0fe667901ed 100644 --- a/include/target/target_core_base.h +++ b/include/target/target_core_base.h @@ -36,6 +36,7 @@ #define TRANSPORT_SENSE_BUFFER SCSI_SENSE_BUFFERSIZE /* Used by transport_send_check_condition_and_sense() */ #define SPC_SENSE_KEY_OFFSET 2 +#define SPC_ADD_SENSE_LEN_OFFSET 7 #define SPC_ASC_KEY_OFFSET 12 #define SPC_ASCQ_KEY_OFFSET 13 #define TRANSPORT_IQN_LEN 224 diff --git a/include/xen/interface/io/xs_wire.h b/include/xen/interface/io/xs_wire.h index 99fcffb372d..454ee262923 100644 --- a/include/xen/interface/io/xs_wire.h +++ b/include/xen/interface/io/xs_wire.h @@ -84,4 +84,7 @@ struct xenstore_domain_interface { XENSTORE_RING_IDX rsp_cons, rsp_prod; }; +/* Violating this is very bad. See docs/misc/xenstore.txt. */ +#define XENSTORE_PAYLOAD_MAX 4096 + #endif /* _XS_WIRE_H */ diff --git a/init/do_mounts.c b/init/do_mounts.c index c0851a8e030..ef6478fbb54 100644 --- a/init/do_mounts.c +++ b/init/do_mounts.c @@ -360,15 +360,42 @@ void __init mount_block_root(char *name, int flags) } #ifdef CONFIG_ROOT_NFS + +#define NFSROOT_TIMEOUT_MIN 5 +#define NFSROOT_TIMEOUT_MAX 30 +#define NFSROOT_RETRY_MAX 5 + static int __init mount_nfs_root(void) { char *root_dev, *root_data; + unsigned int timeout; + int try, err; - if (nfs_root_data(&root_dev, &root_data) != 0) - return 0; - if (do_mount_root(root_dev, "nfs", root_mountflags, root_data) != 0) + err = nfs_root_data(&root_dev, &root_data); + if (err != 0) return 0; - return 1; + + /* + * The server or network may not be ready, so try several + * times. Stop after a few tries in case the client wants + * to fall back to other boot methods. + */ + timeout = NFSROOT_TIMEOUT_MIN; + for (try = 1; ; try++) { + err = do_mount_root(root_dev, "nfs", + root_mountflags, root_data); + if (err == 0) + return 1; + if (try > NFSROOT_RETRY_MAX) + break; + + /* Wait, in case the server refused us immediately */ + ssleep(timeout); + timeout <<= 1; + if (timeout > NFSROOT_TIMEOUT_MAX) + timeout = NFSROOT_TIMEOUT_MAX; + } + return 0; } #endif diff --git a/kernel/kprobes.c b/kernel/kprobes.c index 77981813a1e..e0f0bdd92ca 100644 --- a/kernel/kprobes.c +++ b/kernel/kprobes.c @@ -1077,6 +1077,7 @@ void __kprobes kprobe_flush_task(struct task_struct *tk) /* Early boot. kretprobe_table_locks not yet initialized. */ return; + INIT_HLIST_HEAD(&empty_rp); hash = hash_ptr(tk, KPROBE_HASH_BITS); head = &kretprobe_inst_table[hash]; kretprobe_table_lock(hash, &flags); @@ -1085,7 +1086,6 @@ void __kprobes kprobe_flush_task(struct task_struct *tk) recycle_rp_inst(ri, &empty_rp); } kretprobe_table_unlock(hash, &flags); - INIT_HLIST_HEAD(&empty_rp); hlist_for_each_entry_safe(ri, node, tmp, &empty_rp, hlist) { hlist_del(&ri->hlist); kfree(ri); diff --git a/mm/filemap.c b/mm/filemap.c index dd828ea59dc..3c981baadb7 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -396,24 +396,11 @@ EXPORT_SYMBOL(filemap_write_and_wait_range); int replace_page_cache_page(struct page *old, struct page *new, gfp_t gfp_mask) { int error; - struct mem_cgroup *memcg = NULL; VM_BUG_ON(!PageLocked(old)); VM_BUG_ON(!PageLocked(new)); VM_BUG_ON(new->mapping); - /* - * This is not page migration, but prepare_migration and - * end_migration does enough work for charge replacement. - * - * In the longer term we probably want a specialized function - * for moving the charge from old to new in a more efficient - * manner. - */ - error = mem_cgroup_prepare_migration(old, new, &memcg, gfp_mask); - if (error) - return error; - error = radix_tree_preload(gfp_mask & ~__GFP_HIGHMEM); if (!error) { struct address_space *mapping = old->mapping; @@ -435,13 +422,12 @@ int replace_page_cache_page(struct page *old, struct page *new, gfp_t gfp_mask) if (PageSwapBacked(new)) __inc_zone_page_state(new, NR_SHMEM); spin_unlock_irq(&mapping->tree_lock); + /* mem_cgroup codes must not be called under tree_lock */ + mem_cgroup_replace_page_cache(old, new); radix_tree_preload_end(); if (freepage) freepage(old); page_cache_release(old); - mem_cgroup_end_migration(memcg, old, new, true); - } else { - mem_cgroup_end_migration(memcg, old, new, false); } return error; diff --git a/mm/memcontrol.c b/mm/memcontrol.c index d99217b322a..3791581d3e5 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -3422,6 +3422,50 @@ int mem_cgroup_shmem_charge_fallback(struct page *page, return ret; } +/* + * At replace page cache, newpage is not under any memcg but it's on + * LRU. So, this function doesn't touch res_counter but handles LRU + * in correct way. Both pages are locked so we cannot race with uncharge. + */ +void mem_cgroup_replace_page_cache(struct page *oldpage, + struct page *newpage) +{ + struct mem_cgroup *memcg; + struct page_cgroup *pc; + struct zone *zone; + enum charge_type type = MEM_CGROUP_CHARGE_TYPE_CACHE; + unsigned long flags; + + if (mem_cgroup_disabled()) + return; + + pc = lookup_page_cgroup(oldpage); + /* fix accounting on old pages */ + lock_page_cgroup(pc); + memcg = pc->mem_cgroup; + mem_cgroup_charge_statistics(memcg, PageCgroupCache(pc), -1); + ClearPageCgroupUsed(pc); + unlock_page_cgroup(pc); + + if (PageSwapBacked(oldpage)) + type = MEM_CGROUP_CHARGE_TYPE_SHMEM; + + zone = page_zone(newpage); + pc = lookup_page_cgroup(newpage); + /* + * Even if newpage->mapping was NULL before starting replacement, + * the newpage may be on LRU(or pagevec for LRU) already. We lock + * LRU while we overwrite pc->mem_cgroup. + */ + spin_lock_irqsave(&zone->lru_lock, flags); + if (PageLRU(newpage)) + del_page_from_lru_list(zone, newpage, page_lru(newpage)); + __mem_cgroup_commit_charge(memcg, newpage, 1, pc, type); + if (PageLRU(newpage)) + add_page_to_lru_list(zone, newpage, page_lru(newpage)); + spin_unlock_irqrestore(&zone->lru_lock, flags); +} + #ifdef CONFIG_DEBUG_VM static struct page_cgroup *lookup_page_cgroup_used(struct page *page) { diff --git a/mm/page_alloc.c b/mm/page_alloc.c index a33fe6339b0..cf077d1d38a 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -5599,6 +5599,17 @@ __count_immobile_pages(struct zone *zone, struct page *page, int count) bool is_pageblock_removable_nolock(struct page *page) { struct zone *zone = page_zone(page); + unsigned long pfn = page_to_pfn(page); + + /* + * We have to be careful here because we are iterating over memory + * sections which are not zone aware so we might end up outside of + * the zone but still within the section. + */ + if (!zone || zone->zone_start_pfn > pfn || + zone->zone_start_pfn + zone->spanned_pages <= pfn) + return false; + return __count_immobile_pages(zone, page, 0); } diff --git a/net/ipv4/ah4.c b/net/ipv4/ah4.c index c1f4154552f..c7056b2e831 100644 --- a/net/ipv4/ah4.c +++ b/net/ipv4/ah4.c @@ -136,8 +136,6 @@ static void ah_output_done(struct crypto_async_request *base, int err) memcpy(top_iph+1, iph+1, top_iph->ihl*4 - sizeof(struct iphdr)); } - err = ah->nexthdr; - kfree(AH_SKB_CB(skb)->tmp); xfrm_output_resume(skb, err); } @@ -264,12 +262,12 @@ static void ah_input_done(struct crypto_async_request *base, int err) if (err) goto out; + err = ah->nexthdr; + skb->network_header += ah_hlen; memcpy(skb_network_header(skb), work_iph, ihl); __skb_pull(skb, ah_hlen + ihl); skb_set_transport_header(skb, -ihl); - - err = ah->nexthdr; out: kfree(AH_SKB_CB(skb)->tmp); xfrm_input_resume(skb, err); diff --git a/net/ipv6/ah6.c b/net/ipv6/ah6.c index 2195ae65192..7a33aaa0022 100644 --- a/net/ipv6/ah6.c +++ b/net/ipv6/ah6.c @@ -324,8 +324,6 @@ static void ah6_output_done(struct crypto_async_request *base, int err) #endif } - err = ah->nexthdr; - kfree(AH_SKB_CB(skb)->tmp); xfrm_output_resume(skb, err); } @@ -466,12 +464,12 @@ static void ah6_input_done(struct crypto_async_request *base, int err) if (err) goto out; + err = ah->nexthdr; + skb->network_header += ah_hlen; memcpy(skb_network_header(skb), work_iph, hdr_len); __skb_pull(skb, ah_hlen + hdr_len); skb_set_transport_header(skb, -hdr_len); - - err = ah->nexthdr; out: kfree(AH_SKB_CB(skb)->tmp); xfrm_input_resume(skb, err); diff --git a/net/mac80211/wpa.c b/net/mac80211/wpa.c index 8f6a302d2ac..aa1c40ab6a7 100644 --- a/net/mac80211/wpa.c +++ b/net/mac80211/wpa.c @@ -109,7 +109,7 @@ ieee80211_rx_h_michael_mic_verify(struct ieee80211_rx_data *rx) if (status->flag & RX_FLAG_MMIC_ERROR) goto mic_fail; - if (!(status->flag & RX_FLAG_IV_STRIPPED)) + if (!(status->flag & RX_FLAG_IV_STRIPPED) && rx->key) goto update_iv; return RX_CONTINUE; diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c index 2b90292e950..ce5f111fe32 100644 --- a/net/sunrpc/svc.c +++ b/net/sunrpc/svc.c @@ -167,6 +167,7 @@ svc_pool_map_alloc_arrays(struct svc_pool_map *m, unsigned int maxpools) fail_free: kfree(m->to_pool); + m->to_pool = NULL; fail: return -ENOMEM; } @@ -287,7 +288,9 @@ svc_pool_map_put(void) if (!--m->count) { m->mode = SVC_POOL_DEFAULT; kfree(m->to_pool); + m->to_pool = NULL; kfree(m->pool_to); + m->pool_to = NULL; m->npools = 0; } @@ -472,17 +475,20 @@ svc_destroy(struct svc_serv *serv) printk("svc_destroy: no threads for serv=%p!\n", serv); del_timer_sync(&serv->sv_temptimer); - - svc_close_all(&serv->sv_tempsocks); + /* + * The set of xprts (contained in the sv_tempsocks and + * sv_permsocks lists) is now constant, since it is modified + * only by accepting new sockets (done by service threads in + * svc_recv) or aging old ones (done by sv_temptimer), or + * configuration changes (excluded by whatever locking the + * caller is using--nfsd_mutex in the case of nfsd). So it's + * safe to traverse those lists and shut everything down: + */ + svc_close_all(serv); if (serv->sv_shutdown) serv->sv_shutdown(serv); - svc_close_all(&serv->sv_permsocks); - - BUG_ON(!list_empty(&serv->sv_permsocks)); - BUG_ON(!list_empty(&serv->sv_tempsocks)); - cache_clean_deferred(serv); if (svc_serv_is_pooled(serv)) diff --git a/net/sunrpc/svc_xprt.c b/net/sunrpc/svc_xprt.c index bd31208bbb6..9d7ed0b48b5 100644 --- a/net/sunrpc/svc_xprt.c +++ b/net/sunrpc/svc_xprt.c @@ -901,14 +901,7 @@ void svc_delete_xprt(struct svc_xprt *xprt) spin_lock_bh(&serv->sv_lock); if (!test_and_set_bit(XPT_DETACHED, &xprt->xpt_flags)) list_del_init(&xprt->xpt_list); - /* - * The only time we're called while xpt_ready is still on a list - * is while the list itself is about to be destroyed (in - * svc_destroy). BUT svc_xprt_enqueue could still be attempting - * to add new entries to the sp_sockets list, so we can't leave - * a freed xprt on it. - */ - list_del_init(&xprt->xpt_ready); + BUG_ON(!list_empty(&xprt->xpt_ready)); if (test_bit(XPT_TEMP, &xprt->xpt_flags)) serv->sv_tmpcnt--; spin_unlock_bh(&serv->sv_lock); @@ -936,22 +929,48 @@ void svc_close_xprt(struct svc_xprt *xprt) } EXPORT_SYMBOL_GPL(svc_close_xprt); -void svc_close_all(struct list_head *xprt_list) +static void svc_close_list(struct list_head *xprt_list) +{ + struct svc_xprt *xprt; + + list_for_each_entry(xprt, xprt_list, xpt_list) { + set_bit(XPT_CLOSE, &xprt->xpt_flags); + set_bit(XPT_BUSY, &xprt->xpt_flags); + } +} + +void svc_close_all(struct svc_serv *serv) { + struct svc_pool *pool; struct svc_xprt *xprt; struct svc_xprt *tmp; + int i; + + svc_close_list(&serv->sv_tempsocks); + svc_close_list(&serv->sv_permsocks); + for (i = 0; i < serv->sv_nrpools; i++) { + pool = &serv->sv_pools[i]; + + spin_lock_bh(&pool->sp_lock); + while (!list_empty(&pool->sp_sockets)) { + xprt = list_first_entry(&pool->sp_sockets, struct svc_xprt, xpt_ready); + list_del_init(&xprt->xpt_ready); + } + spin_unlock_bh(&pool->sp_lock); + } /* - * The server is shutting down, and no more threads are running. - * svc_xprt_enqueue() might still be running, but at worst it - * will re-add the xprt to sp_sockets, which will soon get - * freed. So we don't bother with any more locking, and don't - * leave the close to the (nonexistent) server threads: + * At this point the sp_sockets lists will stay empty, since + * svc_enqueue will not add new entries without taking the + * sp_lock and checking XPT_BUSY. */ - list_for_each_entry_safe(xprt, tmp, xprt_list, xpt_list) { - set_bit(XPT_CLOSE, &xprt->xpt_flags); + list_for_each_entry_safe(xprt, tmp, &serv->sv_tempsocks, xpt_list) svc_delete_xprt(xprt); - } + list_for_each_entry_safe(xprt, tmp, &serv->sv_permsocks, xpt_list) + svc_delete_xprt(xprt); + + BUG_ON(!list_empty(&serv->sv_permsocks)); + BUG_ON(!list_empty(&serv->sv_tempsocks)); } /* diff --git a/scripts/kconfig/streamline_config.pl b/scripts/kconfig/streamline_config.pl index a4fe923c013..25f1e71c9bb 100644 --- a/scripts/kconfig/streamline_config.pl +++ b/scripts/kconfig/streamline_config.pl @@ -242,33 +242,61 @@ sub read_kconfig { read_kconfig($kconfig); } +sub convert_vars { + my ($line, %vars) = @_; + + my $process = ""; + + while ($line =~ s/^(.*?)(\$\((.*?)\))//) { + my $start = $1; + my $variable = $2; + my $var = $3; + + if (defined($vars{$var})) { + $process .= $start . $vars{$var}; + } else { + $process .= $start . $variable; + } + } + + $process .= $line; + + return $process; +} + # Read all Makefiles to map the configs to the objects foreach my $makefile (@makefiles) { - my $cont = 0; + my $line = ""; + my %make_vars; open(MIN,$makefile) || die "Can't open $makefile"; while () { + # if this line ends with a backslash, continue + chomp; + if (/^(.*)\\$/) { + $line .= $1; + next; + } + + $line .= $_; + $_ = $line; + $line = ""; + my $objs; - # is this a line after a line with a backslash? - if ($cont && /(\S.*)$/) { - $objs = $1; - } - $cont = 0; + $_ = convert_vars($_, %make_vars); # collect objects after obj-$(CONFIG_FOO_BAR) if (/obj-\$\((CONFIG_[^\)]*)\)\s*[+:]?=\s*(.*)/) { $var = $1; $objs = $2; + + # check if variables are set + } elsif (/^\s*(\S+)\s*[:]?=\s*(.*\S)/) { + $make_vars{$1} = $2; } if (defined($objs)) { - # test if the line ends with a backslash - if ($objs =~ m,(.*)\\$,) { - $objs = $1; - $cont = 1; - } - foreach my $obj (split /\s+/,$objs) { $obj =~ s/-/_/g; if ($obj =~ /(.*)\.o$/) { diff --git a/scripts/recordmcount.h b/scripts/recordmcount.h index f40a6af6bf4..54e35c1e594 100644 --- a/scripts/recordmcount.h +++ b/scripts/recordmcount.h @@ -462,7 +462,7 @@ __has_rel_mcount(Elf_Shdr const *const relhdr, /* is SHT_REL or SHT_RELA */ succeed_file(); } if (w(txthdr->sh_type) != SHT_PROGBITS || - !(w(txthdr->sh_flags) & SHF_EXECINSTR)) + !(_w(txthdr->sh_flags) & SHF_EXECINSTR)) return NULL; return txtname; } diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c index da36d2c085a..5335605571f 100644 --- a/security/integrity/ima/ima_api.c +++ b/security/integrity/ima/ima_api.c @@ -177,8 +177,8 @@ void ima_store_measurement(struct ima_iint_cache *iint, struct file *file, strncpy(entry->template.file_name, filename, IMA_EVENT_NAME_LEN_MAX); result = ima_store_template(entry, violation, inode); - if (!result) + if (!result || result == -EEXIST) iint->flags |= IMA_MEASURED; - else + if (result < 0) kfree(entry); } diff --git a/security/integrity/ima/ima_queue.c b/security/integrity/ima/ima_queue.c index 8e28f04a5e2..55a6271bce7 100644 --- a/security/integrity/ima/ima_queue.c +++ b/security/integrity/ima/ima_queue.c @@ -23,6 +23,8 @@ #include #include "ima.h" +#define AUDIT_CAUSE_LEN_MAX 32 + LIST_HEAD(ima_measurements); /* list of all measurements */ /* key: inode (before secure-hashing a file) */ @@ -94,7 +96,8 @@ static int ima_pcr_extend(const u8 *hash) result = tpm_pcr_extend(TPM_ANY_NUM, CONFIG_IMA_MEASURE_PCR_IDX, hash); if (result != 0) - pr_err("IMA: Error Communicating to TPM chip\n"); + pr_err("IMA: Error Communicating to TPM chip, result: %d\n", + result); return result; } @@ -106,14 +109,16 @@ int ima_add_template_entry(struct ima_template_entry *entry, int violation, { u8 digest[IMA_DIGEST_SIZE]; const char *audit_cause = "hash_added"; + char tpm_audit_cause[AUDIT_CAUSE_LEN_MAX]; int audit_info = 1; - int result = 0; + int result = 0, tpmresult = 0; mutex_lock(&ima_extend_list_mutex); if (!violation) { memcpy(digest, entry->digest, sizeof digest); if (ima_lookup_digest_entry(digest)) { audit_cause = "hash_exists"; + result = -EEXIST; goto out; } } @@ -128,9 +133,11 @@ int ima_add_template_entry(struct ima_template_entry *entry, int violation, if (violation) /* invalidate pcr */ memset(digest, 0xff, sizeof digest); - result = ima_pcr_extend(digest); - if (result != 0) { - audit_cause = "TPM error"; + tpmresult = ima_pcr_extend(digest); + if (tpmresult != 0) { + snprintf(tpm_audit_cause, AUDIT_CAUSE_LEN_MAX, "TPM_error(%d)", + tpmresult); + audit_cause = tpm_audit_cause; audit_info = 0; } out: diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index 08ec073444e..e289a13c488 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -474,7 +474,12 @@ static inline u32 get_wcaps(struct hda_codec *codec, hda_nid_t nid) } /* get the widget type from widget capability bits */ -#define get_wcaps_type(wcaps) (((wcaps) & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT) +static inline int get_wcaps_type(unsigned int wcaps) +{ + if (!wcaps) + return -1; /* invalid type */ + return (wcaps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; +} static inline unsigned int get_wcaps_channels(u32 wcaps) { diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c index bfe74c2fb07..6fe944a386c 100644 --- a/sound/pci/hda/hda_proc.c +++ b/sound/pci/hda/hda_proc.c @@ -54,6 +54,8 @@ static const char *get_wid_type_name(unsigned int wid_value) [AC_WID_BEEP] = "Beep Generator Widget", [AC_WID_VENDOR] = "Vendor Defined Widget", }; + if (wid_value == -1) + return "UNKNOWN Widget"; wid_value &= 0xf; if (names[wid_value]) return names[wid_value]; diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 5d2e97abb2e..0d8db75535d 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -1602,7 +1602,7 @@ static const struct snd_pci_quirk stac92hd73xx_cfg_tbl[] = { SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02bd, "Dell Studio 1557", STAC_DELL_M6_DMIC), SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02fe, - "Dell Studio XPS 1645", STAC_DELL_M6_BOTH), + "Dell Studio XPS 1645", STAC_DELL_M6_DMIC), SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0413, "Dell Studio 1558", STAC_DELL_M6_DMIC), {} /* terminator */ diff --git a/sound/pci/ice1712/amp.c b/sound/pci/ice1712/amp.c index e328cfb7620..e525da2673b 100644 --- a/sound/pci/ice1712/amp.c +++ b/sound/pci/ice1712/amp.c @@ -68,8 +68,11 @@ static int __devinit snd_vt1724_amp_init(struct snd_ice1712 *ice) static int __devinit snd_vt1724_amp_add_controls(struct snd_ice1712 *ice) { - /* we use pins 39 and 41 of the VT1616 for left and right read outputs */ - snd_ac97_write_cache(ice->ac97, 0x5a, snd_ac97_read(ice->ac97, 0x5a) & ~0x8000); + if (ice->ac97) + /* we use pins 39 and 41 of the VT1616 for left and right + read outputs */ + snd_ac97_write_cache(ice->ac97, 0x5a, + snd_ac97_read(ice->ac97, 0x5a) & ~0x8000); return 0; } diff --git a/sound/pci/oxygen/xonar_wm87x6.c b/sound/pci/oxygen/xonar_wm87x6.c index 42d1ab13621..915546a7954 100644 --- a/sound/pci/oxygen/xonar_wm87x6.c +++ b/sound/pci/oxygen/xonar_wm87x6.c @@ -177,6 +177,7 @@ static void wm8776_registers_init(struct oxygen *chip) struct xonar_wm87x6 *data = chip->model_data; wm8776_write(chip, WM8776_RESET, 0); + wm8776_write(chip, WM8776_PHASESWAP, WM8776_PH_MASK); wm8776_write(chip, WM8776_DACCTRL1, WM8776_DZCEN | WM8776_PL_LEFT_LEFT | WM8776_PL_RIGHT_RIGHT); wm8776_write(chip, WM8776_DACMUTE, chip->dac_mute ? WM8776_DMUTE : 0); diff --git a/sound/usb/usx2y/usb_stream.c b/sound/usb/usx2y/usb_stream.c index c400ade3ff0..1e7a47a8660 100644 --- a/sound/usb/usx2y/usb_stream.c +++ b/sound/usb/usx2y/usb_stream.c @@ -674,7 +674,7 @@ int usb_stream_start(struct usb_stream_kernel *sk) inurb->transfer_buffer_length = inurb->number_of_packets * inurb->iso_frame_desc[0].length; - preempt_disable(); + if (u == 0) { int now; struct usb_device *dev = inurb->dev; @@ -686,19 +686,17 @@ int usb_stream_start(struct usb_stream_kernel *sk) } err = usb_submit_urb(inurb, GFP_ATOMIC); if (err < 0) { - preempt_enable(); snd_printk(KERN_ERR"usb_submit_urb(sk->inurb[%i])" " returned %i\n", u, err); return err; } err = usb_submit_urb(outurb, GFP_ATOMIC); if (err < 0) { - preempt_enable(); snd_printk(KERN_ERR"usb_submit_urb(sk->outurb[%i])" " returned %i\n", u, err); return err; } - preempt_enable(); + if (inurb->start_frame != outurb->start_frame) { snd_printd(KERN_DEBUG "u[%i] start_frames differ in:%u out:%u\n", From 8f738fc775d4a08055d0dea7e9531966541ff465 Mon Sep 17 00:00:00 2001 From: Ethan Chen Date: Sun, 2 Sep 2012 17:39:33 -0700 Subject: [PATCH 023/111] Linux 3.0.19 --- Makefile | 2 +- arch/arm/mach-ux500/Kconfig | 1 + arch/arm/mm/proc-v7.S | 6 -- arch/x86/include/asm/uv/uv_hub.h | 4 +- arch/x86/kernel/microcode_amd.c | 24 +++++- arch/x86/net/bpf_jit_comp.c | 36 ++++---- crypto/sha512_generic.c | 62 +++++++------- drivers/gpu/drm/drm_auth.c | 6 +- drivers/gpu/drm/drm_fops.c | 5 ++ drivers/gpu/drm/i915/intel_sdvo.c | 8 +- drivers/gpu/drm/radeon/radeon_irq_kms.c | 6 ++ drivers/gpu/drm/vmwgfx/vmwgfx_kms.c | 2 +- drivers/hwmon/f71805f.c | 10 +-- drivers/hwmon/sht15.c | 3 +- drivers/hwmon/w83627ehf.c | 6 ++ drivers/net/bonding/bond_alb.c | 27 ++---- drivers/tty/serial/amba-pl011.c | 14 ++++ drivers/tty/serial/jsm/jsm_driver.c | 1 + drivers/tty/tty_port.c | 12 +-- drivers/usb/class/cdc-wdm.c | 59 ++++++++----- drivers/usb/host/ehci-fsl.c | 2 +- drivers/usb/host/xhci-ring.c | 6 +- drivers/usb/misc/usbsevseg.c | 2 +- drivers/usb/serial/cp210x.c | 107 +++++++++++++++++------- drivers/usb/serial/ftdi_sio.c | 14 +++- drivers/usb/serial/ftdi_sio_ids.h | 19 +++++ drivers/usb/serial/io_ti.c | 10 +-- drivers/usb/serial/option.c | 5 ++ drivers/usb/serial/qcaux.c | 7 +- fs/ecryptfs/crypto.c | 49 ++--------- fs/ecryptfs/inode.c | 48 ++++++++--- fs/ecryptfs/miscdev.c | 56 +++++++++---- fs/ecryptfs/read_write.c | 19 +++-- fs/xfs/linux-2.6/xfs_discard.c | 4 +- fs/xfs/xfs_vnodeops.c | 3 +- include/drm/drmP.h | 1 + include/net/netns/generic.h | 1 + kernel/printk.c | 6 +- kernel/trace/ftrace.c | 76 ++++++++++------- net/caif/caif_dev.c | 11 +-- net/caif/cfcnfg.c | 1 - net/core/net_namespace.c | 31 +++---- net/ipv4/ah4.c | 2 - net/ipv4/tcp_ipv4.c | 2 +- net/ipv4/tcp_output.c | 6 +- net/ipv6/ah6.c | 2 - net/ipv6/tcp_ipv6.c | 2 +- net/l2tp/l2tp_ip.c | 5 -- net/rds/af_rds.c | 20 ++--- sound/pci/hda/patch_realtek.c | 11 +++ sound/pci/hda/patch_sigmatel.c | 8 +- 51 files changed, 507 insertions(+), 323 deletions(-) diff --git a/Makefile b/Makefile index ccc9f39b2fc..718ef7319a1 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ VERSION = 3 PATCHLEVEL = 0 -SUBLEVEL = 18 +SUBLEVEL = 19 EXTRAVERSION = NAME = Sneaky Weasel diff --git a/arch/arm/mach-ux500/Kconfig b/arch/arm/mach-ux500/Kconfig index 9a9706cf149..6ebdb0d0382 100644 --- a/arch/arm/mach-ux500/Kconfig +++ b/arch/arm/mach-ux500/Kconfig @@ -7,6 +7,7 @@ config UX500_SOC_COMMON select HAS_MTU select ARM_ERRATA_753970 select ARM_ERRATA_754322 + select ARM_ERRATA_764369 menu "Ux500 SoC" diff --git a/arch/arm/mm/proc-v7.S b/arch/arm/mm/proc-v7.S index 59430058fc3..a5fb13c0bdc 100644 --- a/arch/arm/mm/proc-v7.S +++ b/arch/arm/mm/proc-v7.S @@ -287,10 +287,6 @@ cpu_resume_l1_flags: * Initialise TLB, Caches, and MMU state ready to switch the MMU * on. Return in r0 the new CP15 C1 control register setting. * - * We automatically detect if we have a Harvard cache, and use the - * Harvard cache control instructions insead of the unified cache - * control instructions. - * * This should be able to cover all ARMv7 cores. * * It is assumed that: @@ -387,9 +383,7 @@ __v7_setup: #endif 3: mov r10, #0 -#ifdef HARVARD_CACHE mcr p15, 0, r10, c7, c5, 0 @ I+BTB cache invalidate -#endif dsb #ifdef CONFIG_MMU mcr p15, 0, r10, c8, c7, 0 @ invalidate I + D TLBs diff --git a/arch/x86/include/asm/uv/uv_hub.h b/arch/x86/include/asm/uv/uv_hub.h index 54a13aaebc4..21f7385badb 100644 --- a/arch/x86/include/asm/uv/uv_hub.h +++ b/arch/x86/include/asm/uv/uv_hub.h @@ -318,13 +318,13 @@ uv_gpa_in_mmr_space(unsigned long gpa) /* UV global physical address --> socket phys RAM */ static inline unsigned long uv_gpa_to_soc_phys_ram(unsigned long gpa) { - unsigned long paddr = gpa & uv_hub_info->gpa_mask; + unsigned long paddr; unsigned long remap_base = uv_hub_info->lowmem_remap_base; unsigned long remap_top = uv_hub_info->lowmem_remap_top; gpa = ((gpa << uv_hub_info->m_shift) >> uv_hub_info->m_shift) | ((gpa >> uv_hub_info->n_lshift) << uv_hub_info->m_val); - gpa = gpa & uv_hub_info->gpa_mask; + paddr = gpa & uv_hub_info->gpa_mask; if (paddr >= remap_base && paddr < remap_base + remap_top) paddr -= remap_base; return paddr; diff --git a/arch/x86/kernel/microcode_amd.c b/arch/x86/kernel/microcode_amd.c index c5610384ab1..b727450f5d7 100644 --- a/arch/x86/kernel/microcode_amd.c +++ b/arch/x86/kernel/microcode_amd.c @@ -298,13 +298,33 @@ generic_load_microcode(int cpu, const u8 *data, size_t size) return state; } +/* + * AMD microcode firmware naming convention, up to family 15h they are in + * the legacy file: + * + * amd-ucode/microcode_amd.bin + * + * This legacy file is always smaller than 2K in size. + * + * Starting at family 15h they are in family specific firmware files: + * + * amd-ucode/microcode_amd_fam15h.bin + * amd-ucode/microcode_amd_fam16h.bin + * ... + * + * These might be larger than 2K. + */ static enum ucode_state request_microcode_amd(int cpu, struct device *device) { - const char *fw_name = "amd-ucode/microcode_amd.bin"; + char fw_name[36] = "amd-ucode/microcode_amd.bin"; const struct firmware *fw; enum ucode_state ret = UCODE_NFOUND; + struct cpuinfo_x86 *c = &cpu_data(cpu); + + if (c->x86 >= 0x15) + snprintf(fw_name, sizeof(fw_name), "amd-ucode/microcode_amd_fam%.2xh.bin", c->x86); - if (request_firmware(&fw, fw_name, device)) { + if (request_firmware(&fw, (const char *)fw_name, device)) { pr_err("failed to load file %s\n", fw_name); goto out; } diff --git a/arch/x86/net/bpf_jit_comp.c b/arch/x86/net/bpf_jit_comp.c index 7b65f752c5f..7c1b765ecc5 100644 --- a/arch/x86/net/bpf_jit_comp.c +++ b/arch/x86/net/bpf_jit_comp.c @@ -151,17 +151,18 @@ void bpf_jit_compile(struct sk_filter *fp) cleanup_addr = proglen; /* epilogue address */ for (pass = 0; pass < 10; pass++) { + u8 seen_or_pass0 = (pass == 0) ? (SEEN_XREG | SEEN_DATAREF | SEEN_MEM) : seen; /* no prologue/epilogue for trivial filters (RET something) */ proglen = 0; prog = temp; - if (seen) { + if (seen_or_pass0) { EMIT4(0x55, 0x48, 0x89, 0xe5); /* push %rbp; mov %rsp,%rbp */ EMIT4(0x48, 0x83, 0xec, 96); /* subq $96,%rsp */ /* note : must save %rbx in case bpf_error is hit */ - if (seen & (SEEN_XREG | SEEN_DATAREF)) + if (seen_or_pass0 & (SEEN_XREG | SEEN_DATAREF)) EMIT4(0x48, 0x89, 0x5d, 0xf8); /* mov %rbx, -8(%rbp) */ - if (seen & SEEN_XREG) + if (seen_or_pass0 & SEEN_XREG) CLEAR_X(); /* make sure we dont leek kernel memory */ /* @@ -170,7 +171,7 @@ void bpf_jit_compile(struct sk_filter *fp) * r9 = skb->len - skb->data_len * r8 = skb->data */ - if (seen & SEEN_DATAREF) { + if (seen_or_pass0 & SEEN_DATAREF) { if (offsetof(struct sk_buff, len) <= 127) /* mov off8(%rdi),%r9d */ EMIT4(0x44, 0x8b, 0x4f, offsetof(struct sk_buff, len)); @@ -260,9 +261,14 @@ void bpf_jit_compile(struct sk_filter *fp) case BPF_S_ALU_DIV_X: /* A /= X; */ seen |= SEEN_XREG; EMIT2(0x85, 0xdb); /* test %ebx,%ebx */ - if (pc_ret0 != -1) - EMIT_COND_JMP(X86_JE, addrs[pc_ret0] - (addrs[i] - 4)); - else { + if (pc_ret0 > 0) { + /* addrs[pc_ret0 - 1] is start address of target + * (addrs[i] - 4) is the address following this jmp + * ("xor %edx,%edx; div %ebx" being 4 bytes long) + */ + EMIT_COND_JMP(X86_JE, addrs[pc_ret0 - 1] - + (addrs[i] - 4)); + } else { EMIT_COND_JMP(X86_JNE, 2 + 5); CLEAR_A(); EMIT1_off32(0xe9, cleanup_addr - (addrs[i] - 4)); /* jmp .+off32 */ @@ -335,12 +341,12 @@ void bpf_jit_compile(struct sk_filter *fp) } /* fallinto */ case BPF_S_RET_A: - if (seen) { + if (seen_or_pass0) { if (i != flen - 1) { EMIT_JMP(cleanup_addr - addrs[i]); break; } - if (seen & SEEN_XREG) + if (seen_or_pass0 & SEEN_XREG) EMIT4(0x48, 0x8b, 0x5d, 0xf8); /* mov -8(%rbp),%rbx */ EMIT1(0xc9); /* leaveq */ } @@ -483,8 +489,9 @@ common_load: seen |= SEEN_DATAREF; goto common_load; case BPF_S_LDX_B_MSH: if ((int)K < 0) { - if (pc_ret0 != -1) { - EMIT_JMP(addrs[pc_ret0] - addrs[i]); + if (pc_ret0 > 0) { + /* addrs[pc_ret0 - 1] is the start address */ + EMIT_JMP(addrs[pc_ret0 - 1] - addrs[i]); break; } CLEAR_A(); @@ -599,13 +606,14 @@ cond_branch: f_offset = addrs[i + filter[i].jf] - addrs[i]; * use it to give the cleanup instruction(s) addr */ cleanup_addr = proglen - 1; /* ret */ - if (seen) + if (seen_or_pass0) cleanup_addr -= 1; /* leaveq */ - if (seen & SEEN_XREG) + if (seen_or_pass0 & SEEN_XREG) cleanup_addr -= 4; /* mov -8(%rbp),%rbx */ if (image) { - WARN_ON(proglen != oldproglen); + if (proglen != oldproglen) + pr_err("bpb_jit_compile proglen=%u != oldproglen=%u\n", proglen, oldproglen); break; } if (proglen == oldproglen) { diff --git a/crypto/sha512_generic.c b/crypto/sha512_generic.c index 9ed9f60316e..88f160b77b1 100644 --- a/crypto/sha512_generic.c +++ b/crypto/sha512_generic.c @@ -21,8 +21,6 @@ #include #include -static DEFINE_PER_CPU(u64[80], msg_schedule); - static inline u64 Ch(u64 x, u64 y, u64 z) { return z ^ (x & (y ^ z)); @@ -80,7 +78,7 @@ static inline void LOAD_OP(int I, u64 *W, const u8 *input) static inline void BLEND_OP(int I, u64 *W) { - W[I] = s1(W[I-2]) + W[I-7] + s0(W[I-15]) + W[I-16]; + W[I % 16] += s1(W[(I-2) % 16]) + W[(I-7) % 16] + s0(W[(I-15) % 16]); } static void @@ -89,38 +87,48 @@ sha512_transform(u64 *state, const u8 *input) u64 a, b, c, d, e, f, g, h, t1, t2; int i; - u64 *W = get_cpu_var(msg_schedule); + u64 W[16]; /* load the input */ for (i = 0; i < 16; i++) LOAD_OP(i, W, input); - for (i = 16; i < 80; i++) { - BLEND_OP(i, W); - } - /* load the state into our registers */ a=state[0]; b=state[1]; c=state[2]; d=state[3]; e=state[4]; f=state[5]; g=state[6]; h=state[7]; - /* now iterate */ - for (i=0; i<80; i+=8) { - t1 = h + e1(e) + Ch(e,f,g) + sha512_K[i ] + W[i ]; - t2 = e0(a) + Maj(a,b,c); d+=t1; h=t1+t2; - t1 = g + e1(d) + Ch(d,e,f) + sha512_K[i+1] + W[i+1]; - t2 = e0(h) + Maj(h,a,b); c+=t1; g=t1+t2; - t1 = f + e1(c) + Ch(c,d,e) + sha512_K[i+2] + W[i+2]; - t2 = e0(g) + Maj(g,h,a); b+=t1; f=t1+t2; - t1 = e + e1(b) + Ch(b,c,d) + sha512_K[i+3] + W[i+3]; - t2 = e0(f) + Maj(f,g,h); a+=t1; e=t1+t2; - t1 = d + e1(a) + Ch(a,b,c) + sha512_K[i+4] + W[i+4]; - t2 = e0(e) + Maj(e,f,g); h+=t1; d=t1+t2; - t1 = c + e1(h) + Ch(h,a,b) + sha512_K[i+5] + W[i+5]; - t2 = e0(d) + Maj(d,e,f); g+=t1; c=t1+t2; - t1 = b + e1(g) + Ch(g,h,a) + sha512_K[i+6] + W[i+6]; - t2 = e0(c) + Maj(c,d,e); f+=t1; b=t1+t2; - t1 = a + e1(f) + Ch(f,g,h) + sha512_K[i+7] + W[i+7]; - t2 = e0(b) + Maj(b,c,d); e+=t1; a=t1+t2; +#define SHA512_0_15(i, a, b, c, d, e, f, g, h) \ + t1 = h + e1(e) + Ch(e, f, g) + sha512_K[i] + W[i]; \ + t2 = e0(a) + Maj(a, b, c); \ + d += t1; \ + h = t1 + t2 + +#define SHA512_16_79(i, a, b, c, d, e, f, g, h) \ + BLEND_OP(i, W); \ + t1 = h + e1(e) + Ch(e, f, g) + sha512_K[i] + W[(i)%16]; \ + t2 = e0(a) + Maj(a, b, c); \ + d += t1; \ + h = t1 + t2 + + for (i = 0; i < 16; i += 8) { + SHA512_0_15(i, a, b, c, d, e, f, g, h); + SHA512_0_15(i + 1, h, a, b, c, d, e, f, g); + SHA512_0_15(i + 2, g, h, a, b, c, d, e, f); + SHA512_0_15(i + 3, f, g, h, a, b, c, d, e); + SHA512_0_15(i + 4, e, f, g, h, a, b, c, d); + SHA512_0_15(i + 5, d, e, f, g, h, a, b, c); + SHA512_0_15(i + 6, c, d, e, f, g, h, a, b); + SHA512_0_15(i + 7, b, c, d, e, f, g, h, a); + } + for (i = 16; i < 80; i += 8) { + SHA512_16_79(i, a, b, c, d, e, f, g, h); + SHA512_16_79(i + 1, h, a, b, c, d, e, f, g); + SHA512_16_79(i + 2, g, h, a, b, c, d, e, f); + SHA512_16_79(i + 3, f, g, h, a, b, c, d, e); + SHA512_16_79(i + 4, e, f, g, h, a, b, c, d); + SHA512_16_79(i + 5, d, e, f, g, h, a, b, c); + SHA512_16_79(i + 6, c, d, e, f, g, h, a, b); + SHA512_16_79(i + 7, b, c, d, e, f, g, h, a); } state[0] += a; state[1] += b; state[2] += c; state[3] += d; @@ -128,8 +136,6 @@ sha512_transform(u64 *state, const u8 *input) /* erase our data */ a = b = c = d = e = f = g = h = t1 = t2 = 0; - memset(W, 0, sizeof(__get_cpu_var(msg_schedule))); - put_cpu_var(msg_schedule); } static int diff --git a/drivers/gpu/drm/drm_auth.c b/drivers/gpu/drm/drm_auth.c index 3f46772f0cb..ba23790450e 100644 --- a/drivers/gpu/drm/drm_auth.c +++ b/drivers/gpu/drm/drm_auth.c @@ -101,7 +101,7 @@ static int drm_add_magic(struct drm_master *master, struct drm_file *priv, * Searches and unlinks the entry in drm_device::magiclist with the magic * number hash key, while holding the drm_device::struct_mutex lock. */ -static int drm_remove_magic(struct drm_master *master, drm_magic_t magic) +int drm_remove_magic(struct drm_master *master, drm_magic_t magic) { struct drm_magic_entry *pt; struct drm_hash_item *hash; @@ -136,6 +136,8 @@ static int drm_remove_magic(struct drm_master *master, drm_magic_t magic) * If there is a magic number in drm_file::magic then use it, otherwise * searches an unique non-zero magic number and add it associating it with \p * file_priv. + * This ioctl needs protection by the drm_global_mutex, which protects + * struct drm_file::magic and struct drm_magic_entry::priv. */ int drm_getmagic(struct drm_device *dev, void *data, struct drm_file *file_priv) { @@ -173,6 +175,8 @@ int drm_getmagic(struct drm_device *dev, void *data, struct drm_file *file_priv) * \return zero if authentication successed, or a negative number otherwise. * * Checks if \p file_priv is associated with the magic number passed in \arg. + * This ioctl needs protection by the drm_global_mutex, which protects + * struct drm_file::magic and struct drm_magic_entry::priv. */ int drm_authmagic(struct drm_device *dev, void *data, struct drm_file *file_priv) diff --git a/drivers/gpu/drm/drm_fops.c b/drivers/gpu/drm/drm_fops.c index 2ec7d48fc4a..c42e12cc2dd 100644 --- a/drivers/gpu/drm/drm_fops.c +++ b/drivers/gpu/drm/drm_fops.c @@ -486,6 +486,11 @@ int drm_release(struct inode *inode, struct file *filp) (long)old_encode_dev(file_priv->minor->device), dev->open_count); + /* Release any auth tokens that might point to this file_priv, + (do that under the drm_global_mutex) */ + if (file_priv->magic) + (void) drm_remove_magic(file_priv->master, file_priv->magic); + /* if the master has gone away we can't do anything with the lock */ if (file_priv->minor->master) drm_master_release(dev, filp); diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index 30fe554d893..bdda08e33c3 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -1059,15 +1059,13 @@ static void intel_sdvo_mode_set(struct drm_encoder *encoder, /* Set the SDVO control regs. */ if (INTEL_INFO(dev)->gen >= 4) { - sdvox = 0; + /* The real mode polarity is set by the SDVO commands, using + * struct intel_sdvo_dtd. */ + sdvox = SDVO_VSYNC_ACTIVE_HIGH | SDVO_HSYNC_ACTIVE_HIGH; if (intel_sdvo->is_hdmi) sdvox |= intel_sdvo->color_range; if (INTEL_INFO(dev)->gen < 5) sdvox |= SDVO_BORDER_ENABLE; - if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC) - sdvox |= SDVO_VSYNC_ACTIVE_HIGH; - if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC) - sdvox |= SDVO_HSYNC_ACTIVE_HIGH; } else { sdvox = I915_READ(intel_sdvo->sdvo_reg); switch (intel_sdvo->sdvo_reg) { diff --git a/drivers/gpu/drm/radeon/radeon_irq_kms.c b/drivers/gpu/drm/radeon/radeon_irq_kms.c index fecc1aae382..5feb6e9edd8 100644 --- a/drivers/gpu/drm/radeon/radeon_irq_kms.c +++ b/drivers/gpu/drm/radeon/radeon_irq_kms.c @@ -131,6 +131,12 @@ static bool radeon_msi_ok(struct radeon_device *rdev) (rdev->pdev->subsystem_device == 0x30c2)) return true; + /* Dell RS690 only seems to work with MSIs. */ + if ((rdev->pdev->device == 0x791f) && + (rdev->pdev->subsystem_vendor == 0x1028) && + (rdev->pdev->subsystem_device == 0x01fc)) + return true; + /* Dell RS690 only seems to work with MSIs. */ if ((rdev->pdev->device == 0x791f) && (rdev->pdev->subsystem_vendor == 0x1028) && diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index dfe32e62bd9..8a38c91f4c9 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -313,7 +313,7 @@ int vmw_framebuffer_create_handle(struct drm_framebuffer *fb, unsigned int *handle) { if (handle) - handle = 0; + *handle = 0; return 0; } diff --git a/drivers/hwmon/f71805f.c b/drivers/hwmon/f71805f.c index 92f949767ec..6dbfd3e516e 100644 --- a/drivers/hwmon/f71805f.c +++ b/drivers/hwmon/f71805f.c @@ -283,11 +283,11 @@ static inline long temp_from_reg(u8 reg) static inline u8 temp_to_reg(long val) { - if (val < 0) - val = 0; - else if (val > 1000 * 0xff) - val = 0xff; - return ((val + 500) / 1000); + if (val <= 0) + return 0; + if (val >= 1000 * 0xff) + return 0xff; + return (val + 500) / 1000; } /* diff --git a/drivers/hwmon/sht15.c b/drivers/hwmon/sht15.c index cf4330b352e..9594cdb1cd0 100644 --- a/drivers/hwmon/sht15.c +++ b/drivers/hwmon/sht15.c @@ -883,7 +883,7 @@ static int sht15_invalidate_voltage(struct notifier_block *nb, static int __devinit sht15_probe(struct platform_device *pdev) { - int ret = 0; + int ret; struct sht15_data *data = kzalloc(sizeof(*data), GFP_KERNEL); u8 status = 0; @@ -901,6 +901,7 @@ static int __devinit sht15_probe(struct platform_device *pdev) init_waitqueue_head(&data->wait_queue); if (pdev->dev.platform_data == NULL) { + ret = -EINVAL; dev_err(&pdev->dev, "no platform data supplied\n"); goto err_free_data; } diff --git a/drivers/hwmon/w83627ehf.c b/drivers/hwmon/w83627ehf.c index 4b2fc50c84f..62845157245 100644 --- a/drivers/hwmon/w83627ehf.c +++ b/drivers/hwmon/w83627ehf.c @@ -1295,6 +1295,7 @@ store_pwm_mode(struct device *dev, struct device_attribute *attr, { struct w83627ehf_data *data = dev_get_drvdata(dev); struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + struct w83627ehf_sio_data *sio_data = dev->platform_data; int nr = sensor_attr->index; unsigned long val; int err; @@ -1306,6 +1307,11 @@ store_pwm_mode(struct device *dev, struct device_attribute *attr, if (val > 1) return -EINVAL; + + /* On NCT67766F, DC mode is only supported for pwm1 */ + if (sio_data->kind == nct6776 && nr && val != 1) + return -EINVAL; + mutex_lock(&data->update_lock); reg = w83627ehf_read_value(data, W83627EHF_REG_PWM_ENABLE[nr]); data->pwm_mode[nr] = val; diff --git a/drivers/net/bonding/bond_alb.c b/drivers/net/bonding/bond_alb.c index 2df9276720a..5e725e07d61 100644 --- a/drivers/net/bonding/bond_alb.c +++ b/drivers/net/bonding/bond_alb.c @@ -871,16 +871,12 @@ static void alb_send_learning_packets(struct slave *slave, u8 mac_addr[]) } } -/* hw is a boolean parameter that determines whether we should try and - * set the hw address of the device as well as the hw address of the - * net_device - */ -static int alb_set_slave_mac_addr(struct slave *slave, u8 addr[], int hw) +static int alb_set_slave_mac_addr(struct slave *slave, u8 addr[]) { struct net_device *dev = slave->dev; struct sockaddr s_addr; - if (!hw) { + if (slave->bond->params.mode == BOND_MODE_TLB) { memcpy(dev->dev_addr, addr, dev->addr_len); return 0; } @@ -910,8 +906,8 @@ static void alb_swap_mac_addr(struct bonding *bond, struct slave *slave1, struct u8 tmp_mac_addr[ETH_ALEN]; memcpy(tmp_mac_addr, slave1->dev->dev_addr, ETH_ALEN); - alb_set_slave_mac_addr(slave1, slave2->dev->dev_addr, bond->alb_info.rlb_enabled); - alb_set_slave_mac_addr(slave2, tmp_mac_addr, bond->alb_info.rlb_enabled); + alb_set_slave_mac_addr(slave1, slave2->dev->dev_addr); + alb_set_slave_mac_addr(slave2, tmp_mac_addr); } @@ -1058,8 +1054,7 @@ static int alb_handle_addr_collision_on_attach(struct bonding *bond, struct slav /* Try setting slave mac to bond address and fall-through to code handling that situation below... */ - alb_set_slave_mac_addr(slave, bond->dev->dev_addr, - bond->alb_info.rlb_enabled); + alb_set_slave_mac_addr(slave, bond->dev->dev_addr); } /* The slave's address is equal to the address of the bond. @@ -1095,8 +1090,7 @@ static int alb_handle_addr_collision_on_attach(struct bonding *bond, struct slav } if (free_mac_slave) { - alb_set_slave_mac_addr(slave, free_mac_slave->perm_hwaddr, - bond->alb_info.rlb_enabled); + alb_set_slave_mac_addr(slave, free_mac_slave->perm_hwaddr); pr_warning("%s: Warning: the hw address of slave %s is in use by the bond; giving it the hw address of %s\n", bond->dev->name, slave->dev->name, @@ -1452,8 +1446,7 @@ int bond_alb_init_slave(struct bonding *bond, struct slave *slave) { int res; - res = alb_set_slave_mac_addr(slave, slave->perm_hwaddr, - bond->alb_info.rlb_enabled); + res = alb_set_slave_mac_addr(slave, slave->perm_hwaddr); if (res) { return res; } @@ -1604,8 +1597,7 @@ void bond_alb_handle_active_change(struct bonding *bond, struct slave *new_slave alb_swap_mac_addr(bond, swap_slave, new_slave); } else { /* set the new_slave to the bond mac address */ - alb_set_slave_mac_addr(new_slave, bond->dev->dev_addr, - bond->alb_info.rlb_enabled); + alb_set_slave_mac_addr(new_slave, bond->dev->dev_addr); } if (swap_slave) { @@ -1665,8 +1657,7 @@ int bond_alb_set_mac_address(struct net_device *bond_dev, void *addr) alb_swap_mac_addr(bond, swap_slave, bond->curr_active_slave); alb_fasten_mac_swap(bond, swap_slave, bond->curr_active_slave); } else { - alb_set_slave_mac_addr(bond->curr_active_slave, bond_dev->dev_addr, - bond->alb_info.rlb_enabled); + alb_set_slave_mac_addr(bond->curr_active_slave, bond_dev->dev_addr); read_lock(&bond->lock); alb_send_learning_packets(bond->curr_active_slave, bond_dev->dev_addr); diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index f5f6831b0a6..4a4733e601d 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -1733,9 +1733,19 @@ pl011_console_write(struct console *co, const char *s, unsigned int count) { struct uart_amba_port *uap = amba_ports[co->index]; unsigned int status, old_cr, new_cr; + unsigned long flags; + int locked = 1; clk_enable(uap->clk); + local_irq_save(flags); + if (uap->port.sysrq) + locked = 0; + else if (oops_in_progress) + locked = spin_trylock(&uap->port.lock); + else + spin_lock(&uap->port.lock); + /* * First save the CR then disable the interrupts */ @@ -1755,6 +1765,10 @@ pl011_console_write(struct console *co, const char *s, unsigned int count) } while (status & UART01x_FR_BUSY); writew(old_cr, uap->port.membase + UART011_CR); + if (locked) + spin_unlock(&uap->port.lock); + local_irq_restore(flags); + clk_disable(uap->clk); } diff --git a/drivers/tty/serial/jsm/jsm_driver.c b/drivers/tty/serial/jsm/jsm_driver.c index 2aaafa9c58a..6c12d94e6d3 100644 --- a/drivers/tty/serial/jsm/jsm_driver.c +++ b/drivers/tty/serial/jsm/jsm_driver.c @@ -269,6 +269,7 @@ static void jsm_io_resume(struct pci_dev *pdev) struct jsm_board *brd = pci_get_drvdata(pdev); pci_restore_state(pdev); + pci_save_state(pdev); jsm_uart_port_init(brd); } diff --git a/drivers/tty/tty_port.c b/drivers/tty/tty_port.c index 33d37d230f8..a4aaca0e014 100644 --- a/drivers/tty/tty_port.c +++ b/drivers/tty/tty_port.c @@ -227,7 +227,6 @@ int tty_port_block_til_ready(struct tty_port *port, int do_clocal = 0, retval; unsigned long flags; DEFINE_WAIT(wait); - int cd; /* block if port is in the process of being closed */ if (tty_hung_up_p(filp) || port->flags & ASYNC_CLOSING) { @@ -284,11 +283,14 @@ int tty_port_block_til_ready(struct tty_port *port, retval = -ERESTARTSYS; break; } - /* Probe the carrier. For devices with no carrier detect this - will always return true */ - cd = tty_port_carrier_raised(port); + /* + * Probe the carrier. For devices with no carrier detect + * tty_port_carrier_raised will always return true. + * Never ask drivers if CLOCAL is set, this causes troubles + * on some hardware. + */ if (!(port->flags & ASYNC_CLOSING) && - (do_clocal || cd)) + (do_clocal || tty_port_carrier_raised(port))) break; if (signal_pending(current)) { retval = -ERESTARTSYS; diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c index 2b9ff518b50..90581a85134 100644 --- a/drivers/usb/class/cdc-wdm.c +++ b/drivers/usb/class/cdc-wdm.c @@ -57,6 +57,8 @@ MODULE_DEVICE_TABLE (usb, wdm_ids); #define WDM_MAX 16 +/* CDC-WMC r1.1 requires wMaxCommand to be "at least 256 decimal (0x100)" */ +#define WDM_DEFAULT_BUFSIZE 256 static DEFINE_MUTEX(wdm_mutex); @@ -88,7 +90,8 @@ struct wdm_device { int count; dma_addr_t shandle; dma_addr_t ihandle; - struct mutex lock; + struct mutex wlock; + struct mutex rlock; wait_queue_head_t wait; struct work_struct rxwork; int werr; @@ -323,7 +326,7 @@ static ssize_t wdm_write } /* concurrent writes and disconnect */ - r = mutex_lock_interruptible(&desc->lock); + r = mutex_lock_interruptible(&desc->wlock); rv = -ERESTARTSYS; if (r) { kfree(buf); @@ -386,7 +389,7 @@ static ssize_t wdm_write out: usb_autopm_put_interface(desc->intf); outnp: - mutex_unlock(&desc->lock); + mutex_unlock(&desc->wlock); outnl: return rv < 0 ? rv : count; } @@ -399,7 +402,7 @@ static ssize_t wdm_read struct wdm_device *desc = file->private_data; - rv = mutex_lock_interruptible(&desc->lock); /*concurrent reads */ + rv = mutex_lock_interruptible(&desc->rlock); /*concurrent reads */ if (rv < 0) return -ERESTARTSYS; @@ -467,14 +470,16 @@ static ssize_t wdm_read for (i = 0; i < desc->length - cntr; i++) desc->ubuf[i] = desc->ubuf[i + cntr]; + spin_lock_irq(&desc->iuspin); desc->length -= cntr; + spin_unlock_irq(&desc->iuspin); /* in case we had outstanding data */ if (!desc->length) clear_bit(WDM_READ, &desc->flags); rv = cntr; err: - mutex_unlock(&desc->lock); + mutex_unlock(&desc->rlock); return rv; } @@ -540,7 +545,8 @@ static int wdm_open(struct inode *inode, struct file *file) } intf->needs_remote_wakeup = 1; - mutex_lock(&desc->lock); + /* using write lock to protect desc->count */ + mutex_lock(&desc->wlock); if (!desc->count++) { desc->werr = 0; desc->rerr = 0; @@ -553,7 +559,7 @@ static int wdm_open(struct inode *inode, struct file *file) } else { rv = 0; } - mutex_unlock(&desc->lock); + mutex_unlock(&desc->wlock); usb_autopm_put_interface(desc->intf); out: mutex_unlock(&wdm_mutex); @@ -565,9 +571,11 @@ static int wdm_release(struct inode *inode, struct file *file) struct wdm_device *desc = file->private_data; mutex_lock(&wdm_mutex); - mutex_lock(&desc->lock); + + /* using write lock to protect desc->count */ + mutex_lock(&desc->wlock); desc->count--; - mutex_unlock(&desc->lock); + mutex_unlock(&desc->wlock); if (!desc->count) { dev_dbg(&desc->intf->dev, "wdm_release: cleanup"); @@ -630,7 +638,7 @@ static int wdm_probe(struct usb_interface *intf, const struct usb_device_id *id) struct usb_cdc_dmm_desc *dmhd; u8 *buffer = intf->altsetting->extra; int buflen = intf->altsetting->extralen; - u16 maxcom = 0; + u16 maxcom = WDM_DEFAULT_BUFSIZE; if (!buffer) goto out; @@ -665,7 +673,8 @@ static int wdm_probe(struct usb_interface *intf, const struct usb_device_id *id) desc = kzalloc(sizeof(struct wdm_device), GFP_KERNEL); if (!desc) goto out; - mutex_init(&desc->lock); + mutex_init(&desc->rlock); + mutex_init(&desc->wlock); spin_lock_init(&desc->iuspin); init_waitqueue_head(&desc->wait); desc->wMaxCommand = maxcom; @@ -716,7 +725,7 @@ static int wdm_probe(struct usb_interface *intf, const struct usb_device_id *id) goto err; desc->inbuf = usb_alloc_coherent(interface_to_usbdev(intf), - desc->bMaxPacketSize0, + desc->wMaxCommand, GFP_KERNEL, &desc->response->transfer_dma); if (!desc->inbuf) @@ -779,11 +788,13 @@ static void wdm_disconnect(struct usb_interface *intf) /* to terminate pending flushes */ clear_bit(WDM_IN_USE, &desc->flags); spin_unlock_irqrestore(&desc->iuspin, flags); - mutex_lock(&desc->lock); + wake_up_all(&desc->wait); + mutex_lock(&desc->rlock); + mutex_lock(&desc->wlock); kill_urbs(desc); cancel_work_sync(&desc->rxwork); - mutex_unlock(&desc->lock); - wake_up_all(&desc->wait); + mutex_unlock(&desc->wlock); + mutex_unlock(&desc->rlock); if (!desc->count) cleanup(desc); mutex_unlock(&wdm_mutex); @@ -798,8 +809,10 @@ static int wdm_suspend(struct usb_interface *intf, pm_message_t message) dev_dbg(&desc->intf->dev, "wdm%d_suspend\n", intf->minor); /* if this is an autosuspend the caller does the locking */ - if (!(message.event & PM_EVENT_AUTO)) - mutex_lock(&desc->lock); + if (!(message.event & PM_EVENT_AUTO)) { + mutex_lock(&desc->rlock); + mutex_lock(&desc->wlock); + } spin_lock_irq(&desc->iuspin); if ((message.event & PM_EVENT_AUTO) && @@ -815,8 +828,10 @@ static int wdm_suspend(struct usb_interface *intf, pm_message_t message) kill_urbs(desc); cancel_work_sync(&desc->rxwork); } - if (!(message.event & PM_EVENT_AUTO)) - mutex_unlock(&desc->lock); + if (!(message.event & PM_EVENT_AUTO)) { + mutex_unlock(&desc->wlock); + mutex_unlock(&desc->rlock); + } return rv; } @@ -854,7 +869,8 @@ static int wdm_pre_reset(struct usb_interface *intf) { struct wdm_device *desc = usb_get_intfdata(intf); - mutex_lock(&desc->lock); + mutex_lock(&desc->rlock); + mutex_lock(&desc->wlock); kill_urbs(desc); /* @@ -876,7 +892,8 @@ static int wdm_post_reset(struct usb_interface *intf) int rv; rv = recover_from_urb_loss(desc); - mutex_unlock(&desc->lock); + mutex_unlock(&desc->wlock); + mutex_unlock(&desc->rlock); return 0; } diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c index f380bf97e5a..bc7f166395a 100644 --- a/drivers/usb/host/ehci-fsl.c +++ b/drivers/usb/host/ehci-fsl.c @@ -125,7 +125,7 @@ static int usb_hcd_fsl_probe(const struct hc_driver *driver, */ if (pdata->init && pdata->init(pdev)) { retval = -ENODEV; - goto err3; + goto err4; } /* Enable USB controller, 83xx or 8536 */ diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index c0c5d6c7cb6..edcedc4e978 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -1218,6 +1218,7 @@ static void handle_vendor_event(struct xhci_hcd *xhci, * * Returns a zero-based port number, which is suitable for indexing into each of * the split roothubs' port arrays and bus state arrays. + * Add one to it in order to call xhci_find_slot_id_by_port. */ static unsigned int find_faked_portnum_from_hw_portnum(struct usb_hcd *hcd, struct xhci_hcd *xhci, u32 port_id) @@ -1340,7 +1341,7 @@ static void handle_port_status(struct xhci_hcd *xhci, temp |= PORT_LINK_STROBE | XDEV_U0; xhci_writel(xhci, temp, port_array[faked_port_index]); slot_id = xhci_find_slot_id_by_port(hcd, xhci, - faked_port_index); + faked_port_index + 1); if (!slot_id) { xhci_dbg(xhci, "slot_id is zero\n"); goto cleanup; @@ -3381,7 +3382,8 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags, /* Check TD length */ if (running_total != td_len) { xhci_err(xhci, "ISOC TD length unmatch\n"); - return -EINVAL; + ret = -EINVAL; + goto cleanup; } } diff --git a/drivers/usb/misc/usbsevseg.c b/drivers/usb/misc/usbsevseg.c index 417b8f207e8..59689fa2f7c 100644 --- a/drivers/usb/misc/usbsevseg.c +++ b/drivers/usb/misc/usbsevseg.c @@ -24,7 +24,7 @@ #define VENDOR_ID 0x0fc5 #define PRODUCT_ID 0x1227 -#define MAXLEN 6 +#define MAXLEN 8 /* table of devices that work with this driver */ static const struct usb_device_id id_table[] = { diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c index a1a324b30d2..a5152379cb4 100644 --- a/drivers/usb/serial/cp210x.c +++ b/drivers/usb/serial/cp210x.c @@ -39,6 +39,8 @@ static void cp210x_get_termios(struct tty_struct *, struct usb_serial_port *port); static void cp210x_get_termios_port(struct usb_serial_port *port, unsigned int *cflagp, unsigned int *baudp); +static void cp210x_change_speed(struct tty_struct *, struct usb_serial_port *, + struct ktermios *); static void cp210x_set_termios(struct tty_struct *, struct usb_serial_port *, struct ktermios*); static int cp210x_tiocmget(struct tty_struct *); @@ -138,6 +140,7 @@ static const struct usb_device_id id_table[] = { { USB_DEVICE(0x1843, 0x0200) }, /* Vaisala USB Instrument Cable */ { USB_DEVICE(0x18EF, 0xE00F) }, /* ELV USB-I2C-Interface */ { USB_DEVICE(0x1BE3, 0x07A6) }, /* WAGO 750-923 USB Service Cable */ + { USB_DEVICE(0x3195, 0xF190) }, /* Link Instruments MSO-19 */ { USB_DEVICE(0x413C, 0x9500) }, /* DW700 GPS USB interface */ { } /* Terminating Entry */ }; @@ -201,6 +204,8 @@ static struct usb_serial_driver cp210x_device = { #define CP210X_EMBED_EVENTS 0x15 #define CP210X_GET_EVENTSTATE 0x16 #define CP210X_SET_CHARS 0x19 +#define CP210X_GET_BAUDRATE 0x1D +#define CP210X_SET_BAUDRATE 0x1E /* CP210X_IFC_ENABLE */ #define UART_ENABLE 0x0001 @@ -354,8 +359,8 @@ static inline int cp210x_set_config_single(struct usb_serial_port *port, * Quantises the baud rate as per AN205 Table 1 */ static unsigned int cp210x_quantise_baudrate(unsigned int baud) { - if (baud <= 56) baud = 0; - else if (baud <= 300) baud = 300; + if (baud <= 300) + baud = 300; else if (baud <= 600) baud = 600; else if (baud <= 1200) baud = 1200; else if (baud <= 1800) baud = 1800; @@ -383,17 +388,15 @@ static unsigned int cp210x_quantise_baudrate(unsigned int baud) { else if (baud <= 491520) baud = 460800; else if (baud <= 567138) baud = 500000; else if (baud <= 670254) baud = 576000; - else if (baud <= 1053257) baud = 921600; - else if (baud <= 1474560) baud = 1228800; - else if (baud <= 2457600) baud = 1843200; - else baud = 3686400; + else if (baud < 1000000) + baud = 921600; + else if (baud > 2000000) + baud = 2000000; return baud; } static int cp210x_open(struct tty_struct *tty, struct usb_serial_port *port) { - int result; - dbg("%s - port %d", __func__, port->number); if (cp210x_set_config_single(port, CP210X_IFC_ENABLE, UART_ENABLE)) { @@ -402,13 +405,14 @@ static int cp210x_open(struct tty_struct *tty, struct usb_serial_port *port) return -EPROTO; } - result = usb_serial_generic_open(tty, port); - if (result) - return result; - /* Configure the termios structure */ cp210x_get_termios(tty, port); - return 0; + + /* The baud rate must be initialised on cp2104 */ + if (tty) + cp210x_change_speed(tty, port, NULL); + + return usb_serial_generic_open(tty, port); } static void cp210x_close(struct usb_serial_port *port) @@ -460,10 +464,7 @@ static void cp210x_get_termios_port(struct usb_serial_port *port, dbg("%s - port %d", __func__, port->number); - cp210x_get_config(port, CP210X_GET_BAUDDIV, &baud, 2); - /* Convert to baudrate */ - if (baud) - baud = cp210x_quantise_baudrate((BAUD_RATE_GEN_FREQ + baud/2)/ baud); + cp210x_get_config(port, CP210X_GET_BAUDRATE, &baud, 4); dbg("%s - baud rate = %d", __func__, baud); *baudp = baud; @@ -577,11 +578,64 @@ static void cp210x_get_termios_port(struct usb_serial_port *port, *cflagp = cflag; } +/* + * CP2101 supports the following baud rates: + * + * 300, 600, 1200, 1800, 2400, 4800, 7200, 9600, 14400, 19200, 28800, + * 38400, 56000, 57600, 115200, 128000, 230400, 460800, 921600 + * + * CP2102 and CP2103 support the following additional rates: + * + * 4000, 16000, 51200, 64000, 76800, 153600, 250000, 256000, 500000, + * 576000 + * + * The device will map a requested rate to a supported one, but the result + * of requests for rates greater than 1053257 is undefined (see AN205). + * + * CP2104, CP2105 and CP2110 support most rates up to 2M, 921k and 1M baud, + * respectively, with an error less than 1%. The actual rates are determined + * by + * + * div = round(freq / (2 x prescale x request)) + * actual = freq / (2 x prescale x div) + * + * For CP2104 and CP2105 freq is 48Mhz and prescale is 4 for request <= 365bps + * or 1 otherwise. + * For CP2110 freq is 24Mhz and prescale is 4 for request <= 300bps or 1 + * otherwise. + */ +static void cp210x_change_speed(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios) +{ + u32 baud; + + baud = tty->termios->c_ospeed; + + /* This maps the requested rate to a rate valid on cp2102 or cp2103, + * or to an arbitrary rate in [1M,2M]. + * + * NOTE: B0 is not implemented. + */ + baud = cp210x_quantise_baudrate(baud); + + dbg("%s - setting baud rate to %u", __func__, baud); + if (cp210x_set_config(port, CP210X_SET_BAUDRATE, &baud, + sizeof(baud))) { + dev_warn(&port->dev, "failed to set baud rate to %u\n", baud); + if (old_termios) + baud = old_termios->c_ospeed; + else + baud = 9600; + } + + tty_encode_baud_rate(tty, baud, baud); +} + static void cp210x_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old_termios) { unsigned int cflag, old_cflag; - unsigned int baud = 0, bits; + unsigned int bits; unsigned int modem_ctl[4]; dbg("%s - port %d", __func__, port->number); @@ -592,20 +646,9 @@ static void cp210x_set_termios(struct tty_struct *tty, tty->termios->c_cflag &= ~CMSPAR; cflag = tty->termios->c_cflag; old_cflag = old_termios->c_cflag; - baud = cp210x_quantise_baudrate(tty_get_baud_rate(tty)); - - /* If the baud rate is to be updated*/ - if (baud != tty_termios_baud_rate(old_termios) && baud != 0) { - dbg("%s - Setting baud rate to %d baud", __func__, - baud); - if (cp210x_set_config_single(port, CP210X_SET_BAUDDIV, - ((BAUD_RATE_GEN_FREQ + baud/2) / baud))) { - dbg("Baud rate requested not supported by device"); - baud = tty_termios_baud_rate(old_termios); - } - } - /* Report back the resulting baud rate */ - tty_encode_baud_rate(tty, baud, baud); + + if (tty->termios->c_ospeed != old_termios->c_ospeed) + cp210x_change_speed(tty, port, old_termios); /* If the number of data bits is to be updated */ if ((cflag & CSIZE) != (old_cflag & CSIZE)) { diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index b02fd5027cc..a872cc2c93a 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -796,6 +796,7 @@ static struct usb_device_id id_table_combined [] = { .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, { USB_DEVICE(ADI_VID, ADI_GNICEPLUS_PID), .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, + { USB_DEVICE(HORNBY_VID, HORNBY_ELITE_PID) }, { USB_DEVICE(JETI_VID, JETI_SPC1201_PID) }, { USB_DEVICE(MARVELL_VID, MARVELL_SHEEVAPLUG_PID), .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, @@ -804,6 +805,8 @@ static struct usb_device_id id_table_combined [] = { { USB_DEVICE(BAYER_VID, BAYER_CONTOUR_CABLE_PID) }, { USB_DEVICE(FTDI_VID, MARVELL_OPENRD_PID), .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, + { USB_DEVICE(FTDI_VID, TI_XDS100V2_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) }, @@ -840,6 +843,7 @@ static struct usb_device_id id_table_combined [] = { .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, { USB_DEVICE(ST_VID, ST_STMCLT1030_PID), .driver_info = (kernel_ulong_t)&ftdi_stmclite_quirk }, + { USB_DEVICE(FTDI_VID, FTDI_RF_R106) }, { }, /* Optional parameter entry */ { } /* Terminating entry */ }; @@ -1326,8 +1330,7 @@ static int set_serial_info(struct tty_struct *tty, goto check_and_exit; } - if ((new_serial.baud_base != priv->baud_base) && - (new_serial.baud_base < 9600)) { + if (new_serial.baud_base != priv->baud_base) { mutex_unlock(&priv->cfg_lock); return -EINVAL; } @@ -1816,6 +1819,7 @@ static int ftdi_sio_port_remove(struct usb_serial_port *port) static int ftdi_open(struct tty_struct *tty, struct usb_serial_port *port) { + struct ktermios dummy; struct usb_device *dev = port->serial->dev; struct ftdi_private *priv = usb_get_serial_port_data(port); int result; @@ -1834,8 +1838,10 @@ static int ftdi_open(struct tty_struct *tty, struct usb_serial_port *port) This is same behaviour as serial.c/rs_open() - Kuba */ /* ftdi_set_termios will send usb control messages */ - if (tty) - ftdi_set_termios(tty, port, tty->termios); + if (tty) { + memset(&dummy, 0, sizeof(dummy)); + ftdi_set_termios(tty, port, &dummy); + } /* Start reading from the device */ result = usb_serial_generic_open(tty, port); diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h index 055b64ef0bb..76d4f31b38c 100644 --- a/drivers/usb/serial/ftdi_sio_ids.h +++ b/drivers/usb/serial/ftdi_sio_ids.h @@ -39,6 +39,13 @@ /* www.candapter.com Ewert Energy Systems CANdapter device */ #define FTDI_CANDAPTER_PID 0x9F80 /* Product Id */ +/* + * Texas Instruments XDS100v2 JTAG / BeagleBone A3 + * http://processors.wiki.ti.com/index.php/XDS100 + * http://beagleboard.org/bone + */ +#define TI_XDS100V2_PID 0xa6d0 + #define FTDI_NXTCAM_PID 0xABB8 /* NXTCam for Mindstorms NXT */ /* US Interface Navigator (http://www.usinterface.com/) */ @@ -524,6 +531,12 @@ #define ADI_GNICE_PID 0xF000 #define ADI_GNICEPLUS_PID 0xF001 +/* + * Hornby Elite + */ +#define HORNBY_VID 0x04D8 +#define HORNBY_ELITE_PID 0x000A + /* * RATOC REX-USB60F */ @@ -1168,3 +1181,9 @@ */ /* TagTracer MIFARE*/ #define FTDI_ZEITCONTROL_TAGTRACE_MIFARE_PID 0xF7C0 + +/* + * Rainforest Automation + */ +/* ZigBee controller */ +#define FTDI_RF_R106 0x8A28 diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c index 0aac00afb5c..8a90d58ee96 100644 --- a/drivers/usb/serial/io_ti.c +++ b/drivers/usb/serial/io_ti.c @@ -2677,15 +2677,7 @@ static int edge_startup(struct usb_serial *serial) static void edge_disconnect(struct usb_serial *serial) { - int i; - struct edgeport_port *edge_port; - dbg("%s", __func__); - - for (i = 0; i < serial->num_ports; ++i) { - edge_port = usb_get_serial_port_data(serial->port[i]); - edge_remove_sysfs_attrs(edge_port->port); - } } static void edge_release(struct usb_serial *serial) @@ -2764,6 +2756,7 @@ static struct usb_serial_driver edgeport_1port_device = { .disconnect = edge_disconnect, .release = edge_release, .port_probe = edge_create_sysfs_attrs, + .port_remove = edge_remove_sysfs_attrs, .ioctl = edge_ioctl, .set_termios = edge_set_termios, .tiocmget = edge_tiocmget, @@ -2795,6 +2788,7 @@ static struct usb_serial_driver edgeport_2port_device = { .disconnect = edge_disconnect, .release = edge_release, .port_probe = edge_create_sysfs_attrs, + .port_remove = edge_remove_sysfs_attrs, .ioctl = edge_ioctl, .set_termios = edge_set_termios, .tiocmget = edge_tiocmget, diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index c96b6b6509f..2a9ed6ec8cb 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -480,6 +480,10 @@ static void option_instat_callback(struct urb *urb); #define ZD_VENDOR_ID 0x0685 #define ZD_PRODUCT_7000 0x7000 +/* LG products */ +#define LG_VENDOR_ID 0x1004 +#define LG_PRODUCT_L02C 0x618f + /* some devices interfaces need special handling due to a number of reasons */ enum option_blacklist_reason { OPTION_BLACKLIST_NONE = 0, @@ -1183,6 +1187,7 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE(YUGA_VENDOR_ID, YUGA_PRODUCT_CLU526) }, { USB_DEVICE_AND_INTERFACE_INFO(VIETTEL_VENDOR_ID, VIETTEL_PRODUCT_VT1000, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZD_VENDOR_ID, ZD_PRODUCT_7000, 0xff, 0xff, 0xff) }, + { USB_DEVICE(LG_VENDOR_ID, LG_PRODUCT_L02C) }, /* docomo L-02C modem */ { } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, option_ids); diff --git a/drivers/usb/serial/qcaux.c b/drivers/usb/serial/qcaux.c index 30b73e68a90..a34819884c1 100644 --- a/drivers/usb/serial/qcaux.c +++ b/drivers/usb/serial/qcaux.c @@ -36,6 +36,7 @@ #define UTSTARCOM_PRODUCT_UM175_V1 0x3712 #define UTSTARCOM_PRODUCT_UM175_V2 0x3714 #define UTSTARCOM_PRODUCT_UM175_ALLTEL 0x3715 +#define PANTECH_PRODUCT_UML190_VZW 0x3716 #define PANTECH_PRODUCT_UML290_VZW 0x3718 /* CMOTECH devices */ @@ -67,7 +68,11 @@ static struct usb_device_id id_table[] = { { USB_DEVICE_AND_INTERFACE_INFO(LG_VENDOR_ID, LG_PRODUCT_VX4400_6000, 0xff, 0xff, 0x00) }, { USB_DEVICE_AND_INTERFACE_INFO(SANYO_VENDOR_ID, SANYO_PRODUCT_KATANA_LX, 0xff, 0xff, 0x00) }, { USB_DEVICE_AND_INTERFACE_INFO(SAMSUNG_VENDOR_ID, SAMSUNG_PRODUCT_U520, 0xff, 0x00, 0x00) }, - { USB_DEVICE_AND_INTERFACE_INFO(UTSTARCOM_VENDOR_ID, PANTECH_PRODUCT_UML290_VZW, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(UTSTARCOM_VENDOR_ID, PANTECH_PRODUCT_UML190_VZW, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(UTSTARCOM_VENDOR_ID, PANTECH_PRODUCT_UML190_VZW, 0xff, 0xfe, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(UTSTARCOM_VENDOR_ID, PANTECH_PRODUCT_UML290_VZW, 0xff, 0xfd, 0xff) }, /* NMEA */ + { USB_DEVICE_AND_INTERFACE_INFO(UTSTARCOM_VENDOR_ID, PANTECH_PRODUCT_UML290_VZW, 0xff, 0xfe, 0xff) }, /* WMC */ + { USB_DEVICE_AND_INTERFACE_INFO(UTSTARCOM_VENDOR_ID, PANTECH_PRODUCT_UML290_VZW, 0xff, 0xff, 0xff) }, /* DIAG */ { }, }; MODULE_DEVICE_TABLE(usb, id_table); diff --git a/fs/ecryptfs/crypto.c b/fs/ecryptfs/crypto.c index 7cf5c3e9c17..c6602d24517 100644 --- a/fs/ecryptfs/crypto.c +++ b/fs/ecryptfs/crypto.c @@ -417,17 +417,6 @@ static int ecryptfs_encrypt_extent(struct page *enc_extent_page, (unsigned long long)(extent_base + extent_offset), rc); goto out; } - if (unlikely(ecryptfs_verbosity > 0)) { - ecryptfs_printk(KERN_DEBUG, "Encrypting extent " - "with iv:\n"); - ecryptfs_dump_hex(extent_iv, crypt_stat->iv_bytes); - ecryptfs_printk(KERN_DEBUG, "First 8 bytes before " - "encryption:\n"); - ecryptfs_dump_hex((char *) - (page_address(page) - + (extent_offset * crypt_stat->extent_size)), - 8); - } rc = ecryptfs_encrypt_page_offset(crypt_stat, enc_extent_page, 0, page, (extent_offset * crypt_stat->extent_size), @@ -440,14 +429,6 @@ static int ecryptfs_encrypt_extent(struct page *enc_extent_page, goto out; } rc = 0; - if (unlikely(ecryptfs_verbosity > 0)) { - ecryptfs_printk(KERN_DEBUG, "Encrypt extent [0x%.16llx]; " - "rc = [%d]\n", - (unsigned long long)(extent_base + extent_offset), rc); - ecryptfs_printk(KERN_DEBUG, "First 8 bytes after " - "encryption:\n"); - ecryptfs_dump_hex((char *)(page_address(enc_extent_page)), 8); - } out: return rc; } @@ -543,17 +524,6 @@ static int ecryptfs_decrypt_extent(struct page *page, (unsigned long long)(extent_base + extent_offset), rc); goto out; } - if (unlikely(ecryptfs_verbosity > 0)) { - ecryptfs_printk(KERN_DEBUG, "Decrypting extent " - "with iv:\n"); - ecryptfs_dump_hex(extent_iv, crypt_stat->iv_bytes); - ecryptfs_printk(KERN_DEBUG, "First 8 bytes before " - "decryption:\n"); - ecryptfs_dump_hex((char *) - (page_address(enc_extent_page) - + (extent_offset * crypt_stat->extent_size)), - 8); - } rc = ecryptfs_decrypt_page_offset(crypt_stat, page, (extent_offset * crypt_stat->extent_size), @@ -567,16 +537,6 @@ static int ecryptfs_decrypt_extent(struct page *page, goto out; } rc = 0; - if (unlikely(ecryptfs_verbosity > 0)) { - ecryptfs_printk(KERN_DEBUG, "Decrypt extent [0x%.16llx]; " - "rc = [%d]\n", - (unsigned long long)(extent_base + extent_offset), rc); - ecryptfs_printk(KERN_DEBUG, "First 8 bytes after " - "decryption:\n"); - ecryptfs_dump_hex((char *)(page_address(page) - + (extent_offset - * crypt_stat->extent_size)), 8); - } out: return rc; } @@ -1618,7 +1578,8 @@ int ecryptfs_read_metadata(struct dentry *ecryptfs_dentry) rc = ecryptfs_read_xattr_region(page_virt, ecryptfs_inode); if (rc) { printk(KERN_DEBUG "Valid eCryptfs headers not found in " - "file header region or xattr region\n"); + "file header region or xattr region, inode %lu\n", + ecryptfs_inode->i_ino); rc = -EINVAL; goto out; } @@ -1627,7 +1588,8 @@ int ecryptfs_read_metadata(struct dentry *ecryptfs_dentry) ECRYPTFS_DONT_VALIDATE_HEADER_SIZE); if (rc) { printk(KERN_DEBUG "Valid eCryptfs headers not found in " - "file xattr region either\n"); + "file xattr region either, inode %lu\n", + ecryptfs_inode->i_ino); rc = -EINVAL; } if (crypt_stat->mount_crypt_stat->flags @@ -1638,7 +1600,8 @@ int ecryptfs_read_metadata(struct dentry *ecryptfs_dentry) "crypto metadata only in the extended attribute " "region, but eCryptfs was mounted without " "xattr support enabled. eCryptfs will not treat " - "this like an encrypted file.\n"); + "this like an encrypted file, inode %lu\n", + ecryptfs_inode->i_ino); rc = -EINVAL; } } diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c index 4a4fad7fb85..e3562f2a59d 100644 --- a/fs/ecryptfs/inode.c +++ b/fs/ecryptfs/inode.c @@ -854,18 +854,6 @@ static int truncate_upper(struct dentry *dentry, struct iattr *ia, size_t num_zeros = (PAGE_CACHE_SIZE - (ia->ia_size & ~PAGE_CACHE_MASK)); - - /* - * XXX(truncate) this should really happen at the begginning - * of ->setattr. But the code is too messy to that as part - * of a larger patch. ecryptfs is also totally missing out - * on the inode_change_ok check at the beginning of - * ->setattr while would include this. - */ - rc = inode_newsize_ok(inode, ia->ia_size); - if (rc) - goto out; - if (!(crypt_stat->flags & ECRYPTFS_ENCRYPTED)) { truncate_setsize(inode, ia->ia_size); lower_ia->ia_size = ia->ia_size; @@ -915,6 +903,28 @@ static int truncate_upper(struct dentry *dentry, struct iattr *ia, return rc; } +static int ecryptfs_inode_newsize_ok(struct inode *inode, loff_t offset) +{ + struct ecryptfs_crypt_stat *crypt_stat; + loff_t lower_oldsize, lower_newsize; + + crypt_stat = &ecryptfs_inode_to_private(inode)->crypt_stat; + lower_oldsize = upper_size_to_lower_size(crypt_stat, + i_size_read(inode)); + lower_newsize = upper_size_to_lower_size(crypt_stat, offset); + if (lower_newsize > lower_oldsize) { + /* + * The eCryptfs inode and the new *lower* size are mixed here + * because we may not have the lower i_mutex held and/or it may + * not be appropriate to call inode_newsize_ok() with inodes + * from other filesystems. + */ + return inode_newsize_ok(inode, lower_newsize); + } + + return 0; +} + /** * ecryptfs_truncate * @dentry: The ecryptfs layer dentry @@ -931,6 +941,10 @@ int ecryptfs_truncate(struct dentry *dentry, loff_t new_length) struct iattr lower_ia = { .ia_valid = 0 }; int rc; + rc = ecryptfs_inode_newsize_ok(dentry->d_inode, new_length); + if (rc) + return rc; + rc = truncate_upper(dentry, &ia, &lower_ia); if (!rc && lower_ia.ia_valid & ATTR_SIZE) { struct dentry *lower_dentry = ecryptfs_dentry_to_lower(dentry); @@ -1012,6 +1026,16 @@ static int ecryptfs_setattr(struct dentry *dentry, struct iattr *ia) } } mutex_unlock(&crypt_stat->cs_mutex); + + rc = inode_change_ok(inode, ia); + if (rc) + goto out; + if (ia->ia_valid & ATTR_SIZE) { + rc = ecryptfs_inode_newsize_ok(inode, ia->ia_size); + if (rc) + goto out; + } + if (S_ISREG(inode->i_mode)) { rc = filemap_write_and_wait(inode->i_mapping); if (rc) diff --git a/fs/ecryptfs/miscdev.c b/fs/ecryptfs/miscdev.c index 940a82e63dc..0dc5a3d554a 100644 --- a/fs/ecryptfs/miscdev.c +++ b/fs/ecryptfs/miscdev.c @@ -409,11 +409,47 @@ ecryptfs_miscdev_write(struct file *file, const char __user *buf, ssize_t sz = 0; char *data; uid_t euid = current_euid(); + unsigned char packet_size_peek[3]; int rc; - if (count == 0) + if (count == 0) { goto out; + } else if (count == (1 + 4)) { + /* Likely a harmless MSG_HELO or MSG_QUIT - no packet length */ + goto memdup; + } else if (count < (1 + 4 + 1) + || count > (1 + 4 + 2 + sizeof(struct ecryptfs_message) + 4 + + ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES)) { + printk(KERN_WARNING "%s: Acceptable packet size range is " + "[%d-%lu], but amount of data written is [%zu].", + __func__, (1 + 4 + 1), + (1 + 4 + 2 + sizeof(struct ecryptfs_message) + 4 + + ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES), count); + return -EINVAL; + } + + if (copy_from_user(packet_size_peek, (buf + 1 + 4), + sizeof(packet_size_peek))) { + printk(KERN_WARNING "%s: Error while inspecting packet size\n", + __func__); + return -EFAULT; + } + + rc = ecryptfs_parse_packet_length(packet_size_peek, &packet_size, + &packet_size_length); + if (rc) { + printk(KERN_WARNING "%s: Error parsing packet length; " + "rc = [%d]\n", __func__, rc); + return rc; + } + + if ((1 + 4 + packet_size_length + packet_size) != count) { + printk(KERN_WARNING "%s: Invalid packet size [%zu]\n", __func__, + packet_size); + return -EINVAL; + } +memdup: data = memdup_user(buf, count); if (IS_ERR(data)) { printk(KERN_ERR "%s: memdup_user returned error [%ld]\n", @@ -435,23 +471,7 @@ ecryptfs_miscdev_write(struct file *file, const char __user *buf, } memcpy(&counter_nbo, &data[i], 4); seq = be32_to_cpu(counter_nbo); - i += 4; - rc = ecryptfs_parse_packet_length(&data[i], &packet_size, - &packet_size_length); - if (rc) { - printk(KERN_WARNING "%s: Error parsing packet length; " - "rc = [%d]\n", __func__, rc); - goto out_free; - } - i += packet_size_length; - if ((1 + 4 + packet_size_length + packet_size) != count) { - printk(KERN_WARNING "%s: (1 + packet_size_length([%zd])" - " + packet_size([%zd]))([%zd]) != " - "count([%zd]). Invalid packet format.\n", - __func__, packet_size_length, packet_size, - (1 + packet_size_length + packet_size), count); - goto out_free; - } + i += 4 + packet_size_length; rc = ecryptfs_miscdev_response(&data[i], packet_size, euid, current_user_ns(), task_pid(current), seq); diff --git a/fs/ecryptfs/read_write.c b/fs/ecryptfs/read_write.c index 3745f7c2b9c..54eb14caad7 100644 --- a/fs/ecryptfs/read_write.c +++ b/fs/ecryptfs/read_write.c @@ -132,6 +132,11 @@ int ecryptfs_write(struct inode *ecryptfs_inode, char *data, loff_t offset, size_t num_bytes = (PAGE_CACHE_SIZE - start_offset_in_page); size_t total_remaining_bytes = ((offset + size) - pos); + if (fatal_signal_pending(current)) { + rc = -EINTR; + break; + } + if (num_bytes > total_remaining_bytes) num_bytes = total_remaining_bytes; if (pos < offset) { @@ -193,15 +198,19 @@ int ecryptfs_write(struct inode *ecryptfs_inode, char *data, loff_t offset, } pos += num_bytes; } - if ((offset + size) > ecryptfs_file_size) { - i_size_write(ecryptfs_inode, (offset + size)); + if (pos > ecryptfs_file_size) { + i_size_write(ecryptfs_inode, pos); if (crypt_stat->flags & ECRYPTFS_ENCRYPTED) { - rc = ecryptfs_write_inode_size_to_metadata( + int rc2; + + rc2 = ecryptfs_write_inode_size_to_metadata( ecryptfs_inode); - if (rc) { + if (rc2) { printk(KERN_ERR "Problem with " "ecryptfs_write_inode_size_to_metadata; " - "rc = [%d]\n", rc); + "rc = [%d]\n", rc2); + if (!rc) + rc = rc2; goto out; } } diff --git a/fs/xfs/linux-2.6/xfs_discard.c b/fs/xfs/linux-2.6/xfs_discard.c index 244e797dae3..572494faf26 100644 --- a/fs/xfs/linux-2.6/xfs_discard.c +++ b/fs/xfs/linux-2.6/xfs_discard.c @@ -68,7 +68,7 @@ xfs_trim_extents( * Look up the longest btree in the AGF and start with it. */ error = xfs_alloc_lookup_le(cur, 0, - XFS_BUF_TO_AGF(agbp)->agf_longest, &i); + be32_to_cpu(XFS_BUF_TO_AGF(agbp)->agf_longest), &i); if (error) goto out_del_cursor; @@ -84,7 +84,7 @@ xfs_trim_extents( if (error) goto out_del_cursor; XFS_WANT_CORRUPTED_GOTO(i == 1, out_del_cursor); - ASSERT(flen <= XFS_BUF_TO_AGF(agbp)->agf_longest); + ASSERT(flen <= be32_to_cpu(XFS_BUF_TO_AGF(agbp)->agf_longest)); /* * Too small? Give up. diff --git a/fs/xfs/xfs_vnodeops.c b/fs/xfs/xfs_vnodeops.c index 6cc4d41fb0d..59509ae0b27 100644 --- a/fs/xfs/xfs_vnodeops.c +++ b/fs/xfs/xfs_vnodeops.c @@ -554,7 +554,8 @@ xfs_readlink( __func__, (unsigned long long) ip->i_ino, (long long) pathlen); ASSERT(0); - return XFS_ERROR(EFSCORRUPTED); + error = XFS_ERROR(EFSCORRUPTED); + goto out; } diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 738b3a5faa1..40aaebf50af 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -1323,6 +1323,7 @@ extern int drm_getmagic(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int drm_authmagic(struct drm_device *dev, void *data, struct drm_file *file_priv); +extern int drm_remove_magic(struct drm_master *master, drm_magic_t magic); /* Cache management (drm_cache.c) */ void drm_clflush_pages(struct page *pages[], unsigned long num_pages); diff --git a/include/net/netns/generic.h b/include/net/netns/generic.h index 3419bf5cd15..d55f4344333 100644 --- a/include/net/netns/generic.h +++ b/include/net/netns/generic.h @@ -41,6 +41,7 @@ static inline void *net_generic(const struct net *net, int id) ptr = ng->ptr[id - 1]; rcu_read_unlock(); + BUG_ON(!ptr); return ptr; } #endif diff --git a/kernel/printk.c b/kernel/printk.c index 47279f706ad..8f65449a00e 100644 --- a/kernel/printk.c +++ b/kernel/printk.c @@ -365,8 +365,10 @@ static int check_syslog_permissions(int type, bool from_file) return 0; /* For historical reasons, accept CAP_SYS_ADMIN too, with a warning */ if (capable(CAP_SYS_ADMIN)) { - WARN_ONCE(1, "Attempt to access syslog with CAP_SYS_ADMIN " - "but no CAP_SYSLOG (deprecated).\n"); + printk_once(KERN_WARNING "%s (%d): " + "Attempt to access syslog with CAP_SYS_ADMIN " + "but no CAP_SYSLOG (deprecated).\n", + current->comm, task_pid_nr(current)); return 0; } return -EPERM; diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index ef9271b69b4..9f8e2e11020 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -952,7 +952,7 @@ struct ftrace_func_probe { }; enum { - FTRACE_ENABLE_CALLS = (1 << 0), + FTRACE_UPDATE_CALLS = (1 << 0), FTRACE_DISABLE_CALLS = (1 << 1), FTRACE_UPDATE_TRACE_FUNC = (1 << 2), FTRACE_START_FUNC_RET = (1 << 3), @@ -1182,8 +1182,14 @@ alloc_and_copy_ftrace_hash(int size_bits, struct ftrace_hash *hash) return NULL; } +static void +ftrace_hash_rec_disable(struct ftrace_ops *ops, int filter_hash); +static void +ftrace_hash_rec_enable(struct ftrace_ops *ops, int filter_hash); + static int -ftrace_hash_move(struct ftrace_hash **dst, struct ftrace_hash *src) +ftrace_hash_move(struct ftrace_ops *ops, int enable, + struct ftrace_hash **dst, struct ftrace_hash *src) { struct ftrace_func_entry *entry; struct hlist_node *tp, *tn; @@ -1193,8 +1199,15 @@ ftrace_hash_move(struct ftrace_hash **dst, struct ftrace_hash *src) unsigned long key; int size = src->count; int bits = 0; + int ret; int i; + /* + * Remove the current set, update the hash and add + * them back. + */ + ftrace_hash_rec_disable(ops, enable); + /* * If the new source is empty, just free dst and assign it * the empty_hash. @@ -1215,9 +1228,10 @@ ftrace_hash_move(struct ftrace_hash **dst, struct ftrace_hash *src) if (bits > FTRACE_HASH_MAX_BITS) bits = FTRACE_HASH_MAX_BITS; + ret = -ENOMEM; new_hash = alloc_ftrace_hash(bits); if (!new_hash) - return -ENOMEM; + goto out; size = 1 << src->size_bits; for (i = 0; i < size; i++) { @@ -1236,7 +1250,16 @@ ftrace_hash_move(struct ftrace_hash **dst, struct ftrace_hash *src) rcu_assign_pointer(*dst, new_hash); free_ftrace_hash_rcu(old_hash); - return 0; + ret = 0; + out: + /* + * Enable regardless of ret: + * On success, we enable the new hash. + * On failure, we re-enable the original hash. + */ + ftrace_hash_rec_enable(ops, enable); + + return ret; } /* @@ -1498,7 +1521,7 @@ int ftrace_text_reserved(void *start, void *end) static int -__ftrace_replace_code(struct dyn_ftrace *rec, int enable) +__ftrace_replace_code(struct dyn_ftrace *rec, int update) { unsigned long ftrace_addr; unsigned long flag = 0UL; @@ -1506,17 +1529,17 @@ __ftrace_replace_code(struct dyn_ftrace *rec, int enable) ftrace_addr = (unsigned long)FTRACE_ADDR; /* - * If we are enabling tracing: + * If we are updating calls: * * If the record has a ref count, then we need to enable it * because someone is using it. * * Otherwise we make sure its disabled. * - * If we are disabling tracing, then disable all records that + * If we are disabling calls, then disable all records that * are enabled. */ - if (enable && (rec->flags & ~FTRACE_FL_MASK)) + if (update && (rec->flags & ~FTRACE_FL_MASK)) flag = FTRACE_FL_ENABLED; /* If the state of this record hasn't changed, then do nothing */ @@ -1532,7 +1555,7 @@ __ftrace_replace_code(struct dyn_ftrace *rec, int enable) return ftrace_make_nop(NULL, rec, ftrace_addr); } -static void ftrace_replace_code(int enable) +static void ftrace_replace_code(int update) { struct dyn_ftrace *rec; struct ftrace_page *pg; @@ -1546,7 +1569,7 @@ static void ftrace_replace_code(int enable) if (rec->flags & FTRACE_FL_FREE) continue; - failed = __ftrace_replace_code(rec, enable); + failed = __ftrace_replace_code(rec, update); if (failed) { ftrace_bug(failed, rec->ip); /* Stop processing */ @@ -1596,7 +1619,7 @@ static int __ftrace_modify_code(void *data) { int *command = data; - if (*command & FTRACE_ENABLE_CALLS) + if (*command & FTRACE_UPDATE_CALLS) ftrace_replace_code(1); else if (*command & FTRACE_DISABLE_CALLS) ftrace_replace_code(0); @@ -1652,7 +1675,7 @@ static int ftrace_startup(struct ftrace_ops *ops, int command) return -ENODEV; ftrace_start_up++; - command |= FTRACE_ENABLE_CALLS; + command |= FTRACE_UPDATE_CALLS; /* ops marked global share the filter hashes */ if (ops->flags & FTRACE_OPS_FL_GLOBAL) { @@ -1704,8 +1727,7 @@ static void ftrace_shutdown(struct ftrace_ops *ops, int command) if (ops != &global_ops || !global_start_up) ops->flags &= ~FTRACE_OPS_FL_ENABLED; - if (!ftrace_start_up) - command |= FTRACE_DISABLE_CALLS; + command |= FTRACE_UPDATE_CALLS; if (saved_ftrace_func != ftrace_trace_function) { saved_ftrace_func = ftrace_trace_function; @@ -1727,7 +1749,7 @@ static void ftrace_startup_sysctl(void) saved_ftrace_func = NULL; /* ftrace_start_up is true if we want ftrace running */ if (ftrace_start_up) - ftrace_run_update_code(FTRACE_ENABLE_CALLS); + ftrace_run_update_code(FTRACE_UPDATE_CALLS); } static void ftrace_shutdown_sysctl(void) @@ -2877,7 +2899,11 @@ ftrace_set_regex(struct ftrace_ops *ops, unsigned char *buf, int len, ftrace_match_records(hash, buf, len); mutex_lock(&ftrace_lock); - ret = ftrace_hash_move(orig_hash, hash); + ret = ftrace_hash_move(ops, enable, orig_hash, hash); + if (!ret && ops->flags & FTRACE_OPS_FL_ENABLED + && ftrace_enabled) + ftrace_run_update_code(FTRACE_UPDATE_CALLS); + mutex_unlock(&ftrace_lock); mutex_unlock(&ftrace_regex_lock); @@ -3060,18 +3086,12 @@ ftrace_regex_release(struct inode *inode, struct file *file) orig_hash = &iter->ops->notrace_hash; mutex_lock(&ftrace_lock); - /* - * Remove the current set, update the hash and add - * them back. - */ - ftrace_hash_rec_disable(iter->ops, filter_hash); - ret = ftrace_hash_move(orig_hash, iter->hash); - if (!ret) { - ftrace_hash_rec_enable(iter->ops, filter_hash); - if (iter->ops->flags & FTRACE_OPS_FL_ENABLED - && ftrace_enabled) - ftrace_run_update_code(FTRACE_ENABLE_CALLS); - } + ret = ftrace_hash_move(iter->ops, filter_hash, + orig_hash, iter->hash); + if (!ret && (iter->ops->flags & FTRACE_OPS_FL_ENABLED) + && ftrace_enabled) + ftrace_run_update_code(FTRACE_UPDATE_CALLS); + mutex_unlock(&ftrace_lock); } free_ftrace_hash(iter->hash); diff --git a/net/caif/caif_dev.c b/net/caif/caif_dev.c index dbdaa95b800..5ba4366a220 100644 --- a/net/caif/caif_dev.c +++ b/net/caif/caif_dev.c @@ -53,7 +53,6 @@ struct cfcnfg *get_cfcnfg(struct net *net) struct caif_net *caifn; BUG_ON(!net); caifn = net_generic(net, caif_net_id); - BUG_ON(!caifn); return caifn->cfg; } EXPORT_SYMBOL(get_cfcnfg); @@ -63,7 +62,6 @@ static struct caif_device_entry_list *caif_device_list(struct net *net) struct caif_net *caifn; BUG_ON(!net); caifn = net_generic(net, caif_net_id); - BUG_ON(!caifn); return &caifn->caifdevs; } @@ -92,7 +90,6 @@ static struct caif_device_entry *caif_device_alloc(struct net_device *dev) struct caif_device_entry *caifd; caifdevs = caif_device_list(dev_net(dev)); - BUG_ON(!caifdevs); caifd = kzalloc(sizeof(*caifd), GFP_ATOMIC); if (!caifd) @@ -108,7 +105,7 @@ static struct caif_device_entry *caif_get(struct net_device *dev) struct caif_device_entry_list *caifdevs = caif_device_list(dev_net(dev)); struct caif_device_entry *caifd; - BUG_ON(!caifdevs); + list_for_each_entry_rcu(caifd, &caifdevs->list, list) { if (caifd->netdev == dev) return caifd; @@ -349,7 +346,7 @@ static struct notifier_block caif_device_notifier = { static int caif_init_net(struct net *net) { struct caif_net *caifn = net_generic(net, caif_net_id); - BUG_ON(!caifn); + INIT_LIST_HEAD(&caifn->caifdevs.list); mutex_init(&caifn->caifdevs.lock); @@ -414,7 +411,7 @@ static int __init caif_device_init(void) { int result; - result = register_pernet_device(&caif_net_ops); + result = register_pernet_subsys(&caif_net_ops); if (result) return result; @@ -427,7 +424,7 @@ static int __init caif_device_init(void) static void __exit caif_device_exit(void) { - unregister_pernet_device(&caif_net_ops); + unregister_pernet_subsys(&caif_net_ops); unregister_netdevice_notifier(&caif_device_notifier); dev_remove_pack(&caif_packet_type); } diff --git a/net/caif/cfcnfg.c b/net/caif/cfcnfg.c index 52fe33bee02..bca32d7c15c 100644 --- a/net/caif/cfcnfg.c +++ b/net/caif/cfcnfg.c @@ -313,7 +313,6 @@ int caif_connect_client(struct net *net, struct caif_connect_request *conn_req, int err; struct cfctrl_link_param param; struct cfcnfg *cfg = get_cfcnfg(net); - caif_assert(cfg != NULL); rcu_read_lock(); err = caif_connect_req_to_link_param(cfg, conn_req, ¶m); diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c index ea489db1bc2..0b0211d7fc3 100644 --- a/net/core/net_namespace.c +++ b/net/core/net_namespace.c @@ -29,6 +29,20 @@ EXPORT_SYMBOL(init_net); #define INITIAL_NET_GEN_PTRS 13 /* +1 for len +2 for rcu_head */ +static unsigned int max_gen_ptrs = INITIAL_NET_GEN_PTRS; + +static struct net_generic *net_alloc_generic(void) +{ + struct net_generic *ng; + size_t generic_size = offsetof(struct net_generic, ptr[max_gen_ptrs]); + + ng = kzalloc(generic_size, GFP_KERNEL); + if (ng) + ng->len = max_gen_ptrs; + + return ng; +} + static int net_assign_generic(struct net *net, int id, void *data) { struct net_generic *ng, *old_ng; @@ -42,8 +56,7 @@ static int net_assign_generic(struct net *net, int id, void *data) if (old_ng->len >= id) goto assign; - ng = kzalloc(sizeof(struct net_generic) + - id * sizeof(void *), GFP_KERNEL); + ng = net_alloc_generic(); if (ng == NULL) return -ENOMEM; @@ -58,7 +71,6 @@ static int net_assign_generic(struct net *net, int id, void *data) * the old copy for kfree after a grace period. */ - ng->len = id; memcpy(&ng->ptr, &old_ng->ptr, old_ng->len * sizeof(void*)); rcu_assign_pointer(net->gen, ng); @@ -159,18 +171,6 @@ static __net_init int setup_net(struct net *net) goto out; } -static struct net_generic *net_alloc_generic(void) -{ - struct net_generic *ng; - size_t generic_size = sizeof(struct net_generic) + - INITIAL_NET_GEN_PTRS * sizeof(void *); - - ng = kzalloc(generic_size, GFP_KERNEL); - if (ng) - ng->len = INITIAL_NET_GEN_PTRS; - - return ng; -} #ifdef CONFIG_NET_NS static struct kmem_cache *net_cachep; @@ -481,6 +481,7 @@ static int register_pernet_operations(struct list_head *list, } return error; } + max_gen_ptrs = max_t(unsigned int, max_gen_ptrs, *ops->id); } error = __register_pernet_operations(list, ops); if (error) { diff --git a/net/ipv4/ah4.c b/net/ipv4/ah4.c index c7056b2e831..36d14406261 100644 --- a/net/ipv4/ah4.c +++ b/net/ipv4/ah4.c @@ -369,8 +369,6 @@ static int ah_input(struct xfrm_state *x, struct sk_buff *skb) if (err == -EINPROGRESS) goto out; - if (err == -EBUSY) - err = NET_XMIT_DROP; goto out_free; } diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index a825a84d3c1..e3762bc8263 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -635,7 +635,7 @@ static void tcp_v4_send_reset(struct sock *sk, struct sk_buff *skb) arg.iov[0].iov_len = sizeof(rep.th); #ifdef CONFIG_TCP_MD5SIG - key = sk ? tcp_v4_md5_do_lookup(sk, ip_hdr(skb)->daddr) : NULL; + key = sk ? tcp_v4_md5_do_lookup(sk, ip_hdr(skb)->saddr) : NULL; if (key) { rep.opt[0] = htonl((TCPOPT_NOP << 24) | (TCPOPT_NOP << 16) | diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index d901a2417b5..84333df77d0 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -1146,11 +1146,9 @@ int tcp_trim_head(struct sock *sk, struct sk_buff *skb, u32 len) sk_mem_uncharge(sk, len); sock_set_flag(sk, SOCK_QUEUE_SHRUNK); - /* Any change of skb->len requires recalculation of tso - * factor and mss. - */ + /* Any change of skb->len requires recalculation of tso factor. */ if (tcp_skb_pcount(skb) > 1) - tcp_set_skb_tso_segs(sk, skb, tcp_current_mss(sk)); + tcp_set_skb_tso_segs(sk, skb, tcp_skb_mss(skb)); return 0; } diff --git a/net/ipv6/ah6.c b/net/ipv6/ah6.c index 7a33aaa0022..4c0f894d084 100644 --- a/net/ipv6/ah6.c +++ b/net/ipv6/ah6.c @@ -581,8 +581,6 @@ static int ah6_input(struct xfrm_state *x, struct sk_buff *skb) if (err == -EINPROGRESS) goto out; - if (err == -EBUSY) - err = NET_XMIT_DROP; goto out_free; } diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 8ae18c6c3f1..a03f3bdcdb3 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -1101,7 +1101,7 @@ static void tcp_v6_send_reset(struct sock *sk, struct sk_buff *skb) #ifdef CONFIG_TCP_MD5SIG if (sk) - key = tcp_v6_md5_do_lookup(sk, &ipv6_hdr(skb)->daddr); + key = tcp_v6_md5_do_lookup(sk, &ipv6_hdr(skb)->saddr); #endif if (th->ack) diff --git a/net/l2tp/l2tp_ip.c b/net/l2tp/l2tp_ip.c index b6466e71f5e..858ca23aa6d 100644 --- a/net/l2tp/l2tp_ip.c +++ b/net/l2tp/l2tp_ip.c @@ -393,11 +393,6 @@ static int l2tp_ip_backlog_recv(struct sock *sk, struct sk_buff *skb) { int rc; - if (!xfrm4_policy_check(sk, XFRM_POLICY_IN, skb)) - goto drop; - - nf_reset(skb); - /* Charge it to the socket, dropping if the queue is full. */ rc = sock_queue_rcv_skb(sk, skb); if (rc < 0) diff --git a/net/rds/af_rds.c b/net/rds/af_rds.c index bb6ad81b671..424ff622ab5 100644 --- a/net/rds/af_rds.c +++ b/net/rds/af_rds.c @@ -68,7 +68,6 @@ static int rds_release(struct socket *sock) { struct sock *sk = sock->sk; struct rds_sock *rs; - unsigned long flags; if (!sk) goto out; @@ -94,10 +93,10 @@ static int rds_release(struct socket *sock) rds_rdma_drop_keys(rs); rds_notify_queue_get(rs, NULL); - spin_lock_irqsave(&rds_sock_lock, flags); + spin_lock_bh(&rds_sock_lock); list_del_init(&rs->rs_item); rds_sock_count--; - spin_unlock_irqrestore(&rds_sock_lock, flags); + spin_unlock_bh(&rds_sock_lock); rds_trans_put(rs->rs_transport); @@ -409,7 +408,6 @@ static const struct proto_ops rds_proto_ops = { static int __rds_create(struct socket *sock, struct sock *sk, int protocol) { - unsigned long flags; struct rds_sock *rs; sock_init_data(sock, sk); @@ -426,10 +424,10 @@ static int __rds_create(struct socket *sock, struct sock *sk, int protocol) spin_lock_init(&rs->rs_rdma_lock); rs->rs_rdma_keys = RB_ROOT; - spin_lock_irqsave(&rds_sock_lock, flags); + spin_lock_bh(&rds_sock_lock); list_add_tail(&rs->rs_item, &rds_sock_list); rds_sock_count++; - spin_unlock_irqrestore(&rds_sock_lock, flags); + spin_unlock_bh(&rds_sock_lock); return 0; } @@ -471,12 +469,11 @@ static void rds_sock_inc_info(struct socket *sock, unsigned int len, { struct rds_sock *rs; struct rds_incoming *inc; - unsigned long flags; unsigned int total = 0; len /= sizeof(struct rds_info_message); - spin_lock_irqsave(&rds_sock_lock, flags); + spin_lock_bh(&rds_sock_lock); list_for_each_entry(rs, &rds_sock_list, rs_item) { read_lock(&rs->rs_recv_lock); @@ -492,7 +489,7 @@ static void rds_sock_inc_info(struct socket *sock, unsigned int len, read_unlock(&rs->rs_recv_lock); } - spin_unlock_irqrestore(&rds_sock_lock, flags); + spin_unlock_bh(&rds_sock_lock); lens->nr = total; lens->each = sizeof(struct rds_info_message); @@ -504,11 +501,10 @@ static void rds_sock_info(struct socket *sock, unsigned int len, { struct rds_info_socket sinfo; struct rds_sock *rs; - unsigned long flags; len /= sizeof(struct rds_info_socket); - spin_lock_irqsave(&rds_sock_lock, flags); + spin_lock_bh(&rds_sock_lock); if (len < rds_sock_count) goto out; @@ -529,7 +525,7 @@ static void rds_sock_info(struct socket *sock, unsigned int len, lens->nr = rds_sock_count; lens->each = sizeof(struct rds_info_socket); - spin_unlock_irqrestore(&rds_sock_lock, flags); + spin_unlock_bh(&rds_sock_lock); } static void rds_exit(void) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index eb0a141966a..51412e1296f 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -16419,6 +16419,7 @@ static const struct alc_config_preset alc861_presets[] = { /* Pin config fixes */ enum { PINFIX_FSC_AMILO_PI1505, + PINFIX_ASUS_A6RP, }; static const struct alc_fixup alc861_fixups[] = { @@ -16430,9 +16431,19 @@ static const struct alc_fixup alc861_fixups[] = { { } } }, + [PINFIX_ASUS_A6RP] = { + .type = ALC_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + /* node 0x0f VREF seems controlling the master output */ + { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50 }, + { } + }, + }, }; static const struct snd_pci_quirk alc861_fixup_tbl[] = { + SND_PCI_QUIRK(0x1043, 0x1393, "ASUS A6Rp", PINFIX_ASUS_A6RP), + SND_PCI_QUIRK(0x1584, 0x2b01, "Haier W18", PINFIX_ASUS_A6RP), SND_PCI_QUIRK(0x1734, 0x10c7, "FSC Amilo Pi1505", PINFIX_FSC_AMILO_PI1505), {} }; diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 0d8db75535d..43d88c72493 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -4162,13 +4162,15 @@ static int enable_pin_detect(struct hda_codec *codec, hda_nid_t nid, return 1; } -static int is_nid_hp_pin(struct auto_pin_cfg *cfg, hda_nid_t nid) +static int is_nid_out_jack_pin(struct auto_pin_cfg *cfg, hda_nid_t nid) { int i; for (i = 0; i < cfg->hp_outs; i++) if (cfg->hp_pins[i] == nid) return 1; /* nid is a HP-Out */ - + for (i = 0; i < cfg->line_outs; i++) + if (cfg->line_out_pins[i] == nid) + return 1; /* nid is a line-Out */ return 0; /* nid is not a HP-Out */ }; @@ -4354,7 +4356,7 @@ static int stac92xx_init(struct hda_codec *codec) continue; } - if (is_nid_hp_pin(cfg, nid)) + if (is_nid_out_jack_pin(cfg, nid)) continue; /* already has an unsol event */ pinctl = snd_hda_codec_read(codec, nid, 0, From 6c97724551e5f428914a81f6519e37aea033d631 Mon Sep 17 00:00:00 2001 From: Ethan Chen Date: Sun, 2 Sep 2012 17:40:48 -0700 Subject: [PATCH 024/111] Linux 3.0.20 --- Makefile | 2 +- drivers/acpi/pci_root.c | 7 +++++ drivers/pci/pci-acpi.c | 1 - drivers/pci/pcie/aspm.c | 58 +++++++++++++++++++++++++--------------- include/linux/pci-aspm.h | 4 +-- 5 files changed, 47 insertions(+), 25 deletions(-) diff --git a/Makefile b/Makefile index 718ef7319a1..96c47d1fd76 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ VERSION = 3 PATCHLEVEL = 0 -SUBLEVEL = 19 +SUBLEVEL = 20 EXTRAVERSION = NAME = Sneaky Weasel diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index d06078d660a..dfafecbddb5 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -595,6 +595,13 @@ static int __devinit acpi_pci_root_add(struct acpi_device *device) if (ACPI_SUCCESS(status)) { dev_info(root->bus->bridge, "ACPI _OSC control (0x%02x) granted\n", flags); + if (acpi_gbl_FADT.boot_flags & ACPI_FADT_NO_ASPM) { + /* + * We have ASPM control, but the FADT indicates + * that it's unsupported. Clear it. + */ + pcie_clear_aspm(root->bus); + } } else { dev_info(root->bus->bridge, "ACPI _OSC request failed (%s), " diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index d36f41ea8cb..56b04bc80a1 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -393,7 +393,6 @@ static int __init acpi_pci_init(void) if (acpi_gbl_FADT.boot_flags & ACPI_FADT_NO_ASPM) { printk(KERN_INFO"ACPI FADT declares the system doesn't support PCIe ASPM, so disable it\n"); - pcie_clear_aspm(); pcie_no_aspm(); } diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index 6892601fc76..e25af67f685 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -68,7 +68,7 @@ struct pcie_link_state { struct aspm_latency acceptable[8]; }; -static int aspm_disabled, aspm_force, aspm_clear_state; +static int aspm_disabled, aspm_force; static bool aspm_support_enabled = true; static DEFINE_MUTEX(aspm_lock); static LIST_HEAD(link_list); @@ -500,9 +500,6 @@ static int pcie_aspm_sanity_check(struct pci_dev *pdev) int pos; u32 reg32; - if (aspm_clear_state) - return -EINVAL; - /* * Some functions in a slot might not all be PCIe functions, * very strange. Disable ASPM for the whole slot @@ -574,9 +571,6 @@ void pcie_aspm_init_link_state(struct pci_dev *pdev) pdev->pcie_type != PCI_EXP_TYPE_DOWNSTREAM) return; - if (aspm_disabled && !aspm_clear_state) - return; - /* VIA has a strange chipset, root port is under a bridge */ if (pdev->pcie_type == PCI_EXP_TYPE_ROOT_PORT && pdev->bus->self) @@ -608,7 +602,7 @@ void pcie_aspm_init_link_state(struct pci_dev *pdev) * the BIOS's expectation, we'll do so once pci_enable_device() is * called. */ - if (aspm_policy != POLICY_POWERSAVE || aspm_clear_state) { + if (aspm_policy != POLICY_POWERSAVE) { pcie_config_aspm_path(link); pcie_set_clkpm(link, policy_to_clkpm_state(link)); } @@ -649,8 +643,7 @@ void pcie_aspm_exit_link_state(struct pci_dev *pdev) struct pci_dev *parent = pdev->bus->self; struct pcie_link_state *link, *root, *parent_link; - if ((aspm_disabled && !aspm_clear_state) || !pci_is_pcie(pdev) || - !parent || !parent->link_state) + if (!pci_is_pcie(pdev) || !parent || !parent->link_state) return; if ((parent->pcie_type != PCI_EXP_TYPE_ROOT_PORT) && (parent->pcie_type != PCI_EXP_TYPE_DOWNSTREAM)) @@ -734,13 +727,18 @@ void pcie_aspm_powersave_config_link(struct pci_dev *pdev) * pci_disable_link_state - disable pci device's link state, so the link will * never enter specific states */ -static void __pci_disable_link_state(struct pci_dev *pdev, int state, bool sem) +static void __pci_disable_link_state(struct pci_dev *pdev, int state, bool sem, + bool force) { struct pci_dev *parent = pdev->bus->self; struct pcie_link_state *link; - if (aspm_disabled || !pci_is_pcie(pdev)) + if (aspm_disabled && !force) + return; + + if (!pci_is_pcie(pdev)) return; + if (pdev->pcie_type == PCI_EXP_TYPE_ROOT_PORT || pdev->pcie_type == PCI_EXP_TYPE_DOWNSTREAM) parent = pdev; @@ -768,16 +766,31 @@ static void __pci_disable_link_state(struct pci_dev *pdev, int state, bool sem) void pci_disable_link_state_locked(struct pci_dev *pdev, int state) { - __pci_disable_link_state(pdev, state, false); + __pci_disable_link_state(pdev, state, false, false); } EXPORT_SYMBOL(pci_disable_link_state_locked); void pci_disable_link_state(struct pci_dev *pdev, int state) { - __pci_disable_link_state(pdev, state, true); + __pci_disable_link_state(pdev, state, true, false); } EXPORT_SYMBOL(pci_disable_link_state); +void pcie_clear_aspm(struct pci_bus *bus) +{ + struct pci_dev *child; + + /* + * Clear any ASPM setup that the firmware has carried out on this bus + */ + list_for_each_entry(child, &bus->devices, bus_list) { + __pci_disable_link_state(child, PCIE_LINK_STATE_L0S | + PCIE_LINK_STATE_L1 | + PCIE_LINK_STATE_CLKPM, + false, true); + } +} + static int pcie_aspm_set_policy(const char *val, struct kernel_param *kp) { int i; @@ -935,6 +948,7 @@ void pcie_aspm_remove_sysfs_dev_files(struct pci_dev *pdev) static int __init pcie_aspm_disable(char *str) { if (!strcmp(str, "off")) { + aspm_policy = POLICY_DEFAULT; aspm_disabled = 1; aspm_support_enabled = false; printk(KERN_INFO "PCIe ASPM is disabled\n"); @@ -947,16 +961,18 @@ static int __init pcie_aspm_disable(char *str) __setup("pcie_aspm=", pcie_aspm_disable); -void pcie_clear_aspm(void) -{ - if (!aspm_force) - aspm_clear_state = 1; -} - void pcie_no_aspm(void) { - if (!aspm_force) + /* + * Disabling ASPM is intended to prevent the kernel from modifying + * existing hardware state, not to clear existing state. To that end: + * (a) set policy to POLICY_DEFAULT in order to avoid changing state + * (b) prevent userspace from changing policy + */ + if (!aspm_force) { + aspm_policy = POLICY_DEFAULT; aspm_disabled = 1; + } } /** diff --git a/include/linux/pci-aspm.h b/include/linux/pci-aspm.h index 7cea7b6c141..c8320144fe7 100644 --- a/include/linux/pci-aspm.h +++ b/include/linux/pci-aspm.h @@ -29,7 +29,7 @@ extern void pcie_aspm_pm_state_change(struct pci_dev *pdev); extern void pcie_aspm_powersave_config_link(struct pci_dev *pdev); extern void pci_disable_link_state(struct pci_dev *pdev, int state); extern void pci_disable_link_state_locked(struct pci_dev *pdev, int state); -extern void pcie_clear_aspm(void); +extern void pcie_clear_aspm(struct pci_bus *bus); extern void pcie_no_aspm(void); #else static inline void pcie_aspm_init_link_state(struct pci_dev *pdev) @@ -47,7 +47,7 @@ static inline void pcie_aspm_powersave_config_link(struct pci_dev *pdev) static inline void pci_disable_link_state(struct pci_dev *pdev, int state) { } -static inline void pcie_clear_aspm(void) +static inline void pcie_clear_aspm(struct pci_bus *bus) { } static inline void pcie_no_aspm(void) From be52cc854a8f3275b7a789769679334d548f5237 Mon Sep 17 00:00:00 2001 From: Ethan Chen Date: Sun, 2 Sep 2012 18:06:59 -0700 Subject: [PATCH 025/111] Linux 3.0.21 --- Makefile | 2 +- arch/arm/kernel/ptrace.c | 8 +- arch/arm/kernel/signal.c | 5 +- arch/arm/mach-omap2/gpmc.c | 6 + drivers/cpufreq/powernow-k8.c | 30 ++-- drivers/dma/at_hdmac.c | 4 +- drivers/dma/at_hdmac_regs.h | 17 ++- drivers/firewire/ohci.c | 6 +- drivers/gpu/drm/i915/i915_drv.h | 2 + drivers/gpu/drm/i915/i915_irq.c | 13 +- drivers/gpu/drm/i915/i915_suspend.c | 4 + drivers/gpu/drm/i915/intel_dp.c | 1 + drivers/gpu/drm/i915/intel_hdmi.c | 8 +- drivers/gpu/drm/i915/intel_tv.c | 16 +-- drivers/gpu/drm/nouveau/nouveau_gem.c | 23 +++- drivers/gpu/drm/radeon/atombios_crtc.c | 4 +- drivers/gpu/drm/radeon/radeon_device.c | 4 + drivers/hwmon/w83627ehf.c | 26 +++- drivers/infiniband/core/addr.c | 16 ++- drivers/infiniband/hw/cxgb3/iwch_cm.c | 16 ++- drivers/infiniband/hw/cxgb4/cm.c | 46 ++++--- drivers/infiniband/hw/mlx4/mad.c | 7 +- drivers/infiniband/hw/mlx4/qp.c | 2 +- drivers/infiniband/hw/nes/nes_cm.c | 8 +- drivers/infiniband/ulp/ipoib/ipoib_main.c | 59 +++++--- .../infiniband/ulp/ipoib/ipoib_multicast.c | 24 ++-- drivers/misc/cb710/core.c | 1 + drivers/net/cxgb3/cxgb3_offload.c | 8 +- drivers/pcmcia/ds.c | 4 +- drivers/s390/net/qeth_l3_main.c | 25 +++- drivers/scsi/cxgbi/cxgb3i/cxgb3i.c | 2 +- drivers/scsi/cxgbi/cxgb4i/cxgb4i.c | 2 +- drivers/scsi/cxgbi/libcxgbi.c | 4 +- drivers/staging/asus_oled/asus_oled.c | 13 +- drivers/staging/rtl8712/usb_intf.c | 1 + drivers/target/target_core_pr.c | 5 +- drivers/target/target_core_transport.c | 8 +- drivers/tty/vt/vt_ioctl.c | 1 - drivers/usb/gadget/f_loopback.c | 2 +- drivers/usb/host/pci-quirks.c | 6 + drivers/usb/serial/ftdi_sio.c | 1 + drivers/usb/serial/ftdi_sio_ids.h | 7 + drivers/usb/serial/option.c | 129 +++++++++++++++++- drivers/video/atmel_lcdfb.c | 2 +- fs/cifs/sess.c | 7 +- fs/ecryptfs/read_write.c | 4 +- fs/proc/base.c | 111 +++++++-------- fs/udf/super.c | 6 + include/net/arp.h | 1 + include/net/dst.h | 27 +++- kernel/kprobes.c | 6 +- kernel/panic.c | 12 +- kernel/sched_rt.c | 5 + mm/compaction.c | 24 +++- mm/filemap.c | 8 +- mm/filemap_xip.c | 7 +- mm/huge_memory.c | 4 +- mm/swap.c | 2 +- net/atm/clip.c | 16 ++- net/bridge/br_netfilter.c | 6 +- net/core/dst.c | 15 +- net/core/neighbour.c | 19 ++- net/decnet/dn_neigh.c | 8 +- net/decnet/dn_route.c | 18 +-- net/ipv4/arp.c | 28 ++-- net/ipv4/ip_gre.c | 2 +- net/ipv4/ip_output.c | 22 ++- net/ipv4/route.c | 31 +++-- net/ipv6/addrconf.c | 2 +- net/ipv6/ip6_fib.c | 2 +- net/ipv6/ip6_output.c | 40 ++++-- net/ipv6/ndisc.c | 4 +- net/ipv6/route.c | 59 +++++--- net/ipv6/sit.c | 4 +- net/sched/sch_teql.c | 31 +++-- net/xfrm/xfrm_policy.c | 2 +- sound/pci/hda/hda_codec.c | 2 +- sound/soc/codecs/wm8962.c | 6 +- sound/soc/codecs/wm_hubs.c | 18 ++- sound/soc/soc-core.c | 18 ++- 80 files changed, 797 insertions(+), 358 deletions(-) diff --git a/Makefile b/Makefile index 96c47d1fd76..1e04a9f8911 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ VERSION = 3 PATCHLEVEL = 0 -SUBLEVEL = 20 +SUBLEVEL = 21 EXTRAVERSION = NAME = Sneaky Weasel diff --git a/arch/arm/kernel/ptrace.c b/arch/arm/kernel/ptrace.c index 97260060bf2..172ae01c26e 100644 --- a/arch/arm/kernel/ptrace.c +++ b/arch/arm/kernel/ptrace.c @@ -719,10 +719,13 @@ static int vfp_set(struct task_struct *target, { int ret; struct thread_info *thread = task_thread_info(target); - struct vfp_hard_struct new_vfp = thread->vfpstate.hard; + struct vfp_hard_struct new_vfp; const size_t user_fpregs_offset = offsetof(struct user_vfp, fpregs); const size_t user_fpscr_offset = offsetof(struct user_vfp, fpscr); + vfp_sync_hwstate(thread); + new_vfp = thread->vfpstate.hard; + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &new_vfp.fpregs, user_fpregs_offset, @@ -743,9 +746,8 @@ static int vfp_set(struct task_struct *target, if (ret) return ret; - vfp_sync_hwstate(thread); - thread->vfpstate.hard = new_vfp; vfp_flush_hwstate(thread); + thread->vfpstate.hard = new_vfp; return 0; } diff --git a/arch/arm/kernel/signal.c b/arch/arm/kernel/signal.c index 0340224cf73..9e617bd4a14 100644 --- a/arch/arm/kernel/signal.c +++ b/arch/arm/kernel/signal.c @@ -227,6 +227,8 @@ static int restore_vfp_context(struct vfp_sigframe __user *frame) if (magic != VFP_MAGIC || size != VFP_STORAGE_SIZE) return -EINVAL; + vfp_flush_hwstate(thread); + /* * Copy the floating point registers. There can be unused * registers see asm/hwcap.h for details. @@ -251,9 +253,6 @@ static int restore_vfp_context(struct vfp_sigframe __user *frame) __get_user_error(h->fpinst, &frame->ufp_exc.fpinst, err); __get_user_error(h->fpinst2, &frame->ufp_exc.fpinst2, err); - if (!err) - vfp_flush_hwstate(thread); - return err ? -EFAULT : 0; } diff --git a/arch/arm/mach-omap2/gpmc.c b/arch/arm/mach-omap2/gpmc.c index 130034bf01d..dfffbbf4c00 100644 --- a/arch/arm/mach-omap2/gpmc.c +++ b/arch/arm/mach-omap2/gpmc.c @@ -528,7 +528,13 @@ int gpmc_cs_configure(int cs, int cmd, int wval) case GPMC_CONFIG_DEV_SIZE: regval = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG1); + + /* clear 2 target bits */ + regval &= ~GPMC_CONFIG1_DEVICESIZE(3); + + /* set the proper value */ regval |= GPMC_CONFIG1_DEVICESIZE(wval); + gpmc_cs_write_reg(cs, GPMC_CS_CONFIG1, regval); break; diff --git a/drivers/cpufreq/powernow-k8.c b/drivers/cpufreq/powernow-k8.c index bce576d7478..ad683ec2c57 100644 --- a/drivers/cpufreq/powernow-k8.c +++ b/drivers/cpufreq/powernow-k8.c @@ -54,6 +54,9 @@ static DEFINE_PER_CPU(struct powernow_k8_data *, powernow_data); static int cpu_family = CPU_OPTERON; +/* array to map SW pstate number to acpi state */ +static u32 ps_to_as[8]; + /* core performance boost */ static bool cpb_capable, cpb_enabled; static struct msr __percpu *msrs; @@ -80,9 +83,9 @@ static u32 find_khz_freq_from_fid(u32 fid) } static u32 find_khz_freq_from_pstate(struct cpufreq_frequency_table *data, - u32 pstate) + u32 pstate) { - return data[pstate].frequency; + return data[ps_to_as[pstate]].frequency; } /* Return the vco fid for an input fid @@ -926,23 +929,27 @@ static int fill_powernow_table_pstate(struct powernow_k8_data *data, invalidate_entry(powernow_table, i); continue; } - rdmsr(MSR_PSTATE_DEF_BASE + index, lo, hi); - if (!(hi & HW_PSTATE_VALID_MASK)) { - pr_debug("invalid pstate %d, ignoring\n", index); - invalidate_entry(powernow_table, i); - continue; - } - powernow_table[i].index = index; + ps_to_as[index] = i; /* Frequency may be rounded for these */ if ((boot_cpu_data.x86 == 0x10 && boot_cpu_data.x86_model < 10) || boot_cpu_data.x86 == 0x11) { + + rdmsr(MSR_PSTATE_DEF_BASE + index, lo, hi); + if (!(hi & HW_PSTATE_VALID_MASK)) { + pr_debug("invalid pstate %d, ignoring\n", index); + invalidate_entry(powernow_table, i); + continue; + } + powernow_table[i].frequency = freq_from_fid_did(lo & 0x3f, (lo >> 6) & 7); } else powernow_table[i].frequency = data->acpi_data.states[i].core_frequency * 1000; + + powernow_table[i].index = index; } return 0; } @@ -1189,7 +1196,8 @@ static int powernowk8_target(struct cpufreq_policy *pol, powernow_k8_acpi_pst_values(data, newstate); if (cpu_family == CPU_HW_PSTATE) - ret = transition_frequency_pstate(data, newstate); + ret = transition_frequency_pstate(data, + data->powernow_table[newstate].index); else ret = transition_frequency_fidvid(data, newstate); if (ret) { @@ -1202,7 +1210,7 @@ static int powernowk8_target(struct cpufreq_policy *pol, if (cpu_family == CPU_HW_PSTATE) pol->cur = find_khz_freq_from_pstate(data->powernow_table, - newstate); + data->powernow_table[newstate].index); else pol->cur = find_khz_freq_from_fid(data->currfid); ret = 0; diff --git a/drivers/dma/at_hdmac.c b/drivers/dma/at_hdmac.c index 36144f88d71..9e9318abc51 100644 --- a/drivers/dma/at_hdmac.c +++ b/drivers/dma/at_hdmac.c @@ -1279,7 +1279,7 @@ static int __init at_dma_probe(struct platform_device *pdev) tasklet_init(&atchan->tasklet, atc_tasklet, (unsigned long)atchan); - atc_enable_irq(atchan); + atc_enable_chan_irq(atdma, i); } /* set base routines */ @@ -1348,7 +1348,7 @@ static int __exit at_dma_remove(struct platform_device *pdev) struct at_dma_chan *atchan = to_at_dma_chan(chan); /* Disable interrupts */ - atc_disable_irq(atchan); + atc_disable_chan_irq(atdma, chan->chan_id); tasklet_disable(&atchan->tasklet); tasklet_kill(&atchan->tasklet); diff --git a/drivers/dma/at_hdmac_regs.h b/drivers/dma/at_hdmac_regs.h index 087dbf1dd39..19ed47056da 100644 --- a/drivers/dma/at_hdmac_regs.h +++ b/drivers/dma/at_hdmac_regs.h @@ -319,28 +319,27 @@ static void atc_dump_lli(struct at_dma_chan *atchan, struct at_lli *lli) } -static void atc_setup_irq(struct at_dma_chan *atchan, int on) +static void atc_setup_irq(struct at_dma *atdma, int chan_id, int on) { - struct at_dma *atdma = to_at_dma(atchan->chan_common.device); - u32 ebci; + u32 ebci; /* enable interrupts on buffer transfer completion & error */ - ebci = AT_DMA_BTC(atchan->chan_common.chan_id) - | AT_DMA_ERR(atchan->chan_common.chan_id); + ebci = AT_DMA_BTC(chan_id) + | AT_DMA_ERR(chan_id); if (on) dma_writel(atdma, EBCIER, ebci); else dma_writel(atdma, EBCIDR, ebci); } -static inline void atc_enable_irq(struct at_dma_chan *atchan) +static void atc_enable_chan_irq(struct at_dma *atdma, int chan_id) { - atc_setup_irq(atchan, 1); + atc_setup_irq(atdma, chan_id, 1); } -static inline void atc_disable_irq(struct at_dma_chan *atchan) +static void atc_disable_chan_irq(struct at_dma *atdma, int chan_id) { - atc_setup_irq(atchan, 0); + atc_setup_irq(atdma, chan_id, 0); } diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index ee76c8ec72f..7f97c309790 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -262,6 +262,7 @@ static inline struct fw_ohci *fw_ohci(struct fw_card *card) static char ohci_driver_name[] = KBUILD_MODNAME; #define PCI_DEVICE_ID_AGERE_FW643 0x5901 +#define PCI_DEVICE_ID_CREATIVE_SB1394 0x4001 #define PCI_DEVICE_ID_JMICRON_JMB38X_FW 0x2380 #define PCI_DEVICE_ID_TI_TSB12LV22 0x8009 #define PCI_VENDOR_ID_PINNACLE_SYSTEMS 0x11bd @@ -285,6 +286,9 @@ static const struct { {PCI_VENDOR_ID_ATT, PCI_DEVICE_ID_AGERE_FW643, 6, QUIRK_NO_MSI}, + {PCI_VENDOR_ID_CREATIVE, PCI_DEVICE_ID_CREATIVE_SB1394, PCI_ANY_ID, + QUIRK_RESET_PACKET}, + {PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB38X_FW, PCI_ANY_ID, QUIRK_NO_MSI}, @@ -295,7 +299,7 @@ static const struct { QUIRK_NO_MSI}, {PCI_VENDOR_ID_RICOH, PCI_ANY_ID, PCI_ANY_ID, - QUIRK_CYCLE_TIMER}, + QUIRK_CYCLE_TIMER | QUIRK_NO_MSI}, {PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_TSB12LV22, PCI_ANY_ID, QUIRK_CYCLE_TIMER | QUIRK_RESET_PACKET | QUIRK_NO_1394A}, diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 335564e35c3..b570415c3fd 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -325,6 +325,8 @@ typedef struct drm_i915_private { struct timer_list hangcheck_timer; int hangcheck_count; uint32_t last_acthd; + uint32_t last_acthd_bsd; + uint32_t last_acthd_blt; uint32_t last_instdone; uint32_t last_instdone1; diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 363564704b1..997db7fab21 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -1665,7 +1665,7 @@ void i915_hangcheck_elapsed(unsigned long data) { struct drm_device *dev = (struct drm_device *)data; drm_i915_private_t *dev_priv = dev->dev_private; - uint32_t acthd, instdone, instdone1; + uint32_t acthd, instdone, instdone1, acthd_bsd, acthd_blt; bool err = false; /* If all work is done then ACTHD clearly hasn't advanced. */ @@ -1679,16 +1679,21 @@ void i915_hangcheck_elapsed(unsigned long data) } if (INTEL_INFO(dev)->gen < 4) { - acthd = I915_READ(ACTHD); instdone = I915_READ(INSTDONE); instdone1 = 0; } else { - acthd = I915_READ(ACTHD_I965); instdone = I915_READ(INSTDONE_I965); instdone1 = I915_READ(INSTDONE1); } + acthd = intel_ring_get_active_head(&dev_priv->ring[RCS]); + acthd_bsd = HAS_BSD(dev) ? + intel_ring_get_active_head(&dev_priv->ring[VCS]) : 0; + acthd_blt = HAS_BLT(dev) ? + intel_ring_get_active_head(&dev_priv->ring[BCS]) : 0; if (dev_priv->last_acthd == acthd && + dev_priv->last_acthd_bsd == acthd_bsd && + dev_priv->last_acthd_blt == acthd_blt && dev_priv->last_instdone == instdone && dev_priv->last_instdone1 == instdone1) { if (dev_priv->hangcheck_count++ > 1) { @@ -1720,6 +1725,8 @@ void i915_hangcheck_elapsed(unsigned long data) dev_priv->hangcheck_count = 0; dev_priv->last_acthd = acthd; + dev_priv->last_acthd_bsd = acthd_bsd; + dev_priv->last_acthd_blt = acthd_blt; dev_priv->last_instdone = instdone; dev_priv->last_instdone1 = instdone1; } diff --git a/drivers/gpu/drm/i915/i915_suspend.c b/drivers/gpu/drm/i915/i915_suspend.c index cf15533aabf..bc7dcaa1c68 100644 --- a/drivers/gpu/drm/i915/i915_suspend.c +++ b/drivers/gpu/drm/i915/i915_suspend.c @@ -34,6 +34,10 @@ static bool i915_pipe_enabled(struct drm_device *dev, enum pipe pipe) struct drm_i915_private *dev_priv = dev->dev_private; u32 dpll_reg; + /* On IVB, 3rd pipe shares PLL with another one */ + if (pipe > 1) + return false; + if (HAS_PCH_SPLIT(dev)) dpll_reg = (pipe == PIPE_A) ? _PCH_DPLL_A : _PCH_DPLL_B; else diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 14264a8c03e..bf9fea94161 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -1554,6 +1554,7 @@ intel_dp_link_down(struct intel_dp *intel_dp) intel_wait_for_vblank(dev, to_intel_crtc(crtc)->pipe); } + DP &= ~DP_AUDIO_OUTPUT_ENABLE; I915_WRITE(intel_dp->output_reg, DP & ~DP_PORT_EN); POSTING_READ(intel_dp->output_reg); } diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index aa0a8e83142..236bbe09abd 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -158,6 +158,10 @@ static void intel_hdmi_dpms(struct drm_encoder *encoder, int mode) struct drm_i915_private *dev_priv = dev->dev_private; struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder); u32 temp; + u32 enable_bits = SDVO_ENABLE; + + if (intel_hdmi->has_audio) + enable_bits |= SDVO_AUDIO_ENABLE; temp = I915_READ(intel_hdmi->sdvox_reg); @@ -170,9 +174,9 @@ static void intel_hdmi_dpms(struct drm_encoder *encoder, int mode) } if (mode != DRM_MODE_DPMS_ON) { - temp &= ~SDVO_ENABLE; + temp &= ~enable_bits; } else { - temp |= SDVO_ENABLE; + temp |= enable_bits; } I915_WRITE(intel_hdmi->sdvox_reg, temp); diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c index 113e4e7264c..f57b08bd8c6 100644 --- a/drivers/gpu/drm/i915/intel_tv.c +++ b/drivers/gpu/drm/i915/intel_tv.c @@ -417,7 +417,7 @@ static const struct tv_mode tv_modes[] = { { .name = "NTSC-M", .clock = 108000, - .refresh = 29970, + .refresh = 59940, .oversample = TV_OVERSAMPLE_8X, .component_only = 0, /* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 3.580MHz */ @@ -460,7 +460,7 @@ static const struct tv_mode tv_modes[] = { { .name = "NTSC-443", .clock = 108000, - .refresh = 29970, + .refresh = 59940, .oversample = TV_OVERSAMPLE_8X, .component_only = 0, /* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 4.43MHz */ @@ -502,7 +502,7 @@ static const struct tv_mode tv_modes[] = { { .name = "NTSC-J", .clock = 108000, - .refresh = 29970, + .refresh = 59940, .oversample = TV_OVERSAMPLE_8X, .component_only = 0, @@ -545,7 +545,7 @@ static const struct tv_mode tv_modes[] = { { .name = "PAL-M", .clock = 108000, - .refresh = 29970, + .refresh = 59940, .oversample = TV_OVERSAMPLE_8X, .component_only = 0, @@ -589,7 +589,7 @@ static const struct tv_mode tv_modes[] = { /* 625 Lines, 50 Fields, 15.625KHz line, Sub-Carrier 4.434MHz */ .name = "PAL-N", .clock = 108000, - .refresh = 25000, + .refresh = 50000, .oversample = TV_OVERSAMPLE_8X, .component_only = 0, @@ -634,7 +634,7 @@ static const struct tv_mode tv_modes[] = { /* 625 Lines, 50 Fields, 15.625KHz line, Sub-Carrier 4.434MHz */ .name = "PAL", .clock = 108000, - .refresh = 25000, + .refresh = 50000, .oversample = TV_OVERSAMPLE_8X, .component_only = 0, @@ -821,7 +821,7 @@ static const struct tv_mode tv_modes[] = { { .name = "1080i@50Hz", .clock = 148800, - .refresh = 25000, + .refresh = 50000, .oversample = TV_OVERSAMPLE_2X, .component_only = 1, @@ -847,7 +847,7 @@ static const struct tv_mode tv_modes[] = { { .name = "1080i@60Hz", .clock = 148800, - .refresh = 30000, + .refresh = 60000, .oversample = TV_OVERSAMPLE_2X, .component_only = 1, diff --git a/drivers/gpu/drm/nouveau/nouveau_gem.c b/drivers/gpu/drm/nouveau/nouveau_gem.c index b52e4601824..cee78b26f60 100644 --- a/drivers/gpu/drm/nouveau/nouveau_gem.c +++ b/drivers/gpu/drm/nouveau/nouveau_gem.c @@ -314,6 +314,25 @@ validate_init(struct nouveau_channel *chan, struct drm_file *file_priv, return 0; } +static int +validate_sync(struct nouveau_channel *chan, struct nouveau_bo *nvbo) +{ + struct nouveau_fence *fence = NULL; + int ret = 0; + + spin_lock(&nvbo->bo.bdev->fence_lock); + if (nvbo->bo.sync_obj) + fence = nouveau_fence_ref(nvbo->bo.sync_obj); + spin_unlock(&nvbo->bo.bdev->fence_lock); + + if (fence) { + ret = nouveau_fence_sync(fence, chan); + nouveau_fence_unref(&fence); + } + + return ret; +} + static int validate_list(struct nouveau_channel *chan, struct list_head *list, struct drm_nouveau_gem_pushbuf_bo *pbbo, uint64_t user_pbbo_ptr) @@ -327,7 +346,7 @@ validate_list(struct nouveau_channel *chan, struct list_head *list, list_for_each_entry(nvbo, list, entry) { struct drm_nouveau_gem_pushbuf_bo *b = &pbbo[nvbo->pbbo_index]; - ret = nouveau_fence_sync(nvbo->bo.sync_obj, chan); + ret = validate_sync(chan, nvbo); if (unlikely(ret)) { NV_ERROR(dev, "fail pre-validate sync\n"); return ret; @@ -350,7 +369,7 @@ validate_list(struct nouveau_channel *chan, struct list_head *list, return ret; } - ret = nouveau_fence_sync(nvbo->bo.sync_obj, chan); + ret = validate_sync(chan, nvbo); if (unlikely(ret)) { NV_ERROR(dev, "fail post-validate sync\n"); return ret; diff --git a/drivers/gpu/drm/radeon/atombios_crtc.c b/drivers/gpu/drm/radeon/atombios_crtc.c index 9541995e4b2..071ded119eb 100644 --- a/drivers/gpu/drm/radeon/atombios_crtc.c +++ b/drivers/gpu/drm/radeon/atombios_crtc.c @@ -1173,7 +1173,7 @@ static int dce4_crtc_do_set_base(struct drm_crtc *crtc, WREG32(EVERGREEN_GRPH_ENABLE + radeon_crtc->crtc_offset, 1); WREG32(EVERGREEN_DESKTOP_HEIGHT + radeon_crtc->crtc_offset, - crtc->mode.vdisplay); + target_fb->height); x &= ~3; y &= ~1; WREG32(EVERGREEN_VIEWPORT_START + radeon_crtc->crtc_offset, @@ -1342,7 +1342,7 @@ static int avivo_crtc_do_set_base(struct drm_crtc *crtc, WREG32(AVIVO_D1GRPH_ENABLE + radeon_crtc->crtc_offset, 1); WREG32(AVIVO_D1MODE_DESKTOP_HEIGHT + radeon_crtc->crtc_offset, - crtc->mode.vdisplay); + target_fb->height); x &= ~3; y &= ~1; WREG32(AVIVO_D1MODE_VIEWPORT_START + radeon_crtc->crtc_offset, diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c index 5d0c1236dd4..e87893c2c88 100644 --- a/drivers/gpu/drm/radeon/radeon_device.c +++ b/drivers/gpu/drm/radeon/radeon_device.c @@ -857,6 +857,8 @@ int radeon_suspend_kms(struct drm_device *dev, pm_message_t state) if (dev->switch_power_state == DRM_SWITCH_POWER_OFF) return 0; + drm_kms_helper_poll_disable(dev); + /* turn off display hw */ list_for_each_entry(connector, &dev->mode_config.connector_list, head) { drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF); @@ -943,6 +945,8 @@ int radeon_resume_kms(struct drm_device *dev) list_for_each_entry(connector, &dev->mode_config.connector_list, head) { drm_helper_connector_dpms(connector, DRM_MODE_DPMS_ON); } + + drm_kms_helper_poll_enable(dev); return 0; } diff --git a/drivers/hwmon/w83627ehf.c b/drivers/hwmon/w83627ehf.c index 62845157245..359bb1e23ac 100644 --- a/drivers/hwmon/w83627ehf.c +++ b/drivers/hwmon/w83627ehf.c @@ -2104,9 +2104,29 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev) fan4min = 0; fan5pin = 0; } else if (sio_data->kind == nct6776) { - fan3pin = !(superio_inb(sio_data->sioreg, 0x24) & 0x40); - fan4pin = !!(superio_inb(sio_data->sioreg, 0x1C) & 0x01); - fan5pin = !!(superio_inb(sio_data->sioreg, 0x1C) & 0x02); + bool gpok = superio_inb(sio_data->sioreg, 0x27) & 0x80; + u8 regval; + + superio_select(sio_data->sioreg, W83627EHF_LD_HWM); + regval = superio_inb(sio_data->sioreg, SIO_REG_ENABLE); + + if (regval & 0x80) + fan3pin = gpok; + else + fan3pin = !(superio_inb(sio_data->sioreg, 0x24) & 0x40); + + if (regval & 0x40) + fan4pin = gpok; + else + fan4pin = !!(superio_inb(sio_data->sioreg, 0x1C) + & 0x01); + + if (regval & 0x20) + fan5pin = gpok; + else + fan5pin = !!(superio_inb(sio_data->sioreg, 0x1C) + & 0x02); + fan4min = fan4pin; } else if (sio_data->kind == w83667hg || sio_data->kind == w83667hg_b) { fan3pin = 1; diff --git a/drivers/infiniband/core/addr.c b/drivers/infiniband/core/addr.c index 8e21d457b89..f2a84c6f854 100644 --- a/drivers/infiniband/core/addr.c +++ b/drivers/infiniband/core/addr.c @@ -215,7 +215,9 @@ static int addr4_resolve(struct sockaddr_in *src_in, neigh = neigh_lookup(&arp_tbl, &rt->rt_gateway, rt->dst.dev); if (!neigh || !(neigh->nud_state & NUD_VALID)) { - neigh_event_send(rt->dst.neighbour, NULL); + rcu_read_lock(); + neigh_event_send(dst_get_neighbour(&rt->dst), NULL); + rcu_read_unlock(); ret = -ENODATA; if (neigh) goto release; @@ -273,14 +275,16 @@ static int addr6_resolve(struct sockaddr_in6 *src_in, goto put; } - neigh = dst->neighbour; + rcu_read_lock(); + neigh = dst_get_neighbour(dst); if (!neigh || !(neigh->nud_state & NUD_VALID)) { - neigh_event_send(dst->neighbour, NULL); + if (neigh) + neigh_event_send(neigh, NULL); ret = -ENODATA; - goto put; + } else { + ret = rdma_copy_addr(addr, dst->dev, neigh->ha); } - - ret = rdma_copy_addr(addr, dst->dev, neigh->ha); + rcu_read_unlock(); put: dst_release(dst); return ret; diff --git a/drivers/infiniband/hw/cxgb3/iwch_cm.c b/drivers/infiniband/hw/cxgb3/iwch_cm.c index 2332dc22aa0..e55ce7a428b 100644 --- a/drivers/infiniband/hw/cxgb3/iwch_cm.c +++ b/drivers/infiniband/hw/cxgb3/iwch_cm.c @@ -1328,6 +1328,7 @@ static int pass_accept_req(struct t3cdev *tdev, struct sk_buff *skb, void *ctx) struct iwch_ep *child_ep, *parent_ep = ctx; struct cpl_pass_accept_req *req = cplhdr(skb); unsigned int hwtid = GET_TID(req); + struct neighbour *neigh; struct dst_entry *dst; struct l2t_entry *l2t; struct rtable *rt; @@ -1364,7 +1365,10 @@ static int pass_accept_req(struct t3cdev *tdev, struct sk_buff *skb, void *ctx) goto reject; } dst = &rt->dst; - l2t = t3_l2t_get(tdev, dst->neighbour, dst->neighbour->dev); + rcu_read_lock(); + neigh = dst_get_neighbour(dst); + l2t = t3_l2t_get(tdev, neigh, neigh->dev); + rcu_read_unlock(); if (!l2t) { printk(KERN_ERR MOD "%s - failed to allocate l2t entry!\n", __func__); @@ -1874,10 +1878,11 @@ static int is_loopback_dst(struct iw_cm_id *cm_id) int iwch_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param) { - int err = 0; struct iwch_dev *h = to_iwch_dev(cm_id->device); + struct neighbour *neigh; struct iwch_ep *ep; struct rtable *rt; + int err = 0; if (is_loopback_dst(cm_id)) { err = -ENOSYS; @@ -1933,9 +1938,12 @@ int iwch_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param) } ep->dst = &rt->dst; + rcu_read_lock(); + neigh = dst_get_neighbour(ep->dst); + /* get a l2t entry */ - ep->l2t = t3_l2t_get(ep->com.tdev, ep->dst->neighbour, - ep->dst->neighbour->dev); + ep->l2t = t3_l2t_get(ep->com.tdev, neigh, neigh->dev); + rcu_read_unlock(); if (!ep->l2t) { printk(KERN_ERR MOD "%s - cannot alloc l2e.\n", __func__); err = -ENOMEM; diff --git a/drivers/infiniband/hw/cxgb4/cm.c b/drivers/infiniband/hw/cxgb4/cm.c index 31fb44085c9..daa93e942e1 100644 --- a/drivers/infiniband/hw/cxgb4/cm.c +++ b/drivers/infiniband/hw/cxgb4/cm.c @@ -1325,6 +1325,7 @@ static int pass_accept_req(struct c4iw_dev *dev, struct sk_buff *skb) unsigned int stid = GET_POPEN_TID(ntohl(req->tos_stid)); struct tid_info *t = dev->rdev.lldi.tids; unsigned int hwtid = GET_TID(req); + struct neighbour *neigh; struct dst_entry *dst; struct l2t_entry *l2t; struct rtable *rt; @@ -1357,11 +1358,12 @@ static int pass_accept_req(struct c4iw_dev *dev, struct sk_buff *skb) goto reject; } dst = &rt->dst; - if (dst->neighbour->dev->flags & IFF_LOOPBACK) { + rcu_read_lock(); + neigh = dst_get_neighbour(dst); + if (neigh->dev->flags & IFF_LOOPBACK) { pdev = ip_dev_find(&init_net, peer_ip); BUG_ON(!pdev); - l2t = cxgb4_l2t_get(dev->rdev.lldi.l2t, dst->neighbour, - pdev, 0); + l2t = cxgb4_l2t_get(dev->rdev.lldi.l2t, neigh, pdev, 0); mtu = pdev->mtu; tx_chan = cxgb4_port_chan(pdev); smac_idx = (cxgb4_port_viid(pdev) & 0x7F) << 1; @@ -1372,18 +1374,18 @@ static int pass_accept_req(struct c4iw_dev *dev, struct sk_buff *skb) rss_qid = dev->rdev.lldi.rxq_ids[cxgb4_port_idx(pdev) * step]; dev_put(pdev); } else { - l2t = cxgb4_l2t_get(dev->rdev.lldi.l2t, dst->neighbour, - dst->neighbour->dev, 0); + l2t = cxgb4_l2t_get(dev->rdev.lldi.l2t, neigh, neigh->dev, 0); mtu = dst_mtu(dst); - tx_chan = cxgb4_port_chan(dst->neighbour->dev); - smac_idx = (cxgb4_port_viid(dst->neighbour->dev) & 0x7F) << 1; + tx_chan = cxgb4_port_chan(neigh->dev); + smac_idx = (cxgb4_port_viid(neigh->dev) & 0x7F) << 1; step = dev->rdev.lldi.ntxq / dev->rdev.lldi.nchan; - txq_idx = cxgb4_port_idx(dst->neighbour->dev) * step; - ctrlq_idx = cxgb4_port_idx(dst->neighbour->dev); + txq_idx = cxgb4_port_idx(neigh->dev) * step; + ctrlq_idx = cxgb4_port_idx(neigh->dev); step = dev->rdev.lldi.nrxq / dev->rdev.lldi.nchan; rss_qid = dev->rdev.lldi.rxq_ids[ - cxgb4_port_idx(dst->neighbour->dev) * step]; + cxgb4_port_idx(neigh->dev) * step]; } + rcu_read_unlock(); if (!l2t) { printk(KERN_ERR MOD "%s - failed to allocate l2t entry!\n", __func__); @@ -1847,6 +1849,7 @@ int c4iw_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param) struct c4iw_ep *ep; struct rtable *rt; struct net_device *pdev; + struct neighbour *neigh; int step; if ((conn_param->ord > c4iw_max_read_depth) || @@ -1908,14 +1911,16 @@ int c4iw_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param) } ep->dst = &rt->dst; + rcu_read_lock(); + neigh = dst_get_neighbour(ep->dst); + /* get a l2t entry */ - if (ep->dst->neighbour->dev->flags & IFF_LOOPBACK) { + if (neigh->dev->flags & IFF_LOOPBACK) { PDBG("%s LOOPBACK\n", __func__); pdev = ip_dev_find(&init_net, cm_id->remote_addr.sin_addr.s_addr); ep->l2t = cxgb4_l2t_get(ep->com.dev->rdev.lldi.l2t, - ep->dst->neighbour, - pdev, 0); + neigh, pdev, 0); ep->mtu = pdev->mtu; ep->tx_chan = cxgb4_port_chan(pdev); ep->smac_idx = (cxgb4_port_viid(pdev) & 0x7F) << 1; @@ -1930,21 +1935,20 @@ int c4iw_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param) dev_put(pdev); } else { ep->l2t = cxgb4_l2t_get(ep->com.dev->rdev.lldi.l2t, - ep->dst->neighbour, - ep->dst->neighbour->dev, 0); + neigh, neigh->dev, 0); ep->mtu = dst_mtu(ep->dst); - ep->tx_chan = cxgb4_port_chan(ep->dst->neighbour->dev); - ep->smac_idx = (cxgb4_port_viid(ep->dst->neighbour->dev) & - 0x7F) << 1; + ep->tx_chan = cxgb4_port_chan(neigh->dev); + ep->smac_idx = (cxgb4_port_viid(neigh->dev) & 0x7F) << 1; step = ep->com.dev->rdev.lldi.ntxq / ep->com.dev->rdev.lldi.nchan; - ep->txq_idx = cxgb4_port_idx(ep->dst->neighbour->dev) * step; - ep->ctrlq_idx = cxgb4_port_idx(ep->dst->neighbour->dev); + ep->txq_idx = cxgb4_port_idx(neigh->dev) * step; + ep->ctrlq_idx = cxgb4_port_idx(neigh->dev); step = ep->com.dev->rdev.lldi.nrxq / ep->com.dev->rdev.lldi.nchan; ep->rss_qid = ep->com.dev->rdev.lldi.rxq_ids[ - cxgb4_port_idx(ep->dst->neighbour->dev) * step]; + cxgb4_port_idx(neigh->dev) * step]; } + rcu_read_unlock(); if (!ep->l2t) { printk(KERN_ERR MOD "%s - cannot alloc l2e.\n", __func__); err = -ENOMEM; diff --git a/drivers/infiniband/hw/mlx4/mad.c b/drivers/infiniband/hw/mlx4/mad.c index 57ffa50f509..44fc3104e91 100644 --- a/drivers/infiniband/hw/mlx4/mad.c +++ b/drivers/infiniband/hw/mlx4/mad.c @@ -255,12 +255,9 @@ int mlx4_ib_process_mad(struct ib_device *ibdev, int mad_flags, u8 port_num, return IB_MAD_RESULT_SUCCESS; /* - * Don't process SMInfo queries or vendor-specific - * MADs -- the SMA can't handle them. + * Don't process SMInfo queries -- the SMA can't handle them. */ - if (in_mad->mad_hdr.attr_id == IB_SMP_ATTR_SM_INFO || - ((in_mad->mad_hdr.attr_id & IB_SMP_ATTR_VENDOR_MASK) == - IB_SMP_ATTR_VENDOR_MASK)) + if (in_mad->mad_hdr.attr_id == IB_SMP_ATTR_SM_INFO) return IB_MAD_RESULT_SUCCESS; } else if (in_mad->mad_hdr.mgmt_class == IB_MGMT_CLASS_PERF_MGMT || in_mad->mad_hdr.mgmt_class == MLX4_IB_VENDOR_CLASS1 || diff --git a/drivers/infiniband/hw/mlx4/qp.c b/drivers/infiniband/hw/mlx4/qp.c index 2001f20a436..23c04ff6519 100644 --- a/drivers/infiniband/hw/mlx4/qp.c +++ b/drivers/infiniband/hw/mlx4/qp.c @@ -1301,7 +1301,7 @@ static int build_mlx_header(struct mlx4_ib_sqp *sqp, struct ib_send_wr *wr, int is_eth; int is_vlan = 0; int is_grh; - u16 vlan; + u16 vlan = 0; send_size = 0; for (i = 0; i < wr->num_sge; ++i) diff --git a/drivers/infiniband/hw/nes/nes_cm.c b/drivers/infiniband/hw/nes/nes_cm.c index e74cdf9ef47..a1f74f6381b 100644 --- a/drivers/infiniband/hw/nes/nes_cm.c +++ b/drivers/infiniband/hw/nes/nes_cm.c @@ -1150,9 +1150,11 @@ static int nes_addr_resolve_neigh(struct nes_vnic *nesvnic, u32 dst_ip, int arpi neigh_release(neigh); } - if ((neigh == NULL) || (!(neigh->nud_state & NUD_VALID))) - neigh_event_send(rt->dst.neighbour, NULL); - + if ((neigh == NULL) || (!(neigh->nud_state & NUD_VALID))) { + rcu_read_lock(); + neigh_event_send(dst_get_neighbour(&rt->dst), NULL); + rcu_read_unlock(); + } ip_rt_put(rt); return rc; } diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c index 86addca9ddf..a98c414978e 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_main.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c @@ -555,14 +555,17 @@ static int path_rec_start(struct net_device *dev, return 0; } +/* called with rcu_read_lock */ static void neigh_add_path(struct sk_buff *skb, struct net_device *dev) { struct ipoib_dev_priv *priv = netdev_priv(dev); struct ipoib_path *path; struct ipoib_neigh *neigh; + struct neighbour *n; unsigned long flags; - neigh = ipoib_neigh_alloc(skb_dst(skb)->neighbour, skb->dev); + n = dst_get_neighbour(skb_dst(skb)); + neigh = ipoib_neigh_alloc(n, skb->dev); if (!neigh) { ++dev->stats.tx_dropped; dev_kfree_skb_any(skb); @@ -571,9 +574,9 @@ static void neigh_add_path(struct sk_buff *skb, struct net_device *dev) spin_lock_irqsave(&priv->lock, flags); - path = __path_find(dev, skb_dst(skb)->neighbour->ha + 4); + path = __path_find(dev, n->ha + 4); if (!path) { - path = path_rec_create(dev, skb_dst(skb)->neighbour->ha + 4); + path = path_rec_create(dev, n->ha + 4); if (!path) goto err_path; @@ -607,7 +610,7 @@ static void neigh_add_path(struct sk_buff *skb, struct net_device *dev) } } else { spin_unlock_irqrestore(&priv->lock, flags); - ipoib_send(dev, skb, path->ah, IPOIB_QPN(skb_dst(skb)->neighbour->ha)); + ipoib_send(dev, skb, path->ah, IPOIB_QPN(n->ha)); return; } } else { @@ -634,20 +637,24 @@ static void neigh_add_path(struct sk_buff *skb, struct net_device *dev) spin_unlock_irqrestore(&priv->lock, flags); } +/* called with rcu_read_lock */ static void ipoib_path_lookup(struct sk_buff *skb, struct net_device *dev) { struct ipoib_dev_priv *priv = netdev_priv(skb->dev); + struct dst_entry *dst = skb_dst(skb); + struct neighbour *n; /* Look up path record for unicasts */ - if (skb_dst(skb)->neighbour->ha[4] != 0xff) { + n = dst_get_neighbour(dst); + if (n->ha[4] != 0xff) { neigh_add_path(skb, dev); return; } /* Add in the P_Key for multicasts */ - skb_dst(skb)->neighbour->ha[8] = (priv->pkey >> 8) & 0xff; - skb_dst(skb)->neighbour->ha[9] = priv->pkey & 0xff; - ipoib_mcast_send(dev, skb_dst(skb)->neighbour->ha + 4, skb); + n->ha[8] = (priv->pkey >> 8) & 0xff; + n->ha[9] = priv->pkey & 0xff; + ipoib_mcast_send(dev, n->ha + 4, skb); } static void unicast_arp_send(struct sk_buff *skb, struct net_device *dev, @@ -712,18 +719,23 @@ static int ipoib_start_xmit(struct sk_buff *skb, struct net_device *dev) { struct ipoib_dev_priv *priv = netdev_priv(dev); struct ipoib_neigh *neigh; + struct neighbour *n = NULL; unsigned long flags; - if (likely(skb_dst(skb) && skb_dst(skb)->neighbour)) { - if (unlikely(!*to_ipoib_neigh(skb_dst(skb)->neighbour))) { + rcu_read_lock(); + if (likely(skb_dst(skb))) + n = dst_get_neighbour(skb_dst(skb)); + + if (likely(n)) { + if (unlikely(!*to_ipoib_neigh(n))) { ipoib_path_lookup(skb, dev); - return NETDEV_TX_OK; + goto unlock; } - neigh = *to_ipoib_neigh(skb_dst(skb)->neighbour); + neigh = *to_ipoib_neigh(n); if (unlikely((memcmp(&neigh->dgid.raw, - skb_dst(skb)->neighbour->ha + 4, + n->ha + 4, sizeof(union ib_gid))) || (neigh->dev != dev))) { spin_lock_irqsave(&priv->lock, flags); @@ -740,17 +752,17 @@ static int ipoib_start_xmit(struct sk_buff *skb, struct net_device *dev) ipoib_neigh_free(dev, neigh); spin_unlock_irqrestore(&priv->lock, flags); ipoib_path_lookup(skb, dev); - return NETDEV_TX_OK; + goto unlock; } if (ipoib_cm_get(neigh)) { if (ipoib_cm_up(neigh)) { ipoib_cm_send(dev, skb, ipoib_cm_get(neigh)); - return NETDEV_TX_OK; + goto unlock; } } else if (neigh->ah) { - ipoib_send(dev, skb, neigh->ah, IPOIB_QPN(skb_dst(skb)->neighbour->ha)); - return NETDEV_TX_OK; + ipoib_send(dev, skb, neigh->ah, IPOIB_QPN(n->ha)); + goto unlock; } if (skb_queue_len(&neigh->queue) < IPOIB_MAX_PATH_REC_QUEUE) { @@ -784,13 +796,14 @@ static int ipoib_start_xmit(struct sk_buff *skb, struct net_device *dev) phdr->hwaddr + 4); dev_kfree_skb_any(skb); ++dev->stats.tx_dropped; - return NETDEV_TX_OK; + goto unlock; } unicast_arp_send(skb, dev, phdr); } } - +unlock: + rcu_read_unlock(); return NETDEV_TX_OK; } @@ -812,6 +825,8 @@ static int ipoib_hard_header(struct sk_buff *skb, const void *daddr, const void *saddr, unsigned len) { struct ipoib_header *header; + struct dst_entry *dst; + struct neighbour *n; header = (struct ipoib_header *) skb_push(skb, sizeof *header); @@ -823,7 +838,11 @@ static int ipoib_hard_header(struct sk_buff *skb, * destination address onto the front of the skb so we can * figure out where to send the packet later. */ - if ((!skb_dst(skb) || !skb_dst(skb)->neighbour) && daddr) { + dst = skb_dst(skb); + n = NULL; + if (dst) + n = dst_get_neighbour_raw(dst); + if ((!dst || !n) && daddr) { struct ipoib_pseudoheader *phdr = (struct ipoib_pseudoheader *) skb_push(skb, sizeof *phdr); memcpy(phdr->hwaddr, daddr, INFINIBAND_ALEN); diff --git a/drivers/infiniband/ulp/ipoib/ipoib_multicast.c b/drivers/infiniband/ulp/ipoib/ipoib_multicast.c index 3871ac66355..a8d2a891b84 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_multicast.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_multicast.c @@ -258,11 +258,15 @@ static int ipoib_mcast_join_finish(struct ipoib_mcast *mcast, netif_tx_lock_bh(dev); while (!skb_queue_empty(&mcast->pkt_queue)) { struct sk_buff *skb = skb_dequeue(&mcast->pkt_queue); + struct dst_entry *dst = skb_dst(skb); + struct neighbour *n = NULL; + netif_tx_unlock_bh(dev); skb->dev = dev; - - if (!skb_dst(skb) || !skb_dst(skb)->neighbour) { + if (dst) + n = dst_get_neighbour_raw(dst); + if (!dst || !n) { /* put pseudoheader back on for next time */ skb_push(skb, sizeof (struct ipoib_pseudoheader)); } @@ -715,11 +719,15 @@ void ipoib_mcast_send(struct net_device *dev, void *mgid, struct sk_buff *skb) out: if (mcast && mcast->ah) { - if (skb_dst(skb) && - skb_dst(skb)->neighbour && - !*to_ipoib_neigh(skb_dst(skb)->neighbour)) { - struct ipoib_neigh *neigh = ipoib_neigh_alloc(skb_dst(skb)->neighbour, - skb->dev); + struct dst_entry *dst = skb_dst(skb); + struct neighbour *n = NULL; + + rcu_read_lock(); + if (dst) + n = dst_get_neighbour(dst); + if (n && !*to_ipoib_neigh(n)) { + struct ipoib_neigh *neigh = ipoib_neigh_alloc(n, + skb->dev); if (neigh) { kref_get(&mcast->ah->ref); @@ -727,7 +735,7 @@ void ipoib_mcast_send(struct net_device *dev, void *mgid, struct sk_buff *skb) list_add_tail(&neigh->list, &mcast->neigh_list); } } - + rcu_read_unlock(); spin_unlock_irqrestore(&priv->lock, flags); ipoib_send(dev, skb, mcast->ah, IB_MULTICAST_QPN); return; diff --git a/drivers/misc/cb710/core.c b/drivers/misc/cb710/core.c index efec4139c3f..b1f16d6084a 100644 --- a/drivers/misc/cb710/core.c +++ b/drivers/misc/cb710/core.c @@ -244,6 +244,7 @@ static int __devinit cb710_probe(struct pci_dev *pdev, if (err) return err; + spin_lock_init(&chip->irq_lock); chip->pdev = pdev; chip->iobase = pcim_iomap_table(pdev)[0]; diff --git a/drivers/net/cxgb3/cxgb3_offload.c b/drivers/net/cxgb3/cxgb3_offload.c index 3f2e12c3ac1..015b5152b0d 100644 --- a/drivers/net/cxgb3/cxgb3_offload.c +++ b/drivers/net/cxgb3/cxgb3_offload.c @@ -971,7 +971,7 @@ static int nb_callback(struct notifier_block *self, unsigned long event, case (NETEVENT_REDIRECT):{ struct netevent_redirect *nr = ctx; cxgb_redirect(nr->old, nr->new); - cxgb_neigh_update(nr->new->neighbour); + cxgb_neigh_update(dst_get_neighbour(nr->new)); break; } default: @@ -1116,8 +1116,8 @@ static void cxgb_redirect(struct dst_entry *old, struct dst_entry *new) struct l2t_entry *e; struct t3c_tid_entry *te; - olddev = old->neighbour->dev; - newdev = new->neighbour->dev; + olddev = dst_get_neighbour(old)->dev; + newdev = dst_get_neighbour(new)->dev; if (!is_offloading(olddev)) return; if (!is_offloading(newdev)) { @@ -1134,7 +1134,7 @@ static void cxgb_redirect(struct dst_entry *old, struct dst_entry *new) } /* Add new L2T entry */ - e = t3_l2t_get(tdev, new->neighbour, newdev); + e = t3_l2t_get(tdev, dst_get_neighbour(new), newdev); if (!e) { printk(KERN_ERR "%s: couldn't allocate new l2t entry!\n", __func__); diff --git a/drivers/pcmcia/ds.c b/drivers/pcmcia/ds.c index 749c2a16012..1932029de48 100644 --- a/drivers/pcmcia/ds.c +++ b/drivers/pcmcia/ds.c @@ -1269,10 +1269,8 @@ static int pcmcia_bus_add(struct pcmcia_socket *skt) static int pcmcia_bus_early_resume(struct pcmcia_socket *skt) { - if (!verify_cis_cache(skt)) { - pcmcia_put_socket(skt); + if (!verify_cis_cache(skt)) return 0; - } dev_dbg(&skt->dev, "cis mismatch - different card\n"); diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index fd69da3fa6b..e2c9ac5fcb3 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -2742,9 +2742,14 @@ static int qeth_l3_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) int inline qeth_l3_get_cast_type(struct qeth_card *card, struct sk_buff *skb) { int cast_type = RTN_UNSPEC; - - if (skb_dst(skb) && skb_dst(skb)->neighbour) { - cast_type = skb_dst(skb)->neighbour->type; + struct neighbour *n = NULL; + struct dst_entry *dst; + + dst = skb_dst(skb); + if (dst) + n = dst_get_neighbour(dst); + if (n) { + cast_type = n->type; if ((cast_type == RTN_BROADCAST) || (cast_type == RTN_MULTICAST) || (cast_type == RTN_ANYCAST)) @@ -2787,6 +2792,9 @@ int inline qeth_l3_get_cast_type(struct qeth_card *card, struct sk_buff *skb) static void qeth_l3_fill_header(struct qeth_card *card, struct qeth_hdr *hdr, struct sk_buff *skb, int ipv, int cast_type) { + struct neighbour *n = NULL; + struct dst_entry *dst; + memset(hdr, 0, sizeof(struct qeth_hdr)); hdr->hdr.l3.id = QETH_HEADER_TYPE_LAYER3; hdr->hdr.l3.ext_flags = 0; @@ -2804,13 +2812,16 @@ static void qeth_l3_fill_header(struct qeth_card *card, struct qeth_hdr *hdr, } hdr->hdr.l3.length = skb->len - sizeof(struct qeth_hdr); + dst = skb_dst(skb); + if (dst) + n = dst_get_neighbour(dst); if (ipv == 4) { /* IPv4 */ hdr->hdr.l3.flags = qeth_l3_get_qeth_hdr_flags4(cast_type); memset(hdr->hdr.l3.dest_addr, 0, 12); - if ((skb_dst(skb)) && (skb_dst(skb)->neighbour)) { + if (n) { *((u32 *) (&hdr->hdr.l3.dest_addr[12])) = - *((u32 *) skb_dst(skb)->neighbour->primary_key); + *((u32 *) n->primary_key); } else { /* fill in destination address used in ip header */ *((u32 *) (&hdr->hdr.l3.dest_addr[12])) = @@ -2821,9 +2832,9 @@ static void qeth_l3_fill_header(struct qeth_card *card, struct qeth_hdr *hdr, hdr->hdr.l3.flags = qeth_l3_get_qeth_hdr_flags6(cast_type); if (card->info.type == QETH_CARD_TYPE_IQD) hdr->hdr.l3.flags &= ~QETH_HDR_PASSTHRU; - if ((skb_dst(skb)) && (skb_dst(skb)->neighbour)) { + if (n) { memcpy(hdr->hdr.l3.dest_addr, - skb_dst(skb)->neighbour->primary_key, 16); + n->primary_key, 16); } else { /* fill in destination address used in ip header */ memcpy(hdr->hdr.l3.dest_addr, diff --git a/drivers/scsi/cxgbi/cxgb3i/cxgb3i.c b/drivers/scsi/cxgbi/cxgb3i/cxgb3i.c index b2d661147a4..143f2682bda 100644 --- a/drivers/scsi/cxgbi/cxgb3i/cxgb3i.c +++ b/drivers/scsi/cxgbi/cxgb3i/cxgb3i.c @@ -985,7 +985,7 @@ static int init_act_open(struct cxgbi_sock *csk) csk->saddr.sin_addr.s_addr = chba->ipv4addr; csk->rss_qid = 0; - csk->l2t = t3_l2t_get(t3dev, dst->neighbour, ndev); + csk->l2t = t3_l2t_get(t3dev, dst_get_neighbour(dst), ndev); if (!csk->l2t) { pr_err("NO l2t available.\n"); return -EINVAL; diff --git a/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c b/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c index f3a4cd7cf78..ae13c4993aa 100644 --- a/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c +++ b/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c @@ -1160,7 +1160,7 @@ static int init_act_open(struct cxgbi_sock *csk) cxgbi_sock_set_flag(csk, CTPF_HAS_ATID); cxgbi_sock_get(csk); - csk->l2t = cxgb4_l2t_get(lldi->l2t, csk->dst->neighbour, ndev, 0); + csk->l2t = cxgb4_l2t_get(lldi->l2t, dst_get_neighbour(csk->dst), ndev, 0); if (!csk->l2t) { pr_err("%s, cannot alloc l2t.\n", ndev->name); goto rel_resource; diff --git a/drivers/scsi/cxgbi/libcxgbi.c b/drivers/scsi/cxgbi/libcxgbi.c index a2a9c7c6c64..77ac217ad5c 100644 --- a/drivers/scsi/cxgbi/libcxgbi.c +++ b/drivers/scsi/cxgbi/libcxgbi.c @@ -492,7 +492,7 @@ static struct cxgbi_sock *cxgbi_check_route(struct sockaddr *dst_addr) goto err_out; } dst = &rt->dst; - ndev = dst->neighbour->dev; + ndev = dst_get_neighbour(dst)->dev; if (rt->rt_flags & (RTCF_MULTICAST | RTCF_BROADCAST)) { pr_info("multi-cast route %pI4, port %u, dev %s.\n", @@ -506,7 +506,7 @@ static struct cxgbi_sock *cxgbi_check_route(struct sockaddr *dst_addr) ndev = ip_dev_find(&init_net, daddr->sin_addr.s_addr); mtu = ndev->mtu; pr_info("rt dev %s, loopback -> %s, mtu %u.\n", - dst->neighbour->dev->name, ndev->name, mtu); + dst_get_neighbour(dst)->dev->name, ndev->name, mtu); } cdev = cxgbi_device_find_by_netdev(ndev, &port); diff --git a/drivers/staging/asus_oled/asus_oled.c b/drivers/staging/asus_oled/asus_oled.c index 7bb7da7959a..63bafbb0980 100644 --- a/drivers/staging/asus_oled/asus_oled.c +++ b/drivers/staging/asus_oled/asus_oled.c @@ -355,7 +355,14 @@ static void send_data(struct asus_oled_dev *odev) static int append_values(struct asus_oled_dev *odev, uint8_t val, size_t count) { - while (count-- > 0 && val) { + odev->last_val = val; + + if (val == 0) { + odev->buf_offs += count; + return 0; + } + + while (count-- > 0) { size_t x = odev->buf_offs % odev->width; size_t y = odev->buf_offs / odev->width; size_t i; @@ -406,7 +413,6 @@ static int append_values(struct asus_oled_dev *odev, uint8_t val, size_t count) ; } - odev->last_val = val; odev->buf_offs++; } @@ -805,10 +811,9 @@ static int __init asus_oled_init(void) static void __exit asus_oled_exit(void) { + usb_deregister(&oled_driver); class_remove_file(oled_class, &class_attr_version.attr); class_destroy(oled_class); - - usb_deregister(&oled_driver); } module_init(asus_oled_init); diff --git a/drivers/staging/rtl8712/usb_intf.c b/drivers/staging/rtl8712/usb_intf.c index 6cb7e28c99a..6d88d1a45f1 100644 --- a/drivers/staging/rtl8712/usb_intf.c +++ b/drivers/staging/rtl8712/usb_intf.c @@ -86,6 +86,7 @@ static struct usb_device_id rtl871x_usb_id_tbl[] = { {USB_DEVICE(0x0DF6, 0x0045)}, {USB_DEVICE(0x0DF6, 0x0059)}, /* 11n mode disable */ {USB_DEVICE(0x0DF6, 0x004B)}, + {USB_DEVICE(0x0DF6, 0x005B)}, {USB_DEVICE(0x0DF6, 0x005D)}, {USB_DEVICE(0x0DF6, 0x0063)}, /* Sweex */ diff --git a/drivers/target/target_core_pr.c b/drivers/target/target_core_pr.c index b662db3a320..98e12d31c9c 100644 --- a/drivers/target/target_core_pr.c +++ b/drivers/target/target_core_pr.c @@ -471,6 +471,7 @@ static int core_scsi3_pr_seq_non_holder( case READ_MEDIA_SERIAL_NUMBER: case REPORT_LUNS: case REQUEST_SENSE: + case PERSISTENT_RESERVE_IN: ret = 0; /*/ Allowed CDBs */ break; default: @@ -3079,7 +3080,7 @@ static int core_scsi3_pro_preempt( if (!(calling_it_nexus)) core_scsi3_ua_allocate(pr_reg_nacl, pr_res_mapped_lun, 0x2A, - ASCQ_2AH_RESERVATIONS_PREEMPTED); + ASCQ_2AH_REGISTRATIONS_PREEMPTED); } spin_unlock(&pr_tmpl->registration_lock); /* @@ -3191,7 +3192,7 @@ static int core_scsi3_pro_preempt( * additional sense code set to REGISTRATIONS PREEMPTED; */ core_scsi3_ua_allocate(pr_reg_nacl, pr_res_mapped_lun, 0x2A, - ASCQ_2AH_RESERVATIONS_PREEMPTED); + ASCQ_2AH_REGISTRATIONS_PREEMPTED); } spin_unlock(&pr_tmpl->registration_lock); /* diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index bb86655dd40..d3a7342317e 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -5709,8 +5709,8 @@ int transport_send_check_condition_and_sense( /* CURRENT ERROR */ buffer[offset] = 0x70; buffer[offset+SPC_ADD_SENSE_LEN_OFFSET] = 10; - /* ABORTED COMMAND */ - buffer[offset+SPC_SENSE_KEY_OFFSET] = ABORTED_COMMAND; + /* ILLEGAL REQUEST */ + buffer[offset+SPC_SENSE_KEY_OFFSET] = ILLEGAL_REQUEST; /* INVALID FIELD IN CDB */ buffer[offset+SPC_ASC_KEY_OFFSET] = 0x24; break; @@ -5718,8 +5718,8 @@ int transport_send_check_condition_and_sense( /* CURRENT ERROR */ buffer[offset] = 0x70; buffer[offset+SPC_ADD_SENSE_LEN_OFFSET] = 10; - /* ABORTED COMMAND */ - buffer[offset+SPC_SENSE_KEY_OFFSET] = ABORTED_COMMAND; + /* ILLEGAL REQUEST */ + buffer[offset+SPC_SENSE_KEY_OFFSET] = ILLEGAL_REQUEST; /* INVALID FIELD IN PARAMETER LIST */ buffer[offset+SPC_ASC_KEY_OFFSET] = 0x26; break; diff --git a/drivers/tty/vt/vt_ioctl.c b/drivers/tty/vt/vt_ioctl.c index 5e096f43bce..65447c5f91d 100644 --- a/drivers/tty/vt/vt_ioctl.c +++ b/drivers/tty/vt/vt_ioctl.c @@ -1463,7 +1463,6 @@ compat_kdfontop_ioctl(struct compat_console_font_op __user *fontop, if (!perm && op->op != KD_FONT_OP_GET) return -EPERM; op->data = compat_ptr(((struct compat_console_font_op *)op)->data); - op->flags |= KD_FONT_FLAG_OLD; i = con_font_op(vc, op); if (i) return i; diff --git a/drivers/usb/gadget/f_loopback.c b/drivers/usb/gadget/f_loopback.c index b37960f9e75..0e64a47cd6b 100644 --- a/drivers/usb/gadget/f_loopback.c +++ b/drivers/usb/gadget/f_loopback.c @@ -373,7 +373,7 @@ int __init loopback_add(struct usb_composite_dev *cdev, bool autoresume) /* support autoresume for remote wakeup testing */ if (autoresume) - sourcesink_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP; + loopback_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP; /* support OTG systems */ if (gadget_is_otg(cdev->gadget)) { diff --git a/drivers/usb/host/pci-quirks.c b/drivers/usb/host/pci-quirks.c index 23e04fb038b..20f2f216347 100644 --- a/drivers/usb/host/pci-quirks.c +++ b/drivers/usb/host/pci-quirks.c @@ -866,6 +866,12 @@ static void __devinit quirk_usb_handoff_xhci(struct pci_dev *pdev) static void __devinit quirk_usb_early_handoff(struct pci_dev *pdev) { + /* Skip Netlogic mips SoC's internal PCI USB controller. + * This device does not need/support EHCI/OHCI handoff + */ + if (pdev->vendor == 0x184e) /* vendor Netlogic */ + return; + if (pdev->class == PCI_CLASS_SERIAL_USB_UHCI) quirk_usb_handoff_uhci(pdev); else if (pdev->class == PCI_CLASS_SERIAL_USB_OHCI) diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index a872cc2c93a..78c7d420bdd 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -838,6 +838,7 @@ static struct usb_device_id id_table_combined [] = { { USB_DEVICE(FTDI_VID, FTDI_SCIENCESCOPE_LOGBOOKML_PID) }, { USB_DEVICE(FTDI_VID, FTDI_SCIENCESCOPE_LS_LOGBOOK_PID) }, { USB_DEVICE(FTDI_VID, FTDI_SCIENCESCOPE_HS_LOGBOOK_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_CINTERION_MC55I_PID) }, { USB_DEVICE(FTDI_VID, FTDI_DOTEC_PID) }, { USB_DEVICE(QIHARDWARE_VID, MILKYMISTONE_JTAGSERIAL_PID), .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h index 76d4f31b38c..4eb77150f03 100644 --- a/drivers/usb/serial/ftdi_sio_ids.h +++ b/drivers/usb/serial/ftdi_sio_ids.h @@ -1187,3 +1187,10 @@ */ /* ZigBee controller */ #define FTDI_RF_R106 0x8A28 + +/* + * Product: HCP HIT GPRS modem + * Manufacturer: HCP d.o.o. + * ATI command output: Cinterion MC55i + */ +#define FTDI_CINTERION_MC55I_PID 0xA951 diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 2a9ed6ec8cb..338d082525b 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -855,6 +855,18 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0083, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0086, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0087, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0088, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0089, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0090, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0091, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0092, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0093, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0094, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0095, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0096, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0097, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0098, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0099, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0104, 0xff, 0xff, 0xff), .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0105, 0xff, 0xff, 0xff) }, @@ -883,7 +895,6 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0151, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0152, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0153, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0154, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0155, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0156, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0157, 0xff, 0xff, 0xff) }, @@ -892,6 +903,12 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0160, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0161, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0162, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0164, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0165, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0168, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0170, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0176, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0178, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1008, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1010, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1012, 0xff, 0xff, 0xff) }, @@ -1066,6 +1083,116 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1298, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1299, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1300, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1401, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1402, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1403, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1404, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1405, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1406, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1407, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1408, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1409, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1410, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1411, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1412, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1413, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1414, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1415, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1416, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1417, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1418, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1419, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1420, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1421, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1422, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1423, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1424, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1425, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1426, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1427, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1428, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1429, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1430, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1431, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1432, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1433, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1434, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1435, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1436, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1437, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1438, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1439, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1440, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1441, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1442, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1443, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1444, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1445, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1446, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1447, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1448, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1449, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1450, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1451, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1452, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1453, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1454, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1455, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1456, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1457, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1458, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1459, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1460, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1461, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1462, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1463, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1464, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1465, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1466, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1467, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1468, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1469, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1470, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1471, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1472, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1473, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1474, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1475, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1476, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1477, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1478, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1479, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1480, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1481, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1482, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1483, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1484, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1485, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1486, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1487, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1488, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1489, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1490, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1491, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1492, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1493, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1494, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1495, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1496, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1497, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1498, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1499, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1500, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1501, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1502, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1503, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1504, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1505, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1506, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1507, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1508, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1509, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1510, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0014, 0xff, 0xff, 0xff) }, /* ZTE CDMA products */ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0027, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0059, 0xff, 0xff, 0xff) }, diff --git a/drivers/video/atmel_lcdfb.c b/drivers/video/atmel_lcdfb.c index 4484c721f0f..c2ceae4bb6c 100644 --- a/drivers/video/atmel_lcdfb.c +++ b/drivers/video/atmel_lcdfb.c @@ -1085,7 +1085,7 @@ static int atmel_lcdfb_suspend(struct platform_device *pdev, pm_message_t mesg) */ lcdc_writel(sinfo, ATMEL_LCDC_IDR, ~0UL); - sinfo->saved_lcdcon = lcdc_readl(sinfo, ATMEL_LCDC_CONTRAST_VAL); + sinfo->saved_lcdcon = lcdc_readl(sinfo, ATMEL_LCDC_CONTRAST_CTR); lcdc_writel(sinfo, ATMEL_LCDC_CONTRAST_CTR, 0); if (sinfo->atmel_lcdfb_power_control) sinfo->atmel_lcdfb_power_control(0); diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c index d3e619692ee..0cfae19129b 100644 --- a/fs/cifs/sess.c +++ b/fs/cifs/sess.c @@ -244,16 +244,15 @@ static void ascii_ssetup_strings(char **pbcc_area, struct cifs_ses *ses, /* copy user */ /* BB what about null user mounts - check that we do this BB */ /* copy user */ - if (ses->user_name != NULL) + if (ses->user_name != NULL) { strncpy(bcc_ptr, ses->user_name, MAX_USERNAME_SIZE); + bcc_ptr += strnlen(ses->user_name, MAX_USERNAME_SIZE); + } /* else null user mount */ - - bcc_ptr += strnlen(ses->user_name, MAX_USERNAME_SIZE); *bcc_ptr = 0; bcc_ptr++; /* account for null termination */ /* copy domain */ - if (ses->domainName != NULL) { strncpy(bcc_ptr, ses->domainName, 256); bcc_ptr += strnlen(ses->domainName, 256); diff --git a/fs/ecryptfs/read_write.c b/fs/ecryptfs/read_write.c index 54eb14caad7..608c1c3fde1 100644 --- a/fs/ecryptfs/read_write.c +++ b/fs/ecryptfs/read_write.c @@ -130,7 +130,7 @@ int ecryptfs_write(struct inode *ecryptfs_inode, char *data, loff_t offset, pgoff_t ecryptfs_page_idx = (pos >> PAGE_CACHE_SHIFT); size_t start_offset_in_page = (pos & ~PAGE_CACHE_MASK); size_t num_bytes = (PAGE_CACHE_SIZE - start_offset_in_page); - size_t total_remaining_bytes = ((offset + size) - pos); + loff_t total_remaining_bytes = ((offset + size) - pos); if (fatal_signal_pending(current)) { rc = -EINTR; @@ -141,7 +141,7 @@ int ecryptfs_write(struct inode *ecryptfs_inode, char *data, loff_t offset, num_bytes = total_remaining_bytes; if (pos < offset) { /* remaining zeros to write, up to destination offset */ - size_t total_remaining_zeros = (offset - pos); + loff_t total_remaining_zeros = (offset - pos); if (num_bytes > total_remaining_zeros) num_bytes = total_remaining_zeros; diff --git a/fs/proc/base.c b/fs/proc/base.c index d4b5e408dcf..0da23792027 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -782,6 +782,13 @@ static int mem_open(struct inode* inode, struct file* file) if (IS_ERR(mm)) return PTR_ERR(mm); + if (mm) { + /* ensure this mm_struct can't be freed */ + atomic_inc(&mm->mm_count); + /* but do not pin its memory */ + mmput(mm); + } + /* OK to pass negative loff_t, we can catch out-of-range */ file->f_mode |= FMODE_UNSIGNED_OFFSET; file->private_data = mm; @@ -789,61 +796,13 @@ static int mem_open(struct inode* inode, struct file* file) return 0; } -static ssize_t mem_read(struct file * file, char __user * buf, - size_t count, loff_t *ppos) +static ssize_t mem_rw(struct file *file, char __user *buf, + size_t count, loff_t *ppos, int write) { - int ret; - char *page; - unsigned long src = *ppos; struct mm_struct *mm = file->private_data; - - if (!mm) - return 0; - - page = (char *)__get_free_page(GFP_TEMPORARY); - if (!page) - return -ENOMEM; - - ret = 0; - - while (count > 0) { - int this_len, retval; - - this_len = (count > PAGE_SIZE) ? PAGE_SIZE : count; - retval = access_remote_vm(mm, src, page, this_len, 0); - if (!retval) { - if (!ret) - ret = -EIO; - break; - } - - if (copy_to_user(buf, page, retval)) { - ret = -EFAULT; - break; - } - - ret += retval; - src += retval; - buf += retval; - count -= retval; - } - *ppos = src; - - free_page((unsigned long) page); - return ret; -} - -#define mem_write NULL - -#ifndef mem_write -/* This is a security hazard */ -static ssize_t mem_write(struct file * file, const char __user *buf, - size_t count, loff_t *ppos) -{ - int copied; + unsigned long addr = *ppos; + ssize_t copied; char *page; - unsigned long dst = *ppos; - struct mm_struct *mm = file->private_data; if (!mm) return 0; @@ -853,31 +812,53 @@ static ssize_t mem_write(struct file * file, const char __user *buf, return -ENOMEM; copied = 0; + if (!atomic_inc_not_zero(&mm->mm_users)) + goto free; + while (count > 0) { - int this_len, retval; + int this_len = min_t(int, count, PAGE_SIZE); - this_len = (count > PAGE_SIZE) ? PAGE_SIZE : count; - if (copy_from_user(page, buf, this_len)) { + if (write && copy_from_user(page, buf, this_len)) { copied = -EFAULT; break; } - retval = access_remote_vm(mm, dst, page, this_len, 1); - if (!retval) { + + this_len = access_remote_vm(mm, addr, page, this_len, write); + if (!this_len) { if (!copied) copied = -EIO; break; } - copied += retval; - buf += retval; - dst += retval; - count -= retval; + + if (!write && copy_to_user(buf, page, this_len)) { + copied = -EFAULT; + break; + } + + buf += this_len; + addr += this_len; + copied += this_len; + count -= this_len; } - *ppos = dst; + *ppos = addr; + mmput(mm); +free: free_page((unsigned long) page); return copied; } -#endif + +static ssize_t mem_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + return mem_rw(file, buf, count, ppos, 0); +} + +static ssize_t mem_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + return mem_rw(file, (char __user*)buf, count, ppos, 1); +} loff_t mem_lseek(struct file *file, loff_t offset, int orig) { @@ -898,8 +879,8 @@ loff_t mem_lseek(struct file *file, loff_t offset, int orig) static int mem_release(struct inode *inode, struct file *file) { struct mm_struct *mm = file->private_data; - - mmput(mm); + if (mm) + mmdrop(mm); return 0; } diff --git a/fs/udf/super.c b/fs/udf/super.c index 7b27b063ff6..7f0e18aa25d 100644 --- a/fs/udf/super.c +++ b/fs/udf/super.c @@ -1830,6 +1830,12 @@ static void udf_close_lvid(struct super_block *sb) le16_to_cpu(lvid->descTag.descCRCLength))); lvid->descTag.tagChecksum = udf_tag_checksum(&lvid->descTag); + /* + * We set buffer uptodate unconditionally here to avoid spurious + * warnings from mark_buffer_dirty() when previous EIO has marked + * the buffer as !uptodate + */ + set_buffer_uptodate(bh); mark_buffer_dirty(bh); sbi->s_lvid_dirty = 0; mutex_unlock(&sbi->s_alloc_mutex); diff --git a/include/net/arp.h b/include/net/arp.h index 91f0568a04e..fb0eb9048b1 100644 --- a/include/net/arp.h +++ b/include/net/arp.h @@ -16,6 +16,7 @@ extern void arp_send(int type, int ptype, __be32 dest_ip, const unsigned char *dest_hw, const unsigned char *src_hw, const unsigned char *th); extern int arp_bind_neighbour(struct dst_entry *dst); +extern struct neighbour *__arp_bind_neighbour(struct dst_entry *dst, __be32 nexthop); extern int arp_mc_map(__be32 addr, u8 *haddr, struct net_device *dev, int dir); extern void arp_ifdown(struct net_device *dev); diff --git a/include/net/dst.h b/include/net/dst.h index e12ddfb9eb1..d0201340bee 100644 --- a/include/net/dst.h +++ b/include/net/dst.h @@ -37,7 +37,7 @@ struct dst_entry { unsigned long _metrics; unsigned long expires; struct dst_entry *path; - struct neighbour *neighbour; + struct neighbour __rcu *_neighbour; struct hh_cache *hh; #ifdef CONFIG_XFRM struct xfrm_state *xfrm; @@ -86,6 +86,21 @@ struct dst_entry { }; }; +static inline struct neighbour *dst_get_neighbour(struct dst_entry *dst) +{ + return rcu_dereference(dst->_neighbour); +} + +static inline struct neighbour *dst_get_neighbour_raw(struct dst_entry *dst) +{ + return rcu_dereference_raw(dst->_neighbour); +} + +static inline void dst_set_neighbour(struct dst_entry *dst, struct neighbour *neigh) +{ + rcu_assign_pointer(dst->_neighbour, neigh); +} + extern u32 *dst_cow_metrics_generic(struct dst_entry *dst, unsigned long old); extern const u32 dst_default_metrics[RTAX_MAX]; @@ -371,8 +386,14 @@ static inline void dst_rcu_free(struct rcu_head *head) static inline void dst_confirm(struct dst_entry *dst) { - if (dst) - neigh_confirm(dst->neighbour); + if (dst) { + struct neighbour *n; + + rcu_read_lock(); + n = dst_get_neighbour(dst); + neigh_confirm(n); + rcu_read_unlock(); + } } static inline void dst_link_failure(struct sk_buff *skb) diff --git a/kernel/kprobes.c b/kernel/kprobes.c index e0f0bdd92ca..749340c1e6f 100644 --- a/kernel/kprobes.c +++ b/kernel/kprobes.c @@ -1660,8 +1660,12 @@ static int __kprobes pre_handler_kretprobe(struct kprobe *p, ri->rp = rp; ri->task = current; - if (rp->entry_handler && rp->entry_handler(ri, regs)) + if (rp->entry_handler && rp->entry_handler(ri, regs)) { + raw_spin_lock_irqsave(&rp->lock, flags); + hlist_add_head(&ri->hlist, &rp->free_instances); + raw_spin_unlock_irqrestore(&rp->lock, flags); return 0; + } arch_prepare_kretprobe(ri, regs); diff --git a/kernel/panic.c b/kernel/panic.c index a136da2f396..564c7bc6ecb 100644 --- a/kernel/panic.c +++ b/kernel/panic.c @@ -242,8 +242,16 @@ void add_taint(unsigned flag) * Also we want to keep up lockdep for staging development and * post-warning case. */ - if (flag != TAINT_CRAP && flag != TAINT_WARN && __debug_locks_off()) - printk(KERN_WARNING "Disabling lock debugging due to kernel taint\n"); + switch (flag) { + case TAINT_CRAP: + case TAINT_WARN: + case TAINT_FIRMWARE_WORKAROUND: + break; + + default: + if (__debug_locks_off()) + printk(KERN_WARNING "Disabling lock debugging due to kernel taint\n"); + } set_bit(flag, &tainted_mask); } diff --git a/kernel/sched_rt.c b/kernel/sched_rt.c index 17f2319d5e4..ac79f9e34fd 100644 --- a/kernel/sched_rt.c +++ b/kernel/sched_rt.c @@ -1390,6 +1390,11 @@ static int push_rt_task(struct rq *rq) if (!next_task) return 0; +#ifdef __ARCH_WANT_INTERRUPTS_ON_CTXSW + if (unlikely(task_running(rq, next_task))) + return 0; +#endif + retry: if (unlikely(next_task == rq->curr)) { WARN_ON(1); diff --git a/mm/compaction.c b/mm/compaction.c index 86c19e83cfa..a7e34815a46 100644 --- a/mm/compaction.c +++ b/mm/compaction.c @@ -313,12 +313,34 @@ static isolate_migrate_t isolate_migratepages(struct zone *zone, } else if (!locked) spin_lock_irq(&zone->lru_lock); + /* + * migrate_pfn does not necessarily start aligned to a + * pageblock. Ensure that pfn_valid is called when moving + * into a new MAX_ORDER_NR_PAGES range in case of large + * memory holes within the zone + */ + if ((low_pfn & (MAX_ORDER_NR_PAGES - 1)) == 0) { + if (!pfn_valid(low_pfn)) { + low_pfn += MAX_ORDER_NR_PAGES - 1; + continue; + } + } + if (!pfn_valid_within(low_pfn)) continue; nr_scanned++; - /* Get the page and skip if free */ + /* + * Get the page and ensure the page is within the same zone. + * See the comment in isolate_freepages about overlapping + * nodes. It is deliberate that the new zone lock is not taken + * as memory compaction should not move pages between nodes. + */ page = pfn_to_page(low_pfn); + if (page_zone(page) != zone) + continue; + + /* Skip if free */ if (PageBuddy(page)) continue; diff --git a/mm/filemap.c b/mm/filemap.c index 3c981baadb7..b7d860390f3 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -1379,15 +1379,12 @@ generic_file_aio_read(struct kiocb *iocb, const struct iovec *iov, unsigned long seg = 0; size_t count; loff_t *ppos = &iocb->ki_pos; - struct blk_plug plug; count = 0; retval = generic_segment_checks(iov, &nr_segs, &count, VERIFY_WRITE); if (retval) return retval; - blk_start_plug(&plug); - /* coalesce the iovecs and go direct-to-BIO for O_DIRECT */ if (filp->f_flags & O_DIRECT) { loff_t size; @@ -1403,8 +1400,12 @@ generic_file_aio_read(struct kiocb *iocb, const struct iovec *iov, retval = filemap_write_and_wait_range(mapping, pos, pos + iov_length(iov, nr_segs) - 1); if (!retval) { + struct blk_plug plug; + + blk_start_plug(&plug); retval = mapping->a_ops->direct_IO(READ, iocb, iov, pos, nr_segs); + blk_finish_plug(&plug); } if (retval > 0) { *ppos = pos + retval; @@ -1460,7 +1461,6 @@ generic_file_aio_read(struct kiocb *iocb, const struct iovec *iov, break; } out: - blk_finish_plug(&plug); return retval; } EXPORT_SYMBOL(generic_file_aio_read); diff --git a/mm/filemap_xip.c b/mm/filemap_xip.c index 93356cd1282..dee94297f39 100644 --- a/mm/filemap_xip.c +++ b/mm/filemap_xip.c @@ -263,7 +263,12 @@ static int xip_file_fault(struct vm_area_struct *vma, struct vm_fault *vmf) xip_pfn); if (err == -ENOMEM) return VM_FAULT_OOM; - BUG_ON(err); + /* + * err == -EBUSY is fine, we've raced against another thread + * that faulted-in the same page + */ + if (err != -EBUSY) + BUG_ON(err); return VM_FAULT_NOPAGE; } else { int err, ret = VM_FAULT_OOM; diff --git a/mm/huge_memory.c b/mm/huge_memory.c index cc5acf9998b..78a83e71699 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c @@ -2020,7 +2020,7 @@ static void collect_mm_slot(struct mm_slot *mm_slot) { struct mm_struct *mm = mm_slot->mm; - VM_BUG_ON(!spin_is_locked(&khugepaged_mm_lock)); + VM_BUG_ON(NR_CPUS != 1 && !spin_is_locked(&khugepaged_mm_lock)); if (khugepaged_test_exit(mm)) { /* free mm_slot */ @@ -2048,7 +2048,7 @@ static unsigned int khugepaged_scan_mm_slot(unsigned int pages, int progress = 0; VM_BUG_ON(!pages); - VM_BUG_ON(!spin_is_locked(&khugepaged_mm_lock)); + VM_BUG_ON(NR_CPUS != 1 && !spin_is_locked(&khugepaged_mm_lock)); if (khugepaged_scan.mm_slot) mm_slot = khugepaged_scan.mm_slot; diff --git a/mm/swap.c b/mm/swap.c index 87627f181c3..4a1fc6db89e 100644 --- a/mm/swap.c +++ b/mm/swap.c @@ -667,7 +667,7 @@ void lru_add_page_tail(struct zone* zone, VM_BUG_ON(!PageHead(page)); VM_BUG_ON(PageCompound(page_tail)); VM_BUG_ON(PageLRU(page_tail)); - VM_BUG_ON(!spin_is_locked(&zone->lru_lock)); + VM_BUG_ON(NR_CPUS != 1 && !spin_is_locked(&zone->lru_lock)); SetPageLRU(page_tail); diff --git a/net/atm/clip.c b/net/atm/clip.c index 1d4be60e139..5889074e971 100644 --- a/net/atm/clip.c +++ b/net/atm/clip.c @@ -364,33 +364,37 @@ static netdev_tx_t clip_start_xmit(struct sk_buff *skb, struct net_device *dev) { struct clip_priv *clip_priv = PRIV(dev); + struct dst_entry *dst = skb_dst(skb); struct atmarp_entry *entry; + struct neighbour *n; struct atm_vcc *vcc; int old; unsigned long flags; pr_debug("(skb %p)\n", skb); - if (!skb_dst(skb)) { + if (!dst) { pr_err("skb_dst(skb) == NULL\n"); dev_kfree_skb(skb); dev->stats.tx_dropped++; return NETDEV_TX_OK; } - if (!skb_dst(skb)->neighbour) { + n = dst_get_neighbour(dst); + if (!n) { #if 0 - skb_dst(skb)->neighbour = clip_find_neighbour(skb_dst(skb), 1); - if (!skb_dst(skb)->neighbour) { + n = clip_find_neighbour(skb_dst(skb), 1); + if (!n) { dev_kfree_skb(skb); /* lost that one */ dev->stats.tx_dropped++; return 0; } + dst_set_neighbour(dst, n); #endif pr_err("NO NEIGHBOUR !\n"); dev_kfree_skb(skb); dev->stats.tx_dropped++; return NETDEV_TX_OK; } - entry = NEIGH2ENTRY(skb_dst(skb)->neighbour); + entry = NEIGH2ENTRY(n); if (!entry->vccs) { if (time_after(jiffies, entry->expires)) { /* should be resolved */ @@ -407,7 +411,7 @@ static netdev_tx_t clip_start_xmit(struct sk_buff *skb, } pr_debug("neigh %p, vccs %p\n", entry, entry->vccs); ATM_SKB(skb)->vcc = vcc = entry->vccs->vcc; - pr_debug("using neighbour %p, vcc %p\n", skb_dst(skb)->neighbour, vcc); + pr_debug("using neighbour %p, vcc %p\n", n, vcc); if (entry->vccs->encap) { void *here; diff --git a/net/bridge/br_netfilter.c b/net/bridge/br_netfilter.c index 56149ec36d7..3dc7f5446a9 100644 --- a/net/bridge/br_netfilter.c +++ b/net/bridge/br_netfilter.c @@ -343,24 +343,26 @@ static int br_nf_pre_routing_finish_ipv6(struct sk_buff *skb) static int br_nf_pre_routing_finish_bridge(struct sk_buff *skb) { struct nf_bridge_info *nf_bridge = skb->nf_bridge; + struct neighbour *neigh; struct dst_entry *dst; skb->dev = bridge_parent(skb->dev); if (!skb->dev) goto free_skb; dst = skb_dst(skb); + neigh = dst_get_neighbour(dst); if (dst->hh) { neigh_hh_bridge(dst->hh, skb); skb->dev = nf_bridge->physindev; return br_handle_frame_finish(skb); - } else if (dst->neighbour) { + } else if (neigh) { /* the neighbour function below overwrites the complete * MAC header, so we save the Ethernet source address and * protocol number. */ skb_copy_from_linear_data_offset(skb, -(ETH_HLEN-ETH_ALEN), skb->nf_bridge->data, ETH_HLEN-ETH_ALEN); /* tell br_dev_xmit to continue with forwarding */ nf_bridge->mask |= BRNF_BRIDGED_DNAT; - return dst->neighbour->output(skb); + return neigh->output(skb); } free_skb: kfree_skb(skb); diff --git a/net/core/dst.c b/net/core/dst.c index 6135f367169..8246d47a218 100644 --- a/net/core/dst.c +++ b/net/core/dst.c @@ -171,7 +171,7 @@ void *dst_alloc(struct dst_ops *ops, struct net_device *dev, dst_init_metrics(dst, dst_default_metrics, true); dst->expires = 0UL; dst->path = dst; - dst->neighbour = NULL; + RCU_INIT_POINTER(dst->_neighbour, NULL); dst->hh = NULL; #ifdef CONFIG_XFRM dst->xfrm = NULL; @@ -231,7 +231,7 @@ struct dst_entry *dst_destroy(struct dst_entry * dst) smp_rmb(); again: - neigh = dst->neighbour; + neigh = rcu_dereference_protected(dst->_neighbour, 1); hh = dst->hh; child = dst->child; @@ -240,7 +240,7 @@ struct dst_entry *dst_destroy(struct dst_entry * dst) hh_cache_put(hh); if (neigh) { - dst->neighbour = NULL; + RCU_INIT_POINTER(dst->_neighbour, NULL); neigh_release(neigh); } @@ -367,14 +367,19 @@ static void dst_ifdown(struct dst_entry *dst, struct net_device *dev, if (!unregister) { dst->input = dst->output = dst_discard; } else { + struct neighbour *neigh; + dst->dev = dev_net(dst->dev)->loopback_dev; dev_hold(dst->dev); dev_put(dev); - if (dst->neighbour && dst->neighbour->dev == dev) { - dst->neighbour->dev = dst->dev; + rcu_read_lock(); + neigh = dst_get_neighbour(dst); + if (neigh && neigh->dev == dev) { + neigh->dev = dst->dev; dev_hold(dst->dev); dev_put(dev); } + rcu_read_unlock(); } } diff --git a/net/core/neighbour.c b/net/core/neighbour.c index bb8d6bf0d41..bea208c5db7 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -1179,12 +1179,17 @@ int neigh_update(struct neighbour *neigh, const u8 *lladdr, u8 new, while (neigh->nud_state & NUD_VALID && (skb = __skb_dequeue(&neigh->arp_queue)) != NULL) { - struct neighbour *n1 = neigh; + struct dst_entry *dst = skb_dst(skb); + struct neighbour *n2, *n1 = neigh; write_unlock_bh(&neigh->lock); + + rcu_read_lock(); /* On shaper/eql skb->dst->neighbour != neigh :( */ - if (skb_dst(skb) && skb_dst(skb)->neighbour) - n1 = skb_dst(skb)->neighbour; + if (dst && (n2 = dst_get_neighbour(dst)) != NULL) + n1 = n2; n1->output(skb); + rcu_read_unlock(); + write_lock_bh(&neigh->lock); } skb_queue_purge(&neigh->arp_queue); @@ -1306,10 +1311,10 @@ EXPORT_SYMBOL(neigh_compat_output); int neigh_resolve_output(struct sk_buff *skb) { struct dst_entry *dst = skb_dst(skb); - struct neighbour *neigh; + struct neighbour *neigh = dst_get_neighbour(dst); int rc = 0; - if (!dst || !(neigh = dst->neighbour)) + if (!dst) goto discard; __skb_pull(skb, skb_network_offset(skb)); @@ -1339,7 +1344,7 @@ int neigh_resolve_output(struct sk_buff *skb) return rc; discard: NEIGH_PRINTK1("neigh_resolve_output: dst=%p neigh=%p\n", - dst, dst ? dst->neighbour : NULL); + dst, neigh); out_kfree_skb: rc = -EINVAL; kfree_skb(skb); @@ -1353,7 +1358,7 @@ int neigh_connected_output(struct sk_buff *skb) { int err; struct dst_entry *dst = skb_dst(skb); - struct neighbour *neigh = dst->neighbour; + struct neighbour *neigh = dst_get_neighbour(dst); struct net_device *dev = neigh->dev; unsigned int seq; diff --git a/net/decnet/dn_neigh.c b/net/decnet/dn_neigh.c index 602dade7e9a..9810610d26c 100644 --- a/net/decnet/dn_neigh.c +++ b/net/decnet/dn_neigh.c @@ -208,7 +208,7 @@ static int dn_neigh_output_packet(struct sk_buff *skb) { struct dst_entry *dst = skb_dst(skb); struct dn_route *rt = (struct dn_route *)dst; - struct neighbour *neigh = dst->neighbour; + struct neighbour *neigh = dst_get_neighbour(dst); struct net_device *dev = neigh->dev; char mac_addr[ETH_ALEN]; @@ -227,7 +227,7 @@ static int dn_neigh_output_packet(struct sk_buff *skb) static int dn_long_output(struct sk_buff *skb) { struct dst_entry *dst = skb_dst(skb); - struct neighbour *neigh = dst->neighbour; + struct neighbour *neigh = dst_get_neighbour(dst); struct net_device *dev = neigh->dev; int headroom = dev->hard_header_len + sizeof(struct dn_long_packet) + 3; unsigned char *data; @@ -274,7 +274,7 @@ static int dn_long_output(struct sk_buff *skb) static int dn_short_output(struct sk_buff *skb) { struct dst_entry *dst = skb_dst(skb); - struct neighbour *neigh = dst->neighbour; + struct neighbour *neigh = dst_get_neighbour(dst); struct net_device *dev = neigh->dev; int headroom = dev->hard_header_len + sizeof(struct dn_short_packet) + 2; struct dn_short_packet *sp; @@ -318,7 +318,7 @@ static int dn_short_output(struct sk_buff *skb) static int dn_phase3_output(struct sk_buff *skb) { struct dst_entry *dst = skb_dst(skb); - struct neighbour *neigh = dst->neighbour; + struct neighbour *neigh = dst_get_neighbour(dst); struct net_device *dev = neigh->dev; int headroom = dev->hard_header_len + sizeof(struct dn_short_packet) + 2; struct dn_short_packet *sp; diff --git a/net/decnet/dn_route.c b/net/decnet/dn_route.c index 74544bc6fde..b91b60363c3 100644 --- a/net/decnet/dn_route.c +++ b/net/decnet/dn_route.c @@ -241,9 +241,11 @@ static int dn_dst_gc(struct dst_ops *ops) */ static void dn_dst_update_pmtu(struct dst_entry *dst, u32 mtu) { + struct neighbour *n = dst_get_neighbour(dst); u32 min_mtu = 230; - struct dn_dev *dn = dst->neighbour ? - rcu_dereference_raw(dst->neighbour->dev->dn_ptr) : NULL; + struct dn_dev *dn; + + dn = n ? rcu_dereference_raw(n->dev->dn_ptr) : NULL; if (dn && dn->use_long == 0) min_mtu -= 6; @@ -715,7 +717,7 @@ static int dn_output(struct sk_buff *skb) int err = -EINVAL; - if ((neigh = dst->neighbour) == NULL) + if ((neigh = dst_get_neighbour(dst)) == NULL) goto error; skb->dev = dev; @@ -750,7 +752,7 @@ static int dn_forward(struct sk_buff *skb) struct dst_entry *dst = skb_dst(skb); struct dn_dev *dn_db = rcu_dereference(dst->dev->dn_ptr); struct dn_route *rt; - struct neighbour *neigh = dst->neighbour; + struct neighbour *neigh = dst_get_neighbour(dst); int header_len; #ifdef CONFIG_NETFILTER struct net_device *dev = skb->dev; @@ -833,11 +835,11 @@ static int dn_rt_set_next_hop(struct dn_route *rt, struct dn_fib_res *res) } rt->rt_type = res->type; - if (dev != NULL && rt->dst.neighbour == NULL) { + if (dev != NULL && dst_get_neighbour(&rt->dst) == NULL) { n = __neigh_lookup_errno(&dn_neigh_table, &rt->rt_gateway, dev); if (IS_ERR(n)) return PTR_ERR(n); - rt->dst.neighbour = n; + dst_set_neighbour(&rt->dst, n); } if (dst_metric(&rt->dst, RTAX_MTU) > rt->dst.dev->mtu) @@ -1144,7 +1146,7 @@ static int dn_route_output_slow(struct dst_entry **pprt, const struct flowidn *o rt->rt_dst_map = fld.daddr; rt->rt_src_map = fld.saddr; - rt->dst.neighbour = neigh; + dst_set_neighbour(&rt->dst, neigh); neigh = NULL; rt->dst.lastuse = jiffies; @@ -1416,7 +1418,7 @@ static int dn_route_input_slow(struct sk_buff *skb) rt->fld.flowidn_iif = in_dev->ifindex; rt->fld.flowidn_mark = fld.flowidn_mark; - rt->dst.neighbour = neigh; + dst_set_neighbour(&rt->dst, neigh); rt->dst.lastuse = jiffies; rt->dst.output = dn_rt_bug; switch(res.type) { diff --git a/net/ipv4/arp.c b/net/ipv4/arp.c index 1b74d3b6437..1d5675efcfb 100644 --- a/net/ipv4/arp.c +++ b/net/ipv4/arp.c @@ -518,26 +518,32 @@ EXPORT_SYMBOL(arp_find); /* END OF OBSOLETE FUNCTIONS */ +struct neighbour *__arp_bind_neighbour(struct dst_entry *dst, __be32 nexthop) +{ + struct net_device *dev = dst->dev; + + if (dev->flags & (IFF_LOOPBACK | IFF_POINTOPOINT)) + nexthop = 0; + return __neigh_lookup_errno( +#if defined(CONFIG_ATM_CLIP) || defined(CONFIG_ATM_CLIP_MODULE) + dev->type == ARPHRD_ATM ? + clip_tbl_hook : +#endif + &arp_tbl, &nexthop, dev); +} + int arp_bind_neighbour(struct dst_entry *dst) { struct net_device *dev = dst->dev; - struct neighbour *n = dst->neighbour; + struct neighbour *n = dst_get_neighbour(dst); if (dev == NULL) return -EINVAL; if (n == NULL) { - __be32 nexthop = ((struct rtable *)dst)->rt_gateway; - if (dev->flags & (IFF_LOOPBACK | IFF_POINTOPOINT)) - nexthop = 0; - n = __neigh_lookup_errno( -#if defined(CONFIG_ATM_CLIP) || defined(CONFIG_ATM_CLIP_MODULE) - dev->type == ARPHRD_ATM ? - clip_tbl_hook : -#endif - &arp_tbl, &nexthop, dev); + n = __arp_bind_neighbour(dst, ((struct rtable *)dst)->rt_gateway); if (IS_ERR(n)) return PTR_ERR(n); - dst->neighbour = n; + dst_set_neighbour(dst, n); } return 0; } diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c index 8871067560d..d7bb94c4834 100644 --- a/net/ipv4/ip_gre.c +++ b/net/ipv4/ip_gre.c @@ -731,9 +731,9 @@ static netdev_tx_t ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev } #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) else if (skb->protocol == htons(ETH_P_IPV6)) { + struct neighbour *neigh = dst_get_neighbour(skb_dst(skb)); const struct in6_addr *addr6; int addr_type; - struct neighbour *neigh = skb_dst(skb)->neighbour; if (neigh == NULL) goto tx_error; diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index 498e356b1ec..6072e10e02b 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -182,6 +182,8 @@ static inline int ip_finish_output2(struct sk_buff *skb) struct rtable *rt = (struct rtable *)dst; struct net_device *dev = dst->dev; unsigned int hh_len = LL_RESERVED_SPACE(dev); + struct neighbour *neigh; + int res; if (rt->rt_type == RTN_MULTICAST) { IP_UPD_PO_STATS(dev_net(dev), IPSTATS_MIB_OUTMCAST, skb->len); @@ -203,10 +205,22 @@ static inline int ip_finish_output2(struct sk_buff *skb) skb = skb2; } - if (dst->hh) - return neigh_hh_output(dst->hh, skb); - else if (dst->neighbour) - return dst->neighbour->output(skb); + rcu_read_lock(); + if (dst->hh) { + int res = neigh_hh_output(dst->hh, skb); + + rcu_read_unlock(); + return res; + } else { + neigh = dst_get_neighbour(dst); + if (neigh) { + res = neigh->output(skb); + + rcu_read_unlock(); + return res; + } + rcu_read_unlock(); + } if (net_ratelimit()) printk(KERN_DEBUG "ip_finish_output2: No header cache and no neighbour!\n"); diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 353e546edb7..1864ca41040 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -416,7 +416,13 @@ static int rt_cache_seq_show(struct seq_file *seq, void *v) "HHUptod\tSpecDst"); else { struct rtable *r = v; - int len; + struct neighbour *n; + int len, HHUptod; + + rcu_read_lock(); + n = dst_get_neighbour(&r->dst); + HHUptod = (n && (n->nud_state & NUD_CONNECTED)) ? 1 : 0; + rcu_read_unlock(); seq_printf(seq, "%s\t%08X\t%08X\t%8X\t%d\t%u\t%d\t" "%08X\t%d\t%u\t%u\t%02X\t%d\t%1d\t%08X%n", @@ -431,8 +437,7 @@ static int rt_cache_seq_show(struct seq_file *seq, void *v) dst_metric(&r->dst, RTAX_RTTVAR)), r->rt_key_tos, r->dst.hh ? atomic_read(&r->dst.hh->hh_refcnt) : -1, - r->dst.hh ? (r->dst.hh->hh_output == - dev_queue_xmit) : 0, + HHUptod, r->rt_spec_dst, &len); seq_printf(seq, "%*s\n", 127 - len, ""); @@ -1688,23 +1693,25 @@ static int check_peer_redir(struct dst_entry *dst, struct inet_peer *peer) { struct rtable *rt = (struct rtable *) dst; __be32 orig_gw = rt->rt_gateway; + struct neighbour *n, *old_n; dst_confirm(&rt->dst); - neigh_release(rt->dst.neighbour); - rt->dst.neighbour = NULL; - rt->rt_gateway = peer->redirect_learned.a4; - if (arp_bind_neighbour(&rt->dst) || - !(rt->dst.neighbour->nud_state & NUD_VALID)) { - if (rt->dst.neighbour) - neigh_event_send(rt->dst.neighbour, NULL); + n = __arp_bind_neighbour(&rt->dst, rt->rt_gateway); + if (IS_ERR(n)) + return PTR_ERR(n); + old_n = xchg(&rt->dst._neighbour, n); + if (old_n) + neigh_release(old_n); + if (!n || !(n->nud_state & NUD_VALID)) { + if (n) + neigh_event_send(n, NULL); rt->rt_gateway = orig_gw; return -EAGAIN; } else { rt->rt_flags |= RTCF_REDIRECTED; - call_netevent_notifiers(NETEVENT_NEIGH_UPDATE, - rt->dst.neighbour); + call_netevent_notifiers(NETEVENT_NEIGH_UPDATE, n); } return 0; } diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 713e09d0704..7c4a2c542b0 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -656,7 +656,7 @@ ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr, int pfxlen, * layer address of our nexhop router */ - if (rt->rt6i_nexthop == NULL) + if (dst_get_neighbour_raw(&rt->dst) == NULL) ifa->flags &= ~IFA_F_OPTIMISTIC; ifa->idev = idev; diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c index 4076a0b14b2..0f9b37a1c1d 100644 --- a/net/ipv6/ip6_fib.c +++ b/net/ipv6/ip6_fib.c @@ -1455,7 +1455,7 @@ static int fib6_age(struct rt6_info *rt, void *arg) RT6_TRACE("aging clone %p\n", rt); return -1; } else if ((rt->rt6i_flags & RTF_GATEWAY) && - (!(rt->rt6i_nexthop->flags & NTF_ROUTER))) { + (!(dst_get_neighbour_raw(&rt->dst)->flags & NTF_ROUTER))) { RT6_TRACE("purging route %p via non-router but gateway\n", rt); return -1; diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 29b95418500..1b9d5c97cd0 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -100,6 +100,8 @@ static int ip6_finish_output2(struct sk_buff *skb) { struct dst_entry *dst = skb_dst(skb); struct net_device *dev = dst->dev; + struct neighbour *neigh; + int res; skb->protocol = htons(ETH_P_IPV6); skb->dev = dev; @@ -134,10 +136,22 @@ static int ip6_finish_output2(struct sk_buff *skb) skb->len); } - if (dst->hh) - return neigh_hh_output(dst->hh, skb); - else if (dst->neighbour) - return dst->neighbour->output(skb); + rcu_read_lock(); + if (dst->hh) { + res = neigh_hh_output(dst->hh, skb); + + rcu_read_unlock(); + return res; + } else { + neigh = dst_get_neighbour(dst); + if (neigh) { + res = neigh->output(skb); + + rcu_read_unlock(); + return res; + } + rcu_read_unlock(); + } IP6_INC_STATS_BH(dev_net(dst->dev), ip6_dst_idev(dst), IPSTATS_MIB_OUTNOROUTES); @@ -385,6 +399,7 @@ int ip6_forward(struct sk_buff *skb) struct ipv6hdr *hdr = ipv6_hdr(skb); struct inet6_skb_parm *opt = IP6CB(skb); struct net *net = dev_net(dst->dev); + struct neighbour *n; u32 mtu; if (net->ipv6.devconf_all->forwarding == 0) @@ -460,11 +475,10 @@ int ip6_forward(struct sk_buff *skb) send redirects to source routed frames. We don't send redirects to frames decapsulated from IPsec. */ - if (skb->dev == dst->dev && dst->neighbour && opt->srcrt == 0 && - !skb_sec_path(skb)) { + n = dst_get_neighbour(dst); + if (skb->dev == dst->dev && n && opt->srcrt == 0 && !skb_sec_path(skb)) { struct in6_addr *target = NULL; struct rt6_info *rt; - struct neighbour *n = dst->neighbour; /* * incoming and outgoing devices are the same @@ -950,8 +964,11 @@ static struct dst_entry *ip6_sk_dst_check(struct sock *sk, static int ip6_dst_lookup_tail(struct sock *sk, struct dst_entry **dst, struct flowi6 *fl6) { - int err; struct net *net = sock_net(sk); +#ifdef CONFIG_IPV6_OPTIMISTIC_DAD + struct neighbour *n; +#endif + int err; if (*dst == NULL) *dst = ip6_route_output(net, sk, fl6); @@ -977,11 +994,14 @@ static int ip6_dst_lookup_tail(struct sock *sk, * dst entry and replace it instead with the * dst entry of the nexthop router */ - if ((*dst)->neighbour && !((*dst)->neighbour->nud_state & NUD_VALID)) { + rcu_read_lock(); + n = dst_get_neighbour(*dst); + if (n && !(n->nud_state & NUD_VALID)) { struct inet6_ifaddr *ifp; struct flowi6 fl_gw6; int redirect; + rcu_read_unlock(); ifp = ipv6_get_ifaddr(net, &fl6->saddr, (*dst)->dev, 1); @@ -1001,6 +1021,8 @@ static int ip6_dst_lookup_tail(struct sock *sk, if ((err = (*dst)->error)) goto out_err_release; } + } else { + rcu_read_unlock(); } #endif diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index 7596f071d30..10a8d411707 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -1244,7 +1244,7 @@ static void ndisc_router_discovery(struct sk_buff *skb) rt = rt6_get_dflt_router(&ipv6_hdr(skb)->saddr, skb->dev); if (rt) - neigh = rt->rt6i_nexthop; + neigh = dst_get_neighbour(&rt->dst); if (rt && lifetime == 0) { neigh_clone(neigh); @@ -1265,7 +1265,7 @@ static void ndisc_router_discovery(struct sk_buff *skb) return; } - neigh = rt->rt6i_nexthop; + neigh = dst_get_neighbour(&rt->dst); if (neigh == NULL) { ND_PRINTK0(KERN_ERR "ICMPv6 RA: %s() got default router without neighbour.\n", diff --git a/net/ipv6/route.c b/net/ipv6/route.c index ebd6e2ecdce..85c899c9c7e 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -356,7 +356,7 @@ static inline struct rt6_info *rt6_device_match(struct net *net, #ifdef CONFIG_IPV6_ROUTER_PREF static void rt6_probe(struct rt6_info *rt) { - struct neighbour *neigh = rt ? rt->rt6i_nexthop : NULL; + struct neighbour *neigh; /* * Okay, this does not seem to be appropriate * for now, however, we need to check if it @@ -365,8 +365,10 @@ static void rt6_probe(struct rt6_info *rt) * Router Reachability Probe MUST be rate-limited * to no more than one per minute. */ + rcu_read_lock(); + neigh = rt ? dst_get_neighbour(&rt->dst) : NULL; if (!neigh || (neigh->nud_state & NUD_VALID)) - return; + goto out; read_lock_bh(&neigh->lock); if (!(neigh->nud_state & NUD_VALID) && time_after(jiffies, neigh->updated + rt->rt6i_idev->cnf.rtr_probe_interval)) { @@ -379,8 +381,11 @@ static void rt6_probe(struct rt6_info *rt) target = (struct in6_addr *)&neigh->primary_key; addrconf_addr_solict_mult(target, &mcaddr); ndisc_send_ns(rt->rt6i_dev, NULL, target, &mcaddr, NULL); - } else + } else { read_unlock_bh(&neigh->lock); + } +out: + rcu_read_unlock(); } #else static inline void rt6_probe(struct rt6_info *rt) @@ -404,8 +409,11 @@ static inline int rt6_check_dev(struct rt6_info *rt, int oif) static inline int rt6_check_neigh(struct rt6_info *rt) { - struct neighbour *neigh = rt->rt6i_nexthop; + struct neighbour *neigh; int m; + + rcu_read_lock(); + neigh = dst_get_neighbour(&rt->dst); if (rt->rt6i_flags & RTF_NONEXTHOP || !(rt->rt6i_flags & RTF_GATEWAY)) m = 1; @@ -422,6 +430,7 @@ static inline int rt6_check_neigh(struct rt6_info *rt) read_unlock_bh(&neigh->lock); } else m = 0; + rcu_read_unlock(); return m; } @@ -745,8 +754,7 @@ static struct rt6_info *rt6_alloc_cow(struct rt6_info *ort, const struct in6_add dst_free(&rt->dst); return NULL; } - rt->rt6i_nexthop = neigh; - + dst_set_neighbour(&rt->dst, neigh); } return rt; @@ -760,7 +768,7 @@ static struct rt6_info *rt6_alloc_clone(struct rt6_info *ort, const struct in6_a rt->rt6i_dst.plen = 128; rt->rt6i_flags |= RTF_CACHE; rt->dst.flags |= DST_HOST; - rt->rt6i_nexthop = neigh_clone(ort->rt6i_nexthop); + dst_set_neighbour(&rt->dst, neigh_clone(dst_get_neighbour_raw(&ort->dst))); } return rt; } @@ -794,7 +802,7 @@ static struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table, dst_hold(&rt->dst); read_unlock_bh(&table->tb6_lock); - if (!rt->rt6i_nexthop && !(rt->rt6i_flags & RTF_NONEXTHOP)) + if (!dst_get_neighbour_raw(&rt->dst) && !(rt->rt6i_flags & RTF_NONEXTHOP)) nrt = rt6_alloc_cow(rt, &fl6->daddr, &fl6->saddr); else if (!(rt->dst.flags & DST_HOST)) nrt = rt6_alloc_clone(rt, &fl6->daddr); @@ -1058,7 +1066,7 @@ struct dst_entry *icmp6_dst_alloc(struct net_device *dev, } rt->rt6i_idev = idev; - rt->rt6i_nexthop = neigh; + dst_set_neighbour(&rt->dst, neigh); atomic_set(&rt->dst.__refcnt, 1); dst_metric_set(&rt->dst, RTAX_HOPLIMIT, 255); rt->dst.output = ip6_output; @@ -1338,12 +1346,12 @@ int ip6_route_add(struct fib6_config *cfg) rt->rt6i_prefsrc.plen = 0; if (cfg->fc_flags & (RTF_GATEWAY | RTF_NONEXTHOP)) { - rt->rt6i_nexthop = __neigh_lookup_errno(&nd_tbl, &rt->rt6i_gateway, dev); - if (IS_ERR(rt->rt6i_nexthop)) { - err = PTR_ERR(rt->rt6i_nexthop); - rt->rt6i_nexthop = NULL; + struct neighbour *neigh = __neigh_lookup_errno(&nd_tbl, &rt->rt6i_gateway, dev); + if (IS_ERR(neigh)) { + err = PTR_ERR(neigh); goto out; } + dst_set_neighbour(&rt->dst, neigh); } rt->rt6i_flags = cfg->fc_flags; @@ -1579,7 +1587,7 @@ void rt6_redirect(const struct in6_addr *dest, const struct in6_addr *src, dst_confirm(&rt->dst); /* Duplicate redirect: silently ignore. */ - if (neigh == rt->dst.neighbour) + if (neigh == dst_get_neighbour_raw(&rt->dst)) goto out; nrt = ip6_rt_copy(rt); @@ -1595,7 +1603,7 @@ void rt6_redirect(const struct in6_addr *dest, const struct in6_addr *src, nrt->dst.flags |= DST_HOST; ipv6_addr_copy(&nrt->rt6i_gateway, (struct in6_addr*)neigh->primary_key); - nrt->rt6i_nexthop = neigh_clone(neigh); + dst_set_neighbour(&nrt->dst, neigh_clone(neigh)); if (ip6_ins_rt(nrt)) goto out; @@ -1675,7 +1683,7 @@ static void rt6_do_pmtu_disc(const struct in6_addr *daddr, const struct in6_addr 1. It is connected route. Action: COW 2. It is gatewayed route or NONEXTHOP route. Action: clone it. */ - if (!rt->rt6i_nexthop && !(rt->rt6i_flags & RTF_NONEXTHOP)) + if (!dst_get_neighbour_raw(&rt->dst) && !(rt->rt6i_flags & RTF_NONEXTHOP)) nrt = rt6_alloc_cow(rt, daddr, saddr); else nrt = rt6_alloc_clone(rt, daddr); @@ -2040,7 +2048,7 @@ struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev, return ERR_CAST(neigh); } - rt->rt6i_nexthop = neigh; + dst_set_neighbour(&rt->dst, neigh); ipv6_addr_copy(&rt->rt6i_dst.addr, addr); rt->rt6i_dst.plen = 128; @@ -2317,6 +2325,7 @@ static int rt6_fill_node(struct net *net, struct nlmsghdr *nlh; long expires; u32 table; + struct neighbour *n; if (prefix) { /* user wants prefix routes only */ if (!(rt->rt6i_flags & RTF_PREFIX_RT)) { @@ -2405,8 +2414,11 @@ static int rt6_fill_node(struct net *net, if (rtnetlink_put_metrics(skb, dst_metrics_ptr(&rt->dst)) < 0) goto nla_put_failure; - if (rt->dst.neighbour) - NLA_PUT(skb, RTA_GATEWAY, 16, &rt->dst.neighbour->primary_key); + rcu_read_lock(); + n = dst_get_neighbour(&rt->dst); + if (n) + NLA_PUT(skb, RTA_GATEWAY, 16, &n->primary_key); + rcu_read_unlock(); if (rt->dst.dev) NLA_PUT_U32(skb, RTA_OIF, rt->rt6i_dev->ifindex); @@ -2590,6 +2602,7 @@ struct rt6_proc_arg static int rt6_info_route(struct rt6_info *rt, void *p_arg) { struct seq_file *m = p_arg; + struct neighbour *n; seq_printf(m, "%pi6 %02x ", &rt->rt6i_dst.addr, rt->rt6i_dst.plen); @@ -2598,12 +2611,14 @@ static int rt6_info_route(struct rt6_info *rt, void *p_arg) #else seq_puts(m, "00000000000000000000000000000000 00 "); #endif - - if (rt->rt6i_nexthop) { - seq_printf(m, "%pi6", rt->rt6i_nexthop->primary_key); + rcu_read_lock(); + n = dst_get_neighbour(&rt->dst); + if (n) { + seq_printf(m, "%pi6", n->primary_key); } else { seq_puts(m, "00000000000000000000000000000000"); } + rcu_read_unlock(); seq_printf(m, " %08x %08x %08x %08x %8s\n", rt->rt6i_metric, atomic_read(&rt->dst.__refcnt), rt->dst.__use, rt->rt6i_flags, diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c index 38490d5c7e1..f56acd09659 100644 --- a/net/ipv6/sit.c +++ b/net/ipv6/sit.c @@ -679,7 +679,7 @@ static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb, struct neighbour *neigh = NULL; if (skb_dst(skb)) - neigh = skb_dst(skb)->neighbour; + neigh = dst_get_neighbour(skb_dst(skb)); if (neigh == NULL) { if (net_ratelimit()) @@ -704,7 +704,7 @@ static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb, struct neighbour *neigh = NULL; if (skb_dst(skb)) - neigh = skb_dst(skb)->neighbour; + neigh = dst_get_neighbour(skb_dst(skb)); if (neigh == NULL) { if (net_ratelimit()) diff --git a/net/sched/sch_teql.c b/net/sched/sch_teql.c index 45cd30098e3..4f4c52c0eeb 100644 --- a/net/sched/sch_teql.c +++ b/net/sched/sch_teql.c @@ -225,11 +225,11 @@ static int teql_qdisc_init(struct Qdisc *sch, struct nlattr *opt) static int -__teql_resolve(struct sk_buff *skb, struct sk_buff *skb_res, struct net_device *dev) +__teql_resolve(struct sk_buff *skb, struct sk_buff *skb_res, + struct net_device *dev, struct netdev_queue *txq, + struct neighbour *mn) { - struct netdev_queue *dev_queue = netdev_get_tx_queue(dev, 0); - struct teql_sched_data *q = qdisc_priv(dev_queue->qdisc); - struct neighbour *mn = skb_dst(skb)->neighbour; + struct teql_sched_data *q = qdisc_priv(txq->qdisc); struct neighbour *n = q->ncache; if (mn->tbl == NULL) @@ -262,17 +262,26 @@ __teql_resolve(struct sk_buff *skb, struct sk_buff *skb_res, struct net_device * } static inline int teql_resolve(struct sk_buff *skb, - struct sk_buff *skb_res, struct net_device *dev) + struct sk_buff *skb_res, + struct net_device *dev, + struct netdev_queue *txq) { - struct netdev_queue *txq = netdev_get_tx_queue(dev, 0); + struct dst_entry *dst = skb_dst(skb); + struct neighbour *mn; + int res; + if (txq->qdisc == &noop_qdisc) return -ENODEV; - if (dev->header_ops == NULL || - skb_dst(skb) == NULL || - skb_dst(skb)->neighbour == NULL) + if (!dev->header_ops || !dst) return 0; - return __teql_resolve(skb, skb_res, dev); + + rcu_read_lock(); + mn = dst_get_neighbour(dst); + res = mn ? __teql_resolve(skb, skb_res, dev, txq, mn) : 0; + rcu_read_unlock(); + + return res; } static netdev_tx_t teql_master_xmit(struct sk_buff *skb, struct net_device *dev) @@ -307,7 +316,7 @@ static netdev_tx_t teql_master_xmit(struct sk_buff *skb, struct net_device *dev) continue; } - switch (teql_resolve(skb, skb_res, slave)) { + switch (teql_resolve(skb, skb_res, slave, slave_txq)) { case 0: if (__netif_tx_trylock(slave_txq)) { unsigned int length = qdisc_pkt_len(skb); diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index 5ce74a38552..7803eb6af41 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -1497,7 +1497,7 @@ static struct dst_entry *xfrm_bundle_create(struct xfrm_policy *policy, goto free_dst; /* Copy neighbour for reachability confirmation */ - dst0->neighbour = neigh_clone(dst->neighbour); + dst_set_neighbour(dst0, neigh_clone(dst_get_neighbour(dst))); xfrm_init_path((struct xfrm_dst *)dst0, dst, nfheader_len); xfrm_init_pmtu(dst_prev); diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 21958518467..67d341fa7d1 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -1328,7 +1328,7 @@ void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid, for (i = 0; i < c->cvt_setups.used; i++) { p = snd_array_elem(&c->cvt_setups, i); if (!p->active && p->stream_tag == stream_tag && - get_wcaps_type(get_wcaps(codec, p->nid)) == type) + get_wcaps_type(get_wcaps(c, p->nid)) == type) p->dirty = 1; } } diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index 4a0f666a90b..6d0cae4681b 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -2975,13 +2975,13 @@ static int wm8962_hw_params(struct snd_pcm_substream *substream, case SNDRV_PCM_FORMAT_S16_LE: break; case SNDRV_PCM_FORMAT_S20_3LE: - aif0 |= 0x40; + aif0 |= 0x4; break; case SNDRV_PCM_FORMAT_S24_LE: - aif0 |= 0x80; + aif0 |= 0x8; break; case SNDRV_PCM_FORMAT_S32_LE: - aif0 |= 0xc0; + aif0 |= 0xc; break; default: return -EINVAL; diff --git a/sound/soc/codecs/wm_hubs.c b/sound/soc/codecs/wm_hubs.c index 9e370d14ad8..8712a9f6d6e 100644 --- a/sound/soc/codecs/wm_hubs.c +++ b/sound/soc/codecs/wm_hubs.c @@ -562,14 +562,14 @@ SOC_DAPM_SINGLE("Left Output Switch", WM8993_LINE_MIXER1, 0, 1, 0), }; static const struct snd_kcontrol_new line2_mix[] = { -SOC_DAPM_SINGLE("IN2R Switch", WM8993_LINE_MIXER2, 2, 1, 0), -SOC_DAPM_SINGLE("IN2L Switch", WM8993_LINE_MIXER2, 1, 1, 0), +SOC_DAPM_SINGLE("IN1L Switch", WM8993_LINE_MIXER2, 2, 1, 0), +SOC_DAPM_SINGLE("IN1R Switch", WM8993_LINE_MIXER2, 1, 1, 0), SOC_DAPM_SINGLE("Output Switch", WM8993_LINE_MIXER2, 0, 1, 0), }; static const struct snd_kcontrol_new line2n_mix[] = { -SOC_DAPM_SINGLE("Left Output Switch", WM8993_LINE_MIXER2, 6, 1, 0), -SOC_DAPM_SINGLE("Right Output Switch", WM8993_LINE_MIXER2, 5, 1, 0), +SOC_DAPM_SINGLE("Left Output Switch", WM8993_LINE_MIXER2, 5, 1, 0), +SOC_DAPM_SINGLE("Right Output Switch", WM8993_LINE_MIXER2, 6, 1, 0), }; static const struct snd_kcontrol_new line2p_mix[] = { @@ -589,6 +589,8 @@ SND_SOC_DAPM_INPUT("IN2RP:VXRP"), SND_SOC_DAPM_MICBIAS("MICBIAS2", WM8993_POWER_MANAGEMENT_1, 5, 0), SND_SOC_DAPM_MICBIAS("MICBIAS1", WM8993_POWER_MANAGEMENT_1, 4, 0), +SND_SOC_DAPM_SUPPLY("LINEOUT_VMID_BUF", WM8993_ANTIPOP1, 7, 0, NULL, 0), + SND_SOC_DAPM_MIXER("IN1L PGA", WM8993_POWER_MANAGEMENT_2, 6, 0, in1l_pga, ARRAY_SIZE(in1l_pga)), SND_SOC_DAPM_MIXER("IN1R PGA", WM8993_POWER_MANAGEMENT_2, 4, 0, @@ -794,9 +796,11 @@ static const struct snd_soc_dapm_route lineout1_diff_routes[] = { }; static const struct snd_soc_dapm_route lineout1_se_routes[] = { + { "LINEOUT1N Mixer", NULL, "LINEOUT_VMID_BUF" }, { "LINEOUT1N Mixer", "Left Output Switch", "Left Output PGA" }, { "LINEOUT1N Mixer", "Right Output Switch", "Right Output PGA" }, + { "LINEOUT1P Mixer", NULL, "LINEOUT_VMID_BUF" }, { "LINEOUT1P Mixer", "Left Output Switch", "Left Output PGA" }, { "LINEOUT1N Driver", NULL, "LINEOUT1N Mixer" }, @@ -804,8 +808,8 @@ 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", "IN1L Switch", "IN1L PGA" }, + { "LINEOUT2 Mixer", "IN1R Switch", "IN1R PGA" }, { "LINEOUT2 Mixer", "Output Switch", "Right Output PGA" }, { "LINEOUT2N Driver", NULL, "LINEOUT2 Mixer" }, @@ -813,9 +817,11 @@ static const struct snd_soc_dapm_route lineout2_diff_routes[] = { }; static const struct snd_soc_dapm_route lineout2_se_routes[] = { + { "LINEOUT2N Mixer", NULL, "LINEOUT_VMID_BUF" }, { "LINEOUT2N Mixer", "Left Output Switch", "Left Output PGA" }, { "LINEOUT2N Mixer", "Right Output Switch", "Right Output PGA" }, + { "LINEOUT2P Mixer", NULL, "LINEOUT_VMID_BUF" }, { "LINEOUT2P Mixer", "Right Output Switch", "Right Output PGA" }, { "LINEOUT2N Driver", NULL, "LINEOUT2N Mixer" }, diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index fa709a78cab..45a87e3fc30 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -2125,9 +2126,20 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card) "%s", card->name); snprintf(card->snd_card->longname, sizeof(card->snd_card->longname), "%s", card->long_name ? card->long_name : card->name); - if (card->driver_name) - strlcpy(card->snd_card->driver, card->driver_name, - sizeof(card->snd_card->driver)); + snprintf(card->snd_card->driver, sizeof(card->snd_card->driver), + "%s", card->driver_name ? card->driver_name : card->name); + for (i = 0; i < ARRAY_SIZE(card->snd_card->driver); i++) { + switch (card->snd_card->driver[i]) { + case '_': + case '-': + case '\0': + break; + default: + if (!isalnum(card->snd_card->driver[i])) + card->snd_card->driver[i] = '_'; + break; + } + } if (card->late_probe) { ret = card->late_probe(card); From da1099211637508344977d8dfbeeedc96c42b473 Mon Sep 17 00:00:00 2001 From: Ethan Chen Date: Sun, 2 Sep 2012 18:09:20 -0700 Subject: [PATCH 026/111] Linux 3.0.22 --- Makefile | 2 +- arch/x86/pci/xen.c | 2 +- crypto/sha512_generic.c | 83 +++++++++++------------- drivers/gpio/pca953x.c | 6 +- drivers/gpu/drm/i915/intel_lvds.c | 8 +++ drivers/hwmon/f75375s.c | 4 +- include/linux/bitops.h | 20 ++++++ include/linux/proportions.h | 4 ++ include/trace/events/writeback.h | 5 +- kernel/relay.c | 10 ++- mm/slub.c | 6 ++ net/mac80211/rx.c | 2 +- sound/pci/intel8x0.c | 6 ++ tools/perf/bench/mem-memcpy-x86-64-asm.S | 6 ++ tools/perf/util/evsel.c | 1 + 15 files changed, 108 insertions(+), 57 deletions(-) diff --git a/Makefile b/Makefile index 1e04a9f8911..d0db33d8d0f 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ VERSION = 3 PATCHLEVEL = 0 -SUBLEVEL = 21 +SUBLEVEL = 22 EXTRAVERSION = NAME = Sneaky Weasel diff --git a/arch/x86/pci/xen.c b/arch/x86/pci/xen.c index f567965c062..6e96e65e7ca 100644 --- a/arch/x86/pci/xen.c +++ b/arch/x86/pci/xen.c @@ -308,7 +308,7 @@ int __init pci_xen_init(void) int __init pci_xen_hvm_init(void) { - if (!xen_feature(XENFEAT_hvm_pirqs)) + if (!xen_have_vector_callback || !xen_feature(XENFEAT_hvm_pirqs)) return 0; #ifdef CONFIG_ACPI diff --git a/crypto/sha512_generic.c b/crypto/sha512_generic.c index 88f160b77b1..107f6f7be5e 100644 --- a/crypto/sha512_generic.c +++ b/crypto/sha512_generic.c @@ -31,11 +31,6 @@ static inline u64 Maj(u64 x, u64 y, u64 z) return (x & y) | (z & (x | y)); } -static inline u64 RORu64(u64 x, u64 y) -{ - return (x >> y) | (x << (64 - y)); -} - static const u64 sha512_K[80] = { 0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL, 0xb5c0fbcfec4d3b2fULL, 0xe9b5dba58189dbbcULL, 0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL, @@ -66,10 +61,10 @@ static const u64 sha512_K[80] = { 0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL, }; -#define e0(x) (RORu64(x,28) ^ RORu64(x,34) ^ RORu64(x,39)) -#define e1(x) (RORu64(x,14) ^ RORu64(x,18) ^ RORu64(x,41)) -#define s0(x) (RORu64(x, 1) ^ RORu64(x, 8) ^ (x >> 7)) -#define s1(x) (RORu64(x,19) ^ RORu64(x,61) ^ (x >> 6)) +#define e0(x) (ror64(x,28) ^ ror64(x,34) ^ ror64(x,39)) +#define e1(x) (ror64(x,14) ^ ror64(x,18) ^ ror64(x,41)) +#define s0(x) (ror64(x, 1) ^ ror64(x, 8) ^ (x >> 7)) +#define s1(x) (ror64(x,19) ^ ror64(x,61) ^ (x >> 6)) static inline void LOAD_OP(int I, u64 *W, const u8 *input) { @@ -78,7 +73,7 @@ static inline void LOAD_OP(int I, u64 *W, const u8 *input) static inline void BLEND_OP(int I, u64 *W) { - W[I % 16] += s1(W[(I-2) % 16]) + W[(I-7) % 16] + s0(W[(I-15) % 16]); + W[I & 15] += s1(W[(I-2) & 15]) + W[(I-7) & 15] + s0(W[(I-15) & 15]); } static void @@ -89,46 +84,42 @@ sha512_transform(u64 *state, const u8 *input) int i; u64 W[16]; - /* load the input */ - for (i = 0; i < 16; i++) - LOAD_OP(i, W, input); - /* load the state into our registers */ a=state[0]; b=state[1]; c=state[2]; d=state[3]; e=state[4]; f=state[5]; g=state[6]; h=state[7]; -#define SHA512_0_15(i, a, b, c, d, e, f, g, h) \ - t1 = h + e1(e) + Ch(e, f, g) + sha512_K[i] + W[i]; \ - t2 = e0(a) + Maj(a, b, c); \ - d += t1; \ - h = t1 + t2 - -#define SHA512_16_79(i, a, b, c, d, e, f, g, h) \ - BLEND_OP(i, W); \ - t1 = h + e1(e) + Ch(e, f, g) + sha512_K[i] + W[(i)%16]; \ - t2 = e0(a) + Maj(a, b, c); \ - d += t1; \ - h = t1 + t2 - - for (i = 0; i < 16; i += 8) { - SHA512_0_15(i, a, b, c, d, e, f, g, h); - SHA512_0_15(i + 1, h, a, b, c, d, e, f, g); - SHA512_0_15(i + 2, g, h, a, b, c, d, e, f); - SHA512_0_15(i + 3, f, g, h, a, b, c, d, e); - SHA512_0_15(i + 4, e, f, g, h, a, b, c, d); - SHA512_0_15(i + 5, d, e, f, g, h, a, b, c); - SHA512_0_15(i + 6, c, d, e, f, g, h, a, b); - SHA512_0_15(i + 7, b, c, d, e, f, g, h, a); - } - for (i = 16; i < 80; i += 8) { - SHA512_16_79(i, a, b, c, d, e, f, g, h); - SHA512_16_79(i + 1, h, a, b, c, d, e, f, g); - SHA512_16_79(i + 2, g, h, a, b, c, d, e, f); - SHA512_16_79(i + 3, f, g, h, a, b, c, d, e); - SHA512_16_79(i + 4, e, f, g, h, a, b, c, d); - SHA512_16_79(i + 5, d, e, f, g, h, a, b, c); - SHA512_16_79(i + 6, c, d, e, f, g, h, a, b); - SHA512_16_79(i + 7, b, c, d, e, f, g, h, a); + /* now iterate */ + for (i=0; i<80; i+=8) { + if (!(i & 8)) { + int j; + + if (i < 16) { + /* load the input */ + for (j = 0; j < 16; j++) + LOAD_OP(i + j, W, input); + } else { + for (j = 0; j < 16; j++) { + BLEND_OP(i + j, W); + } + } + } + + t1 = h + e1(e) + Ch(e,f,g) + sha512_K[i ] + W[(i & 15)]; + t2 = e0(a) + Maj(a,b,c); d+=t1; h=t1+t2; + t1 = g + e1(d) + Ch(d,e,f) + sha512_K[i+1] + W[(i & 15) + 1]; + t2 = e0(h) + Maj(h,a,b); c+=t1; g=t1+t2; + t1 = f + e1(c) + Ch(c,d,e) + sha512_K[i+2] + W[(i & 15) + 2]; + t2 = e0(g) + Maj(g,h,a); b+=t1; f=t1+t2; + t1 = e + e1(b) + Ch(b,c,d) + sha512_K[i+3] + W[(i & 15) + 3]; + t2 = e0(f) + Maj(f,g,h); a+=t1; e=t1+t2; + t1 = d + e1(a) + Ch(a,b,c) + sha512_K[i+4] + W[(i & 15) + 4]; + t2 = e0(e) + Maj(e,f,g); h+=t1; d=t1+t2; + t1 = c + e1(h) + Ch(h,a,b) + sha512_K[i+5] + W[(i & 15) + 5]; + t2 = e0(d) + Maj(d,e,f); g+=t1; c=t1+t2; + t1 = b + e1(g) + Ch(g,h,a) + sha512_K[i+6] + W[(i & 15) + 6]; + t2 = e0(c) + Maj(c,d,e); f+=t1; b=t1+t2; + t1 = a + e1(f) + Ch(f,g,h) + sha512_K[i+7] + W[(i & 15) + 7]; + t2 = e0(b) + Maj(b,c,d); e+=t1; a=t1+t2; } state[0] += a; state[1] += b; state[2] += c; state[3] += d; diff --git a/drivers/gpio/pca953x.c b/drivers/gpio/pca953x.c index 0451d7ac94a..532f6900626 100644 --- a/drivers/gpio/pca953x.c +++ b/drivers/gpio/pca953x.c @@ -437,7 +437,7 @@ static irqreturn_t pca953x_irq_handler(int irq, void *devid) do { level = __ffs(pending); - generic_handle_irq(level + chip->irq_base); + handle_nested_irq(level + chip->irq_base); pending &= ~(1 << level); } while (pending); @@ -481,8 +481,8 @@ static int pca953x_irq_setup(struct pca953x_chip *chip, int irq = lvl + chip->irq_base; irq_set_chip_data(irq, chip); - irq_set_chip_and_handler(irq, &pca953x_irq_chip, - handle_simple_irq); + irq_set_chip(irq, &pca953x_irq_chip); + irq_set_nested_thread(irq, true); #ifdef CONFIG_ARM set_irq_flags(irq, IRQF_VALID); #else diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index b28f7bd9f88..21257f8232d 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -712,6 +712,14 @@ static const struct dmi_system_id intel_no_lvds[] = { DMI_MATCH(DMI_BOARD_NAME, "i915GMm-HFS"), }, }, + { + .callback = intel_no_lvds_dmi_callback, + .ident = "AOpen i45GMx-I", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "AOpen"), + DMI_MATCH(DMI_BOARD_NAME, "i45GMx-I"), + }, + }, { .callback = intel_no_lvds_dmi_callback, .ident = "Aopen i945GTt-VFA", diff --git a/drivers/hwmon/f75375s.c b/drivers/hwmon/f75375s.c index 95cbfb3a707..e4ab4918f63 100644 --- a/drivers/hwmon/f75375s.c +++ b/drivers/hwmon/f75375s.c @@ -159,7 +159,7 @@ static inline void f75375_write8(struct i2c_client *client, u8 reg, static inline void f75375_write16(struct i2c_client *client, u8 reg, u16 value) { - int err = i2c_smbus_write_byte_data(client, reg, (value << 8)); + int err = i2c_smbus_write_byte_data(client, reg, (value >> 8)); if (err) return; i2c_smbus_write_byte_data(client, reg + 1, (value & 0xFF)); @@ -311,7 +311,7 @@ static int set_pwm_enable_direct(struct i2c_client *client, int nr, int val) fanmode |= (3 << FAN_CTRL_MODE(nr)); break; case 2: /* AUTOMATIC*/ - fanmode |= (2 << FAN_CTRL_MODE(nr)); + fanmode |= (1 << FAN_CTRL_MODE(nr)); break; case 3: /* fan speed */ break; diff --git a/include/linux/bitops.h b/include/linux/bitops.h index a3ef66a2a08..fc8a3ffce32 100644 --- a/include/linux/bitops.h +++ b/include/linux/bitops.h @@ -49,6 +49,26 @@ static inline unsigned long hweight_long(unsigned long w) return sizeof(w) == 4 ? hweight32(w) : hweight64(w); } +/** + * rol64 - rotate a 64-bit value left + * @word: value to rotate + * @shift: bits to roll + */ +static inline __u64 rol64(__u64 word, unsigned int shift) +{ + return (word << shift) | (word >> (64 - shift)); +} + +/** + * ror64 - rotate a 64-bit value right + * @word: value to rotate + * @shift: bits to roll + */ +static inline __u64 ror64(__u64 word, unsigned int shift) +{ + return (word >> shift) | (word << (64 - shift)); +} + /** * rol32 - rotate a 32-bit value left * @word: value to rotate diff --git a/include/linux/proportions.h b/include/linux/proportions.h index cf793bbbd05..22653d7c3f8 100644 --- a/include/linux/proportions.h +++ b/include/linux/proportions.h @@ -81,7 +81,11 @@ void prop_inc_percpu(struct prop_descriptor *pd, struct prop_local_percpu *pl) * Limit the time part in order to ensure there are some bits left for the * cycle counter and fraction multiply. */ +#if BITS_PER_LONG == 32 #define PROP_MAX_SHIFT (3*BITS_PER_LONG/4) +#else +#define PROP_MAX_SHIFT (BITS_PER_LONG/2) +#endif #define PROP_FRAC_SHIFT (BITS_PER_LONG - PROP_MAX_SHIFT - 1) #define PROP_FRAC_BASE (1UL << PROP_FRAC_SHIFT) diff --git a/include/trace/events/writeback.h b/include/trace/events/writeback.h index 4e249b927ea..9b60c6fc6df 100644 --- a/include/trace/events/writeback.h +++ b/include/trace/events/writeback.h @@ -23,7 +23,10 @@ DECLARE_EVENT_CLASS(writeback_work_class, __field(int, for_background) ), TP_fast_assign( - strncpy(__entry->name, dev_name(bdi->dev), 32); + struct device *dev = bdi->dev; + if (!dev) + dev = default_backing_dev_info.dev; + strncpy(__entry->name, dev_name(dev), 32); __entry->nr_pages = work->nr_pages; __entry->sb_dev = work->sb ? work->sb->s_dev : 0; __entry->sync_mode = work->sync_mode; diff --git a/kernel/relay.c b/kernel/relay.c index 859ea5a9605..2c242fb2368 100644 --- a/kernel/relay.c +++ b/kernel/relay.c @@ -164,10 +164,14 @@ static void *relay_alloc_buf(struct rchan_buf *buf, size_t *size) */ static struct rchan_buf *relay_create_buf(struct rchan *chan) { - struct rchan_buf *buf = kzalloc(sizeof(struct rchan_buf), GFP_KERNEL); - if (!buf) + struct rchan_buf *buf; + + if (chan->n_subbufs > UINT_MAX / sizeof(size_t *)) return NULL; + buf = kzalloc(sizeof(struct rchan_buf), GFP_KERNEL); + if (!buf) + return NULL; buf->padding = kmalloc(chan->n_subbufs * sizeof(size_t *), GFP_KERNEL); if (!buf->padding) goto free_buf; @@ -574,6 +578,8 @@ struct rchan *relay_open(const char *base_filename, if (!(subbuf_size && n_subbufs)) return NULL; + if (subbuf_size > UINT_MAX / n_subbufs) + return NULL; chan = kzalloc(sizeof(struct rchan), GFP_KERNEL); if (!chan) diff --git a/mm/slub.c b/mm/slub.c index 03bc30b703c..32430db38db 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -1883,6 +1883,11 @@ static void *__slab_alloc(struct kmem_cache *s, gfp_t gfpflags, int node, if (unlikely(!node_match(c, node))) goto another_slab; + /* must check again c->freelist in case of cpu migration or IRQ */ + object = c->freelist; + if (object) + goto update_freelist; + stat(s, ALLOC_REFILL); load_freelist: @@ -1892,6 +1897,7 @@ static void *__slab_alloc(struct kmem_cache *s, gfp_t gfpflags, int node, if (kmem_cache_debug(s)) goto debug; +update_freelist: c->freelist = get_freepointer(s, object); page->inuse = page->objects; page->freelist = NULL; diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 378bd67334b..41000650f4a 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -610,7 +610,7 @@ static void ieee80211_sta_reorder_release(struct ieee80211_hw *hw, index = seq_sub(tid_agg_rx->head_seq_num, tid_agg_rx->ssn) % tid_agg_rx->buf_size; if (!tid_agg_rx->reorder_buf[index] && - tid_agg_rx->stored_mpdu_num > 1) { + tid_agg_rx->stored_mpdu_num) { /* * No buffers ready to be released, but check whether any * frames in the reorder buffer have timed out. diff --git a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c index 6c896dbfd79..2e799a9a494 100644 --- a/sound/pci/intel8x0.c +++ b/sound/pci/intel8x0.c @@ -2074,6 +2074,12 @@ static struct ac97_quirk ac97_quirks[] __devinitdata = { .name = "MSI P4 ATX 645 Ultra", .type = AC97_TUNE_HP_ONLY }, + { + .subvendor = 0x161f, + .subdevice = 0x202f, + .name = "Gateway M520", + .type = AC97_TUNE_INV_EAPD + }, { .subvendor = 0x161f, .subdevice = 0x203a, diff --git a/tools/perf/bench/mem-memcpy-x86-64-asm.S b/tools/perf/bench/mem-memcpy-x86-64-asm.S index a57b66e853c..185a96d66dd 100644 --- a/tools/perf/bench/mem-memcpy-x86-64-asm.S +++ b/tools/perf/bench/mem-memcpy-x86-64-asm.S @@ -1,2 +1,8 @@ #include "../../../arch/x86/lib/memcpy_64.S" +/* + * We need to provide note.GNU-stack section, saying that we want + * NOT executable stack. Otherwise the final linking will assume that + * the ELF stack should not be restricted at all and set it RWX. + */ +.section .note.GNU-stack,"",@progbits diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 0239eb87b23..ad2183c98ac 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -348,6 +348,7 @@ int perf_event__parse_sample(const union perf_event *event, u64 type, data->cpu = data->pid = data->tid = -1; data->stream_id = data->id = data->time = -1ULL; + data->period = 1; if (event->header.type != PERF_RECORD_SAMPLE) { if (!sample_id_all) From 046b1d5cdff6f27a638e85b7d3ae85b2cee42326 Mon Sep 17 00:00:00 2001 From: Ethan Chen Date: Sun, 2 Sep 2012 18:11:21 -0700 Subject: [PATCH 027/111] Linux 3.0.23 --- Makefile | 2 +- arch/arm/include/asm/assembler.h | 5 + arch/arm/mm/cache-v7.S | 6 + arch/powerpc/kernel/perf_event.c | 8 +- arch/x86/include/asm/i387.h | 284 ++++++++++++++---- arch/x86/include/asm/processor.h | 1 + arch/x86/include/asm/thread_info.h | 2 - arch/x86/kernel/cpu/intel_cacheinfo.c | 44 ++- arch/x86/kernel/process_32.c | 25 +- arch/x86/kernel/process_64.c | 29 +- arch/x86/kernel/traps.c | 41 ++- arch/x86/kernel/xsave.c | 12 +- arch/x86/kvm/vmx.c | 2 +- drivers/cdrom/cdrom.c | 8 +- drivers/gpu/drm/radeon/r100.c | 4 +- drivers/gpu/drm/radeon/rs600.c | 4 +- drivers/hwmon/ads1015.c | 3 +- drivers/hwmon/f75375s.c | 5 +- drivers/hwmon/max6639.c | 22 +- drivers/infiniband/ulp/ipoib/ipoib.h | 6 +- drivers/infiniband/ulp/ipoib/ipoib_main.c | 55 ++-- .../infiniband/ulp/ipoib/ipoib_multicast.c | 9 +- drivers/media/video/hdpvr/hdpvr-video.c | 3 +- drivers/mmc/card/block.c | 82 ++--- drivers/net/3c59x.c | 2 +- drivers/net/davinci_emac.c | 6 +- drivers/net/jme.c | 10 +- drivers/net/jme.h | 2 +- drivers/net/usb/ipheth.c | 5 + drivers/net/veth.c | 4 +- drivers/net/via-velocity.c | 3 - drivers/net/wireless/ath/ath9k/rc.c | 2 +- drivers/pci/probe.c | 5 + drivers/scsi/scsi_pm.c | 16 + drivers/scsi/scsi_priv.h | 1 + drivers/scsi/scsi_scan.c | 4 +- drivers/usb/core/hcd-pci.c | 5 +- drivers/usb/core/hcd.c | 6 +- drivers/usb/core/hub.c | 30 +- drivers/usb/host/pci-quirks.c | 11 + drivers/usb/host/xhci-hub.c | 2 +- drivers/usb/host/xhci-mem.c | 32 +- drivers/usb/host/xhci.c | 5 + drivers/usb/serial/cp210x.c | 2 + drivers/usb/serial/option.c | 143 +-------- drivers/usb/serial/ti_usb_3410_5052.c | 6 +- drivers/usb/serial/ti_usb_3410_5052.h | 4 + drivers/usb/storage/usb.c | 89 ++---- drivers/usb/storage/usb.h | 7 +- fs/ecryptfs/inode.c | 2 + fs/eventpoll.c | 264 ++++++++++++++-- fs/nfs/nfs4state.c | 2 + fs/signalfd.c | 15 + include/asm-generic/poll.h | 2 + include/linux/eventpoll.h | 1 + include/linux/fs.h | 1 + include/linux/signalfd.h | 5 +- include/linux/usb/ch11.h | 10 +- include/net/flow.h | 10 + include/net/inet_sock.h | 2 + include/net/route.h | 4 + include/net/sch_generic.h | 9 +- kernel/fork.c | 5 +- kernel/irq/autoprobe.c | 4 +- kernel/irq/chip.c | 42 ++- kernel/irq/internals.h | 2 +- kernel/irq/manage.c | 2 +- mm/nommu.c | 7 + net/core/dev.c | 10 +- net/core/netpoll.c | 2 +- net/ipv4/arp.c | 3 +- net/ipv4/ip_forward.c | 2 +- net/ipv4/ip_options.c | 4 +- net/ipv4/route.c | 97 ++++-- net/ipv4/tcp_input.c | 45 ++- net/ipv4/tcp_ipv4.c | 5 + net/ipv6/ip6mr.c | 8 +- net/mac80211/main.c | 4 +- net/netfilter/ipvs/ip_vs_core.c | 2 +- net/sched/sch_choke.c | 3 +- net/sched/sch_netem.c | 7 +- net/sched/sch_sfb.c | 3 +- scripts/package/builddeb | 12 +- sound/pci/hda/patch_conexant.c | 11 +- sound/soc/codecs/wm8962.c | 2 +- 85 files changed, 1061 insertions(+), 612 deletions(-) diff --git a/Makefile b/Makefile index d0db33d8d0f..40eb138a5a5 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ VERSION = 3 PATCHLEVEL = 0 -SUBLEVEL = 22 +SUBLEVEL = 23 EXTRAVERSION = NAME = Sneaky Weasel diff --git a/arch/arm/include/asm/assembler.h b/arch/arm/include/asm/assembler.h index 65c3f2474f5..4e25f188835 100644 --- a/arch/arm/include/asm/assembler.h +++ b/arch/arm/include/asm/assembler.h @@ -137,6 +137,11 @@ disable_irq .endm + .macro save_and_disable_irqs_notrace, oldcpsr + mrs \oldcpsr, cpsr + disable_irq_notrace + .endm + /* * Restore interrupt state previously stored in a register. We don't * guarantee that this will preserve the flags. diff --git a/arch/arm/mm/cache-v7.S b/arch/arm/mm/cache-v7.S index 5cd1b0530f5..f74810951fd 100644 --- a/arch/arm/mm/cache-v7.S +++ b/arch/arm/mm/cache-v7.S @@ -54,9 +54,15 @@ loop1: and r1, r1, #7 @ mask of the bits for current cache only cmp r1, #2 @ see what cache we have at this level blt skip @ skip if no cache, or just i-cache +#ifdef CONFIG_PREEMPT + save_and_disable_irqs_notrace r9 @ make cssr&csidr read atomic +#endif mcr p15, 2, r10, c0, c0, 0 @ select current cache level in cssr isb @ isb to sych the new cssr&csidr mrc p15, 1, r1, c0, c0, 0 @ read the new csidr +#ifdef CONFIG_PREEMPT + restore_irqs_notrace r9 +#endif and r2, r1, #7 @ extract the length of the cache lines add r2, r2, #4 @ add 4 (line length offset) ldr r4, =0x3ff diff --git a/arch/powerpc/kernel/perf_event.c b/arch/powerpc/kernel/perf_event.c index 822f63008ae..5793c4ba5a0 100644 --- a/arch/powerpc/kernel/perf_event.c +++ b/arch/powerpc/kernel/perf_event.c @@ -865,6 +865,7 @@ static void power_pmu_start(struct perf_event *event, int ef_flags) { unsigned long flags; s64 left; + unsigned long val; if (!event->hw.idx || !event->hw.sample_period) return; @@ -880,7 +881,12 @@ static void power_pmu_start(struct perf_event *event, int ef_flags) event->hw.state = 0; left = local64_read(&event->hw.period_left); - write_pmc(event->hw.idx, left); + + val = 0; + if (left < 0x80000000L) + val = 0x80000000L - left; + + write_pmc(event->hw.idx, val); perf_event_update_userpage(event); perf_pmu_enable(event->pmu); diff --git a/arch/x86/include/asm/i387.h b/arch/x86/include/asm/i387.h index c9e09ea0564..a850b4d8d14 100644 --- a/arch/x86/include/asm/i387.h +++ b/arch/x86/include/asm/i387.h @@ -29,8 +29,8 @@ extern unsigned int sig_xstate_size; extern void fpu_init(void); extern void mxcsr_feature_mask_init(void); extern int init_fpu(struct task_struct *child); -extern asmlinkage void math_state_restore(void); -extern void __math_state_restore(void); +extern void __math_state_restore(struct task_struct *); +extern void math_state_restore(void); extern int dump_fpu(struct pt_regs *, struct user_i387_struct *); extern user_regset_active_fn fpregs_active, xfpregs_active; @@ -212,19 +212,11 @@ static inline void fpu_fxsave(struct fpu *fpu) #endif /* CONFIG_X86_64 */ -/* We need a safe address that is cheap to find and that is already - in L1 during context switch. The best choices are unfortunately - different for UP and SMP */ -#ifdef CONFIG_SMP -#define safe_address (__per_cpu_offset[0]) -#else -#define safe_address (kstat_cpu(0).cpustat.user) -#endif - /* - * These must be called with preempt disabled + * These must be called with preempt disabled. Returns + * 'true' if the FPU state is still intact. */ -static inline void fpu_save_init(struct fpu *fpu) +static inline int fpu_save_init(struct fpu *fpu) { if (use_xsave()) { fpu_xsave(fpu); @@ -233,33 +225,33 @@ static inline void fpu_save_init(struct fpu *fpu) * xsave header may indicate the init state of the FP. */ if (!(fpu->state->xsave.xsave_hdr.xstate_bv & XSTATE_FP)) - return; + return 1; } else if (use_fxsr()) { fpu_fxsave(fpu); } else { asm volatile("fnsave %[fx]; fwait" : [fx] "=m" (fpu->state->fsave)); - return; + return 0; } - if (unlikely(fpu->state->fxsave.swd & X87_FSW_ES)) + /* + * If exceptions are pending, we need to clear them so + * that we don't randomly get exceptions later. + * + * FIXME! Is this perhaps only true for the old-style + * irq13 case? Maybe we could leave the x87 state + * intact otherwise? + */ + if (unlikely(fpu->state->fxsave.swd & X87_FSW_ES)) { asm volatile("fnclex"); - - /* AMD K7/K8 CPUs don't save/restore FDP/FIP/FOP unless an exception - is pending. Clear the x87 state here by setting it to fixed - values. safe_address is a random variable that should be in L1 */ - alternative_input( - ASM_NOP8 ASM_NOP2, - "emms\n\t" /* clear stack tags */ - "fildl %P[addr]", /* set F?P to defined value */ - X86_FEATURE_FXSAVE_LEAK, - [addr] "m" (safe_address)); + return 0; + } + return 1; } -static inline void __save_init_fpu(struct task_struct *tsk) +static inline int __save_init_fpu(struct task_struct *tsk) { - fpu_save_init(&tsk->thread.fpu); - task_thread_info(tsk)->status &= ~TS_USEDFPU; + return fpu_save_init(&tsk->thread.fpu); } static inline int fpu_fxrstor_checking(struct fpu *fpu) @@ -281,39 +273,185 @@ static inline int restore_fpu_checking(struct task_struct *tsk) } /* - * Signal frame handlers... + * Software FPU state helpers. Careful: these need to + * be preemption protection *and* they need to be + * properly paired with the CR0.TS changes! */ -extern int save_i387_xstate(void __user *buf); -extern int restore_i387_xstate(void __user *buf); +static inline int __thread_has_fpu(struct task_struct *tsk) +{ + return tsk->thread.has_fpu; +} -static inline void __unlazy_fpu(struct task_struct *tsk) +/* Must be paired with an 'stts' after! */ +static inline void __thread_clear_has_fpu(struct task_struct *tsk) { - if (task_thread_info(tsk)->status & TS_USEDFPU) { - __save_init_fpu(tsk); - stts(); - } else - tsk->fpu_counter = 0; + tsk->thread.has_fpu = 0; +} + +/* Must be paired with a 'clts' before! */ +static inline void __thread_set_has_fpu(struct task_struct *tsk) +{ + tsk->thread.has_fpu = 1; } +/* + * Encapsulate the CR0.TS handling together with the + * software flag. + * + * These generally need preemption protection to work, + * do try to avoid using these on their own. + */ +static inline void __thread_fpu_end(struct task_struct *tsk) +{ + __thread_clear_has_fpu(tsk); + stts(); +} + +static inline void __thread_fpu_begin(struct task_struct *tsk) +{ + clts(); + __thread_set_has_fpu(tsk); +} + +/* + * FPU state switching for scheduling. + * + * This is a two-stage process: + * + * - switch_fpu_prepare() saves the old state and + * sets the new state of the CR0.TS bit. This is + * done within the context of the old process. + * + * - switch_fpu_finish() restores the new state as + * necessary. + */ +typedef struct { int preload; } fpu_switch_t; + +/* + * FIXME! We could do a totally lazy restore, but we need to + * add a per-cpu "this was the task that last touched the FPU + * on this CPU" variable, and the task needs to have a "I last + * touched the FPU on this CPU" and check them. + * + * We don't do that yet, so "fpu_lazy_restore()" always returns + * false, but some day.. + */ +#define fpu_lazy_restore(tsk) (0) +#define fpu_lazy_state_intact(tsk) do { } while (0) + +static inline fpu_switch_t switch_fpu_prepare(struct task_struct *old, struct task_struct *new) +{ + fpu_switch_t fpu; + + fpu.preload = tsk_used_math(new) && new->fpu_counter > 5; + if (__thread_has_fpu(old)) { + if (__save_init_fpu(old)) + fpu_lazy_state_intact(old); + __thread_clear_has_fpu(old); + old->fpu_counter++; + + /* Don't change CR0.TS if we just switch! */ + if (fpu.preload) { + __thread_set_has_fpu(new); + prefetch(new->thread.fpu.state); + } else + stts(); + } else { + old->fpu_counter = 0; + if (fpu.preload) { + if (fpu_lazy_restore(new)) + fpu.preload = 0; + else + prefetch(new->thread.fpu.state); + __thread_fpu_begin(new); + } + } + return fpu; +} + +/* + * By the time this gets called, we've already cleared CR0.TS and + * given the process the FPU if we are going to preload the FPU + * state - all we need to do is to conditionally restore the register + * state itself. + */ +static inline void switch_fpu_finish(struct task_struct *new, fpu_switch_t fpu) +{ + if (fpu.preload) + __math_state_restore(new); +} + +/* + * Signal frame handlers... + */ +extern int save_i387_xstate(void __user *buf); +extern int restore_i387_xstate(void __user *buf); + static inline void __clear_fpu(struct task_struct *tsk) { - if (task_thread_info(tsk)->status & TS_USEDFPU) { + if (__thread_has_fpu(tsk)) { /* Ignore delayed exceptions from user space */ asm volatile("1: fwait\n" "2:\n" _ASM_EXTABLE(1b, 2b)); - task_thread_info(tsk)->status &= ~TS_USEDFPU; - stts(); + __thread_fpu_end(tsk); } } +/* + * Were we in an interrupt that interrupted kernel mode? + * + * We can do a kernel_fpu_begin/end() pair *ONLY* if that + * pair does nothing at all: the thread must not have fpu (so + * that we don't try to save the FPU state), and TS must + * be set (so that the clts/stts pair does nothing that is + * visible in the interrupted kernel thread). + */ +static inline bool interrupted_kernel_fpu_idle(void) +{ + return !__thread_has_fpu(current) && + (read_cr0() & X86_CR0_TS); +} + +/* + * Were we in user mode (or vm86 mode) when we were + * interrupted? + * + * Doing kernel_fpu_begin/end() is ok if we are running + * in an interrupt context from user mode - we'll just + * save the FPU state as required. + */ +static inline bool interrupted_user_mode(void) +{ + struct pt_regs *regs = get_irq_regs(); + return regs && user_mode_vm(regs); +} + +/* + * Can we use the FPU in kernel mode with the + * whole "kernel_fpu_begin/end()" sequence? + * + * It's always ok in process context (ie "not interrupt") + * but it is sometimes ok even from an irq. + */ +static inline bool irq_fpu_usable(void) +{ + return !in_interrupt() || + interrupted_user_mode() || + interrupted_kernel_fpu_idle(); +} + static inline void kernel_fpu_begin(void) { - struct thread_info *me = current_thread_info(); + struct task_struct *me = current; + + WARN_ON_ONCE(!irq_fpu_usable()); preempt_disable(); - if (me->status & TS_USEDFPU) - __save_init_fpu(me->task); - else + if (__thread_has_fpu(me)) { + __save_init_fpu(me); + __thread_clear_has_fpu(me); + /* We do 'stts()' in kernel_fpu_end() */ + } else clts(); } @@ -323,14 +461,6 @@ static inline void kernel_fpu_end(void) preempt_enable(); } -static inline bool irq_fpu_usable(void) -{ - struct pt_regs *regs; - - return !in_interrupt() || !(regs = get_irq_regs()) || \ - user_mode(regs) || (read_cr0() & X86_CR0_TS); -} - /* * Some instructions like VIA's padlock instructions generate a spurious * DNA fault but don't modify SSE registers. And these instructions @@ -362,21 +492,65 @@ static inline void irq_ts_restore(int TS_state) stts(); } +/* + * The question "does this thread have fpu access?" + * is slightly racy, since preemption could come in + * and revoke it immediately after the test. + * + * However, even in that very unlikely scenario, + * we can just assume we have FPU access - typically + * to save the FP state - we'll just take a #NM + * fault and get the FPU access back. + * + * The actual user_fpu_begin/end() functions + * need to be preemption-safe, though. + * + * NOTE! user_fpu_end() must be used only after you + * have saved the FP state, and user_fpu_begin() must + * be used only immediately before restoring it. + * These functions do not do any save/restore on + * their own. + */ +static inline int user_has_fpu(void) +{ + return __thread_has_fpu(current); +} + +static inline void user_fpu_end(void) +{ + preempt_disable(); + __thread_fpu_end(current); + preempt_enable(); +} + +static inline void user_fpu_begin(void) +{ + preempt_disable(); + if (!user_has_fpu()) + __thread_fpu_begin(current); + preempt_enable(); +} + /* * These disable preemption on their own and are safe */ static inline void save_init_fpu(struct task_struct *tsk) { + WARN_ON_ONCE(!__thread_has_fpu(tsk)); preempt_disable(); __save_init_fpu(tsk); - stts(); + __thread_fpu_end(tsk); preempt_enable(); } static inline void unlazy_fpu(struct task_struct *tsk) { preempt_disable(); - __unlazy_fpu(tsk); + if (__thread_has_fpu(tsk)) { + __save_init_fpu(tsk); + __thread_fpu_end(tsk); + } else + tsk->fpu_counter = 0; preempt_enable(); } diff --git a/arch/x86/include/asm/processor.h b/arch/x86/include/asm/processor.h index 219371546af..5d9c61d0b27 100644 --- a/arch/x86/include/asm/processor.h +++ b/arch/x86/include/asm/processor.h @@ -454,6 +454,7 @@ struct thread_struct { unsigned long trap_no; unsigned long error_code; /* floating point and extended processor state */ + unsigned long has_fpu; struct fpu fpu; #ifdef CONFIG_X86_32 /* Virtual 86 mode info */ diff --git a/arch/x86/include/asm/thread_info.h b/arch/x86/include/asm/thread_info.h index 1f2e61e2898..278d3d5f906 100644 --- a/arch/x86/include/asm/thread_info.h +++ b/arch/x86/include/asm/thread_info.h @@ -242,8 +242,6 @@ static inline struct thread_info *current_thread_info(void) * ever touches our thread-synchronous status, so we don't * have to worry about atomic accesses. */ -#define TS_USEDFPU 0x0001 /* FPU was used by this task - this quantum (SMP) */ #define TS_COMPAT 0x0002 /* 32bit syscall active (64BIT)*/ #define TS_POLLING 0x0004 /* idle task polling need_resched, skip sending interrupt */ diff --git a/arch/x86/kernel/cpu/intel_cacheinfo.c b/arch/x86/kernel/cpu/intel_cacheinfo.c index c105c533ed9..fde44284cf2 100644 --- a/arch/x86/kernel/cpu/intel_cacheinfo.c +++ b/arch/x86/kernel/cpu/intel_cacheinfo.c @@ -330,8 +330,7 @@ static void __cpuinit amd_calc_l3_indices(struct amd_l3_cache *l3) l3->indices = (max(max3(sc0, sc1, sc2), sc3) << 10) - 1; } -static void __cpuinit amd_init_l3_cache(struct _cpuid4_info_regs *this_leaf, - int index) +static void __cpuinit amd_init_l3_cache(struct _cpuid4_info_regs *this_leaf, int index) { static struct amd_l3_cache *__cpuinitdata l3_caches; int node; @@ -748,14 +747,16 @@ static DEFINE_PER_CPU(struct _cpuid4_info *, ici_cpuid4_info); #define CPUID4_INFO_IDX(x, y) (&((per_cpu(ici_cpuid4_info, x))[y])) #ifdef CONFIG_SMP -static void __cpuinit cache_shared_cpu_map_setup(unsigned int cpu, int index) + +static int __cpuinit cache_shared_amd_cpu_map_setup(unsigned int cpu, int index) { - struct _cpuid4_info *this_leaf, *sibling_leaf; - unsigned long num_threads_sharing; - int index_msb, i, sibling; + struct _cpuid4_info *this_leaf; + int ret, i, sibling; struct cpuinfo_x86 *c = &cpu_data(cpu); - if ((index == 3) && (c->x86_vendor == X86_VENDOR_AMD)) { + ret = 0; + if (index == 3) { + ret = 1; for_each_cpu(i, cpu_llc_shared_mask(cpu)) { if (!per_cpu(ici_cpuid4_info, i)) continue; @@ -766,8 +767,35 @@ static void __cpuinit cache_shared_cpu_map_setup(unsigned int cpu, int index) set_bit(sibling, this_leaf->shared_cpu_map); } } - return; + } else if ((c->x86 == 0x15) && ((index == 1) || (index == 2))) { + ret = 1; + for_each_cpu(i, cpu_sibling_mask(cpu)) { + if (!per_cpu(ici_cpuid4_info, i)) + continue; + this_leaf = CPUID4_INFO_IDX(i, index); + for_each_cpu(sibling, cpu_sibling_mask(cpu)) { + if (!cpu_online(sibling)) + continue; + set_bit(sibling, this_leaf->shared_cpu_map); + } + } } + + return ret; +} + +static void __cpuinit cache_shared_cpu_map_setup(unsigned int cpu, int index) +{ + struct _cpuid4_info *this_leaf, *sibling_leaf; + unsigned long num_threads_sharing; + int index_msb, i; + struct cpuinfo_x86 *c = &cpu_data(cpu); + + if (c->x86_vendor == X86_VENDOR_AMD) { + if (cache_shared_amd_cpu_map_setup(cpu, index)) + return; + } + this_leaf = CPUID4_INFO_IDX(cpu, index); num_threads_sharing = 1 + this_leaf->eax.split.num_threads_sharing; diff --git a/arch/x86/kernel/process_32.c b/arch/x86/kernel/process_32.c index a3d0dc59067..fcdb1b34aa1 100644 --- a/arch/x86/kernel/process_32.c +++ b/arch/x86/kernel/process_32.c @@ -293,22 +293,11 @@ __switch_to(struct task_struct *prev_p, struct task_struct *next_p) *next = &next_p->thread; int cpu = smp_processor_id(); struct tss_struct *tss = &per_cpu(init_tss, cpu); - bool preload_fpu; + fpu_switch_t fpu; /* never put a printk in __switch_to... printk() calls wake_up*() indirectly */ - /* - * If the task has used fpu the last 5 timeslices, just do a full - * restore of the math state immediately to avoid the trap; the - * chances of needing FPU soon are obviously high now - */ - preload_fpu = tsk_used_math(next_p) && next_p->fpu_counter > 5; - - __unlazy_fpu(prev_p); - - /* we're going to use this soon, after a few expensive things */ - if (preload_fpu) - prefetch(next->fpu.state); + fpu = switch_fpu_prepare(prev_p, next_p); /* * Reload esp0. @@ -348,11 +337,6 @@ __switch_to(struct task_struct *prev_p, struct task_struct *next_p) task_thread_info(next_p)->flags & _TIF_WORK_CTXSW_NEXT)) __switch_to_xtra(prev_p, next_p, tss); - /* If we're going to preload the fpu context, make sure clts - is run while we're batching the cpu state updates. */ - if (preload_fpu) - clts(); - /* * Leave lazy mode, flushing any hypercalls made here. * This must be done before restoring TLS segments so @@ -362,15 +346,14 @@ __switch_to(struct task_struct *prev_p, struct task_struct *next_p) */ arch_end_context_switch(next_p); - if (preload_fpu) - __math_state_restore(); - /* * Restore %gs if needed (which is common) */ if (prev->gs | next->gs) lazy_load_gs(next->gs); + switch_fpu_finish(next_p, fpu); + percpu_write(current_task, next_p); return prev_p; diff --git a/arch/x86/kernel/process_64.c b/arch/x86/kernel/process_64.c index ca6f7ab8df3..b01898d2744 100644 --- a/arch/x86/kernel/process_64.c +++ b/arch/x86/kernel/process_64.c @@ -377,18 +377,9 @@ __switch_to(struct task_struct *prev_p, struct task_struct *next_p) int cpu = smp_processor_id(); struct tss_struct *tss = &per_cpu(init_tss, cpu); unsigned fsindex, gsindex; - bool preload_fpu; + fpu_switch_t fpu; - /* - * If the task has used fpu the last 5 timeslices, just do a full - * restore of the math state immediately to avoid the trap; the - * chances of needing FPU soon are obviously high now - */ - preload_fpu = tsk_used_math(next_p) && next_p->fpu_counter > 5; - - /* we're going to use this soon, after a few expensive things */ - if (preload_fpu) - prefetch(next->fpu.state); + fpu = switch_fpu_prepare(prev_p, next_p); /* * Reload esp0, LDT and the page table pointer: @@ -418,13 +409,6 @@ __switch_to(struct task_struct *prev_p, struct task_struct *next_p) load_TLS(next, cpu); - /* Must be after DS reload */ - __unlazy_fpu(prev_p); - - /* Make sure cpu is ready for new context */ - if (preload_fpu) - clts(); - /* * Leave lazy mode, flushing any hypercalls made here. * This must be done before restoring TLS segments so @@ -465,6 +449,8 @@ __switch_to(struct task_struct *prev_p, struct task_struct *next_p) wrmsrl(MSR_KERNEL_GS_BASE, next->gs); prev->gsindex = gsindex; + switch_fpu_finish(next_p, fpu); + /* * Switch the PDA and FPU contexts. */ @@ -483,13 +469,6 @@ __switch_to(struct task_struct *prev_p, struct task_struct *next_p) task_thread_info(prev_p)->flags & _TIF_WORK_CTXSW_PREV)) __switch_to_xtra(prev_p, next_p, tss); - /* - * Preload the FPU context, now that we've determined that the - * task is likely to be using it. - */ - if (preload_fpu) - __math_state_restore(); - return prev_p; } diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c index b9b67166f9d..1b26e01047b 100644 --- a/arch/x86/kernel/traps.c +++ b/arch/x86/kernel/traps.c @@ -717,25 +717,34 @@ asmlinkage void __attribute__((weak)) smp_threshold_interrupt(void) } /* - * __math_state_restore assumes that cr0.TS is already clear and the - * fpu state is all ready for use. Used during context switch. + * This gets called with the process already owning the + * FPU state, and with CR0.TS cleared. It just needs to + * restore the FPU register state. */ -void __math_state_restore(void) +void __math_state_restore(struct task_struct *tsk) { - struct thread_info *thread = current_thread_info(); - struct task_struct *tsk = thread->task; + /* We need a safe address that is cheap to find and that is already + in L1. We've just brought in "tsk->thread.has_fpu", so use that */ +#define safe_address (tsk->thread.has_fpu) + + /* AMD K7/K8 CPUs don't save/restore FDP/FIP/FOP unless an exception + is pending. Clear the x87 state here by setting it to fixed + values. safe_address is a random variable that should be in L1 */ + alternative_input( + ASM_NOP8 ASM_NOP2, + "emms\n\t" /* clear stack tags */ + "fildl %P[addr]", /* set F?P to defined value */ + X86_FEATURE_FXSAVE_LEAK, + [addr] "m" (safe_address)); /* * Paranoid restore. send a SIGSEGV if we fail to restore the state. */ if (unlikely(restore_fpu_checking(tsk))) { - stts(); + __thread_fpu_end(tsk); force_sig(SIGSEGV, tsk); return; } - - thread->status |= TS_USEDFPU; /* So we fnsave on switch_to() */ - tsk->fpu_counter++; } /* @@ -745,13 +754,12 @@ void __math_state_restore(void) * Careful.. There are problems with IBM-designed IRQ13 behaviour. * Don't touch unless you *really* know how it works. * - * Must be called with kernel preemption disabled (in this case, - * local interrupts are disabled at the call-site in entry.S). + * Must be called with kernel preemption disabled (eg with local + * local interrupts as in the case of do_device_not_available). */ -asmlinkage void math_state_restore(void) +void math_state_restore(void) { - struct thread_info *thread = current_thread_info(); - struct task_struct *tsk = thread->task; + struct task_struct *tsk = current; if (!tsk_used_math(tsk)) { local_irq_enable(); @@ -768,9 +776,10 @@ asmlinkage void math_state_restore(void) local_irq_disable(); } - clts(); /* Allow maths ops (or we recurse) */ + __thread_fpu_begin(tsk); + __math_state_restore(tsk); - __math_state_restore(); + tsk->fpu_counter++; } EXPORT_SYMBOL_GPL(math_state_restore); diff --git a/arch/x86/kernel/xsave.c b/arch/x86/kernel/xsave.c index a3911343976..71109111411 100644 --- a/arch/x86/kernel/xsave.c +++ b/arch/x86/kernel/xsave.c @@ -47,7 +47,7 @@ void __sanitize_i387_state(struct task_struct *tsk) if (!fx) return; - BUG_ON(task_thread_info(tsk)->status & TS_USEDFPU); + BUG_ON(__thread_has_fpu(tsk)); xstate_bv = tsk->thread.fpu.state->xsave.xsave_hdr.xstate_bv; @@ -168,7 +168,7 @@ int save_i387_xstate(void __user *buf) if (!used_math()) return 0; - if (task_thread_info(tsk)->status & TS_USEDFPU) { + if (user_has_fpu()) { if (use_xsave()) err = xsave_user(buf); else @@ -176,8 +176,7 @@ int save_i387_xstate(void __user *buf) if (err) return err; - task_thread_info(tsk)->status &= ~TS_USEDFPU; - stts(); + user_fpu_end(); } else { sanitize_i387_state(tsk); if (__copy_to_user(buf, &tsk->thread.fpu.state->fxsave, @@ -292,10 +291,7 @@ int restore_i387_xstate(void __user *buf) return err; } - if (!(task_thread_info(current)->status & TS_USEDFPU)) { - clts(); - task_thread_info(current)->status |= TS_USEDFPU; - } + user_fpu_begin(); if (use_xsave()) err = restore_user_xstate(buf); else diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c index d48ec60ea42..2ad060acc44 100644 --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c @@ -948,7 +948,7 @@ static void __vmx_load_host_state(struct vcpu_vmx *vmx) #ifdef CONFIG_X86_64 wrmsrl(MSR_KERNEL_GS_BASE, vmx->msr_host_kernel_gs_base); #endif - if (current_thread_info()->status & TS_USEDFPU) + if (__thread_has_fpu(current)) clts(); load_gdt(&__get_cpu_var(host_gdt)); } diff --git a/drivers/cdrom/cdrom.c b/drivers/cdrom/cdrom.c index b693cbdb421..cc6471aa9f4 100644 --- a/drivers/cdrom/cdrom.c +++ b/drivers/cdrom/cdrom.c @@ -2114,11 +2114,6 @@ static int cdrom_read_cdda_old(struct cdrom_device_info *cdi, __u8 __user *ubuf, if (!nr) return -ENOMEM; - if (!access_ok(VERIFY_WRITE, ubuf, nframes * CD_FRAMESIZE_RAW)) { - ret = -EFAULT; - goto out; - } - cgc.data_direction = CGC_DATA_READ; while (nframes > 0) { if (nr > nframes) @@ -2127,7 +2122,7 @@ static int cdrom_read_cdda_old(struct cdrom_device_info *cdi, __u8 __user *ubuf, ret = cdrom_read_block(cdi, &cgc, lba, nr, 1, CD_FRAMESIZE_RAW); if (ret) break; - if (__copy_to_user(ubuf, cgc.buffer, CD_FRAMESIZE_RAW * nr)) { + if (copy_to_user(ubuf, cgc.buffer, CD_FRAMESIZE_RAW * nr)) { ret = -EFAULT; break; } @@ -2135,7 +2130,6 @@ static int cdrom_read_cdda_old(struct cdrom_device_info *cdi, __u8 __user *ubuf, nframes -= nr; lba += nr; } -out: kfree(cgc.buffer); return ret; } diff --git a/drivers/gpu/drm/radeon/r100.c b/drivers/gpu/drm/radeon/r100.c index 764249587f1..d94f440f137 100644 --- a/drivers/gpu/drm/radeon/r100.c +++ b/drivers/gpu/drm/radeon/r100.c @@ -681,9 +681,7 @@ int r100_irq_process(struct radeon_device *rdev) WREG32(RADEON_AIC_CNTL, msi_rearm | RS400_MSI_REARM); break; default: - msi_rearm = RREG32(RADEON_MSI_REARM_EN) & ~RV370_MSI_REARM_EN; - WREG32(RADEON_MSI_REARM_EN, msi_rearm); - WREG32(RADEON_MSI_REARM_EN, msi_rearm | RV370_MSI_REARM_EN); + WREG32(RADEON_MSI_REARM_EN, RV370_MSI_REARM_EN); break; } } diff --git a/drivers/gpu/drm/radeon/rs600.c b/drivers/gpu/drm/radeon/rs600.c index 21acfb5449a..2026c2d52c5 100644 --- a/drivers/gpu/drm/radeon/rs600.c +++ b/drivers/gpu/drm/radeon/rs600.c @@ -698,9 +698,7 @@ int rs600_irq_process(struct radeon_device *rdev) WREG32(RADEON_BUS_CNTL, msi_rearm | RS600_MSI_REARM); break; default: - msi_rearm = RREG32(RADEON_MSI_REARM_EN) & ~RV370_MSI_REARM_EN; - WREG32(RADEON_MSI_REARM_EN, msi_rearm); - WREG32(RADEON_MSI_REARM_EN, msi_rearm | RV370_MSI_REARM_EN); + WREG32(RADEON_MSI_REARM_EN, RV370_MSI_REARM_EN); break; } } diff --git a/drivers/hwmon/ads1015.c b/drivers/hwmon/ads1015.c index e9beeda4cbe..9a5af38bffd 100644 --- a/drivers/hwmon/ads1015.c +++ b/drivers/hwmon/ads1015.c @@ -284,7 +284,7 @@ static int ads1015_probe(struct i2c_client *client, continue; err = device_create_file(&client->dev, &ads1015_in[k].dev_attr); if (err) - goto exit_free; + goto exit_remove; } data->hwmon_dev = hwmon_device_register(&client->dev); @@ -298,7 +298,6 @@ static int ads1015_probe(struct i2c_client *client, exit_remove: for (k = 0; k < ADS1015_CHANNELS; ++k) device_remove_file(&client->dev, &ads1015_in[k].dev_attr); -exit_free: kfree(data); exit: return err; diff --git a/drivers/hwmon/f75375s.c b/drivers/hwmon/f75375s.c index e4ab4918f63..040a820acd1 100644 --- a/drivers/hwmon/f75375s.c +++ b/drivers/hwmon/f75375s.c @@ -304,8 +304,6 @@ static int set_pwm_enable_direct(struct i2c_client *client, int nr, int val) case 0: /* Full speed */ fanmode |= (3 << FAN_CTRL_MODE(nr)); data->pwm[nr] = 255; - f75375_write8(client, F75375_REG_FAN_PWM_DUTY(nr), - data->pwm[nr]); break; case 1: /* PWM */ fanmode |= (3 << FAN_CTRL_MODE(nr)); @@ -318,6 +316,9 @@ static int set_pwm_enable_direct(struct i2c_client *client, int nr, int val) } f75375_write8(client, F75375_REG_FAN_TIMER, fanmode); data->pwm_enable[nr] = val; + if (val == 0) + f75375_write8(client, F75375_REG_FAN_PWM_DUTY(nr), + data->pwm[nr]); return 0; } diff --git a/drivers/hwmon/max6639.c b/drivers/hwmon/max6639.c index f20d9978ee7..8c3df047e56 100644 --- a/drivers/hwmon/max6639.c +++ b/drivers/hwmon/max6639.c @@ -72,8 +72,8 @@ static unsigned short normal_i2c[] = { 0x2c, 0x2e, 0x2f, I2C_CLIENT_END }; static const int rpm_ranges[] = { 2000, 4000, 8000, 16000 }; -#define FAN_FROM_REG(val, div, rpm_range) ((val) == 0 ? -1 : \ - (val) == 255 ? 0 : (rpm_ranges[rpm_range] * 30) / ((div + 1) * (val))) +#define FAN_FROM_REG(val, rpm_range) ((val) == 0 || (val) == 255 ? \ + 0 : (rpm_ranges[rpm_range] * 30) / (val)) #define TEMP_LIMIT_TO_REG(val) SENSORS_LIMIT((val) / 1000, 0, 255) /* @@ -333,7 +333,7 @@ static ssize_t show_fan_input(struct device *dev, return PTR_ERR(data); return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan[attr->index], - data->ppr, data->rpm_range)); + data->rpm_range)); } static ssize_t show_alarm(struct device *dev, @@ -429,9 +429,9 @@ static int max6639_init_client(struct i2c_client *client) struct max6639_data *data = i2c_get_clientdata(client); struct max6639_platform_data *max6639_info = client->dev.platform_data; - int i = 0; + int i; int rpm_range = 1; /* default: 4000 RPM */ - int err = 0; + int err; /* Reset chip to default values, see below for GCONFIG setup */ err = i2c_smbus_write_byte_data(client, MAX6639_REG_GCONFIG, @@ -446,11 +446,6 @@ static int max6639_init_client(struct i2c_client *client) else data->ppr = 2; data->ppr -= 1; - err = i2c_smbus_write_byte_data(client, - MAX6639_REG_FAN_PPR(i), - data->ppr << 5); - if (err) - goto exit; if (max6639_info) rpm_range = rpm_range_to_reg(max6639_info->rpm_range); @@ -458,6 +453,13 @@ static int max6639_init_client(struct i2c_client *client) for (i = 0; i < 2; i++) { + /* Set Fan pulse per revolution */ + err = i2c_smbus_write_byte_data(client, + MAX6639_REG_FAN_PPR(i), + data->ppr << 6); + if (err) + goto exit; + /* Fans config PWM, RPM */ err = i2c_smbus_write_byte_data(client, MAX6639_REG_FAN_CONFIG1(i), diff --git a/drivers/infiniband/ulp/ipoib/ipoib.h b/drivers/infiniband/ulp/ipoib/ipoib.h index 7b6985a2e65..936804efb77 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib.h +++ b/drivers/infiniband/ulp/ipoib/ipoib.h @@ -44,6 +44,7 @@ #include #include +#include #include @@ -117,8 +118,9 @@ struct ipoib_header { u16 reserved; }; -struct ipoib_pseudoheader { - u8 hwaddr[INFINIBAND_ALEN]; +struct ipoib_cb { + struct qdisc_skb_cb qdisc_cb; + u8 hwaddr[INFINIBAND_ALEN]; }; /* Used for all multicast joins (broadcast, IPv4 mcast and IPv6 mcast) */ diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c index a98c414978e..b811444dcdd 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_main.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c @@ -658,7 +658,7 @@ static void ipoib_path_lookup(struct sk_buff *skb, struct net_device *dev) } static void unicast_arp_send(struct sk_buff *skb, struct net_device *dev, - struct ipoib_pseudoheader *phdr) + struct ipoib_cb *cb) { struct ipoib_dev_priv *priv = netdev_priv(dev); struct ipoib_path *path; @@ -666,17 +666,15 @@ static void unicast_arp_send(struct sk_buff *skb, struct net_device *dev, spin_lock_irqsave(&priv->lock, flags); - path = __path_find(dev, phdr->hwaddr + 4); + path = __path_find(dev, cb->hwaddr + 4); if (!path || !path->valid) { int new_path = 0; if (!path) { - path = path_rec_create(dev, phdr->hwaddr + 4); + path = path_rec_create(dev, cb->hwaddr + 4); new_path = 1; } if (path) { - /* put pseudoheader back on for next time */ - skb_push(skb, sizeof *phdr); __skb_queue_tail(&path->queue, skb); if (!path->query && path_rec_start(dev, path)) { @@ -700,12 +698,10 @@ static void unicast_arp_send(struct sk_buff *skb, struct net_device *dev, be16_to_cpu(path->pathrec.dlid)); spin_unlock_irqrestore(&priv->lock, flags); - ipoib_send(dev, skb, path->ah, IPOIB_QPN(phdr->hwaddr)); + ipoib_send(dev, skb, path->ah, IPOIB_QPN(cb->hwaddr)); return; } else if ((path->query || !path_rec_start(dev, path)) && skb_queue_len(&path->queue) < IPOIB_MAX_PATH_REC_QUEUE) { - /* put pseudoheader back on for next time */ - skb_push(skb, sizeof *phdr); __skb_queue_tail(&path->queue, skb); } else { ++dev->stats.tx_dropped; @@ -774,16 +770,14 @@ static int ipoib_start_xmit(struct sk_buff *skb, struct net_device *dev) dev_kfree_skb_any(skb); } } else { - struct ipoib_pseudoheader *phdr = - (struct ipoib_pseudoheader *) skb->data; - skb_pull(skb, sizeof *phdr); + struct ipoib_cb *cb = (struct ipoib_cb *) skb->cb; - if (phdr->hwaddr[4] == 0xff) { + if (cb->hwaddr[4] == 0xff) { /* Add in the P_Key for multicast*/ - phdr->hwaddr[8] = (priv->pkey >> 8) & 0xff; - phdr->hwaddr[9] = priv->pkey & 0xff; + cb->hwaddr[8] = (priv->pkey >> 8) & 0xff; + cb->hwaddr[9] = priv->pkey & 0xff; - ipoib_mcast_send(dev, phdr->hwaddr + 4, skb); + ipoib_mcast_send(dev, cb->hwaddr + 4, skb); } else { /* unicast GID -- should be ARP or RARP reply */ @@ -792,14 +786,14 @@ static int ipoib_start_xmit(struct sk_buff *skb, struct net_device *dev) ipoib_warn(priv, "Unicast, no %s: type %04x, QPN %06x %pI6\n", skb_dst(skb) ? "neigh" : "dst", be16_to_cpup((__be16 *) skb->data), - IPOIB_QPN(phdr->hwaddr), - phdr->hwaddr + 4); + IPOIB_QPN(cb->hwaddr), + cb->hwaddr + 4); dev_kfree_skb_any(skb); ++dev->stats.tx_dropped; goto unlock; } - unicast_arp_send(skb, dev, phdr); + unicast_arp_send(skb, dev, cb); } } unlock: @@ -825,8 +819,6 @@ static int ipoib_hard_header(struct sk_buff *skb, const void *daddr, const void *saddr, unsigned len) { struct ipoib_header *header; - struct dst_entry *dst; - struct neighbour *n; header = (struct ipoib_header *) skb_push(skb, sizeof *header); @@ -834,18 +826,13 @@ static int ipoib_hard_header(struct sk_buff *skb, header->reserved = 0; /* - * If we don't have a neighbour structure, stuff the - * destination address onto the front of the skb so we can - * figure out where to send the packet later. + * If we don't have a dst_entry structure, stuff the + * destination address into skb->cb so we can figure out where + * to send the packet later. */ - dst = skb_dst(skb); - n = NULL; - if (dst) - n = dst_get_neighbour_raw(dst); - if ((!dst || !n) && daddr) { - struct ipoib_pseudoheader *phdr = - (struct ipoib_pseudoheader *) skb_push(skb, sizeof *phdr); - memcpy(phdr->hwaddr, daddr, INFINIBAND_ALEN); + if (!skb_dst(skb)) { + struct ipoib_cb *cb = (struct ipoib_cb *) skb->cb; + memcpy(cb->hwaddr, daddr, INFINIBAND_ALEN); } return 0; @@ -1021,11 +1008,7 @@ static void ipoib_setup(struct net_device *dev) dev->flags |= IFF_BROADCAST | IFF_MULTICAST; - /* - * We add in INFINIBAND_ALEN to allow for the destination - * address "pseudoheader" for skbs without neighbour struct. - */ - dev->hard_header_len = IPOIB_ENCAP_LEN + INFINIBAND_ALEN; + dev->hard_header_len = IPOIB_ENCAP_LEN; dev->addr_len = INFINIBAND_ALEN; dev->type = ARPHRD_INFINIBAND; dev->tx_queue_len = ipoib_sendq_size * 2; diff --git a/drivers/infiniband/ulp/ipoib/ipoib_multicast.c b/drivers/infiniband/ulp/ipoib/ipoib_multicast.c index a8d2a891b84..8b6350606d5 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_multicast.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_multicast.c @@ -258,21 +258,14 @@ static int ipoib_mcast_join_finish(struct ipoib_mcast *mcast, netif_tx_lock_bh(dev); while (!skb_queue_empty(&mcast->pkt_queue)) { struct sk_buff *skb = skb_dequeue(&mcast->pkt_queue); - struct dst_entry *dst = skb_dst(skb); - struct neighbour *n = NULL; netif_tx_unlock_bh(dev); skb->dev = dev; - if (dst) - n = dst_get_neighbour_raw(dst); - if (!dst || !n) { - /* put pseudoheader back on for next time */ - skb_push(skb, sizeof (struct ipoib_pseudoheader)); - } if (dev_queue_xmit(skb)) ipoib_warn(priv, "dev_queue_xmit failed to requeue packet\n"); + netif_tx_lock_bh(dev); } netif_tx_unlock_bh(dev); diff --git a/drivers/media/video/hdpvr/hdpvr-video.c b/drivers/media/video/hdpvr/hdpvr-video.c index 514aea76eaa..4c0394a8afd 100644 --- a/drivers/media/video/hdpvr/hdpvr-video.c +++ b/drivers/media/video/hdpvr/hdpvr-video.c @@ -284,12 +284,13 @@ static int hdpvr_start_streaming(struct hdpvr_device *dev) hdpvr_config_call(dev, CTRL_START_STREAMING_VALUE, 0x00); + dev->status = STATUS_STREAMING; + INIT_WORK(&dev->worker, hdpvr_transmit_buffers); queue_work(dev->workqueue, &dev->worker); v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev, "streaming started\n"); - dev->status = STATUS_STREAMING; return 0; } diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index a4454c0a574..a26353bff2e 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -246,6 +246,9 @@ static struct mmc_blk_ioc_data *mmc_blk_ioctl_copy_from_user( goto idata_err; } + if (!idata->buf_bytes) + return idata; + idata->buf = kzalloc(idata->buf_bytes, GFP_KERNEL); if (!idata->buf) { err = -ENOMEM; @@ -292,25 +295,6 @@ static int mmc_blk_ioctl_cmd(struct block_device *bdev, if (IS_ERR(idata)) return PTR_ERR(idata); - cmd.opcode = idata->ic.opcode; - cmd.arg = idata->ic.arg; - cmd.flags = idata->ic.flags; - - data.sg = &sg; - data.sg_len = 1; - data.blksz = idata->ic.blksz; - data.blocks = idata->ic.blocks; - - sg_init_one(data.sg, idata->buf, idata->buf_bytes); - - if (idata->ic.write_flag) - data.flags = MMC_DATA_WRITE; - else - data.flags = MMC_DATA_READ; - - mrq.cmd = &cmd; - mrq.data = &data; - md = mmc_blk_get(bdev->bd_disk); if (!md) { err = -EINVAL; @@ -323,6 +307,48 @@ static int mmc_blk_ioctl_cmd(struct block_device *bdev, goto cmd_done; } + cmd.opcode = idata->ic.opcode; + cmd.arg = idata->ic.arg; + cmd.flags = idata->ic.flags; + + if (idata->buf_bytes) { + data.sg = &sg; + data.sg_len = 1; + data.blksz = idata->ic.blksz; + data.blocks = idata->ic.blocks; + + sg_init_one(data.sg, idata->buf, idata->buf_bytes); + + if (idata->ic.write_flag) + data.flags = MMC_DATA_WRITE; + else + data.flags = MMC_DATA_READ; + + /* data.flags must already be set before doing this. */ + mmc_set_data_timeout(&data, card); + + /* Allow overriding the timeout_ns for empirical tuning. */ + if (idata->ic.data_timeout_ns) + data.timeout_ns = idata->ic.data_timeout_ns; + + if ((cmd.flags & MMC_RSP_R1B) == MMC_RSP_R1B) { + /* + * Pretend this is a data transfer and rely on the + * host driver to compute timeout. When all host + * drivers support cmd.cmd_timeout for R1B, this + * can be changed to: + * + * mrq.data = NULL; + * cmd.cmd_timeout = idata->ic.cmd_timeout_ms; + */ + data.timeout_ns = idata->ic.cmd_timeout_ms * 1000000; + } + + mrq.data = &data; + } + + mrq.cmd = &cmd; + mmc_claim_host(card->host); if (idata->ic.is_acmd) { @@ -331,24 +357,6 @@ static int mmc_blk_ioctl_cmd(struct block_device *bdev, goto cmd_rel_host; } - /* data.flags must already be set before doing this. */ - mmc_set_data_timeout(&data, card); - /* Allow overriding the timeout_ns for empirical tuning. */ - if (idata->ic.data_timeout_ns) - data.timeout_ns = idata->ic.data_timeout_ns; - - if ((cmd.flags & MMC_RSP_R1B) == MMC_RSP_R1B) { - /* - * Pretend this is a data transfer and rely on the host driver - * to compute timeout. When all host drivers support - * cmd.cmd_timeout for R1B, this can be changed to: - * - * mrq.data = NULL; - * cmd.cmd_timeout = idata->ic.cmd_timeout_ms; - */ - data.timeout_ns = idata->ic.cmd_timeout_ms * 1000000; - } - mmc_wait_for_req(card->host, &mrq); if (cmd.error) { diff --git a/drivers/net/3c59x.c b/drivers/net/3c59x.c index 8cc22568ebd..41afc408077 100644 --- a/drivers/net/3c59x.c +++ b/drivers/net/3c59x.c @@ -1842,7 +1842,7 @@ vortex_timer(unsigned long data) ok = 1; } - if (!netif_carrier_ok(dev)) + if (dev->flags & IFF_SLAVE || !netif_carrier_ok(dev)) next_tick = 5*HZ; if (vp->medialock) diff --git a/drivers/net/davinci_emac.c b/drivers/net/davinci_emac.c index dcc4a170b0f..e5efe3aec0f 100644 --- a/drivers/net/davinci_emac.c +++ b/drivers/net/davinci_emac.c @@ -1008,7 +1008,7 @@ static void emac_rx_handler(void *token, int len, int status) int ret; /* free and bail if we are shutting down */ - if (unlikely(!netif_running(ndev) || !netif_carrier_ok(ndev))) { + if (unlikely(!netif_running(ndev))) { dev_kfree_skb_any(skb); return; } @@ -1037,7 +1037,9 @@ static void emac_rx_handler(void *token, int len, int status) recycle: ret = cpdma_chan_submit(priv->rxchan, skb, skb->data, skb_tailroom(skb), GFP_KERNEL); - if (WARN_ON(ret < 0)) + + WARN_ON(ret == -ENOMEM); + if (unlikely(ret < 0)) dev_kfree_skb_any(skb); } diff --git a/drivers/net/jme.c b/drivers/net/jme.c index 19738143aa9..1d1ccec6072 100644 --- a/drivers/net/jme.c +++ b/drivers/net/jme.c @@ -2228,19 +2228,11 @@ jme_change_mtu(struct net_device *netdev, int new_mtu) ((new_mtu) < IPV6_MIN_MTU)) return -EINVAL; - if (new_mtu > 4000) { - jme->reg_rxcs &= ~RXCS_FIFOTHNP; - jme->reg_rxcs |= RXCS_FIFOTHNP_64QW; - jme_restart_rx_engine(jme); - } else { - jme->reg_rxcs &= ~RXCS_FIFOTHNP; - jme->reg_rxcs |= RXCS_FIFOTHNP_128QW; - jme_restart_rx_engine(jme); - } netdev->mtu = new_mtu; netdev_update_features(netdev); + jme_restart_rx_engine(jme); jme_reset_link(jme); return 0; diff --git a/drivers/net/jme.h b/drivers/net/jme.h index e9aaeca96ab..fff885e9274 100644 --- a/drivers/net/jme.h +++ b/drivers/net/jme.h @@ -734,7 +734,7 @@ enum jme_rxcs_values { RXCS_RETRYCNT_60 = 0x00000F00, RXCS_DEFAULT = RXCS_FIFOTHTP_128T | - RXCS_FIFOTHNP_128QW | + RXCS_FIFOTHNP_16QW | RXCS_DMAREQSZ_128B | RXCS_RETRYGAP_256ns | RXCS_RETRYCNT_32, diff --git a/drivers/net/usb/ipheth.c b/drivers/net/usb/ipheth.c index 8f9b7f76045..9cf4e47e55b 100644 --- a/drivers/net/usb/ipheth.c +++ b/drivers/net/usb/ipheth.c @@ -60,6 +60,7 @@ #define USB_PRODUCT_IPHONE_3GS 0x1294 #define USB_PRODUCT_IPHONE_4 0x1297 #define USB_PRODUCT_IPHONE_4_VZW 0x129c +#define USB_PRODUCT_IPHONE_4S 0x12a0 #define IPHETH_USBINTF_CLASS 255 #define IPHETH_USBINTF_SUBCLASS 253 @@ -103,6 +104,10 @@ static struct usb_device_id ipheth_table[] = { USB_VENDOR_APPLE, USB_PRODUCT_IPHONE_4_VZW, IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS, IPHETH_USBINTF_PROTO) }, + { USB_DEVICE_AND_INTERFACE_INFO( + USB_VENDOR_APPLE, USB_PRODUCT_IPHONE_4S, + IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS, + IPHETH_USBINTF_PROTO) }, { } }; MODULE_DEVICE_TABLE(usb, ipheth_table); diff --git a/drivers/net/veth.c b/drivers/net/veth.c index 4bf7c6d4ab9..6c0a3b0f0af 100644 --- a/drivers/net/veth.c +++ b/drivers/net/veth.c @@ -421,7 +421,9 @@ static void veth_dellink(struct net_device *dev, struct list_head *head) unregister_netdevice_queue(peer, head); } -static const struct nla_policy veth_policy[VETH_INFO_MAX + 1]; +static const struct nla_policy veth_policy[VETH_INFO_MAX + 1] = { + [VETH_INFO_PEER] = { .len = sizeof(struct ifinfomsg) }, +}; static struct rtnl_link_ops veth_link_ops = { .kind = DRV_NAME, diff --git a/drivers/net/via-velocity.c b/drivers/net/via-velocity.c index 06daa9d6fee..c7e493461e0 100644 --- a/drivers/net/via-velocity.c +++ b/drivers/net/via-velocity.c @@ -2513,9 +2513,6 @@ static int velocity_close(struct net_device *dev) if (dev->irq != 0) free_irq(dev->irq, dev); - /* Power down the chip */ - pci_set_power_state(vptr->pdev, PCI_D3hot); - velocity_free_rings(vptr); vptr->flags &= (~VELOCITY_FLAGS_OPENED); diff --git a/drivers/net/wireless/ath/ath9k/rc.c b/drivers/net/wireless/ath/ath9k/rc.c index ea35843dd38..9d965e37b00 100644 --- a/drivers/net/wireless/ath/ath9k/rc.c +++ b/drivers/net/wireless/ath/ath9k/rc.c @@ -1328,7 +1328,7 @@ static void ath_tx_status(void *priv, struct ieee80211_supported_band *sband, fc = hdr->frame_control; for (i = 0; i < sc->hw->max_rates; i++) { struct ieee80211_tx_rate *rate = &tx_info->status.rates[i]; - if (!rate->count) + if (rate->idx < 0 || !rate->count) break; final_ts_idx = i; diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index bafb3c3d4a8..5b3771a7a41 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -657,6 +657,11 @@ int __devinit pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max, dev_dbg(&dev->dev, "scanning [bus %02x-%02x] behind bridge, pass %d\n", secondary, subordinate, pass); + if (!primary && (primary != bus->number) && secondary && subordinate) { + dev_warn(&dev->dev, "Primary bus is hard wired to 0\n"); + primary = bus->number; + } + /* Check if setup is sensible at all */ if (!pass && (primary != bus->number || secondary <= bus->number)) { diff --git a/drivers/scsi/scsi_pm.c b/drivers/scsi/scsi_pm.c index d70e91ae60a..122a5a2020a 100644 --- a/drivers/scsi/scsi_pm.c +++ b/drivers/scsi/scsi_pm.c @@ -6,6 +6,7 @@ */ #include +#include #include #include @@ -68,6 +69,19 @@ static int scsi_bus_resume_common(struct device *dev) return err; } +static int scsi_bus_prepare(struct device *dev) +{ + if (scsi_is_sdev_device(dev)) { + /* sd probing uses async_schedule. Wait until it finishes. */ + async_synchronize_full(); + + } else if (scsi_is_host_device(dev)) { + /* Wait until async scanning is finished */ + scsi_complete_async_scans(); + } + return 0; +} + static int scsi_bus_suspend(struct device *dev) { return scsi_bus_suspend_common(dev, PMSG_SUSPEND); @@ -86,6 +100,7 @@ static int scsi_bus_poweroff(struct device *dev) #else /* CONFIG_PM_SLEEP */ #define scsi_bus_resume_common NULL +#define scsi_bus_prepare NULL #define scsi_bus_suspend NULL #define scsi_bus_freeze NULL #define scsi_bus_poweroff NULL @@ -194,6 +209,7 @@ void scsi_autopm_put_host(struct Scsi_Host *shost) #endif /* CONFIG_PM_RUNTIME */ const struct dev_pm_ops scsi_bus_pm_ops = { + .prepare = scsi_bus_prepare, .suspend = scsi_bus_suspend, .resume = scsi_bus_resume_common, .freeze = scsi_bus_freeze, diff --git a/drivers/scsi/scsi_priv.h b/drivers/scsi/scsi_priv.h index 2a588955423..5b475d0832c 100644 --- a/drivers/scsi/scsi_priv.h +++ b/drivers/scsi/scsi_priv.h @@ -110,6 +110,7 @@ extern void scsi_exit_procfs(void); #endif /* CONFIG_PROC_FS */ /* scsi_scan.c */ +extern int scsi_complete_async_scans(void); extern int scsi_scan_host_selected(struct Scsi_Host *, unsigned int, unsigned int, unsigned int, int); extern void scsi_forget_host(struct Scsi_Host *); diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index b3c6d957fbd..6e7ea4a2b7a 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -1815,6 +1815,7 @@ static void scsi_finish_async_scan(struct async_scan_data *data) } spin_unlock(&async_scan_lock); + scsi_autopm_put_host(shost); scsi_host_put(shost); kfree(data); } @@ -1841,7 +1842,6 @@ static int do_scan_async(void *_data) do_scsi_scan_host(shost); scsi_finish_async_scan(data); - scsi_autopm_put_host(shost); return 0; } @@ -1869,7 +1869,7 @@ void scsi_scan_host(struct Scsi_Host *shost) p = kthread_run(do_scan_async, data, "scsi_scan_%d", shost->host_no); if (IS_ERR(p)) do_scan_async(data); - /* scsi_autopm_put_host(shost) is called in do_scan_async() */ + /* scsi_autopm_put_host(shost) is called in scsi_finish_async_scan() */ } EXPORT_SYMBOL(scsi_scan_host); diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c index ce22f4a84ed..6c1642b382f 100644 --- a/drivers/usb/core/hcd-pci.c +++ b/drivers/usb/core/hcd-pci.c @@ -187,7 +187,10 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) return -ENODEV; dev->current_state = PCI_D0; - if (!dev->irq) { + /* The xHCI driver supports MSI and MSI-X, + * so don't fail if the BIOS doesn't provide a legacy IRQ. + */ + if (!dev->irq && (driver->flags & HCD_MASK) != HCD_USB3) { dev_err(&dev->dev, "Found HC with no IRQ. Check BIOS/PCI %s setup!\n", pci_name(dev)); diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 145d2ea234b..6c5444dcee7 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -2441,8 +2441,10 @@ int usb_add_hcd(struct usb_hcd *hcd, && device_can_wakeup(&hcd->self.root_hub->dev)) dev_dbg(hcd->self.controller, "supports USB remote wakeup\n"); - /* enable irqs just before we start the controller */ - if (usb_hcd_is_primary_hcd(hcd)) { + /* enable irqs just before we start the controller, + * if the BIOS provides legacy PCI irqs. + */ + if (usb_hcd_is_primary_hcd(hcd) && irqnum) { retval = usb_hcd_request_irqs(hcd, irqnum, irqflags); if (retval) goto err_request_irq; diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 4d61569b376..99cfa4a6f9d 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -734,10 +734,26 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type) if (type == HUB_INIT3) goto init3; - /* After a resume, port power should still be on. + /* The superspeed hub except for root hub has to use Hub Depth + * value as an offset into the route string to locate the bits + * it uses to determine the downstream port number. So hub driver + * should send a set hub depth request to superspeed hub after + * the superspeed hub is set configuration in initialization or + * reset procedure. + * + * After a resume, port power should still be on. * For any other type of activation, turn it on. */ if (type != HUB_RESUME) { + if (hdev->parent && hub_is_superspeed(hdev)) { + ret = usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0), + HUB_SET_DEPTH, USB_RT_HUB, + hdev->level - 1, 0, NULL, 0, + USB_CTRL_SET_TIMEOUT); + if (ret < 0) + dev_err(hub->intfdev, + "set hub depth failed\n"); + } /* Speed up system boot by using a delayed_work for the * hub's initial power-up delays. This is pretty awkward @@ -1025,18 +1041,6 @@ static int hub_configure(struct usb_hub *hub, goto fail; } - if (hub_is_superspeed(hdev) && (hdev->parent != NULL)) { - ret = usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0), - HUB_SET_DEPTH, USB_RT_HUB, - hdev->level - 1, 0, NULL, 0, - USB_CTRL_SET_TIMEOUT); - - if (ret < 0) { - message = "can't set hub depth"; - goto fail; - } - } - /* Request the entire hub descriptor. * hub->descriptor can handle USB_MAXCHILDREN ports, * but the hub can/will return fewer bytes here. diff --git a/drivers/usb/host/pci-quirks.c b/drivers/usb/host/pci-quirks.c index 20f2f216347..3f387b832fe 100644 --- a/drivers/usb/host/pci-quirks.c +++ b/drivers/usb/host/pci-quirks.c @@ -871,7 +871,17 @@ static void __devinit quirk_usb_early_handoff(struct pci_dev *pdev) */ if (pdev->vendor == 0x184e) /* vendor Netlogic */ return; + if (pdev->class != PCI_CLASS_SERIAL_USB_UHCI && + pdev->class != PCI_CLASS_SERIAL_USB_OHCI && + pdev->class != PCI_CLASS_SERIAL_USB_EHCI && + pdev->class != PCI_CLASS_SERIAL_USB_XHCI) + return; + if (pci_enable_device(pdev) < 0) { + dev_warn(&pdev->dev, "Can't enable PCI device, " + "BIOS handoff failed.\n"); + return; + } if (pdev->class == PCI_CLASS_SERIAL_USB_UHCI) quirk_usb_handoff_uhci(pdev); else if (pdev->class == PCI_CLASS_SERIAL_USB_OHCI) @@ -880,5 +890,6 @@ static void __devinit quirk_usb_early_handoff(struct pci_dev *pdev) quirk_usb_disable_ehci(pdev); else if (pdev->class == PCI_CLASS_SERIAL_USB_XHCI) quirk_usb_handoff_xhci(pdev); + pci_disable_device(pdev); } DECLARE_PCI_FIXUP_FINAL(PCI_ANY_ID, PCI_ANY_ID, quirk_usb_early_handoff); diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index ce9f974dac0..7520ebb4454 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -75,7 +75,7 @@ static void xhci_usb2_hub_descriptor(struct usb_hcd *hcd, struct xhci_hcd *xhci, */ memset(port_removable, 0, sizeof(port_removable)); for (i = 0; i < ports; i++) { - portsc = xhci_readl(xhci, xhci->usb3_ports[i]); + portsc = xhci_readl(xhci, xhci->usb2_ports[i]); /* If a device is removable, PORTSC reports a 0, same as in the * hub descriptor DeviceRemovable bits. */ diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index ffeee575fd2..64fbf6f26f9 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -1002,26 +1002,42 @@ static unsigned int xhci_parse_exponent_interval(struct usb_device *udev, } /* - * Convert bInterval expressed in frames (in 1-255 range) to exponent of + * Convert bInterval expressed in microframes (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) +static unsigned int xhci_microframes_to_exponent(struct usb_device *udev, + struct usb_host_endpoint *ep, unsigned int desc_interval, + unsigned int min_exponent, unsigned int max_exponent) { unsigned int interval; - interval = fls(8 * ep->desc.bInterval) - 1; - interval = clamp_val(interval, 3, 10); - if ((1 << interval) != 8 * ep->desc.bInterval) + interval = fls(desc_interval) - 1; + interval = clamp_val(interval, min_exponent, max_exponent); + if ((1 << interval) != desc_interval) 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); + desc_interval); return interval; } +static unsigned int xhci_parse_microframe_interval(struct usb_device *udev, + struct usb_host_endpoint *ep) +{ + return xhci_microframes_to_exponent(udev, ep, + ep->desc.bInterval, 0, 15); +} + + +static unsigned int xhci_parse_frame_interval(struct usb_device *udev, + struct usb_host_endpoint *ep) +{ + return xhci_microframes_to_exponent(udev, ep, + ep->desc.bInterval * 8, 3, 10); +} + /* Return the polling or NAK interval. * * The polling interval is expressed in "microframes". If xHCI's Interval field @@ -1040,7 +1056,7 @@ static unsigned int xhci_get_endpoint_interval(struct usb_device *udev, /* Max NAK rate */ if (usb_endpoint_xfer_control(&ep->desc) || usb_endpoint_xfer_bulk(&ep->desc)) { - interval = ep->desc.bInterval; + interval = xhci_parse_microframe_interval(udev, ep); break; } /* Fall through - SS and HS isoc/int have same decoding */ diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 107438eca2b..b4416d8dd48 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -444,6 +444,11 @@ int xhci_run(struct usb_hcd *hcd) if (ret) { legacy_irq: + if (!pdev->irq) { + xhci_err(xhci, "No msi-x/msi found and " + "no IRQ in BIOS\n"); + return -EINVAL; + } /* fall back to legacy interrupt*/ ret = request_irq(pdev->irq, &usb_hcd_irq, IRQF_SHARED, hcd->irq_descr, hcd); diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c index a5152379cb4..33d25d4e680 100644 --- a/drivers/usb/serial/cp210x.c +++ b/drivers/usb/serial/cp210x.c @@ -136,6 +136,8 @@ static const struct usb_device_id id_table[] = { { USB_DEVICE(0x16DC, 0x0011) }, /* W-IE-NE-R Plein & Baus GmbH RCM Remote Control for MARATON Power Supply */ { USB_DEVICE(0x16DC, 0x0012) }, /* W-IE-NE-R Plein & Baus GmbH MPOD Multi Channel Power Supply */ { USB_DEVICE(0x16DC, 0x0015) }, /* W-IE-NE-R Plein & Baus GmbH CML Control, Monitoring and Data Logger */ + { USB_DEVICE(0x17A8, 0x0001) }, /* Kamstrup Optical Eye/3-wire */ + { USB_DEVICE(0x17A8, 0x0005) }, /* Kamstrup M-Bus Master MultiPort 250D */ { USB_DEVICE(0x17F4, 0xAAAA) }, /* Wavesense Jazz blood glucose meter */ { USB_DEVICE(0x1843, 0x0200) }, /* Vaisala USB Instrument Cable */ { USB_DEVICE(0x18EF, 0xE00F) }, /* ELV USB-I2C-Interface */ diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 338d082525b..68fa8c7c162 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -788,7 +788,6 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0012, 0xff, 0xff, 0xff), .driver_info = (kernel_ulong_t)&net_intf1_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0013, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0014, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_MF628, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0016, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0017, 0xff, 0xff, 0xff), @@ -803,7 +802,6 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0024, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0025, 0xff, 0xff, 0xff), .driver_info = (kernel_ulong_t)&net_intf1_blacklist }, - /* { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0026, 0xff, 0xff, 0xff) }, */ { 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) }, @@ -828,7 +826,6 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0051, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0052, 0xff, 0xff, 0xff), .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, - /* { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0053, 0xff, 0xff, 0xff) }, */ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0054, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0055, 0xff, 0xff, 0xff), .driver_info = (kernel_ulong_t)&net_intf1_blacklist }, @@ -836,7 +833,6 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0057, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0058, 0xff, 0xff, 0xff), .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0059, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0061, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0062, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0063, 0xff, 0xff, 0xff), @@ -846,7 +842,6 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0066, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0067, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0069, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0070, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0076, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0077, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0078, 0xff, 0xff, 0xff) }, @@ -865,8 +860,6 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0095, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0096, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0097, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0098, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0099, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0104, 0xff, 0xff, 0xff), .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0105, 0xff, 0xff, 0xff) }, @@ -887,28 +880,18 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0143, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0144, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0145, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0146, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0147, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0148, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0149, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0150, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0151, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0152, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0153, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0155, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0156, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0157, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0158, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0159, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0160, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0161, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0162, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0164, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0165, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0168, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0170, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0176, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0178, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1008, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1010, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1012, 0xff, 0xff, 0xff) }, @@ -1083,127 +1066,27 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1298, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1299, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1300, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1401, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1402, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1403, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1404, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1405, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1406, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1407, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1408, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1409, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1410, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1411, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1412, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1413, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1414, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1415, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1416, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1417, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1418, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1419, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1420, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1421, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1422, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1423, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1424, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1425, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1426, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1427, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1428, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1429, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1430, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1431, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1432, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1433, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1434, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1435, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1436, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1437, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1438, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1439, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1440, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1441, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1442, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1443, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1444, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1445, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1446, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1447, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1448, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1449, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1450, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1451, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1452, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1453, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1454, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1455, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1456, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1457, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1458, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1459, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1460, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1461, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1462, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1463, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1464, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1465, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1466, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1467, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1468, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1469, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1470, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1471, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1472, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1473, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1474, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1475, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1476, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1477, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1478, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1479, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1480, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1481, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1482, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1483, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1484, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1485, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1486, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1487, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1488, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1489, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1490, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1491, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1492, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1493, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1494, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1495, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1496, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1497, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1498, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1499, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1500, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1501, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1502, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1503, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1504, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1505, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1506, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1507, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1508, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1509, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1510, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x2002, 0xff, + 0xff, 0xff), .driver_info = (kernel_ulong_t)&zte_k3765_z_blacklist }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x2003, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0014, 0xff, 0xff, 0xff) }, /* ZTE CDMA products */ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0027, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0059, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0060, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0070, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0073, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0094, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0130, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0133, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0141, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x2002, 0xff, - 0xff, 0xff), .driver_info = (kernel_ulong_t)&zte_k3765_z_blacklist }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x2003, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0147, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0152, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0168, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0170, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0176, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0178, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_CDMA_TECH, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_AC8710, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_AC2726, 0xff, 0xff, 0xff) }, diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c index ea8445689c8..21c82b043d1 100644 --- a/drivers/usb/serial/ti_usb_3410_5052.c +++ b/drivers/usb/serial/ti_usb_3410_5052.c @@ -165,7 +165,7 @@ static unsigned int product_5052_count; /* the array dimension is the number of default entries plus */ /* TI_EXTRA_VID_PID_COUNT user defined entries plus 1 terminating */ /* null entry */ -static struct usb_device_id ti_id_table_3410[13+TI_EXTRA_VID_PID_COUNT+1] = { +static struct usb_device_id ti_id_table_3410[14+TI_EXTRA_VID_PID_COUNT+1] = { { USB_DEVICE(TI_VENDOR_ID, TI_3410_PRODUCT_ID) }, { USB_DEVICE(TI_VENDOR_ID, TI_3410_EZ430_ID) }, { USB_DEVICE(MTS_VENDOR_ID, MTS_GSM_NO_FW_PRODUCT_ID) }, @@ -179,6 +179,7 @@ static struct usb_device_id ti_id_table_3410[13+TI_EXTRA_VID_PID_COUNT+1] = { { USB_DEVICE(IBM_VENDOR_ID, IBM_4543_PRODUCT_ID) }, { USB_DEVICE(IBM_VENDOR_ID, IBM_454B_PRODUCT_ID) }, { USB_DEVICE(IBM_VENDOR_ID, IBM_454C_PRODUCT_ID) }, + { USB_DEVICE(ABBOTT_VENDOR_ID, ABBOTT_PRODUCT_ID) }, }; static struct usb_device_id ti_id_table_5052[5+TI_EXTRA_VID_PID_COUNT+1] = { @@ -188,7 +189,7 @@ static struct usb_device_id ti_id_table_5052[5+TI_EXTRA_VID_PID_COUNT+1] = { { USB_DEVICE(TI_VENDOR_ID, TI_5052_FIRMWARE_PRODUCT_ID) }, }; -static struct usb_device_id ti_id_table_combined[17+2*TI_EXTRA_VID_PID_COUNT+1] = { +static struct usb_device_id ti_id_table_combined[18+2*TI_EXTRA_VID_PID_COUNT+1] = { { USB_DEVICE(TI_VENDOR_ID, TI_3410_PRODUCT_ID) }, { USB_DEVICE(TI_VENDOR_ID, TI_3410_EZ430_ID) }, { USB_DEVICE(MTS_VENDOR_ID, MTS_GSM_NO_FW_PRODUCT_ID) }, @@ -206,6 +207,7 @@ static struct usb_device_id ti_id_table_combined[17+2*TI_EXTRA_VID_PID_COUNT+1] { USB_DEVICE(IBM_VENDOR_ID, IBM_4543_PRODUCT_ID) }, { USB_DEVICE(IBM_VENDOR_ID, IBM_454B_PRODUCT_ID) }, { USB_DEVICE(IBM_VENDOR_ID, IBM_454C_PRODUCT_ID) }, + { USB_DEVICE(ABBOTT_VENDOR_ID, ABBOTT_PRODUCT_ID) }, { } }; diff --git a/drivers/usb/serial/ti_usb_3410_5052.h b/drivers/usb/serial/ti_usb_3410_5052.h index 2aac1953993..f140f1b9d5c 100644 --- a/drivers/usb/serial/ti_usb_3410_5052.h +++ b/drivers/usb/serial/ti_usb_3410_5052.h @@ -49,6 +49,10 @@ #define MTS_MT9234ZBA_PRODUCT_ID 0xF115 #define MTS_MT9234ZBAOLD_PRODUCT_ID 0x0319 +/* Abbott Diabetics vendor and product ids */ +#define ABBOTT_VENDOR_ID 0x1a61 +#define ABBOTT_PRODUCT_ID 0x3410 + /* Commands */ #define TI_GET_VERSION 0x01 #define TI_GET_PORT_STATUS 0x02 diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c index 9e069efeefe..db51ba16dc0 100644 --- a/drivers/usb/storage/usb.c +++ b/drivers/usb/storage/usb.c @@ -788,15 +788,19 @@ static void quiesce_and_remove_host(struct us_data *us) struct Scsi_Host *host = us_to_host(us); /* If the device is really gone, cut short reset delays */ - if (us->pusb_dev->state == USB_STATE_NOTATTACHED) + if (us->pusb_dev->state == USB_STATE_NOTATTACHED) { set_bit(US_FLIDX_DISCONNECTING, &us->dflags); + wake_up(&us->delay_wait); + } - /* Prevent SCSI-scanning (if it hasn't started yet) - * and wait for the SCSI-scanning thread to stop. + /* Prevent SCSI scanning (if it hasn't started yet) + * or wait for the SCSI-scanning routine to stop. */ - set_bit(US_FLIDX_DONT_SCAN, &us->dflags); - wake_up(&us->delay_wait); - wait_for_completion(&us->scanning_done); + cancel_delayed_work_sync(&us->scan_dwork); + + /* Balance autopm calls if scanning was cancelled */ + if (test_bit(US_FLIDX_SCAN_PENDING, &us->dflags)) + usb_autopm_put_interface_no_suspend(us->pusb_intf); /* Removing the host will perform an orderly shutdown: caches * synchronized, disks spun down, etc. @@ -823,52 +827,28 @@ static void release_everything(struct us_data *us) scsi_host_put(us_to_host(us)); } -/* Thread to carry out delayed SCSI-device scanning */ -static int usb_stor_scan_thread(void * __us) +/* Delayed-work routine to carry out SCSI-device scanning */ +static void usb_stor_scan_dwork(struct work_struct *work) { - struct us_data *us = (struct us_data *)__us; + struct us_data *us = container_of(work, struct us_data, + scan_dwork.work); struct device *dev = &us->pusb_intf->dev; - dev_dbg(dev, "device found\n"); + dev_dbg(dev, "starting scan\n"); - set_freezable_with_signal(); - /* - * Wait for the timeout to expire or for a disconnect - * - * We can't freeze in this thread or we risk causing khubd to - * fail to freeze, but we can't be non-freezable either. Nor can - * khubd freeze while waiting for scanning to complete as it may - * hold the device lock, causing a hang when suspending devices. - * So we request a fake signal when freezing and use - * interruptible sleep to kick us out of our wait early when - * freezing happens. - */ - if (delay_use > 0) { - dev_dbg(dev, "waiting for device to settle " - "before scanning\n"); - wait_event_interruptible_timeout(us->delay_wait, - test_bit(US_FLIDX_DONT_SCAN, &us->dflags), - delay_use * HZ); + /* For bulk-only devices, determine the max LUN value */ + if (us->protocol == USB_PR_BULK && !(us->fflags & US_FL_SINGLE_LUN)) { + mutex_lock(&us->dev_mutex); + us->max_lun = usb_stor_Bulk_max_lun(us); + mutex_unlock(&us->dev_mutex); } + scsi_scan_host(us_to_host(us)); + dev_dbg(dev, "scan complete\n"); - /* If the device is still connected, perform the scanning */ - if (!test_bit(US_FLIDX_DONT_SCAN, &us->dflags)) { - - /* For bulk-only devices, determine the max LUN value */ - if (us->protocol == USB_PR_BULK && - !(us->fflags & US_FL_SINGLE_LUN)) { - mutex_lock(&us->dev_mutex); - us->max_lun = usb_stor_Bulk_max_lun(us); - mutex_unlock(&us->dev_mutex); - } - scsi_scan_host(us_to_host(us)); - dev_dbg(dev, "scan complete\n"); - - /* Should we unbind if no devices were detected? */ - } + /* Should we unbind if no devices were detected? */ usb_autopm_put_interface(us->pusb_intf); - complete_and_exit(&us->scanning_done, 0); + clear_bit(US_FLIDX_SCAN_PENDING, &us->dflags); } static unsigned int usb_stor_sg_tablesize(struct usb_interface *intf) @@ -915,7 +895,7 @@ int usb_stor_probe1(struct us_data **pus, init_completion(&us->cmnd_ready); init_completion(&(us->notify)); init_waitqueue_head(&us->delay_wait); - init_completion(&us->scanning_done); + INIT_DELAYED_WORK(&us->scan_dwork, usb_stor_scan_dwork); /* Associate the us_data structure with the USB device */ result = associate_dev(us, intf); @@ -946,7 +926,6 @@ EXPORT_SYMBOL_GPL(usb_stor_probe1); /* Second part of general USB mass-storage probing */ int usb_stor_probe2(struct us_data *us) { - struct task_struct *th; int result; struct device *dev = &us->pusb_intf->dev; @@ -987,20 +966,14 @@ int usb_stor_probe2(struct us_data *us) goto BadDevice; } - /* Start up the thread for delayed SCSI-device scanning */ - th = kthread_create(usb_stor_scan_thread, us, "usb-stor-scan"); - if (IS_ERR(th)) { - dev_warn(dev, - "Unable to start the device-scanning thread\n"); - complete(&us->scanning_done); - quiesce_and_remove_host(us); - result = PTR_ERR(th); - goto BadDevice; - } - + /* Submit the delayed_work for SCSI-device scanning */ usb_autopm_get_interface_no_resume(us->pusb_intf); - wake_up_process(th); + set_bit(US_FLIDX_SCAN_PENDING, &us->dflags); + if (delay_use > 0) + dev_dbg(dev, "waiting for device to settle before scanning\n"); + queue_delayed_work(system_freezable_wq, &us->scan_dwork, + delay_use * HZ); return 0; /* We come here if there are any problems */ diff --git a/drivers/usb/storage/usb.h b/drivers/usb/storage/usb.h index 7b0f2113632..75f70f04f37 100644 --- a/drivers/usb/storage/usb.h +++ b/drivers/usb/storage/usb.h @@ -47,6 +47,7 @@ #include #include #include +#include #include struct us_data; @@ -72,7 +73,7 @@ struct us_unusual_dev { #define US_FLIDX_DISCONNECTING 3 /* disconnect in progress */ #define US_FLIDX_RESETTING 4 /* device reset in progress */ #define US_FLIDX_TIMED_OUT 5 /* SCSI midlayer timed out */ -#define US_FLIDX_DONT_SCAN 6 /* don't scan (disconnect) */ +#define US_FLIDX_SCAN_PENDING 6 /* scanning not yet done */ #define US_FLIDX_REDO_READ10 7 /* redo READ(10) command */ #define US_FLIDX_READ10_WORKED 8 /* previous READ(10) succeeded */ @@ -147,8 +148,8 @@ struct us_data { /* mutual exclusion and synchronization structures */ struct completion cmnd_ready; /* to sleep thread on */ struct completion notify; /* thread begin/end */ - wait_queue_head_t delay_wait; /* wait during scan, reset */ - struct completion scanning_done; /* wait for scan thread */ + wait_queue_head_t delay_wait; /* wait during reset */ + struct delayed_work scan_dwork; /* for async scanning */ /* subdriver information */ void *extra; /* Any extra data */ diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c index e3562f2a59d..2717329386d 100644 --- a/fs/ecryptfs/inode.c +++ b/fs/ecryptfs/inode.c @@ -1119,6 +1119,8 @@ ecryptfs_setxattr(struct dentry *dentry, const char *name, const void *value, } rc = vfs_setxattr(lower_dentry, name, value, size, flags); + if (!rc) + fsstack_copy_attr_all(dentry->d_inode, lower_dentry->d_inode); out: return rc; } diff --git a/fs/eventpoll.c b/fs/eventpoll.c index 2acaf60f4e4..6879d0c5cb5 100644 --- a/fs/eventpoll.c +++ b/fs/eventpoll.c @@ -197,6 +197,12 @@ struct eventpoll { /* The user that created the eventpoll descriptor */ struct user_struct *user; + + struct file *file; + + /* used to optimize loop detection check */ + int visited; + struct list_head visited_list_link; }; /* Wait structure used by the poll hooks */ @@ -255,6 +261,15 @@ static struct kmem_cache *epi_cache __read_mostly; /* Slab cache used to allocate "struct eppoll_entry" */ static struct kmem_cache *pwq_cache __read_mostly; +/* Visited nodes during ep_loop_check(), so we can unset them when we finish */ +static LIST_HEAD(visited_list); + +/* + * List of files with newly added links, where we may need to limit the number + * of emanating paths. Protected by the epmutex. + */ +static LIST_HEAD(tfile_check_list); + #ifdef CONFIG_SYSCTL #include @@ -276,6 +291,12 @@ ctl_table epoll_table[] = { }; #endif /* CONFIG_SYSCTL */ +static const struct file_operations eventpoll_fops; + +static inline int is_file_epoll(struct file *f) +{ + return f->f_op == &eventpoll_fops; +} /* Setup the structure that is used as key for the RB tree */ static inline void ep_set_ffd(struct epoll_filefd *ffd, @@ -299,6 +320,11 @@ static inline int ep_is_linked(struct list_head *p) return !list_empty(p); } +static inline struct eppoll_entry *ep_pwq_from_wait(wait_queue_t *p) +{ + return container_of(p, struct eppoll_entry, wait); +} + /* Get the "struct epitem" from a wait queue pointer */ static inline struct epitem *ep_item_from_wait(wait_queue_t *p) { @@ -446,6 +472,18 @@ static void ep_poll_safewake(wait_queue_head_t *wq) put_cpu(); } +static void ep_remove_wait_queue(struct eppoll_entry *pwq) +{ + wait_queue_head_t *whead; + + rcu_read_lock(); + /* If it is cleared by POLLFREE, it should be rcu-safe */ + whead = rcu_dereference(pwq->whead); + if (whead) + remove_wait_queue(whead, &pwq->wait); + rcu_read_unlock(); +} + /* * This function unregisters poll callbacks from the associated file * descriptor. Must be called with "mtx" held (or "epmutex" if called from @@ -460,7 +498,7 @@ static void ep_unregister_pollwait(struct eventpoll *ep, struct epitem *epi) pwq = list_first_entry(lsthead, struct eppoll_entry, llink); list_del(&pwq->llink); - remove_wait_queue(pwq->whead, &pwq->wait); + ep_remove_wait_queue(pwq); kmem_cache_free(pwq_cache, pwq); } } @@ -711,12 +749,6 @@ static const struct file_operations eventpoll_fops = { .llseek = noop_llseek, }; -/* Fast test to see if the file is an evenpoll file */ -static inline int is_file_epoll(struct file *f) -{ - return f->f_op == &eventpoll_fops; -} - /* * This is called from eventpoll_release() to unlink files from the eventpoll * interface. We need to have this facility to cleanup correctly files that are @@ -827,6 +859,17 @@ static int ep_poll_callback(wait_queue_t *wait, unsigned mode, int sync, void *k struct epitem *epi = ep_item_from_wait(wait); struct eventpoll *ep = epi->ep; + if ((unsigned long)key & POLLFREE) { + ep_pwq_from_wait(wait)->whead = NULL; + /* + * whead = NULL above can race with ep_remove_wait_queue() + * which can do another remove_wait_queue() after us, so we + * can't use __remove_wait_queue(). whead->lock is held by + * the caller. + */ + list_del_init(&wait->task_list); + } + spin_lock_irqsave(&ep->lock, flags); /* @@ -926,6 +969,99 @@ static void ep_rbtree_insert(struct eventpoll *ep, struct epitem *epi) rb_insert_color(&epi->rbn, &ep->rbr); } + + +#define PATH_ARR_SIZE 5 +/* + * These are the number paths of length 1 to 5, that we are allowing to emanate + * from a single file of interest. For example, we allow 1000 paths of length + * 1, to emanate from each file of interest. This essentially represents the + * potential wakeup paths, which need to be limited in order to avoid massive + * uncontrolled wakeup storms. The common use case should be a single ep which + * is connected to n file sources. In this case each file source has 1 path + * of length 1. Thus, the numbers below should be more than sufficient. These + * path limits are enforced during an EPOLL_CTL_ADD operation, since a modify + * and delete can't add additional paths. Protected by the epmutex. + */ +static const int path_limits[PATH_ARR_SIZE] = { 1000, 500, 100, 50, 10 }; +static int path_count[PATH_ARR_SIZE]; + +static int path_count_inc(int nests) +{ + if (++path_count[nests] > path_limits[nests]) + return -1; + return 0; +} + +static void path_count_init(void) +{ + int i; + + for (i = 0; i < PATH_ARR_SIZE; i++) + path_count[i] = 0; +} + +static int reverse_path_check_proc(void *priv, void *cookie, int call_nests) +{ + int error = 0; + struct file *file = priv; + struct file *child_file; + struct epitem *epi; + + list_for_each_entry(epi, &file->f_ep_links, fllink) { + child_file = epi->ep->file; + if (is_file_epoll(child_file)) { + if (list_empty(&child_file->f_ep_links)) { + if (path_count_inc(call_nests)) { + error = -1; + break; + } + } else { + error = ep_call_nested(&poll_loop_ncalls, + EP_MAX_NESTS, + reverse_path_check_proc, + child_file, child_file, + current); + } + if (error != 0) + break; + } else { + printk(KERN_ERR "reverse_path_check_proc: " + "file is not an ep!\n"); + } + } + return error; +} + +/** + * reverse_path_check - The tfile_check_list is list of file *, which have + * links that are proposed to be newly added. We need to + * make sure that those added links don't add too many + * paths such that we will spend all our time waking up + * eventpoll objects. + * + * Returns: Returns zero if the proposed links don't create too many paths, + * -1 otherwise. + */ +static int reverse_path_check(void) +{ + int length = 0; + int error = 0; + struct file *current_file; + + /* let's call this for all tfiles */ + list_for_each_entry(current_file, &tfile_check_list, f_tfile_llink) { + length++; + path_count_init(); + error = ep_call_nested(&poll_loop_ncalls, EP_MAX_NESTS, + reverse_path_check_proc, current_file, + current_file, current); + if (error) + break; + } + return error; +} + /* * Must be called with "mtx" held. */ @@ -987,6 +1123,11 @@ static int ep_insert(struct eventpoll *ep, struct epoll_event *event, */ ep_rbtree_insert(ep, epi); + /* now check if we've created too many backpaths */ + error = -EINVAL; + if (reverse_path_check()) + goto error_remove_epi; + /* We have to drop the new item inside our item list to keep track of it */ spin_lock_irqsave(&ep->lock, flags); @@ -1011,6 +1152,14 @@ static int ep_insert(struct eventpoll *ep, struct epoll_event *event, return 0; +error_remove_epi: + spin_lock(&tfile->f_lock); + if (ep_is_linked(&epi->fllink)) + list_del_init(&epi->fllink); + spin_unlock(&tfile->f_lock); + + rb_erase(&epi->rbn, &ep->rbr); + error_unregister: ep_unregister_pollwait(ep, epi); @@ -1275,18 +1424,36 @@ static int ep_loop_check_proc(void *priv, void *cookie, int call_nests) int error = 0; struct file *file = priv; struct eventpoll *ep = file->private_data; + struct eventpoll *ep_tovisit; struct rb_node *rbp; struct epitem *epi; mutex_lock_nested(&ep->mtx, call_nests + 1); + ep->visited = 1; + list_add(&ep->visited_list_link, &visited_list); for (rbp = rb_first(&ep->rbr); rbp; rbp = rb_next(rbp)) { epi = rb_entry(rbp, struct epitem, rbn); if (unlikely(is_file_epoll(epi->ffd.file))) { + ep_tovisit = epi->ffd.file->private_data; + if (ep_tovisit->visited) + continue; error = ep_call_nested(&poll_loop_ncalls, EP_MAX_NESTS, - ep_loop_check_proc, epi->ffd.file, - epi->ffd.file->private_data, current); + ep_loop_check_proc, epi->ffd.file, + ep_tovisit, current); if (error != 0) break; + } else { + /* + * If we've reached a file that is not associated with + * an ep, then we need to check if the newly added + * links are going to add too many wakeup paths. We do + * this by adding it to the tfile_check_list, if it's + * not already there, and calling reverse_path_check() + * during ep_insert(). + */ + if (list_empty(&epi->ffd.file->f_tfile_llink)) + list_add(&epi->ffd.file->f_tfile_llink, + &tfile_check_list); } } mutex_unlock(&ep->mtx); @@ -1307,8 +1474,31 @@ static int ep_loop_check_proc(void *priv, void *cookie, int call_nests) */ static int ep_loop_check(struct eventpoll *ep, struct file *file) { - return ep_call_nested(&poll_loop_ncalls, EP_MAX_NESTS, + int ret; + struct eventpoll *ep_cur, *ep_next; + + ret = ep_call_nested(&poll_loop_ncalls, EP_MAX_NESTS, ep_loop_check_proc, file, ep, current); + /* clear visited list */ + list_for_each_entry_safe(ep_cur, ep_next, &visited_list, + visited_list_link) { + ep_cur->visited = 0; + list_del(&ep_cur->visited_list_link); + } + return ret; +} + +static void clear_tfile_check_list(void) +{ + struct file *file; + + /* first clear the tfile_check_list */ + while (!list_empty(&tfile_check_list)) { + file = list_first_entry(&tfile_check_list, struct file, + f_tfile_llink); + list_del_init(&file->f_tfile_llink); + } + INIT_LIST_HEAD(&tfile_check_list); } /* @@ -1316,8 +1506,9 @@ static int ep_loop_check(struct eventpoll *ep, struct file *file) */ SYSCALL_DEFINE1(epoll_create1, int, flags) { - int error; + int error, fd; struct eventpoll *ep = NULL; + struct file *file; /* Check the EPOLL_* constant for consistency. */ BUILD_BUG_ON(EPOLL_CLOEXEC != O_CLOEXEC); @@ -1334,11 +1525,25 @@ SYSCALL_DEFINE1(epoll_create1, int, flags) * Creates all the items needed to setup an eventpoll file. That is, * a file structure and a free file descriptor. */ - error = anon_inode_getfd("[eventpoll]", &eventpoll_fops, ep, + fd = get_unused_fd_flags(O_RDWR | (flags & O_CLOEXEC)); + if (fd < 0) { + error = fd; + goto out_free_ep; + } + file = anon_inode_getfile("[eventpoll]", &eventpoll_fops, ep, O_RDWR | (flags & O_CLOEXEC)); - if (error < 0) - ep_free(ep); - + if (IS_ERR(file)) { + error = PTR_ERR(file); + goto out_free_fd; + } + fd_install(fd, file); + ep->file = file; + return fd; + +out_free_fd: + put_unused_fd(fd); +out_free_ep: + ep_free(ep); return error; } @@ -1404,21 +1609,27 @@ SYSCALL_DEFINE4(epoll_ctl, int, epfd, int, op, int, fd, /* * When we insert an epoll file descriptor, inside another epoll file * descriptor, there is the change of creating closed loops, which are - * better be handled here, than in more critical paths. + * better be handled here, than in more critical paths. While we are + * checking for loops we also determine the list of files reachable + * and hang them on the tfile_check_list, so we can check that we + * haven't created too many possible wakeup paths. * - * We hold epmutex across the loop check and the insert in this case, in - * order to prevent two separate inserts from racing and each doing the - * insert "at the same time" such that ep_loop_check passes on both - * before either one does the insert, thereby creating a cycle. + * We need to hold the epmutex across both ep_insert and ep_remove + * b/c we want to make sure we are looking at a coherent view of + * epoll network. */ - if (unlikely(is_file_epoll(tfile) && op == EPOLL_CTL_ADD)) { + if (op == EPOLL_CTL_ADD || op == EPOLL_CTL_DEL) { mutex_lock(&epmutex); did_lock_epmutex = 1; - error = -ELOOP; - if (ep_loop_check(ep, tfile) != 0) - goto error_tgt_fput; } - + if (op == EPOLL_CTL_ADD) { + if (is_file_epoll(tfile)) { + error = -ELOOP; + if (ep_loop_check(ep, tfile) != 0) + goto error_tgt_fput; + } else + list_add(&tfile->f_tfile_llink, &tfile_check_list); + } mutex_lock_nested(&ep->mtx, 0); @@ -1437,6 +1648,7 @@ SYSCALL_DEFINE4(epoll_ctl, int, epfd, int, op, int, fd, error = ep_insert(ep, &epds, tfile, fd); } else error = -EEXIST; + clear_tfile_check_list(); break; case EPOLL_CTL_DEL: if (epi) @@ -1455,7 +1667,7 @@ SYSCALL_DEFINE4(epoll_ctl, int, epfd, int, op, int, fd, mutex_unlock(&ep->mtx); error_tgt_fput: - if (unlikely(did_lock_epmutex)) + if (did_lock_epmutex) mutex_unlock(&epmutex); fput(tfile); diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index 87822a32709..9e7e9a52416 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -1065,6 +1065,8 @@ void nfs4_schedule_stateid_recovery(const struct nfs_server *server, struct nfs4 { struct nfs_client *clp = server->nfs_client; + if (test_and_clear_bit(NFS_DELEGATED_STATE, &state->flags)) + nfs_async_inode_return_delegation(state->inode, &state->stateid); nfs4_state_mark_reclaim_nograce(clp, state); nfs4_schedule_state_manager(clp); } diff --git a/fs/signalfd.c b/fs/signalfd.c index 492465b451d..7ae2a574cb2 100644 --- a/fs/signalfd.c +++ b/fs/signalfd.c @@ -30,6 +30,21 @@ #include #include +void signalfd_cleanup(struct sighand_struct *sighand) +{ + wait_queue_head_t *wqh = &sighand->signalfd_wqh; + /* + * The lockless check can race with remove_wait_queue() in progress, + * but in this case its caller should run under rcu_read_lock() and + * sighand_cachep is SLAB_DESTROY_BY_RCU, we can safely return. + */ + if (likely(!waitqueue_active(wqh))) + return; + + /* wait_queue_t->func(POLLFREE) should do remove_wait_queue() */ + wake_up_poll(wqh, POLLHUP | POLLFREE); +} + struct signalfd_ctx { sigset_t sigmask; }; diff --git a/include/asm-generic/poll.h b/include/asm-generic/poll.h index 44bce836d35..9ce7f44aebd 100644 --- a/include/asm-generic/poll.h +++ b/include/asm-generic/poll.h @@ -28,6 +28,8 @@ #define POLLRDHUP 0x2000 #endif +#define POLLFREE 0x4000 /* currently only for epoll */ + struct pollfd { int fd; short events; diff --git a/include/linux/eventpoll.h b/include/linux/eventpoll.h index f362733186a..657ab55beda 100644 --- a/include/linux/eventpoll.h +++ b/include/linux/eventpoll.h @@ -61,6 +61,7 @@ struct file; static inline void eventpoll_init_file(struct file *file) { INIT_LIST_HEAD(&file->f_ep_links); + INIT_LIST_HEAD(&file->f_tfile_llink); } diff --git a/include/linux/fs.h b/include/linux/fs.h index 7b17db7c5a6..d8ecb015ff8 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -969,6 +969,7 @@ struct file { #ifdef CONFIG_EPOLL /* Used by fs/eventpoll.c to link all the hooks to this file */ struct list_head f_ep_links; + struct list_head f_tfile_llink; #endif /* #ifdef CONFIG_EPOLL */ struct address_space *f_mapping; #ifdef CONFIG_DEBUG_WRITECOUNT diff --git a/include/linux/signalfd.h b/include/linux/signalfd.h index 3ff4961da9b..247399b2979 100644 --- a/include/linux/signalfd.h +++ b/include/linux/signalfd.h @@ -61,13 +61,16 @@ static inline void signalfd_notify(struct task_struct *tsk, int sig) wake_up(&tsk->sighand->signalfd_wqh); } +extern void signalfd_cleanup(struct sighand_struct *sighand); + #else /* CONFIG_SIGNALFD */ static inline void signalfd_notify(struct task_struct *tsk, int sig) { } +static inline void signalfd_cleanup(struct sighand_struct *sighand) { } + #endif /* CONFIG_SIGNALFD */ #endif /* __KERNEL__ */ #endif /* _LINUX_SIGNALFD_H */ - diff --git a/include/linux/usb/ch11.h b/include/linux/usb/ch11.h index 4ebaf082417..1eb735b53fc 100644 --- a/include/linux/usb/ch11.h +++ b/include/linux/usb/ch11.h @@ -62,12 +62,6 @@ #define USB_PORT_FEAT_TEST 21 #define USB_PORT_FEAT_INDICATOR 22 #define USB_PORT_FEAT_C_PORT_L1 23 -#define USB_PORT_FEAT_C_PORT_LINK_STATE 25 -#define USB_PORT_FEAT_C_PORT_CONFIG_ERROR 26 -#define USB_PORT_FEAT_PORT_REMOTE_WAKE_MASK 27 -#define USB_PORT_FEAT_BH_PORT_RESET 28 -#define USB_PORT_FEAT_C_BH_PORT_RESET 29 -#define USB_PORT_FEAT_FORCE_LINKPM_ACCEPT 30 /* * Port feature selectors added by USB 3.0 spec. @@ -76,8 +70,8 @@ #define USB_PORT_FEAT_LINK_STATE 5 #define USB_PORT_FEAT_U1_TIMEOUT 23 #define USB_PORT_FEAT_U2_TIMEOUT 24 -#define USB_PORT_FEAT_C_LINK_STATE 25 -#define USB_PORT_FEAT_C_CONFIG_ERR 26 +#define USB_PORT_FEAT_C_PORT_LINK_STATE 25 +#define USB_PORT_FEAT_C_PORT_CONFIG_ERROR 26 #define USB_PORT_FEAT_REMOTE_WAKE_MASK 27 #define USB_PORT_FEAT_BH_PORT_RESET 28 #define USB_PORT_FEAT_C_BH_PORT_RESET 29 diff --git a/include/net/flow.h b/include/net/flow.h index 32359fdc7e7..e37cfda9c0f 100644 --- a/include/net/flow.h +++ b/include/net/flow.h @@ -90,6 +90,16 @@ static inline void flowi4_init_output(struct flowi4 *fl4, int oif, fl4->fl4_dport = dport; fl4->fl4_sport = sport; } + +/* Reset some input parameters after previous lookup */ +static inline void flowi4_update_output(struct flowi4 *fl4, int oif, __u8 tos, + __be32 daddr, __be32 saddr) +{ + fl4->flowi4_oif = oif; + fl4->flowi4_tos = tos; + fl4->daddr = daddr; + fl4->saddr = saddr; +} struct flowi6 { diff --git a/include/net/inet_sock.h b/include/net/inet_sock.h index caaff5f5f39..14dd9c78992 100644 --- a/include/net/inet_sock.h +++ b/include/net/inet_sock.h @@ -31,6 +31,7 @@ /** struct ip_options - IP Options * * @faddr - Saved first hop address + * @nexthop - Saved nexthop address in LSRR and SSRR * @is_data - Options in __data, rather than skb * @is_strictroute - Strict source route * @srr_is_hit - Packet destination addr was our one @@ -41,6 +42,7 @@ */ struct ip_options { __be32 faddr; + __be32 nexthop; unsigned char optlen; unsigned char srr; unsigned char rr; diff --git a/include/net/route.h b/include/net/route.h index db7b3432f07..5d7aae4ab2e 100644 --- a/include/net/route.h +++ b/include/net/route.h @@ -270,6 +270,7 @@ static inline struct rtable *ip_route_connect(struct flowi4 *fl4, if (IS_ERR(rt)) return rt; ip_rt_put(rt); + flowi4_update_output(fl4, oif, tos, fl4->daddr, fl4->saddr); } security_sk_classify_flow(sk, flowi4_to_flowi(fl4)); return ip_route_output_flow(net, fl4, sk); @@ -284,6 +285,9 @@ static inline struct rtable *ip_route_newports(struct flowi4 *fl4, struct rtable fl4->fl4_dport = dport; fl4->fl4_sport = sport; ip_rt_put(rt); + flowi4_update_output(fl4, sk->sk_bound_dev_if, + RT_CONN_FLAGS(sk), fl4->daddr, + fl4->saddr); security_sk_classify_flow(sk, flowi4_to_flowi(fl4)); return ip_route_output_flow(sock_net(sk), fl4, sk); } diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h index b931f021d7a..f1fbe2d5e05 100644 --- a/include/net/sch_generic.h +++ b/include/net/sch_generic.h @@ -219,9 +219,16 @@ struct tcf_proto { struct qdisc_skb_cb { unsigned int pkt_len; - long data[]; + unsigned char data[24]; }; +static inline void qdisc_cb_private_validate(const struct sk_buff *skb, int sz) +{ + struct qdisc_skb_cb *qcb; + BUILD_BUG_ON(sizeof(skb->cb) < sizeof(unsigned int) + sz); + BUILD_BUG_ON(sizeof(qcb->data) < sz); +} + static inline int qdisc_qlen(struct Qdisc *q) { return q->q.qlen; diff --git a/kernel/fork.c b/kernel/fork.c index 4e4b3446511..06909a9da04 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -67,6 +67,7 @@ #include #include #include +#include #include #include @@ -933,8 +934,10 @@ static int copy_sighand(unsigned long clone_flags, struct task_struct *tsk) void __cleanup_sighand(struct sighand_struct *sighand) { - if (atomic_dec_and_test(&sighand->count)) + if (atomic_dec_and_test(&sighand->count)) { + signalfd_cleanup(sighand); kmem_cache_free(sighand_cachep, sighand); + } } diff --git a/kernel/irq/autoprobe.c b/kernel/irq/autoprobe.c index 342d8f44e40..0119b9d467a 100644 --- a/kernel/irq/autoprobe.c +++ b/kernel/irq/autoprobe.c @@ -53,7 +53,7 @@ unsigned long probe_irq_on(void) if (desc->irq_data.chip->irq_set_type) desc->irq_data.chip->irq_set_type(&desc->irq_data, IRQ_TYPE_PROBE); - irq_startup(desc); + irq_startup(desc, false); } raw_spin_unlock_irq(&desc->lock); } @@ -70,7 +70,7 @@ unsigned long probe_irq_on(void) raw_spin_lock_irq(&desc->lock); if (!desc->action && irq_settings_can_probe(desc)) { desc->istate |= IRQS_AUTODETECT | IRQS_WAITING; - if (irq_startup(desc)) + if (irq_startup(desc, false)) desc->istate |= IRQS_PENDING; } raw_spin_unlock_irq(&desc->lock); diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c index fe1f47a4170..c22bace7ad9 100644 --- a/kernel/irq/chip.c +++ b/kernel/irq/chip.c @@ -157,19 +157,22 @@ static void irq_state_set_masked(struct irq_desc *desc) irqd_set(&desc->irq_data, IRQD_IRQ_MASKED); } -int irq_startup(struct irq_desc *desc) +int irq_startup(struct irq_desc *desc, bool resend) { + int ret = 0; + irq_state_clr_disabled(desc); desc->depth = 0; if (desc->irq_data.chip->irq_startup) { - int ret = desc->irq_data.chip->irq_startup(&desc->irq_data); + ret = desc->irq_data.chip->irq_startup(&desc->irq_data); irq_state_clr_masked(desc); - return ret; + } else { + irq_enable(desc); } - - irq_enable(desc); - return 0; + if (resend) + check_irq_resend(desc, desc->irq_data.irq); + return ret; } void irq_shutdown(struct irq_desc *desc) @@ -338,6 +341,24 @@ handle_simple_irq(unsigned int irq, struct irq_desc *desc) } EXPORT_SYMBOL_GPL(handle_simple_irq); +/* + * Called unconditionally from handle_level_irq() and only for oneshot + * interrupts from handle_fasteoi_irq() + */ +static void cond_unmask_irq(struct irq_desc *desc) +{ + /* + * We need to unmask in the following cases: + * - Standard level irq (IRQF_ONESHOT is not set) + * - Oneshot irq which did not wake the thread (caused by a + * spurious interrupt or a primary handler handling it + * completely). + */ + if (!irqd_irq_disabled(&desc->irq_data) && + irqd_irq_masked(&desc->irq_data) && !desc->threads_oneshot) + unmask_irq(desc); +} + /** * handle_level_irq - Level type irq handler * @irq: the interrupt number @@ -370,8 +391,8 @@ handle_level_irq(unsigned int irq, struct irq_desc *desc) handle_irq_event(desc); - if (!irqd_irq_disabled(&desc->irq_data) && !(desc->istate & IRQS_ONESHOT)) - unmask_irq(desc); + cond_unmask_irq(desc); + out_unlock: raw_spin_unlock(&desc->lock); } @@ -426,6 +447,9 @@ handle_fasteoi_irq(unsigned int irq, struct irq_desc *desc) preflow_handler(desc); handle_irq_event(desc); + if (desc->istate & IRQS_ONESHOT) + cond_unmask_irq(desc); + out_eoi: desc->irq_data.chip->irq_eoi(&desc->irq_data); out_unlock: @@ -634,7 +658,7 @@ __irq_set_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained, irq_settings_set_noprobe(desc); irq_settings_set_norequest(desc); irq_settings_set_nothread(desc); - irq_startup(desc); + irq_startup(desc, true); } out: irq_put_desc_busunlock(desc, flags); diff --git a/kernel/irq/internals.h b/kernel/irq/internals.h index a73dd6c7372..e1a8b646c9f 100644 --- a/kernel/irq/internals.h +++ b/kernel/irq/internals.h @@ -67,7 +67,7 @@ extern int __irq_set_trigger(struct irq_desc *desc, unsigned int irq, extern void __disable_irq(struct irq_desc *desc, unsigned int irq, bool susp); extern void __enable_irq(struct irq_desc *desc, unsigned int irq, bool resume); -extern int irq_startup(struct irq_desc *desc); +extern int irq_startup(struct irq_desc *desc, bool resend); extern void irq_shutdown(struct irq_desc *desc); extern void irq_enable(struct irq_desc *desc); extern void irq_disable(struct irq_desc *desc); diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c index 0097e8952a5..07def64b284 100644 --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c @@ -1050,7 +1050,7 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new) desc->istate |= IRQS_ONESHOT; if (irq_settings_can_autoenable(desc)) - irq_startup(desc); + irq_startup(desc, true); else /* Undo nested disables: */ desc->depth = 1; diff --git a/mm/nommu.c b/mm/nommu.c index 9edc897a397..839775875e7 100644 --- a/mm/nommu.c +++ b/mm/nommu.c @@ -697,9 +697,11 @@ static void add_vma_to_mm(struct mm_struct *mm, struct vm_area_struct *vma) if (vma->vm_file) { mapping = vma->vm_file->f_mapping; + mutex_lock(&mapping->i_mmap_mutex); flush_dcache_mmap_lock(mapping); vma_prio_tree_insert(vma, &mapping->i_mmap); flush_dcache_mmap_unlock(mapping); + mutex_unlock(&mapping->i_mmap_mutex); } /* add the VMA to the tree */ @@ -761,9 +763,11 @@ static void delete_vma_from_mm(struct vm_area_struct *vma) if (vma->vm_file) { mapping = vma->vm_file->f_mapping; + mutex_lock(&mapping->i_mmap_mutex); flush_dcache_mmap_lock(mapping); vma_prio_tree_remove(vma, &mapping->i_mmap); flush_dcache_mmap_unlock(mapping); + mutex_unlock(&mapping->i_mmap_mutex); } /* remove from the MM's tree and list */ @@ -2061,6 +2065,7 @@ int nommu_shrink_inode_mappings(struct inode *inode, size_t size, high = (size + PAGE_SIZE - 1) >> PAGE_SHIFT; down_write(&nommu_region_sem); + mutex_lock(&inode->i_mapping->i_mmap_mutex); /* search for VMAs that fall within the dead zone */ vma_prio_tree_foreach(vma, &iter, &inode->i_mapping->i_mmap, @@ -2068,6 +2073,7 @@ int nommu_shrink_inode_mappings(struct inode *inode, size_t size, /* found one - only interested if it's shared out of the page * cache */ if (vma->vm_flags & VM_SHARED) { + mutex_unlock(&inode->i_mapping->i_mmap_mutex); up_write(&nommu_region_sem); return -ETXTBSY; /* not quite true, but near enough */ } @@ -2095,6 +2101,7 @@ int nommu_shrink_inode_mappings(struct inode *inode, size_t size, } } + mutex_unlock(&inode->i_mapping->i_mmap_mutex); up_write(&nommu_region_sem); return 0; } diff --git a/net/core/dev.c b/net/core/dev.c index 221679e9a4c..e965d9f2a45 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -3434,14 +3434,20 @@ static inline gro_result_t __napi_gro_receive(struct napi_struct *napi, struct sk_buff *skb) { struct sk_buff *p; + unsigned int maclen = skb->dev->hard_header_len; for (p = napi->gro_list; p; p = p->next) { unsigned long diffs; diffs = (unsigned long)p->dev ^ (unsigned long)skb->dev; diffs |= p->vlan_tci ^ skb->vlan_tci; - diffs |= compare_ether_header(skb_mac_header(p), - skb_gro_mac_header(skb)); + if (maclen == ETH_HLEN) + diffs |= compare_ether_header(skb_mac_header(p), + skb_gro_mac_header(skb)); + else if (!diffs) + diffs = memcmp(skb_mac_header(p), + skb_gro_mac_header(skb), + maclen); NAPI_GRO_CB(p)->same_flow = !diffs; NAPI_GRO_CB(p)->flush = 0; } diff --git a/net/core/netpoll.c b/net/core/netpoll.c index 18d9cbda3a3..05db410fd13 100644 --- a/net/core/netpoll.c +++ b/net/core/netpoll.c @@ -193,7 +193,7 @@ void netpoll_poll_dev(struct net_device *dev) poll_napi(dev); - if (dev->priv_flags & IFF_SLAVE) { + if (dev->flags & IFF_SLAVE) { if (dev->npinfo) { struct net_device *bond_dev = dev->master; struct sk_buff *skb; diff --git a/net/ipv4/arp.c b/net/ipv4/arp.c index 1d5675efcfb..d8f852dbf66 100644 --- a/net/ipv4/arp.c +++ b/net/ipv4/arp.c @@ -906,7 +906,8 @@ static int arp_process(struct sk_buff *skb) if (addr_type == RTN_UNICAST && (arp_fwd_proxy(in_dev, dev, rt) || arp_fwd_pvlan(in_dev, dev, rt, sip, tip) || - pneigh_lookup(&arp_tbl, net, &tip, dev, 0))) { + (rt->dst.dev != dev && + pneigh_lookup(&arp_tbl, net, &tip, dev, 0)))) { n = neigh_event_ns(&arp_tbl, sha, &sip, dev); if (n) neigh_release(n); diff --git a/net/ipv4/ip_forward.c b/net/ipv4/ip_forward.c index 3b34d1c8627..29a07b6c716 100644 --- a/net/ipv4/ip_forward.c +++ b/net/ipv4/ip_forward.c @@ -84,7 +84,7 @@ int ip_forward(struct sk_buff *skb) rt = skb_rtable(skb); - if (opt->is_strictroute && ip_hdr(skb)->daddr != rt->rt_gateway) + if (opt->is_strictroute && opt->nexthop != rt->rt_gateway) goto sr_failed; if (unlikely(skb->len > dst_mtu(&rt->dst) && !skb_is_gso(skb) && diff --git a/net/ipv4/ip_options.c b/net/ipv4/ip_options.c index ec93335901d..42dd1a90ede 100644 --- a/net/ipv4/ip_options.c +++ b/net/ipv4/ip_options.c @@ -568,11 +568,12 @@ void ip_forward_options(struct sk_buff *skb) ) { if (srrptr + 3 > srrspace) break; - if (memcmp(&ip_hdr(skb)->daddr, &optptr[srrptr-1], 4) == 0) + if (memcmp(&opt->nexthop, &optptr[srrptr-1], 4) == 0) break; } if (srrptr + 3 <= srrspace) { opt->is_changed = 1; + ip_hdr(skb)->daddr = opt->nexthop; ip_rt_get_source(&optptr[srrptr-1], skb, rt); optptr[2] = srrptr+4; } else if (net_ratelimit()) @@ -640,6 +641,7 @@ int ip_options_rcv_srr(struct sk_buff *skb) } if (srrptr <= srrspace) { opt->srr_is_hit = 1; + opt->nexthop = nexthop; opt->is_changed = 1; } return 0; diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 1864ca41040..30cb03aa76f 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -1369,11 +1369,41 @@ static void rt_del(unsigned hash, struct rtable *rt) spin_unlock_bh(rt_hash_lock_addr(hash)); } +static int check_peer_redir(struct dst_entry *dst, struct inet_peer *peer) +{ + struct rtable *rt = (struct rtable *) dst; + __be32 orig_gw = rt->rt_gateway; + struct neighbour *n, *old_n; + + dst_confirm(&rt->dst); + + rt->rt_gateway = peer->redirect_learned.a4; + n = __arp_bind_neighbour(&rt->dst, rt->rt_gateway); + if (IS_ERR(n)) + return PTR_ERR(n); + old_n = xchg(&rt->dst._neighbour, n); + if (old_n) + neigh_release(old_n); + if (!n || !(n->nud_state & NUD_VALID)) { + if (n) + neigh_event_send(n, NULL); + rt->rt_gateway = orig_gw; + return -EAGAIN; + } else { + rt->rt_flags |= RTCF_REDIRECTED; + call_netevent_notifiers(NETEVENT_NEIGH_UPDATE, n); + } + return 0; +} + /* called in rcu_read_lock() section */ void ip_rt_redirect(__be32 old_gw, __be32 daddr, __be32 new_gw, __be32 saddr, struct net_device *dev) { + int s, i; struct in_device *in_dev = __in_dev_get_rcu(dev); + __be32 skeys[2] = { saddr, 0 }; + int ikeys[2] = { dev->ifindex, 0 }; struct inet_peer *peer; struct net *net; @@ -1396,13 +1426,43 @@ void ip_rt_redirect(__be32 old_gw, __be32 daddr, __be32 new_gw, goto reject_redirect; } - peer = inet_getpeer_v4(daddr, 1); - if (peer) { - peer->redirect_learned.a4 = new_gw; + for (s = 0; s < 2; s++) { + for (i = 0; i < 2; i++) { + unsigned int hash; + struct rtable __rcu **rthp; + struct rtable *rt; - inet_putpeer(peer); + hash = rt_hash(daddr, skeys[s], ikeys[i], rt_genid(net)); - atomic_inc(&__rt_peer_genid); + rthp = &rt_hash_table[hash].chain; + + while ((rt = rcu_dereference(*rthp)) != NULL) { + rthp = &rt->dst.rt_next; + + if (rt->rt_key_dst != daddr || + rt->rt_key_src != skeys[s] || + rt->rt_oif != ikeys[i] || + rt_is_input_route(rt) || + rt_is_expired(rt) || + !net_eq(dev_net(rt->dst.dev), net) || + rt->dst.error || + rt->dst.dev != dev || + rt->rt_gateway != old_gw) + continue; + + if (!rt->peer) + rt_bind_peer(rt, rt->rt_dst, 1); + + peer = rt->peer; + if (peer) { + if (peer->redirect_learned.a4 != new_gw) { + peer->redirect_learned.a4 = new_gw; + atomic_inc(&__rt_peer_genid); + } + check_peer_redir(&rt->dst, peer); + } + } + } } return; @@ -1689,33 +1749,6 @@ static void ip_rt_update_pmtu(struct dst_entry *dst, u32 mtu) } } -static int check_peer_redir(struct dst_entry *dst, struct inet_peer *peer) -{ - struct rtable *rt = (struct rtable *) dst; - __be32 orig_gw = rt->rt_gateway; - struct neighbour *n, *old_n; - - dst_confirm(&rt->dst); - - rt->rt_gateway = peer->redirect_learned.a4; - n = __arp_bind_neighbour(&rt->dst, rt->rt_gateway); - if (IS_ERR(n)) - return PTR_ERR(n); - old_n = xchg(&rt->dst._neighbour, n); - if (old_n) - neigh_release(old_n); - if (!n || !(n->nud_state & NUD_VALID)) { - if (n) - neigh_event_send(n, NULL); - rt->rt_gateway = orig_gw; - return -EAGAIN; - } else { - rt->rt_flags |= RTCF_REDIRECTED; - call_netevent_notifiers(NETEVENT_NEIGH_UPDATE, n); - } - return 0; -} - static struct dst_entry *ipv4_dst_check(struct dst_entry *dst, u32 cookie) { struct rtable *rt = (struct rtable *) dst; diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index abbe1ee08a1..9acefde5137 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -1289,25 +1289,26 @@ static int tcp_match_skb_to_sack(struct sock *sk, struct sk_buff *skb, return in_sack; } -static u8 tcp_sacktag_one(struct sk_buff *skb, struct sock *sk, - struct tcp_sacktag_state *state, +/* Mark the given newly-SACKed range as such, adjusting counters and hints. */ +static u8 tcp_sacktag_one(struct sock *sk, + struct tcp_sacktag_state *state, u8 sacked, + u32 start_seq, u32 end_seq, int dup_sack, int pcount) { struct tcp_sock *tp = tcp_sk(sk); - u8 sacked = TCP_SKB_CB(skb)->sacked; int fack_count = state->fack_count; /* Account D-SACK for retransmitted packet. */ if (dup_sack && (sacked & TCPCB_RETRANS)) { if (tp->undo_marker && tp->undo_retrans && - after(TCP_SKB_CB(skb)->end_seq, tp->undo_marker)) + after(end_seq, tp->undo_marker)) tp->undo_retrans--; if (sacked & TCPCB_SACKED_ACKED) state->reord = min(fack_count, state->reord); } /* Nothing to do; acked frame is about to be dropped (was ACKed). */ - if (!after(TCP_SKB_CB(skb)->end_seq, tp->snd_una)) + if (!after(end_seq, tp->snd_una)) return sacked; if (!(sacked & TCPCB_SACKED_ACKED)) { @@ -1326,13 +1327,13 @@ static u8 tcp_sacktag_one(struct sk_buff *skb, struct sock *sk, /* New sack for not retransmitted frame, * which was in hole. It is reordering. */ - if (before(TCP_SKB_CB(skb)->seq, + if (before(start_seq, tcp_highest_sack_seq(tp))) state->reord = min(fack_count, state->reord); /* SACK enhanced F-RTO (RFC4138; Appendix B) */ - if (!after(TCP_SKB_CB(skb)->end_seq, tp->frto_highmark)) + if (!after(end_seq, tp->frto_highmark)) state->flag |= FLAG_ONLY_ORIG_SACKED; } @@ -1350,8 +1351,7 @@ static u8 tcp_sacktag_one(struct sk_buff *skb, struct sock *sk, /* Lost marker hint past SACKed? Tweak RFC3517 cnt */ if (!tcp_is_fack(tp) && (tp->lost_skb_hint != NULL) && - before(TCP_SKB_CB(skb)->seq, - TCP_SKB_CB(tp->lost_skb_hint)->seq)) + before(start_seq, TCP_SKB_CB(tp->lost_skb_hint)->seq)) tp->lost_cnt_hint += pcount; if (fack_count > tp->fackets_out) @@ -1370,6 +1370,9 @@ static u8 tcp_sacktag_one(struct sk_buff *skb, struct sock *sk, return sacked; } +/* Shift newly-SACKed bytes from this skb to the immediately previous + * already-SACKed sk_buff. Mark the newly-SACKed bytes as such. + */ static int tcp_shifted_skb(struct sock *sk, struct sk_buff *skb, struct tcp_sacktag_state *state, unsigned int pcount, int shifted, int mss, @@ -1377,10 +1380,13 @@ static int tcp_shifted_skb(struct sock *sk, struct sk_buff *skb, { struct tcp_sock *tp = tcp_sk(sk); struct sk_buff *prev = tcp_write_queue_prev(sk, skb); + u32 start_seq = TCP_SKB_CB(skb)->seq; /* start of newly-SACKed */ + u32 end_seq = start_seq + shifted; /* end of newly-SACKed */ BUG_ON(!pcount); - if (skb == tp->lost_skb_hint) + /* Adjust hint for FACK. Non-FACK is handled in tcp_sacktag_one(). */ + if (tcp_is_fack(tp) && (skb == tp->lost_skb_hint)) tp->lost_cnt_hint += pcount; TCP_SKB_CB(prev)->end_seq += shifted; @@ -1406,8 +1412,11 @@ static int tcp_shifted_skb(struct sock *sk, struct sk_buff *skb, skb_shinfo(skb)->gso_type = 0; } - /* We discard results */ - tcp_sacktag_one(skb, sk, state, dup_sack, pcount); + /* Adjust counters and hints for the newly sacked sequence range but + * discard the return value since prev is already marked. + */ + tcp_sacktag_one(sk, state, TCP_SKB_CB(skb)->sacked, + start_seq, end_seq, dup_sack, pcount); /* Difference in this won't matter, both ACKed by the same cumul. ACK */ TCP_SKB_CB(prev)->sacked |= (TCP_SKB_CB(skb)->sacked & TCPCB_EVER_RETRANS); @@ -1646,10 +1655,14 @@ static struct sk_buff *tcp_sacktag_walk(struct sk_buff *skb, struct sock *sk, break; if (in_sack) { - TCP_SKB_CB(skb)->sacked = tcp_sacktag_one(skb, sk, - state, - dup_sack, - tcp_skb_pcount(skb)); + TCP_SKB_CB(skb)->sacked = + tcp_sacktag_one(sk, + state, + TCP_SKB_CB(skb)->sacked, + TCP_SKB_CB(skb)->seq, + TCP_SKB_CB(skb)->end_seq, + dup_sack, + tcp_skb_pcount(skb)); if (!before(TCP_SKB_CB(skb)->seq, tcp_highest_sack_seq(tp))) diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index e3762bc8263..1c848a9f4ef 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -655,6 +655,11 @@ static void tcp_v4_send_reset(struct sock *sk, struct sk_buff *skb) arg.iov[0].iov_len, IPPROTO_TCP, 0); arg.csumoffset = offsetof(struct tcphdr, check) / 2; arg.flags = (sk && inet_sk(sk)->transparent) ? IP_REPLY_ARG_NOSRCCHECK : 0; + /* When socket is gone, all binding information is lost. + * routing might fail in this case. using iif for oif to + * make sure we can deliver it + */ + arg.bound_dev_if = sk ? sk->sk_bound_dev_if : inet_iif(skb); net = dev_net(skb_dst(skb)->dev); ip_send_reply(net->ipv4.tcp_sock, skb, ip_hdr(skb)->saddr, diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c index 82a809901f8..86e3cc10fc2 100644 --- a/net/ipv6/ip6mr.c +++ b/net/ipv6/ip6mr.c @@ -696,8 +696,10 @@ static netdev_tx_t reg_vif_xmit(struct sk_buff *skb, int err; err = ip6mr_fib_lookup(net, &fl6, &mrt); - if (err < 0) + if (err < 0) { + kfree_skb(skb); return err; + } read_lock(&mrt_lock); dev->stats.tx_bytes += skb->len; @@ -2051,8 +2053,10 @@ int ip6_mr_input(struct sk_buff *skb) int err; err = ip6mr_fib_lookup(net, &fl6, &mrt); - if (err < 0) + if (err < 0) { + kfree_skb(skb); return err; + } read_lock(&mrt_lock); cache = ip6mr_cache_find(mrt, diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 866f269183c..1e36fb3318c 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -910,6 +910,8 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) wiphy_debug(local->hw.wiphy, "Failed to initialize wep: %d\n", result); + ieee80211_led_init(local); + rtnl_lock(); result = ieee80211_init_rate_ctrl_alg(local, @@ -931,8 +933,6 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) rtnl_unlock(); - ieee80211_led_init(local); - local->network_latency_notifier.notifier_call = ieee80211_max_network_latency; result = pm_qos_add_notifier(PM_QOS_NETWORK_LATENCY, diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c index 24c28d238dc..0787bed0418 100644 --- a/net/netfilter/ipvs/ip_vs_core.c +++ b/net/netfilter/ipvs/ip_vs_core.c @@ -233,6 +233,7 @@ ip_vs_sched_persist(struct ip_vs_service *svc, __be16 dport = 0; /* destination port to forward */ unsigned int flags; struct ip_vs_conn_param param; + const union nf_inet_addr fwmark = { .ip = htonl(svc->fwmark) }; union nf_inet_addr snet; /* source network of the client, after masking */ @@ -268,7 +269,6 @@ ip_vs_sched_persist(struct ip_vs_service *svc, { int protocol = iph.protocol; const union nf_inet_addr *vaddr = &iph.daddr; - const union nf_inet_addr fwmark = { .ip = htonl(svc->fwmark) }; __be16 vport = 0; if (dst_port == svc->port) { diff --git a/net/sched/sch_choke.c b/net/sched/sch_choke.c index 06afbaeb4c8..178ee83175a 100644 --- a/net/sched/sch_choke.c +++ b/net/sched/sch_choke.c @@ -225,8 +225,7 @@ struct choke_skb_cb { static inline struct choke_skb_cb *choke_skb_cb(const struct sk_buff *skb) { - BUILD_BUG_ON(sizeof(skb->cb) < - sizeof(struct qdisc_skb_cb) + sizeof(struct choke_skb_cb)); + qdisc_cb_private_validate(skb, sizeof(struct choke_skb_cb)); return (struct choke_skb_cb *)qdisc_skb_cb(skb)->data; } diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c index 69c35f6cd13..2f684593f35 100644 --- a/net/sched/sch_netem.c +++ b/net/sched/sch_netem.c @@ -117,8 +117,7 @@ struct netem_skb_cb { static inline struct netem_skb_cb *netem_skb_cb(struct sk_buff *skb) { - BUILD_BUG_ON(sizeof(skb->cb) < - sizeof(struct qdisc_skb_cb) + sizeof(struct netem_skb_cb)); + qdisc_cb_private_validate(skb, sizeof(struct netem_skb_cb)); return (struct netem_skb_cb *)qdisc_skb_cb(skb)->data; } @@ -382,8 +381,8 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch) q->counter = 0; __skb_queue_head(&q->qdisc->q, skb); - q->qdisc->qstats.backlog += qdisc_pkt_len(skb); - q->qdisc->qstats.requeues++; + sch->qstats.backlog += qdisc_pkt_len(skb); + sch->qstats.requeues++; ret = NET_XMIT_SUCCESS; } diff --git a/net/sched/sch_sfb.c b/net/sched/sch_sfb.c index 0a833d0c1f6..47ee29fad35 100644 --- a/net/sched/sch_sfb.c +++ b/net/sched/sch_sfb.c @@ -93,8 +93,7 @@ struct sfb_skb_cb { static inline struct sfb_skb_cb *sfb_skb_cb(const struct sk_buff *skb) { - BUILD_BUG_ON(sizeof(skb->cb) < - sizeof(struct qdisc_skb_cb) + sizeof(struct sfb_skb_cb)); + qdisc_cb_private_validate(skb, sizeof(struct sfb_skb_cb)); return (struct sfb_skb_cb *)qdisc_skb_cb(skb)->data; } diff --git a/scripts/package/builddeb b/scripts/package/builddeb index f6cbc3ddb68..3c6c0b14c80 100644 --- a/scripts/package/builddeb +++ b/scripts/package/builddeb @@ -238,14 +238,14 @@ EOF fi # Build header package -(cd $srctree; find . -name Makefile -o -name Kconfig\* -o -name \*.pl > /tmp/files$$) -(cd $srctree; find arch/$SRCARCH/include include scripts -type f >> /tmp/files$$) -(cd $objtree; find .config Module.symvers include scripts -type f >> /tmp/objfiles$$) +(cd $srctree; find . -name Makefile -o -name Kconfig\* -o -name \*.pl > "$objtree/debian/hdrsrcfiles") +(cd $srctree; find arch/$SRCARCH/include include scripts -type f >> "$objtree/debian/hdrsrcfiles") +(cd $objtree; find .config Module.symvers include scripts -type f >> "$objtree/debian/hdrobjfiles") destdir=$kernel_headers_dir/usr/src/linux-headers-$version mkdir -p "$destdir" -(cd $srctree; tar -c -f - -T /tmp/files$$) | (cd $destdir; tar -xf -) -(cd $objtree; tar -c -f - -T /tmp/objfiles$$) | (cd $destdir; tar -xf -) -rm -f /tmp/files$$ /tmp/objfiles$$ +(cd $srctree; tar -c -f - -T "$objtree/debian/hdrsrcfiles") | (cd $destdir; tar -xf -) +(cd $objtree; tar -c -f - -T "$objtree/debian/hdrobjfiles") | (cd $destdir; tar -xf -) +rm -f "$objtree/debian/hdrsrcfiles" "$objtree/debian/hdrobjfiles" arch=$(dpkg --print-architecture) cat <> debian/control diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 3c2381ca372..81ecd6c0321 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -1917,6 +1917,10 @@ static void cxt5051_init_mic_port(struct hda_codec *codec, hda_nid_t nid, snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | event); +} + +static void cxt5051_init_mic_jack(struct hda_codec *codec, hda_nid_t nid) +{ snd_hda_input_jack_add(codec, nid, SND_JACK_MICROPHONE, NULL); snd_hda_input_jack_report(codec, nid); } @@ -1934,7 +1938,6 @@ static int cxt5051_init(struct hda_codec *codec) struct conexant_spec *spec = codec->spec; conexant_init(codec); - conexant_init_jacks(codec); if (spec->auto_mic & AUTO_MIC_PORTB) cxt5051_init_mic_port(codec, 0x17, CXT5051_PORTB_EVENT); @@ -2067,6 +2070,12 @@ static int patch_cxt5051(struct hda_codec *codec) if (spec->beep_amp) snd_hda_attach_beep_device(codec, spec->beep_amp); + conexant_init_jacks(codec); + if (spec->auto_mic & AUTO_MIC_PORTB) + cxt5051_init_mic_jack(codec, 0x17); + if (spec->auto_mic & AUTO_MIC_PORTC) + cxt5051_init_mic_jack(codec, 0x18); + return 0; } diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index 6d0cae4681b..c850e3d84ed 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -2373,7 +2373,7 @@ static int out_pga_event(struct snd_soc_dapm_widget *w, } } -static const char *st_text[] = { "None", "Right", "Left" }; +static const char *st_text[] = { "None", "Left", "Right" }; static const struct soc_enum str_enum = SOC_ENUM_SINGLE(WM8962_DAC_DSP_MIXING_1, 2, 3, st_text); From d58e9e8eb68f99de7fcece147719b5e0165243fb Mon Sep 17 00:00:00 2001 From: Ethan Chen Date: Sun, 2 Sep 2012 18:13:11 -0700 Subject: [PATCH 028/111] Linux 3.0.24 --- Documentation/hwmon/jc42 | 26 +- Makefile | 2 +- arch/alpha/include/asm/futex.h | 2 +- arch/arm/Kconfig | 2 +- arch/arm/mach-dove/common.c | 3 +- arch/arm/mach-kirkwood/common.c | 3 +- arch/arm/mach-kirkwood/mpp.h | 320 ++++++++++---------- arch/arm/mach-lpc32xx/include/mach/irqs.h | 2 +- arch/arm/mach-lpc32xx/irq.c | 25 +- arch/arm/mach-lpc32xx/serial.c | 20 +- arch/arm/mach-mv78xx0/common.c | 3 +- arch/arm/mach-mv78xx0/mpp.h | 226 +++++++------- arch/arm/mach-omap2/board-4430sdp.c | 31 +- arch/arm/mach-omap2/board-omap4panda.c | 22 +- arch/arm/mach-orion5x/common.c | 4 +- arch/arm/mm/proc-v7.S | 4 +- arch/arm/plat-orion/common.c | 9 +- arch/arm/plat-orion/include/plat/common.h | 3 +- arch/arm/plat-orion/mpp.c | 3 +- arch/arm/plat-s3c24xx/dma.c | 2 +- arch/avr32/Kconfig | 1 + arch/s390/Kconfig | 3 + arch/s390/include/asm/compat.h | 7 - arch/s390/kernel/process.c | 1 - arch/s390/kernel/ptrace.c | 2 +- arch/s390/kernel/setup.c | 2 +- arch/s390/mm/fault.c | 1 - arch/s390/mm/mmap.c | 2 +- block/bsg.c | 3 +- drivers/acpi/sleep.c | 16 + drivers/crypto/mv_cesa.c | 1 + drivers/gpu/drm/i915/i915_reg.h | 15 + drivers/gpu/drm/i915/intel_display.c | 22 +- drivers/gpu/drm/radeon/r600_blit_shaders.c | 8 + drivers/hid/hid-ids.h | 3 + drivers/hid/usbhid/hid-quirks.c | 1 + drivers/hwmon/Kconfig | 5 +- drivers/hwmon/jc42.c | 30 +- drivers/hwmon/pmbus_core.c | 3 +- drivers/i2c/busses/i2c-mxs.c | 13 +- drivers/input/mouse/alps.c | 7 +- drivers/md/dm-io.c | 23 +- drivers/md/dm-raid.c | 1 + drivers/mfd/cs5535-mfd.c | 6 +- drivers/mfd/mfd-core.c | 2 +- drivers/misc/cs5535-mfgpt.c | 2 +- drivers/mmc/host/sdhci-esdhc-imx.c | 5 +- drivers/net/usb/cdc_ether.c | 7 + drivers/net/usb/usbnet.c | 2 + drivers/net/usb/zaurus.c | 7 + drivers/net/wireless/ath/ath9k/ar5008_phy.c | 25 +- drivers/net/wireless/ath/ath9k/ar9002_hw.c | 19 ++ drivers/net/wireless/ath/ath9k/hw.h | 1 - drivers/net/wireless/ath/carl9170/tx.c | 1 + drivers/regulator/88pm8607.c | 6 +- drivers/s390/block/dasd_eckd.c | 2 +- drivers/s390/block/dasd_ioctl.c | 1 + drivers/s390/char/fs3270.c | 2 +- drivers/s390/char/vmcp.c | 1 + drivers/s390/cio/chsc_sch.c | 1 + drivers/s390/scsi/zfcp_cfdc.c | 1 + drivers/scsi/osd/osd_uld.c | 4 +- drivers/staging/lirc/lirc_serial.c | 100 +++--- drivers/video/omap2/dss/hdmi.c | 86 +++++- drivers/watchdog/hpwdt.c | 5 +- fs/autofs4/autofs_i.h | 1 + fs/autofs4/dev-ioctl.c | 1 + fs/autofs4/inode.c | 2 + fs/autofs4/waitq.c | 22 +- fs/binfmt_elf.c | 2 +- fs/cifs/dir.c | 20 +- include/linux/compat.h | 4 + include/linux/regset.h | 10 +- include/video/omapdss.h | 5 + kernel/irq/manage.c | 44 ++- kernel/kprobes.c | 4 +- mm/huge_memory.c | 6 +- mm/memcontrol.c | 5 +- mm/nommu.c | 2 - net/mac80211/rate.c | 2 +- sound/pci/hda/hda_codec.c | 8 +- sound/pci/hda/hda_codec.h | 3 + sound/pci/hda/patch_conexant.c | 22 +- sound/pci/hda/patch_sigmatel.c | 2 +- sound/soc/imx/imx-ssi.c | 2 +- sound/soc/soc-dapm.c | 12 +- 86 files changed, 855 insertions(+), 492 deletions(-) diff --git a/Documentation/hwmon/jc42 b/Documentation/hwmon/jc42 index a22ecf48f25..52729a756c1 100644 --- a/Documentation/hwmon/jc42 +++ b/Documentation/hwmon/jc42 @@ -7,21 +7,29 @@ Supported chips: Addresses scanned: I2C 0x18 - 0x1f Datasheets: http://www.analog.com/static/imported-files/data_sheets/ADT7408.pdf - * IDT TSE2002B3, TS3000B3 - Prefix: 'tse2002b3', 'ts3000b3' + * Atmel AT30TS00 + Prefix: 'at30ts00' Addresses scanned: I2C 0x18 - 0x1f Datasheets: - http://www.idt.com/products/getdoc.cfm?docid=18715691 - http://www.idt.com/products/getdoc.cfm?docid=18715692 + http://www.atmel.com/Images/doc8585.pdf + * IDT TSE2002B3, TSE2002GB2, TS3000B3, TS3000GB2 + Prefix: 'tse2002', 'ts3000' + Addresses scanned: I2C 0x18 - 0x1f + Datasheets: + http://www.idt.com/sites/default/files/documents/IDT_TSE2002B3C_DST_20100512_120303152056.pdf + http://www.idt.com/sites/default/files/documents/IDT_TSE2002GB2A1_DST_20111107_120303145914.pdf + http://www.idt.com/sites/default/files/documents/IDT_TS3000B3A_DST_20101129_120303152013.pdf + http://www.idt.com/sites/default/files/documents/IDT_TS3000GB2A1_DST_20111104_120303151012.pdf * Maxim MAX6604 Prefix: 'max6604' Addresses scanned: I2C 0x18 - 0x1f Datasheets: http://datasheets.maxim-ic.com/en/ds/MAX6604.pdf - * Microchip MCP9805, MCP98242, MCP98243, MCP9843 - Prefixes: 'mcp9805', 'mcp98242', 'mcp98243', 'mcp9843' + * Microchip MCP9804, MCP9805, MCP98242, MCP98243, MCP9843 + Prefixes: 'mcp9804', 'mcp9805', 'mcp98242', 'mcp98243', 'mcp9843' Addresses scanned: I2C 0x18 - 0x1f Datasheets: + http://ww1.microchip.com/downloads/en/DeviceDoc/22203C.pdf http://ww1.microchip.com/downloads/en/DeviceDoc/21977b.pdf http://ww1.microchip.com/downloads/en/DeviceDoc/21996a.pdf http://ww1.microchip.com/downloads/en/DeviceDoc/22153c.pdf @@ -48,6 +56,12 @@ Supported chips: Datasheets: http://www.st.com/stonline/products/literature/ds/13447/stts424.pdf http://www.st.com/stonline/products/literature/ds/13448/stts424e02.pdf + * ST Microelectronics STTS2002, STTS3000 + Prefix: 'stts2002', 'stts3000' + Addresses scanned: I2C 0x18 - 0x1f + Datasheets: + http://www.st.com/internet/com/TECHNICAL_RESOURCES/TECHNICAL_LITERATURE/DATASHEET/CD00225278.pdf + http://www.st.com/internet/com/TECHNICAL_RESOURCES/TECHNICAL_LITERATURE/DATA_BRIEF/CD00270920.pdf * JEDEC JC 42.4 compliant temperature sensor chips Prefix: 'jc42' Addresses scanned: I2C 0x18 - 0x1f diff --git a/Makefile b/Makefile index 40eb138a5a5..dca83695db7 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ VERSION = 3 PATCHLEVEL = 0 -SUBLEVEL = 23 +SUBLEVEL = 24 EXTRAVERSION = NAME = Sneaky Weasel diff --git a/arch/alpha/include/asm/futex.h b/arch/alpha/include/asm/futex.h index e8a761aee08..f939794363a 100644 --- a/arch/alpha/include/asm/futex.h +++ b/arch/alpha/include/asm/futex.h @@ -108,7 +108,7 @@ futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr, " lda $31,3b-2b(%0)\n" " .previous\n" : "+r"(ret), "=&r"(prev), "=&r"(cmp) - : "r"(uaddr), "r"((long)oldval), "r"(newval) + : "r"(uaddr), "r"((long)(int)oldval), "r"(newval) : "memory"); *uval = prev; diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 7f6fc98a0d9..a17c10f3d86 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -1199,7 +1199,7 @@ config ARM_ERRATA_743622 depends on CPU_V7 help This option enables the workaround for the 743622 Cortex-A9 - (r2p0..r2p2) erratum. Under very rare conditions, a faulty + (r2p*) erratum. Under very rare conditions, a faulty optimisation in the Cortex-A9 Store Buffer may lead to data corruption. This workaround sets a specific bit in the diagnostic register of the Cortex-A9 which disables the Store Buffer diff --git a/arch/arm/mach-dove/common.c b/arch/arm/mach-dove/common.c index cf7e5985eeb..46c04498629 100644 --- a/arch/arm/mach-dove/common.c +++ b/arch/arm/mach-dove/common.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include "common.h" @@ -74,7 +75,7 @@ void __init dove_map_io(void) void __init dove_ehci0_init(void) { orion_ehci_init(&dove_mbus_dram_info, - DOVE_USB0_PHYS_BASE, IRQ_DOVE_USB0); + DOVE_USB0_PHYS_BASE, IRQ_DOVE_USB0, EHCI_PHY_NA); } /***************************************************************************** diff --git a/arch/arm/mach-kirkwood/common.c b/arch/arm/mach-kirkwood/common.c index f3248cfbe51..c5dbbb35e0b 100644 --- a/arch/arm/mach-kirkwood/common.c +++ b/arch/arm/mach-kirkwood/common.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include "common.h" @@ -74,7 +75,7 @@ void __init kirkwood_ehci_init(void) { kirkwood_clk_ctrl |= CGC_USB0; orion_ehci_init(&kirkwood_mbus_dram_info, - USB_PHYS_BASE, IRQ_KIRKWOOD_USB); + USB_PHYS_BASE, IRQ_KIRKWOOD_USB, EHCI_PHY_NA); } diff --git a/arch/arm/mach-kirkwood/mpp.h b/arch/arm/mach-kirkwood/mpp.h index ac787957e2d..7afccf47220 100644 --- a/arch/arm/mach-kirkwood/mpp.h +++ b/arch/arm/mach-kirkwood/mpp.h @@ -31,313 +31,313 @@ #define MPP_F6282_MASK MPP( 0, 0x0, 0, 0, 0, 0, 0, 0, 1 ) #define MPP0_GPIO MPP( 0, 0x0, 1, 1, 1, 1, 1, 1, 1 ) -#define MPP0_NF_IO2 MPP( 0, 0x1, 1, 1, 1, 1, 1, 1, 1 ) -#define MPP0_SPI_SCn MPP( 0, 0x2, 0, 1, 1, 1, 1, 1, 1 ) +#define MPP0_NF_IO2 MPP( 0, 0x1, 0, 0, 1, 1, 1, 1, 1 ) +#define MPP0_SPI_SCn MPP( 0, 0x2, 0, 0, 1, 1, 1, 1, 1 ) #define MPP1_GPO MPP( 1, 0x0, 0, 1, 1, 1, 1, 1, 1 ) -#define MPP1_NF_IO3 MPP( 1, 0x1, 1, 1, 1, 1, 1, 1, 1 ) -#define MPP1_SPI_MOSI MPP( 1, 0x2, 0, 1, 1, 1, 1, 1, 1 ) +#define MPP1_NF_IO3 MPP( 1, 0x1, 0, 0, 1, 1, 1, 1, 1 ) +#define MPP1_SPI_MOSI MPP( 1, 0x2, 0, 0, 1, 1, 1, 1, 1 ) #define MPP2_GPO MPP( 2, 0x0, 0, 1, 1, 1, 1, 1, 1 ) -#define MPP2_NF_IO4 MPP( 2, 0x1, 1, 1, 1, 1, 1, 1, 1 ) -#define MPP2_SPI_SCK MPP( 2, 0x2, 0, 1, 1, 1, 1, 1, 1 ) +#define MPP2_NF_IO4 MPP( 2, 0x1, 0, 0, 1, 1, 1, 1, 1 ) +#define MPP2_SPI_SCK MPP( 2, 0x2, 0, 0, 1, 1, 1, 1, 1 ) #define MPP3_GPO MPP( 3, 0x0, 0, 1, 1, 1, 1, 1, 1 ) -#define MPP3_NF_IO5 MPP( 3, 0x1, 1, 1, 1, 1, 1, 1, 1 ) -#define MPP3_SPI_MISO MPP( 3, 0x2, 1, 0, 1, 1, 1, 1, 1 ) +#define MPP3_NF_IO5 MPP( 3, 0x1, 0, 0, 1, 1, 1, 1, 1 ) +#define MPP3_SPI_MISO MPP( 3, 0x2, 0, 0, 1, 1, 1, 1, 1 ) #define MPP4_GPIO MPP( 4, 0x0, 1, 1, 1, 1, 1, 1, 1 ) -#define MPP4_NF_IO6 MPP( 4, 0x1, 1, 1, 1, 1, 1, 1, 1 ) -#define MPP4_UART0_RXD MPP( 4, 0x2, 1, 0, 1, 1, 1, 1, 1 ) -#define MPP4_SATA1_ACTn MPP( 4, 0x5, 0, 1, 0, 0, 1, 1, 1 ) +#define MPP4_NF_IO6 MPP( 4, 0x1, 0, 0, 1, 1, 1, 1, 1 ) +#define MPP4_UART0_RXD MPP( 4, 0x2, 0, 0, 1, 1, 1, 1, 1 ) +#define MPP4_SATA1_ACTn MPP( 4, 0x5, 0, 0, 0, 0, 1, 1, 1 ) #define MPP4_LCD_VGA_HSYNC MPP( 4, 0xb, 0, 0, 0, 0, 0, 0, 1 ) -#define MPP4_PTP_CLK MPP( 4, 0xd, 1, 0, 1, 1, 1, 1, 0 ) +#define MPP4_PTP_CLK MPP( 4, 0xd, 0, 0, 1, 1, 1, 1, 0 ) #define MPP5_GPO MPP( 5, 0x0, 0, 1, 1, 1, 1, 1, 1 ) -#define MPP5_NF_IO7 MPP( 5, 0x1, 1, 1, 1, 1, 1, 1, 1 ) -#define MPP5_UART0_TXD MPP( 5, 0x2, 0, 1, 1, 1, 1, 1, 1 ) -#define MPP5_PTP_TRIG_GEN MPP( 5, 0x4, 0, 1, 1, 1, 1, 1, 0 ) -#define MPP5_SATA0_ACTn MPP( 5, 0x5, 0, 1, 0, 1, 1, 1, 1 ) +#define MPP5_NF_IO7 MPP( 5, 0x1, 0, 0, 1, 1, 1, 1, 1 ) +#define MPP5_UART0_TXD MPP( 5, 0x2, 0, 0, 1, 1, 1, 1, 1 ) +#define MPP5_PTP_TRIG_GEN MPP( 5, 0x4, 0, 0, 1, 1, 1, 1, 0 ) +#define MPP5_SATA0_ACTn MPP( 5, 0x5, 0, 0, 0, 1, 1, 1, 1 ) #define MPP5_LCD_VGA_VSYNC MPP( 5, 0xb, 0, 0, 0, 0, 0, 0, 1 ) -#define MPP6_SYSRST_OUTn MPP( 6, 0x1, 0, 1, 1, 1, 1, 1, 1 ) -#define MPP6_SPI_MOSI MPP( 6, 0x2, 0, 1, 1, 1, 1, 1, 1 ) -#define MPP6_PTP_TRIG_GEN MPP( 6, 0x3, 0, 1, 1, 1, 1, 1, 0 ) +#define MPP6_SYSRST_OUTn MPP( 6, 0x1, 0, 0, 1, 1, 1, 1, 1 ) +#define MPP6_SPI_MOSI MPP( 6, 0x2, 0, 0, 1, 1, 1, 1, 1 ) +#define MPP6_PTP_TRIG_GEN MPP( 6, 0x3, 0, 0, 1, 1, 1, 1, 0 ) #define MPP7_GPO MPP( 7, 0x0, 0, 1, 1, 1, 1, 1, 1 ) -#define MPP7_PEX_RST_OUTn MPP( 7, 0x1, 0, 1, 1, 1, 1, 1, 0 ) -#define MPP7_SPI_SCn MPP( 7, 0x2, 0, 1, 1, 1, 1, 1, 1 ) -#define MPP7_PTP_TRIG_GEN MPP( 7, 0x3, 0, 1, 1, 1, 1, 1, 0 ) -#define MPP7_LCD_PWM MPP( 7, 0xb, 0, 1, 0, 0, 0, 0, 1 ) +#define MPP7_PEX_RST_OUTn MPP( 7, 0x1, 0, 0, 1, 1, 1, 1, 0 ) +#define MPP7_SPI_SCn MPP( 7, 0x2, 0, 0, 1, 1, 1, 1, 1 ) +#define MPP7_PTP_TRIG_GEN MPP( 7, 0x3, 0, 0, 1, 1, 1, 1, 0 ) +#define MPP7_LCD_PWM MPP( 7, 0xb, 0, 0, 0, 0, 0, 0, 1 ) #define MPP8_GPIO MPP( 8, 0x0, 1, 1, 1, 1, 1, 1, 1 ) -#define MPP8_TW0_SDA MPP( 8, 0x1, 1, 1, 1, 1, 1, 1, 1 ) -#define MPP8_UART0_RTS MPP( 8, 0x2, 0, 1, 1, 1, 1, 1, 1 ) -#define MPP8_UART1_RTS MPP( 8, 0x3, 0, 1, 1, 1, 1, 1, 1 ) -#define MPP8_MII0_RXERR MPP( 8, 0x4, 1, 0, 0, 1, 1, 1, 1 ) -#define MPP8_SATA1_PRESENTn MPP( 8, 0x5, 0, 1, 0, 0, 1, 1, 1 ) -#define MPP8_PTP_CLK MPP( 8, 0xc, 1, 0, 1, 1, 1, 1, 0 ) -#define MPP8_MII0_COL MPP( 8, 0xd, 1, 0, 1, 1, 1, 1, 1 ) +#define MPP8_TW0_SDA MPP( 8, 0x1, 0, 0, 1, 1, 1, 1, 1 ) +#define MPP8_UART0_RTS MPP( 8, 0x2, 0, 0, 1, 1, 1, 1, 1 ) +#define MPP8_UART1_RTS MPP( 8, 0x3, 0, 0, 1, 1, 1, 1, 1 ) +#define MPP8_MII0_RXERR MPP( 8, 0x4, 0, 0, 0, 1, 1, 1, 1 ) +#define MPP8_SATA1_PRESENTn MPP( 8, 0x5, 0, 0, 0, 0, 1, 1, 1 ) +#define MPP8_PTP_CLK MPP( 8, 0xc, 0, 0, 1, 1, 1, 1, 0 ) +#define MPP8_MII0_COL MPP( 8, 0xd, 0, 0, 1, 1, 1, 1, 1 ) #define MPP9_GPIO MPP( 9, 0x0, 1, 1, 1, 1, 1, 1, 1 ) -#define MPP9_TW0_SCK MPP( 9, 0x1, 1, 1, 1, 1, 1, 1, 1 ) -#define MPP9_UART0_CTS MPP( 9, 0x2, 1, 0, 1, 1, 1, 1, 1 ) -#define MPP9_UART1_CTS MPP( 9, 0x3, 1, 0, 1, 1, 1, 1, 1 ) -#define MPP9_SATA0_PRESENTn MPP( 9, 0x5, 0, 1, 0, 1, 1, 1, 1 ) -#define MPP9_PTP_EVENT_REQ MPP( 9, 0xc, 1, 0, 1, 1, 1, 1, 0 ) -#define MPP9_MII0_CRS MPP( 9, 0xd, 1, 0, 1, 1, 1, 1, 1 ) +#define MPP9_TW0_SCK MPP( 9, 0x1, 0, 0, 1, 1, 1, 1, 1 ) +#define MPP9_UART0_CTS MPP( 9, 0x2, 0, 0, 1, 1, 1, 1, 1 ) +#define MPP9_UART1_CTS MPP( 9, 0x3, 0, 0, 1, 1, 1, 1, 1 ) +#define MPP9_SATA0_PRESENTn MPP( 9, 0x5, 0, 0, 0, 1, 1, 1, 1 ) +#define MPP9_PTP_EVENT_REQ MPP( 9, 0xc, 0, 0, 1, 1, 1, 1, 0 ) +#define MPP9_MII0_CRS MPP( 9, 0xd, 0, 0, 1, 1, 1, 1, 1 ) #define MPP10_GPO MPP( 10, 0x0, 0, 1, 1, 1, 1, 1, 1 ) -#define MPP10_SPI_SCK MPP( 10, 0x2, 0, 1, 1, 1, 1, 1, 1 ) -#define MPP10_UART0_TXD MPP( 10, 0X3, 0, 1, 1, 1, 1, 1, 1 ) -#define MPP10_SATA1_ACTn MPP( 10, 0x5, 0, 1, 0, 0, 1, 1, 1 ) -#define MPP10_PTP_TRIG_GEN MPP( 10, 0xc, 0, 1, 1, 1, 1, 1, 0 ) +#define MPP10_SPI_SCK MPP( 10, 0x2, 0, 0, 1, 1, 1, 1, 1 ) +#define MPP10_UART0_TXD MPP( 10, 0X3, 0, 0, 1, 1, 1, 1, 1 ) +#define MPP10_SATA1_ACTn MPP( 10, 0x5, 0, 0, 0, 0, 1, 1, 1 ) +#define MPP10_PTP_TRIG_GEN MPP( 10, 0xc, 0, 0, 1, 1, 1, 1, 0 ) #define MPP11_GPIO MPP( 11, 0x0, 1, 1, 1, 1, 1, 1, 1 ) -#define MPP11_SPI_MISO MPP( 11, 0x2, 1, 0, 1, 1, 1, 1, 1 ) -#define MPP11_UART0_RXD MPP( 11, 0x3, 1, 0, 1, 1, 1, 1, 1 ) -#define MPP11_PTP_EVENT_REQ MPP( 11, 0x4, 1, 0, 1, 1, 1, 1, 0 ) -#define MPP11_PTP_TRIG_GEN MPP( 11, 0xc, 0, 1, 1, 1, 1, 1, 0 ) -#define MPP11_PTP_CLK MPP( 11, 0xd, 1, 0, 1, 1, 1, 1, 0 ) -#define MPP11_SATA0_ACTn MPP( 11, 0x5, 0, 1, 0, 1, 1, 1, 1 ) +#define MPP11_SPI_MISO MPP( 11, 0x2, 0, 0, 1, 1, 1, 1, 1 ) +#define MPP11_UART0_RXD MPP( 11, 0x3, 0, 0, 1, 1, 1, 1, 1 ) +#define MPP11_PTP_EVENT_REQ MPP( 11, 0x4, 0, 0, 1, 1, 1, 1, 0 ) +#define MPP11_PTP_TRIG_GEN MPP( 11, 0xc, 0, 0, 1, 1, 1, 1, 0 ) +#define MPP11_PTP_CLK MPP( 11, 0xd, 0, 0, 1, 1, 1, 1, 0 ) +#define MPP11_SATA0_ACTn MPP( 11, 0x5, 0, 0, 0, 1, 1, 1, 1 ) #define MPP12_GPO MPP( 12, 0x0, 0, 1, 1, 1, 1, 1, 1 ) -#define MPP12_SD_CLK MPP( 12, 0x1, 0, 1, 1, 1, 1, 1, 1 ) -#define MPP12_AU_SPDIF0 MPP( 12, 0xa, 0, 1, 0, 0, 0, 0, 1 ) -#define MPP12_SPI_MOSI MPP( 12, 0xb, 0, 1, 0, 0, 0, 0, 1 ) -#define MPP12_TW1_SDA MPP( 12, 0xd, 1, 0, 0, 0, 0, 0, 1 ) +#define MPP12_SD_CLK MPP( 12, 0x1, 0, 0, 1, 1, 1, 1, 1 ) +#define MPP12_AU_SPDIF0 MPP( 12, 0xa, 0, 0, 0, 0, 0, 0, 1 ) +#define MPP12_SPI_MOSI MPP( 12, 0xb, 0, 0, 0, 0, 0, 0, 1 ) +#define MPP12_TW1_SDA MPP( 12, 0xd, 0, 0, 0, 0, 0, 0, 1 ) #define MPP13_GPIO MPP( 13, 0x0, 1, 1, 1, 1, 1, 1, 1 ) -#define MPP13_SD_CMD MPP( 13, 0x1, 1, 1, 1, 1, 1, 1, 1 ) -#define MPP13_UART1_TXD MPP( 13, 0x3, 0, 1, 1, 1, 1, 1, 1 ) -#define MPP13_AU_SPDIFRMCLK MPP( 13, 0xa, 0, 1, 0, 0, 0, 0, 1 ) -#define MPP13_LCDPWM MPP( 13, 0xb, 0, 1, 0, 0, 0, 0, 1 ) +#define MPP13_SD_CMD MPP( 13, 0x1, 0, 0, 1, 1, 1, 1, 1 ) +#define MPP13_UART1_TXD MPP( 13, 0x3, 0, 0, 1, 1, 1, 1, 1 ) +#define MPP13_AU_SPDIFRMCLK MPP( 13, 0xa, 0, 0, 0, 0, 0, 0, 1 ) +#define MPP13_LCDPWM MPP( 13, 0xb, 0, 0, 0, 0, 0, 0, 1 ) #define MPP14_GPIO MPP( 14, 0x0, 1, 1, 1, 1, 1, 1, 1 ) -#define MPP14_SD_D0 MPP( 14, 0x1, 1, 1, 1, 1, 1, 1, 1 ) -#define MPP14_UART1_RXD MPP( 14, 0x3, 1, 0, 1, 1, 1, 1, 1 ) -#define MPP14_SATA1_PRESENTn MPP( 14, 0x4, 0, 1, 0, 0, 1, 1, 1 ) -#define MPP14_AU_SPDIFI MPP( 14, 0xa, 1, 0, 0, 0, 0, 0, 1 ) -#define MPP14_AU_I2SDI MPP( 14, 0xb, 1, 0, 0, 0, 0, 0, 1 ) -#define MPP14_MII0_COL MPP( 14, 0xd, 1, 0, 1, 1, 1, 1, 1 ) +#define MPP14_SD_D0 MPP( 14, 0x1, 0, 0, 1, 1, 1, 1, 1 ) +#define MPP14_UART1_RXD MPP( 14, 0x3, 0, 0, 1, 1, 1, 1, 1 ) +#define MPP14_SATA1_PRESENTn MPP( 14, 0x4, 0, 0, 0, 0, 1, 1, 1 ) +#define MPP14_AU_SPDIFI MPP( 14, 0xa, 0, 0, 0, 0, 0, 0, 1 ) +#define MPP14_AU_I2SDI MPP( 14, 0xb, 0, 0, 0, 0, 0, 0, 1 ) +#define MPP14_MII0_COL MPP( 14, 0xd, 0, 0, 1, 1, 1, 1, 1 ) #define MPP15_GPIO MPP( 15, 0x0, 1, 1, 1, 1, 1, 1, 1 ) -#define MPP15_SD_D1 MPP( 15, 0x1, 1, 1, 1, 1, 1, 1, 1 ) -#define MPP15_UART0_RTS MPP( 15, 0x2, 0, 1, 1, 1, 1, 1, 1 ) -#define MPP15_UART1_TXD MPP( 15, 0x3, 0, 1, 1, 1, 1, 1, 1 ) -#define MPP15_SATA0_ACTn MPP( 15, 0x4, 0, 1, 0, 1, 1, 1, 1 ) -#define MPP15_SPI_CSn MPP( 15, 0xb, 0, 1, 0, 0, 0, 0, 1 ) +#define MPP15_SD_D1 MPP( 15, 0x1, 0, 0, 1, 1, 1, 1, 1 ) +#define MPP15_UART0_RTS MPP( 15, 0x2, 0, 0, 1, 1, 1, 1, 1 ) +#define MPP15_UART1_TXD MPP( 15, 0x3, 0, 0, 1, 1, 1, 1, 1 ) +#define MPP15_SATA0_ACTn MPP( 15, 0x4, 0, 0, 0, 1, 1, 1, 1 ) +#define MPP15_SPI_CSn MPP( 15, 0xb, 0, 0, 0, 0, 0, 0, 1 ) #define MPP16_GPIO MPP( 16, 0x0, 1, 1, 1, 1, 1, 1, 1 ) -#define MPP16_SD_D2 MPP( 16, 0x1, 1, 1, 1, 1, 1, 1, 1 ) -#define MPP16_UART0_CTS MPP( 16, 0x2, 1, 0, 1, 1, 1, 1, 1 ) -#define MPP16_UART1_RXD MPP( 16, 0x3, 1, 0, 1, 1, 1, 1, 1 ) -#define MPP16_SATA1_ACTn MPP( 16, 0x4, 0, 1, 0, 0, 1, 1, 1 ) -#define MPP16_LCD_EXT_REF_CLK MPP( 16, 0xb, 1, 0, 0, 0, 0, 0, 1 ) -#define MPP16_MII0_CRS MPP( 16, 0xd, 1, 0, 1, 1, 1, 1, 1 ) +#define MPP16_SD_D2 MPP( 16, 0x1, 0, 0, 1, 1, 1, 1, 1 ) +#define MPP16_UART0_CTS MPP( 16, 0x2, 0, 0, 1, 1, 1, 1, 1 ) +#define MPP16_UART1_RXD MPP( 16, 0x3, 0, 0, 1, 1, 1, 1, 1 ) +#define MPP16_SATA1_ACTn MPP( 16, 0x4, 0, 0, 0, 0, 1, 1, 1 ) +#define MPP16_LCD_EXT_REF_CLK MPP( 16, 0xb, 0, 0, 0, 0, 0, 0, 1 ) +#define MPP16_MII0_CRS MPP( 16, 0xd, 0, 0, 1, 1, 1, 1, 1 ) #define MPP17_GPIO MPP( 17, 0x0, 1, 1, 1, 1, 1, 1, 1 ) -#define MPP17_SD_D3 MPP( 17, 0x1, 1, 1, 1, 1, 1, 1, 1 ) -#define MPP17_SATA0_PRESENTn MPP( 17, 0x4, 0, 1, 0, 1, 1, 1, 1 ) -#define MPP17_SATA1_ACTn MPP( 17, 0xa, 0, 1, 0, 0, 0, 0, 1 ) -#define MPP17_TW1_SCK MPP( 17, 0xd, 1, 1, 0, 0, 0, 0, 1 ) +#define MPP17_SD_D3 MPP( 17, 0x1, 0, 0, 1, 1, 1, 1, 1 ) +#define MPP17_SATA0_PRESENTn MPP( 17, 0x4, 0, 0, 0, 1, 1, 1, 1 ) +#define MPP17_SATA1_ACTn MPP( 17, 0xa, 0, 0, 0, 0, 0, 0, 1 ) +#define MPP17_TW1_SCK MPP( 17, 0xd, 0, 0, 0, 0, 0, 0, 1 ) #define MPP18_GPO MPP( 18, 0x0, 0, 1, 1, 1, 1, 1, 1 ) -#define MPP18_NF_IO0 MPP( 18, 0x1, 1, 1, 1, 1, 1, 1, 1 ) -#define MPP18_PEX0_CLKREQ MPP( 18, 0x2, 0, 1, 0, 0, 0, 0, 1 ) +#define MPP18_NF_IO0 MPP( 18, 0x1, 0, 0, 1, 1, 1, 1, 1 ) +#define MPP18_PEX0_CLKREQ MPP( 18, 0x2, 0, 0, 0, 0, 0, 0, 1 ) #define MPP19_GPO MPP( 19, 0x0, 0, 1, 1, 1, 1, 1, 1 ) -#define MPP19_NF_IO1 MPP( 19, 0x1, 1, 1, 1, 1, 1, 1, 1 ) +#define MPP19_NF_IO1 MPP( 19, 0x1, 0, 0, 1, 1, 1, 1, 1 ) #define MPP20_GPIO MPP( 20, 0x0, 1, 1, 0, 1, 1, 1, 1 ) -#define MPP20_TSMP0 MPP( 20, 0x1, 1, 1, 0, 0, 1, 1, 1 ) -#define MPP20_TDM_CH0_TX_QL MPP( 20, 0x2, 0, 1, 0, 0, 1, 1, 1 ) +#define MPP20_TSMP0 MPP( 20, 0x1, 0, 0, 0, 0, 1, 1, 1 ) +#define MPP20_TDM_CH0_TX_QL MPP( 20, 0x2, 0, 0, 0, 0, 1, 1, 1 ) #define MPP20_GE1_TXD0 MPP( 20, 0x3, 0, 0, 0, 1, 1, 1, 1 ) -#define MPP20_AU_SPDIFI MPP( 20, 0x4, 1, 0, 0, 0, 1, 1, 1 ) -#define MPP20_SATA1_ACTn MPP( 20, 0x5, 0, 1, 0, 0, 1, 1, 1 ) +#define MPP20_AU_SPDIFI MPP( 20, 0x4, 0, 0, 0, 0, 1, 1, 1 ) +#define MPP20_SATA1_ACTn MPP( 20, 0x5, 0, 0, 0, 0, 1, 1, 1 ) #define MPP20_LCD_D0 MPP( 20, 0xb, 0, 0, 0, 0, 0, 0, 1 ) #define MPP21_GPIO MPP( 21, 0x0, 1, 1, 0, 1, 1, 1, 1 ) -#define MPP21_TSMP1 MPP( 21, 0x1, 1, 1, 0, 0, 1, 1, 1 ) -#define MPP21_TDM_CH0_RX_QL MPP( 21, 0x2, 0, 1, 0, 0, 1, 1, 1 ) +#define MPP21_TSMP1 MPP( 21, 0x1, 0, 0, 0, 0, 1, 1, 1 ) +#define MPP21_TDM_CH0_RX_QL MPP( 21, 0x2, 0, 0, 0, 0, 1, 1, 1 ) #define MPP21_GE1_TXD1 MPP( 21, 0x3, 0, 0, 0, 1, 1, 1, 1 ) -#define MPP21_AU_SPDIFO MPP( 21, 0x4, 0, 1, 0, 0, 1, 1, 1 ) -#define MPP21_SATA0_ACTn MPP( 21, 0x5, 0, 1, 0, 1, 1, 1, 1 ) +#define MPP21_AU_SPDIFO MPP( 21, 0x4, 0, 0, 0, 0, 1, 1, 1 ) +#define MPP21_SATA0_ACTn MPP( 21, 0x5, 0, 0, 0, 1, 1, 1, 1 ) #define MPP21_LCD_D1 MPP( 21, 0xb, 0, 0, 0, 0, 0, 0, 1 ) #define MPP22_GPIO MPP( 22, 0x0, 1, 1, 0, 1, 1, 1, 1 ) -#define MPP22_TSMP2 MPP( 22, 0x1, 1, 1, 0, 0, 1, 1, 1 ) -#define MPP22_TDM_CH2_TX_QL MPP( 22, 0x2, 0, 1, 0, 0, 1, 1, 1 ) +#define MPP22_TSMP2 MPP( 22, 0x1, 0, 0, 0, 0, 1, 1, 1 ) +#define MPP22_TDM_CH2_TX_QL MPP( 22, 0x2, 0, 0, 0, 0, 1, 1, 1 ) #define MPP22_GE1_TXD2 MPP( 22, 0x3, 0, 0, 0, 1, 1, 1, 1 ) -#define MPP22_AU_SPDIFRMKCLK MPP( 22, 0x4, 0, 1, 0, 0, 1, 1, 1 ) -#define MPP22_SATA1_PRESENTn MPP( 22, 0x5, 0, 1, 0, 0, 1, 1, 1 ) +#define MPP22_AU_SPDIFRMKCLK MPP( 22, 0x4, 0, 0, 0, 0, 1, 1, 1 ) +#define MPP22_SATA1_PRESENTn MPP( 22, 0x5, 0, 0, 0, 0, 1, 1, 1 ) #define MPP22_LCD_D2 MPP( 22, 0xb, 0, 0, 0, 0, 0, 0, 1 ) #define MPP23_GPIO MPP( 23, 0x0, 1, 1, 0, 1, 1, 1, 1 ) -#define MPP23_TSMP3 MPP( 23, 0x1, 1, 1, 0, 0, 1, 1, 1 ) -#define MPP23_TDM_CH2_RX_QL MPP( 23, 0x2, 1, 0, 0, 0, 1, 1, 1 ) +#define MPP23_TSMP3 MPP( 23, 0x1, 0, 0, 0, 0, 1, 1, 1 ) +#define MPP23_TDM_CH2_RX_QL MPP( 23, 0x2, 0, 0, 0, 0, 1, 1, 1 ) #define MPP23_GE1_TXD3 MPP( 23, 0x3, 0, 0, 0, 1, 1, 1, 1 ) -#define MPP23_AU_I2SBCLK MPP( 23, 0x4, 0, 1, 0, 0, 1, 1, 1 ) -#define MPP23_SATA0_PRESENTn MPP( 23, 0x5, 0, 1, 0, 1, 1, 1, 1 ) +#define MPP23_AU_I2SBCLK MPP( 23, 0x4, 0, 0, 0, 0, 1, 1, 1 ) +#define MPP23_SATA0_PRESENTn MPP( 23, 0x5, 0, 0, 0, 1, 1, 1, 1 ) #define MPP23_LCD_D3 MPP( 23, 0xb, 0, 0, 0, 0, 0, 0, 1 ) #define MPP24_GPIO MPP( 24, 0x0, 1, 1, 0, 1, 1, 1, 1 ) -#define MPP24_TSMP4 MPP( 24, 0x1, 1, 1, 0, 0, 1, 1, 1 ) -#define MPP24_TDM_SPI_CS0 MPP( 24, 0x2, 0, 1, 0, 0, 1, 1, 1 ) +#define MPP24_TSMP4 MPP( 24, 0x1, 0, 0, 0, 0, 1, 1, 1 ) +#define MPP24_TDM_SPI_CS0 MPP( 24, 0x2, 0, 0, 0, 0, 1, 1, 1 ) #define MPP24_GE1_RXD0 MPP( 24, 0x3, 0, 0, 0, 1, 1, 1, 1 ) -#define MPP24_AU_I2SDO MPP( 24, 0x4, 0, 1, 0, 0, 1, 1, 1 ) +#define MPP24_AU_I2SDO MPP( 24, 0x4, 0, 0, 0, 0, 1, 1, 1 ) #define MPP24_LCD_D4 MPP( 24, 0xb, 0, 0, 0, 0, 0, 0, 1 ) #define MPP25_GPIO MPP( 25, 0x0, 1, 1, 0, 1, 1, 1, 1 ) -#define MPP25_TSMP5 MPP( 25, 0x1, 1, 1, 0, 0, 1, 1, 1 ) -#define MPP25_TDM_SPI_SCK MPP( 25, 0x2, 0, 1, 0, 0, 1, 1, 1 ) +#define MPP25_TSMP5 MPP( 25, 0x1, 0, 0, 0, 0, 1, 1, 1 ) +#define MPP25_TDM_SPI_SCK MPP( 25, 0x2, 0, 0, 0, 0, 1, 1, 1 ) #define MPP25_GE1_RXD1 MPP( 25, 0x3, 0, 0, 0, 1, 1, 1, 1 ) -#define MPP25_AU_I2SLRCLK MPP( 25, 0x4, 0, 1, 0, 0, 1, 1, 1 ) +#define MPP25_AU_I2SLRCLK MPP( 25, 0x4, 0, 0, 0, 0, 1, 1, 1 ) #define MPP25_LCD_D5 MPP( 25, 0xb, 0, 0, 0, 0, 0, 0, 1 ) #define MPP26_GPIO MPP( 26, 0x0, 1, 1, 0, 1, 1, 1, 1 ) -#define MPP26_TSMP6 MPP( 26, 0x1, 1, 1, 0, 0, 1, 1, 1 ) -#define MPP26_TDM_SPI_MISO MPP( 26, 0x2, 1, 0, 0, 0, 1, 1, 1 ) +#define MPP26_TSMP6 MPP( 26, 0x1, 0, 0, 0, 0, 1, 1, 1 ) +#define MPP26_TDM_SPI_MISO MPP( 26, 0x2, 0, 0, 0, 0, 1, 1, 1 ) #define MPP26_GE1_RXD2 MPP( 26, 0x3, 0, 0, 0, 1, 1, 1, 1 ) -#define MPP26_AU_I2SMCLK MPP( 26, 0x4, 0, 1, 0, 0, 1, 1, 1 ) +#define MPP26_AU_I2SMCLK MPP( 26, 0x4, 0, 0, 0, 0, 1, 1, 1 ) #define MPP26_LCD_D6 MPP( 26, 0xb, 0, 0, 0, 0, 0, 0, 1 ) #define MPP27_GPIO MPP( 27, 0x0, 1, 1, 0, 1, 1, 1, 1 ) -#define MPP27_TSMP7 MPP( 27, 0x1, 1, 1, 0, 0, 1, 1, 1 ) -#define MPP27_TDM_SPI_MOSI MPP( 27, 0x2, 0, 1, 0, 0, 1, 1, 1 ) +#define MPP27_TSMP7 MPP( 27, 0x1, 0, 0, 0, 0, 1, 1, 1 ) +#define MPP27_TDM_SPI_MOSI MPP( 27, 0x2, 0, 0, 0, 0, 1, 1, 1 ) #define MPP27_GE1_RXD3 MPP( 27, 0x3, 0, 0, 0, 1, 1, 1, 1 ) -#define MPP27_AU_I2SDI MPP( 27, 0x4, 1, 0, 0, 0, 1, 1, 1 ) +#define MPP27_AU_I2SDI MPP( 27, 0x4, 0, 0, 0, 0, 1, 1, 1 ) #define MPP27_LCD_D7 MPP( 27, 0xb, 0, 0, 0, 0, 0, 0, 1 ) #define MPP28_GPIO MPP( 28, 0x0, 1, 1, 0, 1, 1, 1, 1 ) -#define MPP28_TSMP8 MPP( 28, 0x1, 1, 1, 0, 0, 1, 1, 1 ) +#define MPP28_TSMP8 MPP( 28, 0x1, 0, 0, 0, 0, 1, 1, 1 ) #define MPP28_TDM_CODEC_INTn MPP( 28, 0x2, 0, 0, 0, 0, 1, 1, 1 ) #define MPP28_GE1_COL MPP( 28, 0x3, 0, 0, 0, 1, 1, 1, 1 ) -#define MPP28_AU_EXTCLK MPP( 28, 0x4, 1, 0, 0, 0, 1, 1, 1 ) +#define MPP28_AU_EXTCLK MPP( 28, 0x4, 0, 0, 0, 0, 1, 1, 1 ) #define MPP28_LCD_D8 MPP( 28, 0xb, 0, 0, 0, 0, 0, 0, 1 ) #define MPP29_GPIO MPP( 29, 0x0, 1, 1, 0, 1, 1, 1, 1 ) -#define MPP29_TSMP9 MPP( 29, 0x1, 1, 1, 0, 0, 1, 1, 1 ) +#define MPP29_TSMP9 MPP( 29, 0x1, 0, 0, 0, 0, 1, 1, 1 ) #define MPP29_TDM_CODEC_RSTn MPP( 29, 0x2, 0, 0, 0, 0, 1, 1, 1 ) #define MPP29_GE1_TCLK MPP( 29, 0x3, 0, 0, 0, 1, 1, 1, 1 ) #define MPP29_LCD_D9 MPP( 29, 0xb, 0, 0, 0, 0, 0, 0, 1 ) #define MPP30_GPIO MPP( 30, 0x0, 1, 1, 0, 1, 1, 1, 1 ) -#define MPP30_TSMP10 MPP( 30, 0x1, 1, 1, 0, 0, 1, 1, 1 ) -#define MPP30_TDM_PCLK MPP( 30, 0x2, 1, 1, 0, 0, 1, 1, 1 ) +#define MPP30_TSMP10 MPP( 30, 0x1, 0, 0, 0, 0, 1, 1, 1 ) +#define MPP30_TDM_PCLK MPP( 30, 0x2, 0, 0, 0, 0, 1, 1, 1 ) #define MPP30_GE1_RXCTL MPP( 30, 0x3, 0, 0, 0, 1, 1, 1, 1 ) #define MPP30_LCD_D10 MPP( 30, 0xb, 0, 0, 0, 0, 0, 0, 1 ) #define MPP31_GPIO MPP( 31, 0x0, 1, 1, 0, 1, 1, 1, 1 ) -#define MPP31_TSMP11 MPP( 31, 0x1, 1, 1, 0, 0, 1, 1, 1 ) -#define MPP31_TDM_FS MPP( 31, 0x2, 1, 1, 0, 0, 1, 1, 1 ) +#define MPP31_TSMP11 MPP( 31, 0x1, 0, 0, 0, 0, 1, 1, 1 ) +#define MPP31_TDM_FS MPP( 31, 0x2, 0, 0, 0, 0, 1, 1, 1 ) #define MPP31_GE1_RXCLK MPP( 31, 0x3, 0, 0, 0, 1, 1, 1, 1 ) #define MPP31_LCD_D11 MPP( 31, 0xb, 0, 0, 0, 0, 0, 0, 1 ) #define MPP32_GPIO MPP( 32, 0x0, 1, 1, 0, 1, 1, 1, 1 ) -#define MPP32_TSMP12 MPP( 32, 0x1, 1, 1, 0, 0, 1, 1, 1 ) -#define MPP32_TDM_DRX MPP( 32, 0x2, 1, 0, 0, 0, 1, 1, 1 ) +#define MPP32_TSMP12 MPP( 32, 0x1, 0, 0, 0, 0, 1, 1, 1 ) +#define MPP32_TDM_DRX MPP( 32, 0x2, 0, 0, 0, 0, 1, 1, 1 ) #define MPP32_GE1_TCLKOUT MPP( 32, 0x3, 0, 0, 0, 1, 1, 1, 1 ) #define MPP32_LCD_D12 MPP( 32, 0xb, 0, 0, 0, 0, 0, 0, 1 ) #define MPP33_GPO MPP( 33, 0x0, 0, 1, 0, 1, 1, 1, 1 ) -#define MPP33_TDM_DTX MPP( 33, 0x2, 0, 1, 0, 0, 1, 1, 1 ) +#define MPP33_TDM_DTX MPP( 33, 0x2, 0, 0, 0, 0, 1, 1, 1 ) #define MPP33_GE1_TXCTL MPP( 33, 0x3, 0, 0, 0, 1, 1, 1, 1 ) #define MPP33_LCD_D13 MPP( 33, 0xb, 0, 0, 0, 0, 0, 0, 1 ) #define MPP34_GPIO MPP( 34, 0x0, 1, 1, 0, 1, 1, 1, 1 ) -#define MPP34_TDM_SPI_CS1 MPP( 34, 0x2, 0, 1, 0, 0, 1, 1, 1 ) +#define MPP34_TDM_SPI_CS1 MPP( 34, 0x2, 0, 0, 0, 0, 1, 1, 1 ) #define MPP34_GE1_TXEN MPP( 34, 0x3, 0, 0, 0, 1, 1, 1, 1 ) -#define MPP34_SATA1_ACTn MPP( 34, 0x5, 0, 1, 0, 0, 0, 1, 1 ) +#define MPP34_SATA1_ACTn MPP( 34, 0x5, 0, 0, 0, 0, 0, 1, 1 ) #define MPP34_LCD_D14 MPP( 34, 0xb, 0, 0, 0, 0, 0, 0, 1 ) #define MPP35_GPIO MPP( 35, 0x0, 1, 1, 1, 1, 1, 1, 1 ) -#define MPP35_TDM_CH0_TX_QL MPP( 35, 0x2, 0, 1, 0, 0, 1, 1, 1 ) +#define MPP35_TDM_CH0_TX_QL MPP( 35, 0x2, 0, 0, 0, 0, 1, 1, 1 ) #define MPP35_GE1_RXERR MPP( 35, 0x3, 0, 0, 0, 1, 1, 1, 1 ) -#define MPP35_SATA0_ACTn MPP( 35, 0x5, 0, 1, 0, 1, 1, 1, 1 ) +#define MPP35_SATA0_ACTn MPP( 35, 0x5, 0, 0, 0, 1, 1, 1, 1 ) #define MPP35_LCD_D15 MPP( 22, 0xb, 0, 0, 0, 0, 0, 0, 1 ) -#define MPP35_MII0_RXERR MPP( 35, 0xc, 1, 0, 1, 1, 1, 1, 1 ) +#define MPP35_MII0_RXERR MPP( 35, 0xc, 0, 0, 1, 1, 1, 1, 1 ) #define MPP36_GPIO MPP( 36, 0x0, 1, 1, 1, 0, 0, 1, 1 ) -#define MPP36_TSMP0 MPP( 36, 0x1, 1, 1, 0, 0, 0, 1, 1 ) -#define MPP36_TDM_SPI_CS1 MPP( 36, 0x2, 0, 1, 0, 0, 0, 1, 1 ) -#define MPP36_AU_SPDIFI MPP( 36, 0x4, 1, 0, 1, 0, 0, 1, 1 ) -#define MPP36_TW1_SDA MPP( 36, 0xb, 1, 1, 0, 0, 0, 0, 1 ) +#define MPP36_TSMP0 MPP( 36, 0x1, 0, 0, 0, 0, 0, 1, 1 ) +#define MPP36_TDM_SPI_CS1 MPP( 36, 0x2, 0, 0, 0, 0, 0, 1, 1 ) +#define MPP36_AU_SPDIFI MPP( 36, 0x4, 0, 0, 1, 0, 0, 1, 1 ) +#define MPP36_TW1_SDA MPP( 36, 0xb, 0, 0, 0, 0, 0, 0, 1 ) #define MPP37_GPIO MPP( 37, 0x0, 1, 1, 1, 0, 0, 1, 1 ) -#define MPP37_TSMP1 MPP( 37, 0x1, 1, 1, 0, 0, 0, 1, 1 ) -#define MPP37_TDM_CH2_TX_QL MPP( 37, 0x2, 0, 1, 0, 0, 0, 1, 1 ) -#define MPP37_AU_SPDIFO MPP( 37, 0x4, 0, 1, 1, 0, 0, 1, 1 ) -#define MPP37_TW1_SCK MPP( 37, 0xb, 1, 1, 0, 0, 0, 0, 1 ) +#define MPP37_TSMP1 MPP( 37, 0x1, 0, 0, 0, 0, 0, 1, 1 ) +#define MPP37_TDM_CH2_TX_QL MPP( 37, 0x2, 0, 0, 0, 0, 0, 1, 1 ) +#define MPP37_AU_SPDIFO MPP( 37, 0x4, 0, 0, 1, 0, 0, 1, 1 ) +#define MPP37_TW1_SCK MPP( 37, 0xb, 0, 0, 0, 0, 0, 0, 1 ) #define MPP38_GPIO MPP( 38, 0x0, 1, 1, 1, 0, 0, 1, 1 ) -#define MPP38_TSMP2 MPP( 38, 0x1, 1, 1, 0, 0, 0, 1, 1 ) -#define MPP38_TDM_CH2_RX_QL MPP( 38, 0x2, 0, 1, 0, 0, 0, 1, 1 ) -#define MPP38_AU_SPDIFRMLCLK MPP( 38, 0x4, 0, 1, 1, 0, 0, 1, 1 ) +#define MPP38_TSMP2 MPP( 38, 0x1, 0, 0, 0, 0, 0, 1, 1 ) +#define MPP38_TDM_CH2_RX_QL MPP( 38, 0x2, 0, 0, 0, 0, 0, 1, 1 ) +#define MPP38_AU_SPDIFRMLCLK MPP( 38, 0x4, 0, 0, 1, 0, 0, 1, 1 ) #define MPP38_LCD_D18 MPP( 38, 0xb, 0, 0, 0, 0, 0, 0, 1 ) #define MPP39_GPIO MPP( 39, 0x0, 1, 1, 1, 0, 0, 1, 1 ) -#define MPP39_TSMP3 MPP( 39, 0x1, 1, 1, 0, 0, 0, 1, 1 ) -#define MPP39_TDM_SPI_CS0 MPP( 39, 0x2, 0, 1, 0, 0, 0, 1, 1 ) -#define MPP39_AU_I2SBCLK MPP( 39, 0x4, 0, 1, 1, 0, 0, 1, 1 ) +#define MPP39_TSMP3 MPP( 39, 0x1, 0, 0, 0, 0, 0, 1, 1 ) +#define MPP39_TDM_SPI_CS0 MPP( 39, 0x2, 0, 0, 0, 0, 0, 1, 1 ) +#define MPP39_AU_I2SBCLK MPP( 39, 0x4, 0, 0, 1, 0, 0, 1, 1 ) #define MPP39_LCD_D19 MPP( 39, 0xb, 0, 0, 0, 0, 0, 0, 1 ) #define MPP40_GPIO MPP( 40, 0x0, 1, 1, 1, 0, 0, 1, 1 ) -#define MPP40_TSMP4 MPP( 40, 0x1, 1, 1, 0, 0, 0, 1, 1 ) -#define MPP40_TDM_SPI_SCK MPP( 40, 0x2, 0, 1, 0, 0, 0, 1, 1 ) -#define MPP40_AU_I2SDO MPP( 40, 0x4, 0, 1, 1, 0, 0, 1, 1 ) +#define MPP40_TSMP4 MPP( 40, 0x1, 0, 0, 0, 0, 0, 1, 1 ) +#define MPP40_TDM_SPI_SCK MPP( 40, 0x2, 0, 0, 0, 0, 0, 1, 1 ) +#define MPP40_AU_I2SDO MPP( 40, 0x4, 0, 0, 1, 0, 0, 1, 1 ) #define MPP40_LCD_D20 MPP( 40, 0xb, 0, 0, 0, 0, 0, 0, 1 ) #define MPP41_GPIO MPP( 41, 0x0, 1, 1, 1, 0, 0, 1, 1 ) -#define MPP41_TSMP5 MPP( 41, 0x1, 1, 1, 0, 0, 0, 1, 1 ) -#define MPP41_TDM_SPI_MISO MPP( 41, 0x2, 1, 0, 0, 0, 0, 1, 1 ) -#define MPP41_AU_I2SLRCLK MPP( 41, 0x4, 0, 1, 1, 0, 0, 1, 1 ) +#define MPP41_TSMP5 MPP( 41, 0x1, 0, 0, 0, 0, 0, 1, 1 ) +#define MPP41_TDM_SPI_MISO MPP( 41, 0x2, 0, 0, 0, 0, 0, 1, 1 ) +#define MPP41_AU_I2SLRCLK MPP( 41, 0x4, 0, 0, 1, 0, 0, 1, 1 ) #define MPP41_LCD_D21 MPP( 41, 0xb, 0, 0, 0, 0, 0, 0, 1 ) #define MPP42_GPIO MPP( 42, 0x0, 1, 1, 1, 0, 0, 1, 1 ) -#define MPP42_TSMP6 MPP( 42, 0x1, 1, 1, 0, 0, 0, 1, 1 ) -#define MPP42_TDM_SPI_MOSI MPP( 42, 0x2, 0, 1, 0, 0, 0, 1, 1 ) -#define MPP42_AU_I2SMCLK MPP( 42, 0x4, 0, 1, 1, 0, 0, 1, 1 ) +#define MPP42_TSMP6 MPP( 42, 0x1, 0, 0, 0, 0, 0, 1, 1 ) +#define MPP42_TDM_SPI_MOSI MPP( 42, 0x2, 0, 0, 0, 0, 0, 1, 1 ) +#define MPP42_AU_I2SMCLK MPP( 42, 0x4, 0, 0, 1, 0, 0, 1, 1 ) #define MPP42_LCD_D22 MPP( 42, 0xb, 0, 0, 0, 0, 0, 0, 1 ) #define MPP43_GPIO MPP( 43, 0x0, 1, 1, 1, 0, 0, 1, 1 ) -#define MPP43_TSMP7 MPP( 43, 0x1, 1, 1, 0, 0, 0, 1, 1 ) +#define MPP43_TSMP7 MPP( 43, 0x1, 0, 0, 0, 0, 0, 1, 1 ) #define MPP43_TDM_CODEC_INTn MPP( 43, 0x2, 0, 0, 0, 0, 0, 1, 1 ) -#define MPP43_AU_I2SDI MPP( 43, 0x4, 1, 0, 1, 0, 0, 1, 1 ) +#define MPP43_AU_I2SDI MPP( 43, 0x4, 0, 0, 1, 0, 0, 1, 1 ) #define MPP43_LCD_D23 MPP( 22, 0xb, 0, 0, 0, 0, 0, 0, 1 ) #define MPP44_GPIO MPP( 44, 0x0, 1, 1, 1, 0, 0, 1, 1 ) -#define MPP44_TSMP8 MPP( 44, 0x1, 1, 1, 0, 0, 0, 1, 1 ) +#define MPP44_TSMP8 MPP( 44, 0x1, 0, 0, 0, 0, 0, 1, 1 ) #define MPP44_TDM_CODEC_RSTn MPP( 44, 0x2, 0, 0, 0, 0, 0, 1, 1 ) -#define MPP44_AU_EXTCLK MPP( 44, 0x4, 1, 0, 1, 0, 0, 1, 1 ) +#define MPP44_AU_EXTCLK MPP( 44, 0x4, 0, 0, 1, 0, 0, 1, 1 ) #define MPP44_LCD_CLK MPP( 44, 0xb, 0, 0, 0, 0, 0, 0, 1 ) #define MPP45_GPIO MPP( 45, 0x0, 1, 1, 0, 0, 0, 1, 1 ) -#define MPP45_TSMP9 MPP( 45, 0x1, 1, 1, 0, 0, 0, 1, 1 ) -#define MPP45_TDM_PCLK MPP( 45, 0x2, 1, 1, 0, 0, 0, 1, 1 ) +#define MPP45_TSMP9 MPP( 45, 0x1, 0, 0, 0, 0, 0, 1, 1 ) +#define MPP45_TDM_PCLK MPP( 45, 0x2, 0, 0, 0, 0, 0, 1, 1 ) #define MPP245_LCD_E MPP( 45, 0xb, 0, 0, 0, 0, 0, 0, 1 ) #define MPP46_GPIO MPP( 46, 0x0, 1, 1, 0, 0, 0, 1, 1 ) -#define MPP46_TSMP10 MPP( 46, 0x1, 1, 1, 0, 0, 0, 1, 1 ) -#define MPP46_TDM_FS MPP( 46, 0x2, 1, 1, 0, 0, 0, 1, 1 ) +#define MPP46_TSMP10 MPP( 46, 0x1, 0, 0, 0, 0, 0, 1, 1 ) +#define MPP46_TDM_FS MPP( 46, 0x2, 0, 0, 0, 0, 0, 1, 1 ) #define MPP46_LCD_HSYNC MPP( 46, 0xb, 0, 0, 0, 0, 0, 0, 1 ) #define MPP47_GPIO MPP( 47, 0x0, 1, 1, 0, 0, 0, 1, 1 ) -#define MPP47_TSMP11 MPP( 47, 0x1, 1, 1, 0, 0, 0, 1, 1 ) -#define MPP47_TDM_DRX MPP( 47, 0x2, 1, 0, 0, 0, 0, 1, 1 ) +#define MPP47_TSMP11 MPP( 47, 0x1, 0, 0, 0, 0, 0, 1, 1 ) +#define MPP47_TDM_DRX MPP( 47, 0x2, 0, 0, 0, 0, 0, 1, 1 ) #define MPP47_LCD_VSYNC MPP( 47, 0xb, 0, 0, 0, 0, 0, 0, 1 ) #define MPP48_GPIO MPP( 48, 0x0, 1, 1, 0, 0, 0, 1, 1 ) -#define MPP48_TSMP12 MPP( 48, 0x1, 1, 1, 0, 0, 0, 1, 1 ) -#define MPP48_TDM_DTX MPP( 48, 0x2, 0, 1, 0, 0, 0, 1, 1 ) +#define MPP48_TSMP12 MPP( 48, 0x1, 0, 0, 0, 0, 0, 1, 1 ) +#define MPP48_TDM_DTX MPP( 48, 0x2, 0, 0, 0, 0, 0, 1, 1 ) #define MPP48_LCD_D16 MPP( 22, 0xb, 0, 0, 0, 0, 0, 0, 1 ) #define MPP49_GPIO MPP( 49, 0x0, 1, 1, 0, 0, 0, 1, 0 ) #define MPP49_GPO MPP( 49, 0x0, 0, 1, 0, 0, 0, 0, 1 ) -#define MPP49_TSMP9 MPP( 49, 0x1, 1, 1, 0, 0, 0, 1, 0 ) -#define MPP49_TDM_CH0_RX_QL MPP( 49, 0x2, 0, 1, 0, 0, 0, 1, 1 ) -#define MPP49_PTP_CLK MPP( 49, 0x5, 1, 0, 0, 0, 0, 1, 0 ) -#define MPP49_PEX0_CLKREQ MPP( 49, 0xa, 0, 1, 0, 0, 0, 0, 1 ) +#define MPP49_TSMP9 MPP( 49, 0x1, 0, 0, 0, 0, 0, 1, 0 ) +#define MPP49_TDM_CH0_RX_QL MPP( 49, 0x2, 0, 0, 0, 0, 0, 1, 1 ) +#define MPP49_PTP_CLK MPP( 49, 0x5, 0, 0, 0, 0, 0, 1, 0 ) +#define MPP49_PEX0_CLKREQ MPP( 49, 0xa, 0, 0, 0, 0, 0, 0, 1 ) #define MPP49_LCD_D17 MPP( 49, 0xb, 0, 0, 0, 0, 0, 0, 1 ) #define MPP_MAX 49 diff --git a/arch/arm/mach-lpc32xx/include/mach/irqs.h b/arch/arm/mach-lpc32xx/include/mach/irqs.h index 2667f52e3b0..9e3b90df32e 100644 --- a/arch/arm/mach-lpc32xx/include/mach/irqs.h +++ b/arch/arm/mach-lpc32xx/include/mach/irqs.h @@ -61,7 +61,7 @@ */ #define IRQ_LPC32XX_JTAG_COMM_TX LPC32XX_SIC1_IRQ(1) #define IRQ_LPC32XX_JTAG_COMM_RX LPC32XX_SIC1_IRQ(2) -#define IRQ_LPC32XX_GPI_11 LPC32XX_SIC1_IRQ(4) +#define IRQ_LPC32XX_GPI_28 LPC32XX_SIC1_IRQ(4) #define IRQ_LPC32XX_TS_P LPC32XX_SIC1_IRQ(6) #define IRQ_LPC32XX_TS_IRQ LPC32XX_SIC1_IRQ(7) #define IRQ_LPC32XX_TS_AUX LPC32XX_SIC1_IRQ(8) diff --git a/arch/arm/mach-lpc32xx/irq.c b/arch/arm/mach-lpc32xx/irq.c index 4eae566dfdc..c74de01ab5b 100644 --- a/arch/arm/mach-lpc32xx/irq.c +++ b/arch/arm/mach-lpc32xx/irq.c @@ -118,6 +118,10 @@ static const struct lpc32xx_event_info lpc32xx_events[NR_IRQS] = { .event_group = &lpc32xx_event_pin_regs, .mask = LPC32XX_CLKPWR_EXTSRC_GPI_06_BIT, }, + [IRQ_LPC32XX_GPI_28] = { + .event_group = &lpc32xx_event_pin_regs, + .mask = LPC32XX_CLKPWR_EXTSRC_GPI_28_BIT, + }, [IRQ_LPC32XX_GPIO_00] = { .event_group = &lpc32xx_event_int_regs, .mask = LPC32XX_CLKPWR_INTSRC_GPIO_00_BIT, @@ -305,9 +309,18 @@ static int lpc32xx_irq_wake(struct irq_data *d, unsigned int state) if (state) eventreg |= lpc32xx_events[d->irq].mask; - else + else { eventreg &= ~lpc32xx_events[d->irq].mask; + /* + * When disabling the wakeup, clear the latched + * event + */ + __raw_writel(lpc32xx_events[d->irq].mask, + lpc32xx_events[d->irq]. + event_group->rawstat_reg); + } + __raw_writel(eventreg, lpc32xx_events[d->irq].event_group->enab_reg); @@ -380,13 +393,15 @@ void __init lpc32xx_init_irq(void) /* Setup SIC1 */ __raw_writel(0, LPC32XX_INTC_MASK(LPC32XX_SIC1_BASE)); - __raw_writel(MIC_APR_DEFAULT, LPC32XX_INTC_POLAR(LPC32XX_SIC1_BASE)); - __raw_writel(MIC_ATR_DEFAULT, LPC32XX_INTC_ACT_TYPE(LPC32XX_SIC1_BASE)); + __raw_writel(SIC1_APR_DEFAULT, LPC32XX_INTC_POLAR(LPC32XX_SIC1_BASE)); + __raw_writel(SIC1_ATR_DEFAULT, + LPC32XX_INTC_ACT_TYPE(LPC32XX_SIC1_BASE)); /* Setup SIC2 */ __raw_writel(0, LPC32XX_INTC_MASK(LPC32XX_SIC2_BASE)); - __raw_writel(MIC_APR_DEFAULT, LPC32XX_INTC_POLAR(LPC32XX_SIC2_BASE)); - __raw_writel(MIC_ATR_DEFAULT, LPC32XX_INTC_ACT_TYPE(LPC32XX_SIC2_BASE)); + __raw_writel(SIC2_APR_DEFAULT, LPC32XX_INTC_POLAR(LPC32XX_SIC2_BASE)); + __raw_writel(SIC2_ATR_DEFAULT, + LPC32XX_INTC_ACT_TYPE(LPC32XX_SIC2_BASE)); /* Configure supported IRQ's */ for (i = 0; i < NR_IRQS; i++) { diff --git a/arch/arm/mach-lpc32xx/serial.c b/arch/arm/mach-lpc32xx/serial.c index 429cfdbb2b3..f2735281616 100644 --- a/arch/arm/mach-lpc32xx/serial.c +++ b/arch/arm/mach-lpc32xx/serial.c @@ -88,6 +88,7 @@ struct uartinit { char *uart_ck_name; u32 ck_mode_mask; void __iomem *pdiv_clk_reg; + resource_size_t mapbase; }; static struct uartinit uartinit_data[] __initdata = { @@ -97,6 +98,7 @@ static struct uartinit uartinit_data[] __initdata = { .ck_mode_mask = LPC32XX_UART_CLKMODE_LOAD(LPC32XX_UART_CLKMODE_ON, 5), .pdiv_clk_reg = LPC32XX_CLKPWR_UART5_CLK_CTRL, + .mapbase = LPC32XX_UART5_BASE, }, #endif #ifdef CONFIG_ARCH_LPC32XX_UART3_SELECT @@ -105,6 +107,7 @@ static struct uartinit uartinit_data[] __initdata = { .ck_mode_mask = LPC32XX_UART_CLKMODE_LOAD(LPC32XX_UART_CLKMODE_ON, 3), .pdiv_clk_reg = LPC32XX_CLKPWR_UART3_CLK_CTRL, + .mapbase = LPC32XX_UART3_BASE, }, #endif #ifdef CONFIG_ARCH_LPC32XX_UART4_SELECT @@ -113,6 +116,7 @@ static struct uartinit uartinit_data[] __initdata = { .ck_mode_mask = LPC32XX_UART_CLKMODE_LOAD(LPC32XX_UART_CLKMODE_ON, 4), .pdiv_clk_reg = LPC32XX_CLKPWR_UART4_CLK_CTRL, + .mapbase = LPC32XX_UART4_BASE, }, #endif #ifdef CONFIG_ARCH_LPC32XX_UART6_SELECT @@ -121,6 +125,7 @@ static struct uartinit uartinit_data[] __initdata = { .ck_mode_mask = LPC32XX_UART_CLKMODE_LOAD(LPC32XX_UART_CLKMODE_ON, 6), .pdiv_clk_reg = LPC32XX_CLKPWR_UART6_CLK_CTRL, + .mapbase = LPC32XX_UART6_BASE, }, #endif }; @@ -165,11 +170,24 @@ void __init lpc32xx_serial_init(void) /* pre-UART clock divider set to 1 */ __raw_writel(0x0101, uartinit_data[i].pdiv_clk_reg); + + /* + * Force a flush of the RX FIFOs to work around a + * HW bug + */ + puart = uartinit_data[i].mapbase; + __raw_writel(0xC1, LPC32XX_UART_IIR_FCR(puart)); + __raw_writel(0x00, LPC32XX_UART_DLL_FIFO(puart)); + j = LPC32XX_SUART_FIFO_SIZE; + while (j--) + tmp = __raw_readl( + LPC32XX_UART_DLL_FIFO(puart)); + __raw_writel(0, LPC32XX_UART_IIR_FCR(puart)); } /* This needs to be done after all UART clocks are setup */ __raw_writel(clkmodes, LPC32XX_UARTCTL_CLKMODE); - for (i = 0; i < ARRAY_SIZE(uartinit_data) - 1; i++) { + for (i = 0; i < ARRAY_SIZE(uartinit_data); i++) { /* Force a flush of the RX FIFOs to work around a HW bug */ puart = serial_std_platform_data[i].mapbase; __raw_writel(0xC1, LPC32XX_UART_IIR_FCR(puart)); diff --git a/arch/arm/mach-mv78xx0/common.c b/arch/arm/mach-mv78xx0/common.c index 23d3980ef59..d90e244e05e 100644 --- a/arch/arm/mach-mv78xx0/common.c +++ b/arch/arm/mach-mv78xx0/common.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -170,7 +171,7 @@ void __init mv78xx0_map_io(void) void __init mv78xx0_ehci0_init(void) { orion_ehci_init(&mv78xx0_mbus_dram_info, - USB0_PHYS_BASE, IRQ_MV78XX0_USB_0); + USB0_PHYS_BASE, IRQ_MV78XX0_USB_0, EHCI_PHY_NA); } diff --git a/arch/arm/mach-mv78xx0/mpp.h b/arch/arm/mach-mv78xx0/mpp.h index b61b5092712..3752302ae2e 100644 --- a/arch/arm/mach-mv78xx0/mpp.h +++ b/arch/arm/mach-mv78xx0/mpp.h @@ -24,296 +24,296 @@ #define MPP_78100_A0_MASK MPP(0, 0x0, 0, 0, 1) #define MPP0_GPIO MPP(0, 0x0, 1, 1, 1) -#define MPP0_GE0_COL MPP(0, 0x1, 1, 0, 1) -#define MPP0_GE1_TXCLK MPP(0, 0x2, 0, 1, 1) +#define MPP0_GE0_COL MPP(0, 0x1, 0, 0, 1) +#define MPP0_GE1_TXCLK MPP(0, 0x2, 0, 0, 1) #define MPP0_UNUSED MPP(0, 0x3, 0, 0, 1) #define MPP1_GPIO MPP(1, 0x0, 1, 1, 1) -#define MPP1_GE0_RXERR MPP(1, 0x1, 1, 0, 1) -#define MPP1_GE1_TXCTL MPP(1, 0x2, 0, 1, 1) +#define MPP1_GE0_RXERR MPP(1, 0x1, 0, 0, 1) +#define MPP1_GE1_TXCTL MPP(1, 0x2, 0, 0, 1) #define MPP1_UNUSED MPP(1, 0x3, 0, 0, 1) #define MPP2_GPIO MPP(2, 0x0, 1, 1, 1) -#define MPP2_GE0_CRS MPP(2, 0x1, 1, 0, 1) -#define MPP2_GE1_RXCTL MPP(2, 0x2, 1, 0, 1) +#define MPP2_GE0_CRS MPP(2, 0x1, 0, 0, 1) +#define MPP2_GE1_RXCTL MPP(2, 0x2, 0, 0, 1) #define MPP2_UNUSED MPP(2, 0x3, 0, 0, 1) #define MPP3_GPIO MPP(3, 0x0, 1, 1, 1) -#define MPP3_GE0_TXERR MPP(3, 0x1, 0, 1, 1) -#define MPP3_GE1_RXCLK MPP(3, 0x2, 1, 0, 1) +#define MPP3_GE0_TXERR MPP(3, 0x1, 0, 0, 1) +#define MPP3_GE1_RXCLK MPP(3, 0x2, 0, 0, 1) #define MPP3_UNUSED MPP(3, 0x3, 0, 0, 1) #define MPP4_GPIO MPP(4, 0x0, 1, 1, 1) -#define MPP4_GE0_TXD4 MPP(4, 0x1, 0, 1, 1) -#define MPP4_GE1_TXD0 MPP(4, 0x2, 0, 1, 1) +#define MPP4_GE0_TXD4 MPP(4, 0x1, 0, 0, 1) +#define MPP4_GE1_TXD0 MPP(4, 0x2, 0, 0, 1) #define MPP4_UNUSED MPP(4, 0x3, 0, 0, 1) #define MPP5_GPIO MPP(5, 0x0, 1, 1, 1) -#define MPP5_GE0_TXD5 MPP(5, 0x1, 0, 1, 1) -#define MPP5_GE1_TXD1 MPP(5, 0x2, 0, 1, 1) +#define MPP5_GE0_TXD5 MPP(5, 0x1, 0, 0, 1) +#define MPP5_GE1_TXD1 MPP(5, 0x2, 0, 0, 1) #define MPP5_UNUSED MPP(5, 0x3, 0, 0, 1) #define MPP6_GPIO MPP(6, 0x0, 1, 1, 1) -#define MPP6_GE0_TXD6 MPP(6, 0x1, 0, 1, 1) -#define MPP6_GE1_TXD2 MPP(6, 0x2, 0, 1, 1) +#define MPP6_GE0_TXD6 MPP(6, 0x1, 0, 0, 1) +#define MPP6_GE1_TXD2 MPP(6, 0x2, 0, 0, 1) #define MPP6_UNUSED MPP(6, 0x3, 0, 0, 1) #define MPP7_GPIO MPP(7, 0x0, 1, 1, 1) -#define MPP7_GE0_TXD7 MPP(7, 0x1, 0, 1, 1) -#define MPP7_GE1_TXD3 MPP(7, 0x2, 0, 1, 1) +#define MPP7_GE0_TXD7 MPP(7, 0x1, 0, 0, 1) +#define MPP7_GE1_TXD3 MPP(7, 0x2, 0, 0, 1) #define MPP7_UNUSED MPP(7, 0x3, 0, 0, 1) #define MPP8_GPIO MPP(8, 0x0, 1, 1, 1) -#define MPP8_GE0_RXD4 MPP(8, 0x1, 1, 0, 1) -#define MPP8_GE1_RXD0 MPP(8, 0x2, 1, 0, 1) +#define MPP8_GE0_RXD4 MPP(8, 0x1, 0, 0, 1) +#define MPP8_GE1_RXD0 MPP(8, 0x2, 0, 0, 1) #define MPP8_UNUSED MPP(8, 0x3, 0, 0, 1) #define MPP9_GPIO MPP(9, 0x0, 1, 1, 1) -#define MPP9_GE0_RXD5 MPP(9, 0x1, 1, 0, 1) -#define MPP9_GE1_RXD1 MPP(9, 0x2, 1, 0, 1) +#define MPP9_GE0_RXD5 MPP(9, 0x1, 0, 0, 1) +#define MPP9_GE1_RXD1 MPP(9, 0x2, 0, 0, 1) #define MPP9_UNUSED MPP(9, 0x3, 0, 0, 1) #define MPP10_GPIO MPP(10, 0x0, 1, 1, 1) -#define MPP10_GE0_RXD6 MPP(10, 0x1, 1, 0, 1) -#define MPP10_GE1_RXD2 MPP(10, 0x2, 1, 0, 1) +#define MPP10_GE0_RXD6 MPP(10, 0x1, 0, 0, 1) +#define MPP10_GE1_RXD2 MPP(10, 0x2, 0, 0, 1) #define MPP10_UNUSED MPP(10, 0x3, 0, 0, 1) #define MPP11_GPIO MPP(11, 0x0, 1, 1, 1) -#define MPP11_GE0_RXD7 MPP(11, 0x1, 1, 0, 1) -#define MPP11_GE1_RXD3 MPP(11, 0x2, 1, 0, 1) +#define MPP11_GE0_RXD7 MPP(11, 0x1, 0, 0, 1) +#define MPP11_GE1_RXD3 MPP(11, 0x2, 0, 0, 1) #define MPP11_UNUSED MPP(11, 0x3, 0, 0, 1) #define MPP12_GPIO MPP(12, 0x0, 1, 1, 1) -#define MPP12_M_BB MPP(12, 0x3, 1, 0, 1) -#define MPP12_UA0_CTSn MPP(12, 0x4, 1, 0, 1) -#define MPP12_NAND_FLASH_REn0 MPP(12, 0x5, 0, 1, 1) -#define MPP12_TDM0_SCSn MPP(12, 0X6, 0, 1, 1) +#define MPP12_M_BB MPP(12, 0x3, 0, 0, 1) +#define MPP12_UA0_CTSn MPP(12, 0x4, 0, 0, 1) +#define MPP12_NAND_FLASH_REn0 MPP(12, 0x5, 0, 0, 1) +#define MPP12_TDM0_SCSn MPP(12, 0X6, 0, 0, 1) #define MPP12_UNUSED MPP(12, 0x1, 0, 0, 1) #define MPP13_GPIO MPP(13, 0x0, 1, 1, 1) -#define MPP13_SYSRST_OUTn MPP(13, 0x3, 0, 1, 1) -#define MPP13_UA0_RTSn MPP(13, 0x4, 0, 1, 1) -#define MPP13_NAN_FLASH_WEn0 MPP(13, 0x5, 0, 1, 1) -#define MPP13_TDM_SCLK MPP(13, 0x6, 0, 1, 1) +#define MPP13_SYSRST_OUTn MPP(13, 0x3, 0, 0, 1) +#define MPP13_UA0_RTSn MPP(13, 0x4, 0, 0, 1) +#define MPP13_NAN_FLASH_WEn0 MPP(13, 0x5, 0, 0, 1) +#define MPP13_TDM_SCLK MPP(13, 0x6, 0, 0, 1) #define MPP13_UNUSED MPP(13, 0x1, 0, 0, 1) #define MPP14_GPIO MPP(14, 0x0, 1, 1, 1) -#define MPP14_SATA1_ACTn MPP(14, 0x3, 0, 1, 1) -#define MPP14_UA1_CTSn MPP(14, 0x4, 1, 0, 1) -#define MPP14_NAND_FLASH_REn1 MPP(14, 0x5, 0, 1, 1) -#define MPP14_TDM_SMOSI MPP(14, 0x6, 0, 1, 1) +#define MPP14_SATA1_ACTn MPP(14, 0x3, 0, 0, 1) +#define MPP14_UA1_CTSn MPP(14, 0x4, 0, 0, 1) +#define MPP14_NAND_FLASH_REn1 MPP(14, 0x5, 0, 0, 1) +#define MPP14_TDM_SMOSI MPP(14, 0x6, 0, 0, 1) #define MPP14_UNUSED MPP(14, 0x1, 0, 0, 1) #define MPP15_GPIO MPP(15, 0x0, 1, 1, 1) -#define MPP15_SATA0_ACTn MPP(15, 0x3, 0, 1, 1) -#define MPP15_UA1_RTSn MPP(15, 0x4, 0, 1, 1) -#define MPP15_NAND_FLASH_WEn1 MPP(15, 0x5, 0, 1, 1) -#define MPP15_TDM_SMISO MPP(15, 0x6, 1, 0, 1) +#define MPP15_SATA0_ACTn MPP(15, 0x3, 0, 0, 1) +#define MPP15_UA1_RTSn MPP(15, 0x4, 0, 0, 1) +#define MPP15_NAND_FLASH_WEn1 MPP(15, 0x5, 0, 0, 1) +#define MPP15_TDM_SMISO MPP(15, 0x6, 0, 0, 1) #define MPP15_UNUSED MPP(15, 0x1, 0, 0, 1) #define MPP16_GPIO MPP(16, 0x0, 1, 1, 1) -#define MPP16_SATA1_PRESENTn MPP(16, 0x3, 0, 1, 1) -#define MPP16_UA2_TXD MPP(16, 0x4, 0, 1, 1) -#define MPP16_NAND_FLASH_REn3 MPP(16, 0x5, 0, 1, 1) -#define MPP16_TDM_INTn MPP(16, 0x6, 1, 0, 1) +#define MPP16_SATA1_PRESENTn MPP(16, 0x3, 0, 0, 1) +#define MPP16_UA2_TXD MPP(16, 0x4, 0, 0, 1) +#define MPP16_NAND_FLASH_REn3 MPP(16, 0x5, 0, 0, 1) +#define MPP16_TDM_INTn MPP(16, 0x6, 0, 0, 1) #define MPP16_UNUSED MPP(16, 0x1, 0, 0, 1) #define MPP17_GPIO MPP(17, 0x0, 1, 1, 1) -#define MPP17_SATA0_PRESENTn MPP(17, 0x3, 0, 1, 1) -#define MPP17_UA2_RXD MPP(17, 0x4, 1, 0, 1) -#define MPP17_NAND_FLASH_WEn3 MPP(17, 0x5, 0, 1, 1) -#define MPP17_TDM_RSTn MPP(17, 0x6, 0, 1, 1) +#define MPP17_SATA0_PRESENTn MPP(17, 0x3, 0, 0, 1) +#define MPP17_UA2_RXD MPP(17, 0x4, 0, 0, 1) +#define MPP17_NAND_FLASH_WEn3 MPP(17, 0x5, 0, 0, 1) +#define MPP17_TDM_RSTn MPP(17, 0x6, 0, 0, 1) #define MPP17_UNUSED MPP(17, 0x1, 0, 0, 1) #define MPP18_GPIO MPP(18, 0x0, 1, 1, 1) -#define MPP18_UA0_CTSn MPP(18, 0x4, 1, 0, 1) -#define MPP18_BOOT_FLASH_REn MPP(18, 0x5, 0, 1, 1) +#define MPP18_UA0_CTSn MPP(18, 0x4, 0, 0, 1) +#define MPP18_BOOT_FLASH_REn MPP(18, 0x5, 0, 0, 1) #define MPP18_UNUSED MPP(18, 0x1, 0, 0, 1) #define MPP19_GPIO MPP(19, 0x0, 1, 1, 1) -#define MPP19_UA0_CTSn MPP(19, 0x4, 0, 1, 1) -#define MPP19_BOOT_FLASH_WEn MPP(19, 0x5, 0, 1, 1) +#define MPP19_UA0_CTSn MPP(19, 0x4, 0, 0, 1) +#define MPP19_BOOT_FLASH_WEn MPP(19, 0x5, 0, 0, 1) #define MPP19_UNUSED MPP(19, 0x1, 0, 0, 1) #define MPP20_GPIO MPP(20, 0x0, 1, 1, 1) -#define MPP20_UA1_CTSs MPP(20, 0x4, 1, 0, 1) -#define MPP20_TDM_PCLK MPP(20, 0x6, 1, 1, 0) +#define MPP20_UA1_CTSs MPP(20, 0x4, 0, 0, 1) +#define MPP20_TDM_PCLK MPP(20, 0x6, 0, 0, 0) #define MPP20_UNUSED MPP(20, 0x1, 0, 0, 1) #define MPP21_GPIO MPP(21, 0x0, 1, 1, 1) -#define MPP21_UA1_CTSs MPP(21, 0x4, 0, 1, 1) -#define MPP21_TDM_FSYNC MPP(21, 0x6, 1, 1, 0) +#define MPP21_UA1_CTSs MPP(21, 0x4, 0, 0, 1) +#define MPP21_TDM_FSYNC MPP(21, 0x6, 0, 0, 0) #define MPP21_UNUSED MPP(21, 0x1, 0, 0, 1) #define MPP22_GPIO MPP(22, 0x0, 1, 1, 1) -#define MPP22_UA3_TDX MPP(22, 0x4, 0, 1, 1) -#define MPP22_NAND_FLASH_REn2 MPP(22, 0x5, 0, 1, 1) -#define MPP22_TDM_DRX MPP(22, 0x6, 1, 0, 1) +#define MPP22_UA3_TDX MPP(22, 0x4, 0, 0, 1) +#define MPP22_NAND_FLASH_REn2 MPP(22, 0x5, 0, 0, 1) +#define MPP22_TDM_DRX MPP(22, 0x6, 0, 0, 1) #define MPP22_UNUSED MPP(22, 0x1, 0, 0, 1) #define MPP23_GPIO MPP(23, 0x0, 1, 1, 1) -#define MPP23_UA3_RDX MPP(23, 0x4, 1, 0, 1) -#define MPP23_NAND_FLASH_WEn2 MPP(23, 0x5, 0, 1, 1) -#define MPP23_TDM_DTX MPP(23, 0x6, 0, 1, 1) +#define MPP23_UA3_RDX MPP(23, 0x4, 0, 0, 1) +#define MPP23_NAND_FLASH_WEn2 MPP(23, 0x5, 0, 0, 1) +#define MPP23_TDM_DTX MPP(23, 0x6, 0, 0, 1) #define MPP23_UNUSED MPP(23, 0x1, 0, 0, 1) #define MPP24_GPIO MPP(24, 0x0, 1, 1, 1) -#define MPP24_UA2_TXD MPP(24, 0x4, 0, 1, 1) -#define MPP24_TDM_INTn MPP(24, 0x6, 1, 0, 1) +#define MPP24_UA2_TXD MPP(24, 0x4, 0, 0, 1) +#define MPP24_TDM_INTn MPP(24, 0x6, 0, 0, 1) #define MPP24_UNUSED MPP(24, 0x1, 0, 0, 1) #define MPP25_GPIO MPP(25, 0x0, 1, 1, 1) -#define MPP25_UA2_RXD MPP(25, 0x4, 1, 0, 1) -#define MPP25_TDM_RSTn MPP(25, 0x6, 0, 1, 1) +#define MPP25_UA2_RXD MPP(25, 0x4, 0, 0, 1) +#define MPP25_TDM_RSTn MPP(25, 0x6, 0, 0, 1) #define MPP25_UNUSED MPP(25, 0x1, 0, 0, 1) #define MPP26_GPIO MPP(26, 0x0, 1, 1, 1) -#define MPP26_UA2_CTSn MPP(26, 0x4, 1, 0, 1) -#define MPP26_TDM_PCLK MPP(26, 0x6, 1, 1, 1) +#define MPP26_UA2_CTSn MPP(26, 0x4, 0, 0, 1) +#define MPP26_TDM_PCLK MPP(26, 0x6, 0, 0, 1) #define MPP26_UNUSED MPP(26, 0x1, 0, 0, 1) #define MPP27_GPIO MPP(27, 0x0, 1, 1, 1) -#define MPP27_UA2_RTSn MPP(27, 0x4, 0, 1, 1) -#define MPP27_TDM_FSYNC MPP(27, 0x6, 1, 1, 1) +#define MPP27_UA2_RTSn MPP(27, 0x4, 0, 0, 1) +#define MPP27_TDM_FSYNC MPP(27, 0x6, 0, 0, 1) #define MPP27_UNUSED MPP(27, 0x1, 0, 0, 1) #define MPP28_GPIO MPP(28, 0x0, 1, 1, 1) -#define MPP28_UA3_TXD MPP(28, 0x4, 0, 1, 1) -#define MPP28_TDM_DRX MPP(28, 0x6, 1, 0, 1) +#define MPP28_UA3_TXD MPP(28, 0x4, 0, 0, 1) +#define MPP28_TDM_DRX MPP(28, 0x6, 0, 0, 1) #define MPP28_UNUSED MPP(28, 0x1, 0, 0, 1) #define MPP29_GPIO MPP(29, 0x0, 1, 1, 1) -#define MPP29_UA3_RXD MPP(29, 0x4, 1, 0, 1) -#define MPP29_SYSRST_OUTn MPP(29, 0x5, 0, 1, 1) -#define MPP29_TDM_DTX MPP(29, 0x6, 0, 1, 1) +#define MPP29_UA3_RXD MPP(29, 0x4, 0, 0, 1) +#define MPP29_SYSRST_OUTn MPP(29, 0x5, 0, 0, 1) +#define MPP29_TDM_DTX MPP(29, 0x6, 0, 0, 1) #define MPP29_UNUSED MPP(29, 0x1, 0, 0, 1) #define MPP30_GPIO MPP(30, 0x0, 1, 1, 1) -#define MPP30_UA3_CTSn MPP(30, 0x4, 1, 0, 1) +#define MPP30_UA3_CTSn MPP(30, 0x4, 0, 0, 1) #define MPP30_UNUSED MPP(30, 0x1, 0, 0, 1) #define MPP31_GPIO MPP(31, 0x0, 1, 1, 1) -#define MPP31_UA3_RTSn MPP(31, 0x4, 0, 1, 1) -#define MPP31_TDM1_SCSn MPP(31, 0x6, 0, 1, 1) +#define MPP31_UA3_RTSn MPP(31, 0x4, 0, 0, 1) +#define MPP31_TDM1_SCSn MPP(31, 0x6, 0, 0, 1) #define MPP31_UNUSED MPP(31, 0x1, 0, 0, 1) #define MPP32_GPIO MPP(32, 0x1, 1, 1, 1) -#define MPP32_UA3_TDX MPP(32, 0x4, 0, 1, 1) -#define MPP32_SYSRST_OUTn MPP(32, 0x5, 0, 1, 1) -#define MPP32_TDM0_RXQ MPP(32, 0x6, 0, 1, 1) +#define MPP32_UA3_TDX MPP(32, 0x4, 0, 0, 1) +#define MPP32_SYSRST_OUTn MPP(32, 0x5, 0, 0, 1) +#define MPP32_TDM0_RXQ MPP(32, 0x6, 0, 0, 1) #define MPP32_UNUSED MPP(32, 0x3, 0, 0, 1) #define MPP33_GPIO MPP(33, 0x1, 1, 1, 1) -#define MPP33_UA3_RDX MPP(33, 0x4, 1, 0, 1) -#define MPP33_TDM0_TXQ MPP(33, 0x6, 0, 1, 1) +#define MPP33_UA3_RDX MPP(33, 0x4, 0, 0, 1) +#define MPP33_TDM0_TXQ MPP(33, 0x6, 0, 0, 1) #define MPP33_UNUSED MPP(33, 0x3, 0, 0, 1) #define MPP34_GPIO MPP(34, 0x1, 1, 1, 1) -#define MPP34_UA2_TDX MPP(34, 0x4, 0, 1, 1) -#define MPP34_TDM1_RXQ MPP(34, 0x6, 0, 1, 1) +#define MPP34_UA2_TDX MPP(34, 0x4, 0, 0, 1) +#define MPP34_TDM1_RXQ MPP(34, 0x6, 0, 0, 1) #define MPP34_UNUSED MPP(34, 0x3, 0, 0, 1) #define MPP35_GPIO MPP(35, 0x1, 1, 1, 1) -#define MPP35_UA2_RDX MPP(35, 0x4, 1, 0, 1) -#define MPP35_TDM1_TXQ MPP(35, 0x6, 0, 1, 1) +#define MPP35_UA2_RDX MPP(35, 0x4, 0, 0, 1) +#define MPP35_TDM1_TXQ MPP(35, 0x6, 0, 0, 1) #define MPP35_UNUSED MPP(35, 0x3, 0, 0, 1) #define MPP36_GPIO MPP(36, 0x1, 1, 1, 1) -#define MPP36_UA0_CTSn MPP(36, 0x2, 1, 0, 1) -#define MPP36_UA2_TDX MPP(36, 0x4, 0, 1, 1) -#define MPP36_TDM0_SCSn MPP(36, 0x6, 0, 1, 1) +#define MPP36_UA0_CTSn MPP(36, 0x2, 0, 0, 1) +#define MPP36_UA2_TDX MPP(36, 0x4, 0, 0, 1) +#define MPP36_TDM0_SCSn MPP(36, 0x6, 0, 0, 1) #define MPP36_UNUSED MPP(36, 0x3, 0, 0, 1) #define MPP37_GPIO MPP(37, 0x1, 1, 1, 1) -#define MPP37_UA0_RTSn MPP(37, 0x2, 0, 1, 1) -#define MPP37_UA2_RXD MPP(37, 0x4, 1, 0, 1) -#define MPP37_SYSRST_OUTn MPP(37, 0x5, 0, 1, 1) -#define MPP37_TDM_SCLK MPP(37, 0x6, 0, 1, 1) +#define MPP37_UA0_RTSn MPP(37, 0x2, 0, 0, 1) +#define MPP37_UA2_RXD MPP(37, 0x4, 0, 0, 1) +#define MPP37_SYSRST_OUTn MPP(37, 0x5, 0, 0, 1) +#define MPP37_TDM_SCLK MPP(37, 0x6, 0, 0, 1) #define MPP37_UNUSED MPP(37, 0x3, 0, 0, 1) #define MPP38_GPIO MPP(38, 0x1, 1, 1, 1) -#define MPP38_UA1_CTSn MPP(38, 0x2, 1, 0, 1) -#define MPP38_UA3_TXD MPP(38, 0x4, 0, 1, 1) -#define MPP38_SYSRST_OUTn MPP(38, 0x5, 0, 1, 1) -#define MPP38_TDM_SMOSI MPP(38, 0x6, 0, 1, 1) +#define MPP38_UA1_CTSn MPP(38, 0x2, 0, 0, 1) +#define MPP38_UA3_TXD MPP(38, 0x4, 0, 0, 1) +#define MPP38_SYSRST_OUTn MPP(38, 0x5, 0, 0, 1) +#define MPP38_TDM_SMOSI MPP(38, 0x6, 0, 0, 1) #define MPP38_UNUSED MPP(38, 0x3, 0, 0, 1) #define MPP39_GPIO MPP(39, 0x1, 1, 1, 1) -#define MPP39_UA1_RTSn MPP(39, 0x2, 0, 1, 1) -#define MPP39_UA3_RXD MPP(39, 0x4, 1, 0, 1) -#define MPP39_SYSRST_OUTn MPP(39, 0x5, 0, 1, 1) -#define MPP39_TDM_SMISO MPP(39, 0x6, 1, 0, 1) +#define MPP39_UA1_RTSn MPP(39, 0x2, 0, 0, 1) +#define MPP39_UA3_RXD MPP(39, 0x4, 0, 0, 1) +#define MPP39_SYSRST_OUTn MPP(39, 0x5, 0, 0, 1) +#define MPP39_TDM_SMISO MPP(39, 0x6, 0, 0, 1) #define MPP39_UNUSED MPP(39, 0x3, 0, 0, 1) #define MPP40_GPIO MPP(40, 0x1, 1, 1, 1) -#define MPP40_TDM_INTn MPP(40, 0x6, 1, 0, 1) +#define MPP40_TDM_INTn MPP(40, 0x6, 0, 0, 1) #define MPP40_UNUSED MPP(40, 0x0, 0, 0, 1) #define MPP41_GPIO MPP(41, 0x1, 1, 1, 1) -#define MPP41_TDM_RSTn MPP(41, 0x6, 0, 1, 1) +#define MPP41_TDM_RSTn MPP(41, 0x6, 0, 0, 1) #define MPP41_UNUSED MPP(41, 0x0, 0, 0, 1) #define MPP42_GPIO MPP(42, 0x1, 1, 1, 1) -#define MPP42_TDM_PCLK MPP(42, 0x6, 1, 1, 1) +#define MPP42_TDM_PCLK MPP(42, 0x6, 0, 0, 1) #define MPP42_UNUSED MPP(42, 0x0, 0, 0, 1) #define MPP43_GPIO MPP(43, 0x1, 1, 1, 1) -#define MPP43_TDM_FSYNC MPP(43, 0x6, 1, 1, 1) +#define MPP43_TDM_FSYNC MPP(43, 0x6, 0, 0, 1) #define MPP43_UNUSED MPP(43, 0x0, 0, 0, 1) #define MPP44_GPIO MPP(44, 0x1, 1, 1, 1) -#define MPP44_TDM_DRX MPP(44, 0x6, 1, 0, 1) +#define MPP44_TDM_DRX MPP(44, 0x6, 0, 0, 1) #define MPP44_UNUSED MPP(44, 0x0, 0, 0, 1) #define MPP45_GPIO MPP(45, 0x1, 1, 1, 1) -#define MPP45_SATA0_ACTn MPP(45, 0x3, 0, 1, 1) -#define MPP45_TDM_DRX MPP(45, 0x6, 0, 1, 1) +#define MPP45_SATA0_ACTn MPP(45, 0x3, 0, 0, 1) +#define MPP45_TDM_DRX MPP(45, 0x6, 0, 0, 1) #define MPP45_UNUSED MPP(45, 0x0, 0, 0, 1) #define MPP46_GPIO MPP(46, 0x1, 1, 1, 1) -#define MPP46_TDM_SCSn MPP(46, 0x6, 0, 1, 1) +#define MPP46_TDM_SCSn MPP(46, 0x6, 0, 0, 1) #define MPP46_UNUSED MPP(46, 0x0, 0, 0, 1) @@ -323,14 +323,14 @@ #define MPP48_GPIO MPP(48, 0x1, 1, 1, 1) -#define MPP48_SATA1_ACTn MPP(48, 0x3, 0, 1, 1) +#define MPP48_SATA1_ACTn MPP(48, 0x3, 0, 0, 1) #define MPP48_UNUSED MPP(48, 0x2, 0, 0, 1) #define MPP49_GPIO MPP(49, 0x1, 1, 1, 1) -#define MPP49_SATA0_ACTn MPP(49, 0x3, 0, 1, 1) -#define MPP49_M_BB MPP(49, 0x4, 1, 0, 1) +#define MPP49_SATA0_ACTn MPP(49, 0x3, 0, 0, 1) +#define MPP49_M_BB MPP(49, 0x4, 0, 0, 1) #define MPP49_UNUSED MPP(49, 0x2, 0, 0, 1) diff --git a/arch/arm/mach-omap2/board-4430sdp.c b/arch/arm/mach-omap2/board-4430sdp.c index 63de2d396e2..14a5971d0d4 100644 --- a/arch/arm/mach-omap2/board-4430sdp.c +++ b/arch/arm/mach-omap2/board-4430sdp.c @@ -49,8 +49,9 @@ #define ETH_KS8851_QUART 138 #define OMAP4_SFH7741_SENSOR_OUTPUT_GPIO 184 #define OMAP4_SFH7741_ENABLE_GPIO 188 -#define HDMI_GPIO_HPD 60 /* Hot plug pin for HDMI */ +#define HDMI_GPIO_CT_CP_HPD 60 /* HPD mode enable/disable */ #define HDMI_GPIO_LS_OE 41 /* Level shifter for HDMI */ +#define HDMI_GPIO_HPD 63 /* Hotplug detect */ static const int sdp4430_keymap[] = { KEY(0, 0, KEY_E), @@ -578,12 +579,8 @@ static void __init omap_sfh7741prox_init(void) static void sdp4430_hdmi_mux_init(void) { - /* PAD0_HDMI_HPD_PAD1_HDMI_CEC */ - omap_mux_init_signal("hdmi_hpd", - OMAP_PIN_INPUT_PULLUP); omap_mux_init_signal("hdmi_cec", OMAP_PIN_INPUT_PULLUP); - /* PAD0_HDMI_DDC_SCL_PAD1_HDMI_DDC_SDA */ omap_mux_init_signal("hdmi_ddc_scl", OMAP_PIN_INPUT_PULLUP); omap_mux_init_signal("hdmi_ddc_sda", @@ -591,8 +588,9 @@ static void sdp4430_hdmi_mux_init(void) } static struct gpio sdp4430_hdmi_gpios[] = { - { HDMI_GPIO_HPD, GPIOF_OUT_INIT_HIGH, "hdmi_gpio_hpd" }, + { HDMI_GPIO_CT_CP_HPD, GPIOF_OUT_INIT_HIGH, "hdmi_gpio_ct_cp_hpd" }, { HDMI_GPIO_LS_OE, GPIOF_OUT_INIT_HIGH, "hdmi_gpio_ls_oe" }, + { HDMI_GPIO_HPD, GPIOF_DIR_IN, "hdmi_gpio_hpd" }, }; static int sdp4430_panel_enable_hdmi(struct omap_dss_device *dssdev) @@ -609,26 +607,21 @@ static int sdp4430_panel_enable_hdmi(struct omap_dss_device *dssdev) static void sdp4430_panel_disable_hdmi(struct omap_dss_device *dssdev) { - gpio_free(HDMI_GPIO_LS_OE); - gpio_free(HDMI_GPIO_HPD); + gpio_free_array(sdp4430_hdmi_gpios, ARRAY_SIZE(sdp4430_hdmi_gpios)); } +static struct omap_dss_hdmi_data sdp4430_hdmi_data = { + .hpd_gpio = HDMI_GPIO_HPD, +}; + static struct omap_dss_device sdp4430_hdmi_device = { .name = "hdmi", .driver_name = "hdmi_panel", .type = OMAP_DISPLAY_TYPE_HDMI, - .clocks = { - .dispc = { - .dispc_fclk_src = OMAP_DSS_CLK_SRC_FCK, - }, - .hdmi = { - .regn = 15, - .regm2 = 1, - }, - }, .platform_enable = sdp4430_panel_enable_hdmi, .platform_disable = sdp4430_panel_disable_hdmi, .channel = OMAP_DSS_CHANNEL_DIGIT, + .data = &sdp4430_hdmi_data, }; static struct omap_dss_device *sdp4430_dss_devices[] = { @@ -645,6 +638,10 @@ void omap_4430sdp_display_init(void) { sdp4430_hdmi_mux_init(); omap_display_init(&sdp4430_dss_data); + + omap_mux_init_gpio(HDMI_GPIO_LS_OE, OMAP_PIN_OUTPUT); + omap_mux_init_gpio(HDMI_GPIO_CT_CP_HPD, OMAP_PIN_OUTPUT); + omap_mux_init_gpio(HDMI_GPIO_HPD, OMAP_PIN_INPUT_PULLDOWN); } #ifdef CONFIG_OMAP_MUX diff --git a/arch/arm/mach-omap2/board-omap4panda.c b/arch/arm/mach-omap2/board-omap4panda.c index 0cfe2005cb5..107dfc377a8 100644 --- a/arch/arm/mach-omap2/board-omap4panda.c +++ b/arch/arm/mach-omap2/board-omap4panda.c @@ -52,8 +52,9 @@ #define GPIO_HUB_NRESET 62 #define GPIO_WIFI_PMENA 43 #define GPIO_WIFI_IRQ 53 -#define HDMI_GPIO_HPD 60 /* Hot plug pin for HDMI */ +#define HDMI_GPIO_CT_CP_HPD 60 /* HPD mode enable/disable */ #define HDMI_GPIO_LS_OE 41 /* Level shifter for HDMI */ +#define HDMI_GPIO_HPD 63 /* Hotplug detect */ /* wl127x BT, FM, GPS connectivity chip */ static int wl1271_gpios[] = {46, -1, -1}; @@ -614,12 +615,8 @@ int __init omap4_panda_dvi_init(void) static void omap4_panda_hdmi_mux_init(void) { - /* PAD0_HDMI_HPD_PAD1_HDMI_CEC */ - omap_mux_init_signal("hdmi_hpd", - OMAP_PIN_INPUT_PULLUP); omap_mux_init_signal("hdmi_cec", OMAP_PIN_INPUT_PULLUP); - /* PAD0_HDMI_DDC_SCL_PAD1_HDMI_DDC_SDA */ omap_mux_init_signal("hdmi_ddc_scl", OMAP_PIN_INPUT_PULLUP); omap_mux_init_signal("hdmi_ddc_sda", @@ -627,8 +624,9 @@ static void omap4_panda_hdmi_mux_init(void) } static struct gpio panda_hdmi_gpios[] = { - { HDMI_GPIO_HPD, GPIOF_OUT_INIT_HIGH, "hdmi_gpio_hpd" }, + { HDMI_GPIO_CT_CP_HPD, GPIOF_OUT_INIT_HIGH, "hdmi_gpio_ct_cp_hpd" }, { HDMI_GPIO_LS_OE, GPIOF_OUT_INIT_HIGH, "hdmi_gpio_ls_oe" }, + { HDMI_GPIO_HPD, GPIOF_DIR_IN, "hdmi_gpio_hpd" }, }; static int omap4_panda_panel_enable_hdmi(struct omap_dss_device *dssdev) @@ -645,10 +643,13 @@ static int omap4_panda_panel_enable_hdmi(struct omap_dss_device *dssdev) static void omap4_panda_panel_disable_hdmi(struct omap_dss_device *dssdev) { - gpio_free(HDMI_GPIO_LS_OE); - gpio_free(HDMI_GPIO_HPD); + gpio_free_array(panda_hdmi_gpios, ARRAY_SIZE(panda_hdmi_gpios)); } +static struct omap_dss_hdmi_data omap4_panda_hdmi_data = { + .hpd_gpio = HDMI_GPIO_HPD, +}; + static struct omap_dss_device omap4_panda_hdmi_device = { .name = "hdmi", .driver_name = "hdmi_panel", @@ -656,6 +657,7 @@ static struct omap_dss_device omap4_panda_hdmi_device = { .platform_enable = omap4_panda_panel_enable_hdmi, .platform_disable = omap4_panda_panel_disable_hdmi, .channel = OMAP_DSS_CHANNEL_DIGIT, + .data = &omap4_panda_hdmi_data, }; static struct omap_dss_device *omap4_panda_dss_devices[] = { @@ -679,6 +681,10 @@ void omap4_panda_display_init(void) omap4_panda_hdmi_mux_init(); omap_display_init(&omap4_panda_dss_data); + + omap_mux_init_gpio(HDMI_GPIO_LS_OE, OMAP_PIN_OUTPUT); + omap_mux_init_gpio(HDMI_GPIO_CT_CP_HPD, OMAP_PIN_OUTPUT); + omap_mux_init_gpio(HDMI_GPIO_HPD, OMAP_PIN_INPUT_PULLDOWN); } static void __init omap4_panda_init(void) diff --git a/arch/arm/mach-orion5x/common.c b/arch/arm/mach-orion5x/common.c index 0ab531d047f..8a98da0b3f8 100644 --- a/arch/arm/mach-orion5x/common.c +++ b/arch/arm/mach-orion5x/common.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include "common.h" @@ -72,7 +73,8 @@ void __init orion5x_map_io(void) void __init orion5x_ehci0_init(void) { orion_ehci_init(&orion5x_mbus_dram_info, - ORION5X_USB0_PHYS_BASE, IRQ_ORION5X_USB0_CTRL); + ORION5X_USB0_PHYS_BASE, IRQ_ORION5X_USB0_CTRL, + EHCI_PHY_ORION); } diff --git a/arch/arm/mm/proc-v7.S b/arch/arm/mm/proc-v7.S index a5fb13c0bdc..0329c0ae0a4 100644 --- a/arch/arm/mm/proc-v7.S +++ b/arch/arm/mm/proc-v7.S @@ -368,9 +368,7 @@ __v7_setup: mcreq p15, 0, r10, c15, c0, 1 @ write diagnostic register #endif #ifdef CONFIG_ARM_ERRATA_743622 - teq r6, #0x20 @ present in r2p0 - teqne r6, #0x21 @ present in r2p1 - teqne r6, #0x22 @ present in r2p2 + teq r5, #0x00200000 @ only present in r2p* mrceq p15, 0, r10, c15, c0, 1 @ read diagnostic register orreq r10, r10, #1 << 6 @ set bit #6 mcreq p15, 0, r10, c15, c0, 1 @ write diagnostic register diff --git a/arch/arm/plat-orion/common.c b/arch/arm/plat-orion/common.c index 9e5451b3c8e..11dce87c248 100644 --- a/arch/arm/plat-orion/common.c +++ b/arch/arm/plat-orion/common.c @@ -806,10 +806,7 @@ void __init orion_xor1_init(unsigned long mapbase_low, /***************************************************************************** * EHCI ****************************************************************************/ -static struct orion_ehci_data orion_ehci_data = { - .phy_version = EHCI_PHY_NA, -}; - +static struct orion_ehci_data orion_ehci_data; static u64 ehci_dmamask = DMA_BIT_MASK(32); @@ -830,9 +827,11 @@ static struct platform_device orion_ehci = { void __init orion_ehci_init(struct mbus_dram_target_info *mbus_dram_info, unsigned long mapbase, - unsigned long irq) + unsigned long irq, + enum orion_ehci_phy_ver phy_version) { orion_ehci_data.dram = mbus_dram_info; + orion_ehci_data.phy_version = phy_version; fill_resources(&orion_ehci, orion_ehci_resources, mapbase, SZ_4K - 1, irq); diff --git a/arch/arm/plat-orion/include/plat/common.h b/arch/arm/plat-orion/include/plat/common.h index a63c357e2ab..a2c0e31ce0d 100644 --- a/arch/arm/plat-orion/include/plat/common.h +++ b/arch/arm/plat-orion/include/plat/common.h @@ -95,7 +95,8 @@ void __init orion_xor1_init(unsigned long mapbase_low, void __init orion_ehci_init(struct mbus_dram_target_info *mbus_dram_info, unsigned long mapbase, - unsigned long irq); + unsigned long irq, + enum orion_ehci_phy_ver phy_version); void __init orion_ehci_1_init(struct mbus_dram_target_info *mbus_dram_info, unsigned long mapbase, diff --git a/arch/arm/plat-orion/mpp.c b/arch/arm/plat-orion/mpp.c index 91553432711..3b1e17bd3d1 100644 --- a/arch/arm/plat-orion/mpp.c +++ b/arch/arm/plat-orion/mpp.c @@ -64,8 +64,7 @@ void __init orion_mpp_conf(unsigned int *mpp_list, unsigned int variant_mask, gpio_mode |= GPIO_INPUT_OK; if (*mpp_list & MPP_OUTPUT_MASK) gpio_mode |= GPIO_OUTPUT_OK; - if (sel != 0) - gpio_mode = 0; + orion_gpio_set_valid(num, gpio_mode); } diff --git a/arch/arm/plat-s3c24xx/dma.c b/arch/arm/plat-s3c24xx/dma.c index 539bd0e3def..0719f49defb 100644 --- a/arch/arm/plat-s3c24xx/dma.c +++ b/arch/arm/plat-s3c24xx/dma.c @@ -1249,7 +1249,7 @@ static void s3c2410_dma_resume(void) struct s3c2410_dma_chan *cp = s3c2410_chans + dma_channels - 1; int channel; - for (channel = dma_channels - 1; channel >= 0; cp++, channel--) + for (channel = dma_channels - 1; channel >= 0; cp--, channel--) s3c2410_dma_resume_chan(cp); } diff --git a/arch/avr32/Kconfig b/arch/avr32/Kconfig index e9d689b7c83..c614484f0fc 100644 --- a/arch/avr32/Kconfig +++ b/arch/avr32/Kconfig @@ -8,6 +8,7 @@ config AVR32 select HAVE_KPROBES select HAVE_GENERIC_HARDIRQS select GENERIC_IRQ_PROBE + select GENERIC_ATOMIC64 select HARDIRQS_SW_RESEND select GENERIC_IRQ_SHOW help diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig index c03fef7a9c2..9b922b12e9f 100644 --- a/arch/s390/Kconfig +++ b/arch/s390/Kconfig @@ -228,6 +228,9 @@ config COMPAT config SYSVIPC_COMPAT def_bool y if COMPAT && SYSVIPC +config KEYS_COMPAT + def_bool y if COMPAT && KEYS + config AUDIT_ARCH def_bool y diff --git a/arch/s390/include/asm/compat.h b/arch/s390/include/asm/compat.h index da359ca6fe5..f7b74bcce10 100644 --- a/arch/s390/include/asm/compat.h +++ b/arch/s390/include/asm/compat.h @@ -172,13 +172,6 @@ static inline int is_compat_task(void) return is_32bit_task(); } -#else - -static inline int is_compat_task(void) -{ - return 0; -} - #endif static inline void __user *arch_compat_alloc_user_space(long len) diff --git a/arch/s390/kernel/process.c b/arch/s390/kernel/process.c index 541a7509fae..abdc2b1063e 100644 --- a/arch/s390/kernel/process.c +++ b/arch/s390/kernel/process.c @@ -28,7 +28,6 @@ #include #include #include -#include #include #include "entry.h" diff --git a/arch/s390/kernel/ptrace.c b/arch/s390/kernel/ptrace.c index 5804cfa7cba..5c55466e78e 100644 --- a/arch/s390/kernel/ptrace.c +++ b/arch/s390/kernel/ptrace.c @@ -20,8 +20,8 @@ #include #include #include +#include #include -#include #include #include #include diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c index 0c35dee10b0..7547f57e291 100644 --- a/arch/s390/kernel/setup.c +++ b/arch/s390/kernel/setup.c @@ -42,6 +42,7 @@ #include #include #include +#include #include #include @@ -55,7 +56,6 @@ #include #include #include -#include #include long psw_kernel_bits = (PSW_BASE_BITS | PSW_MASK_DAT | PSW_ASC_PRIMARY | diff --git a/arch/s390/mm/fault.c b/arch/s390/mm/fault.c index fe103e891e7..d814f791400 100644 --- a/arch/s390/mm/fault.c +++ b/arch/s390/mm/fault.c @@ -36,7 +36,6 @@ #include #include #include -#include #include "../kernel/entry.h" #ifndef CONFIG_64BIT diff --git a/arch/s390/mm/mmap.c b/arch/s390/mm/mmap.c index c9a9f7f1818..c0cf9ceb383 100644 --- a/arch/s390/mm/mmap.c +++ b/arch/s390/mm/mmap.c @@ -28,8 +28,8 @@ #include #include #include +#include #include -#include static unsigned long stack_maxrandom_size(void) { diff --git a/block/bsg.c b/block/bsg.c index 0c8b64a1648..792ead66675 100644 --- a/block/bsg.c +++ b/block/bsg.c @@ -985,7 +985,8 @@ void bsg_unregister_queue(struct request_queue *q) mutex_lock(&bsg_mutex); idr_remove(&bsg_minor_idr, bcd->minor); - sysfs_remove_link(&q->kobj, "bsg"); + if (q->kobj.sd) + sysfs_remove_link(&q->kobj, "bsg"); device_unregister(bcd->class_dev); bcd->class_dev = NULL; kref_put(&bcd->ref, bsg_kref_release_function); diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c index 6c949602cbd..0bd4832fb1a 100644 --- a/drivers/acpi/sleep.c +++ b/drivers/acpi/sleep.c @@ -428,6 +428,22 @@ static struct dmi_system_id __initdata acpisleep_dmi_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "1000 Series"), }, }, + { + .callback = init_nvs_nosave, + .ident = "Asus K54C", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "K54C"), + }, + }, + { + .callback = init_nvs_nosave, + .ident = "Asus K54HR", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "K54HR"), + }, + }, {}, }; #endif /* CONFIG_SUSPEND */ diff --git a/drivers/crypto/mv_cesa.c b/drivers/crypto/mv_cesa.c index 38a3297ae2b..f53dd83438b 100644 --- a/drivers/crypto/mv_cesa.c +++ b/drivers/crypto/mv_cesa.c @@ -713,6 +713,7 @@ static int mv_hash_final(struct ahash_request *req) { struct mv_req_hash_ctx *ctx = ahash_request_ctx(req); + ahash_request_set_crypt(req, NULL, req->result, 0); mv_update_hash_req_ctx(ctx, 1, 0); return mv_handle_req(&req->base); } diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 673f0d2cd4e..56315cbef1e 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -2847,6 +2847,20 @@ #define DISP_TILE_SURFACE_SWIZZLING (1<<13) #define DISP_FBC_WM_DIS (1<<15) +/* GEN7 chicken */ +#define GEN7_COMMON_SLICE_CHICKEN1 0x7010 +# define GEN7_CSC1_RHWO_OPT_DISABLE_IN_RCC ((1<<10) | (1<<26)) + +#define GEN7_L3CNTLREG1 0xB01C +#define GEN7_WA_FOR_GEN7_L3_CONTROL 0x3C4FFF8C + +#define GEN7_L3_CHICKEN_MODE_REGISTER 0xB030 +#define GEN7_WA_L3_CHICKEN_MODE 0x20000000 + +/* WaCatErrorRejectionIssue */ +#define GEN7_SQ_CHICKEN_MBCUNIT_CONFIG 0x9030 +#define GEN7_SQ_CHICKEN_MBCUNIT_SQINTMOB (1<<11) + /* PCH */ /* south display engine interrupt */ @@ -3371,6 +3385,7 @@ #define GT_FIFO_FREE_ENTRIES 0x120008 #define GEN6_UCGCTL2 0x9404 +# define GEN6_RCZUNIT_CLOCK_GATE_DISABLE (1 << 13) # define GEN6_RCPBUNIT_CLOCK_GATE_DISABLE (1 << 12) # define GEN6_RCCUNIT_CLOCK_GATE_DISABLE (1 << 11) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index fed87d6a5b9..57f90437d08 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -5265,7 +5265,7 @@ void intel_crtc_load_lut(struct drm_crtc *crtc) int i; /* The clocks have to be on to load the palette. */ - if (!crtc->enabled) + if (!crtc->enabled || !intel_crtc->active) return; /* use legacy palette for Ironlake */ @@ -7457,8 +7457,28 @@ static void ivybridge_init_clock_gating(struct drm_device *dev) I915_WRITE(WM2_LP_ILK, 0); I915_WRITE(WM1_LP_ILK, 0); + /* According to the spec, bit 13 (RCZUNIT) must be set on IVB. + * This implements the WaDisableRCZUnitClockGating workaround. + */ + I915_WRITE(GEN6_UCGCTL2, GEN6_RCZUNIT_CLOCK_GATE_DISABLE); + I915_WRITE(ILK_DSPCLK_GATE, IVB_VRHUNIT_CLK_GATE); + /* Apply the WaDisableRHWOOptimizationForRenderHang workaround. */ + I915_WRITE(GEN7_COMMON_SLICE_CHICKEN1, + GEN7_CSC1_RHWO_OPT_DISABLE_IN_RCC); + + /* WaApplyL3ControlAndL3ChickenMode requires those two on Ivy Bridge */ + I915_WRITE(GEN7_L3CNTLREG1, + GEN7_WA_FOR_GEN7_L3_CONTROL); + I915_WRITE(GEN7_L3_CHICKEN_MODE_REGISTER, + GEN7_WA_L3_CHICKEN_MODE); + + /* This is required by WaCatErrorRejectionIssue */ + I915_WRITE(GEN7_SQ_CHICKEN_MBCUNIT_CONFIG, + I915_READ(GEN7_SQ_CHICKEN_MBCUNIT_CONFIG) | + GEN7_SQ_CHICKEN_MBCUNIT_SQINTMOB); + for_each_pipe(pipe) I915_WRITE(DSPCNTR(pipe), I915_READ(DSPCNTR(pipe)) | diff --git a/drivers/gpu/drm/radeon/r600_blit_shaders.c b/drivers/gpu/drm/radeon/r600_blit_shaders.c index 2d1f6c5ee2a..73e2c7c6edb 100644 --- a/drivers/gpu/drm/radeon/r600_blit_shaders.c +++ b/drivers/gpu/drm/radeon/r600_blit_shaders.c @@ -313,6 +313,10 @@ const u32 r6xx_default_state[] = 0x00000000, /* VGT_REUSE_OFF */ 0x00000000, /* VGT_VTX_CNT_EN */ + 0xc0016900, + 0x000000d4, + 0x00000000, /* SX_MISC */ + 0xc0016900, 0x000002c8, 0x00000000, /* VGT_STRMOUT_BUFFER_EN */ @@ -625,6 +629,10 @@ const u32 r7xx_default_state[] = 0x00000000, /* VGT_REUSE_OFF */ 0x00000000, /* VGT_VTX_CNT_EN */ + 0xc0016900, + 0x000000d4, + 0x00000000, /* SX_MISC */ + 0xc0016900, 0x000002c8, 0x00000000, /* VGT_STRMOUT_BUFFER_EN */ diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index e0a28ade952..6781fe0022c 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -59,6 +59,9 @@ #define USB_VENDOR_ID_AIRCABLE 0x16CA #define USB_DEVICE_ID_AIRCABLE1 0x1502 +#define USB_VENDOR_ID_AIREN 0x1a2c +#define USB_DEVICE_ID_AIREN_SLIMPLUS 0x0002 + #define USB_VENDOR_ID_ALCOR 0x058f #define USB_DEVICE_ID_ALCOR_USBRS232 0x9720 diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index 3146fdcda27..85c845f7f61 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -52,6 +52,7 @@ static const struct hid_blacklist { { USB_VENDOR_ID_PLAYDOTCOM, USB_DEVICE_ID_PLAYDOTCOM_EMS_USBII, HID_QUIRK_MULTI_INPUT }, { USB_VENDOR_ID_TOUCHPACK, USB_DEVICE_ID_TOUCHPACK_RTS, HID_QUIRK_MULTI_INPUT }, + { USB_VENDOR_ID_AIREN, USB_DEVICE_ID_AIREN_SLIMPLUS, HID_QUIRK_NOGET }, { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_UC100KM, HID_QUIRK_NOGET }, { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_CS124U, HID_QUIRK_NOGET }, { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_2PORTKVM, HID_QUIRK_NOGET }, diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 92fd1d787ed..947a8416e53 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -482,8 +482,9 @@ config SENSORS_JC42 If you say yes here, you get support for JEDEC JC42.4 compliant temperature sensors, which are used on many DDR3 memory modules for mobile devices and servers. Support will include, but not be limited - to, ADT7408, CAT34TS02, CAT6095, MAX6604, MCP9805, MCP98242, MCP98243, - MCP9843, SE97, SE98, STTS424(E), TSE2002B3, and TS3000B3. + to, ADT7408, AT30TS00, CAT34TS02, CAT6095, MAX6604, MCP9804, MCP9805, + MCP98242, MCP98243, MCP9843, SE97, SE98, STTS424(E), STTS2002, + STTS3000, TSE2002B3, TSE2002GB2, TS3000B3, and TS3000GB2. This driver can also be built as a module. If so, the module will be called jc42. diff --git a/drivers/hwmon/jc42.c b/drivers/hwmon/jc42.c index 02cebb74e20..ed4392406e9 100644 --- a/drivers/hwmon/jc42.c +++ b/drivers/hwmon/jc42.c @@ -64,6 +64,7 @@ static const unsigned short normal_i2c[] = { /* Manufacturer IDs */ #define ADT_MANID 0x11d4 /* Analog Devices */ +#define ATMEL_MANID 0x001f /* Atmel */ #define MAX_MANID 0x004d /* Maxim */ #define IDT_MANID 0x00b3 /* IDT */ #define MCP_MANID 0x0054 /* Microchip */ @@ -77,15 +78,25 @@ static const unsigned short normal_i2c[] = { #define ADT7408_DEVID 0x0801 #define ADT7408_DEVID_MASK 0xffff +/* Atmel */ +#define AT30TS00_DEVID 0x8201 +#define AT30TS00_DEVID_MASK 0xffff + /* IDT */ #define TS3000B3_DEVID 0x2903 /* Also matches TSE2002B3 */ #define TS3000B3_DEVID_MASK 0xffff +#define TS3000GB2_DEVID 0x2912 /* Also matches TSE2002GB2 */ +#define TS3000GB2_DEVID_MASK 0xffff + /* Maxim */ #define MAX6604_DEVID 0x3e00 #define MAX6604_DEVID_MASK 0xffff /* Microchip */ +#define MCP9804_DEVID 0x0200 +#define MCP9804_DEVID_MASK 0xfffc + #define MCP98242_DEVID 0x2000 #define MCP98242_DEVID_MASK 0xfffc @@ -113,6 +124,12 @@ static const unsigned short normal_i2c[] = { #define STTS424E_DEVID 0x0000 #define STTS424E_DEVID_MASK 0xfffe +#define STTS2002_DEVID 0x0300 +#define STTS2002_DEVID_MASK 0xffff + +#define STTS3000_DEVID 0x0200 +#define STTS3000_DEVID_MASK 0xffff + static u16 jc42_hysteresis[] = { 0, 1500, 3000, 6000 }; struct jc42_chips { @@ -123,8 +140,11 @@ struct jc42_chips { static struct jc42_chips jc42_chips[] = { { ADT_MANID, ADT7408_DEVID, ADT7408_DEVID_MASK }, + { ATMEL_MANID, AT30TS00_DEVID, AT30TS00_DEVID_MASK }, { IDT_MANID, TS3000B3_DEVID, TS3000B3_DEVID_MASK }, + { IDT_MANID, TS3000GB2_DEVID, TS3000GB2_DEVID_MASK }, { MAX_MANID, MAX6604_DEVID, MAX6604_DEVID_MASK }, + { MCP_MANID, MCP9804_DEVID, MCP9804_DEVID_MASK }, { MCP_MANID, MCP98242_DEVID, MCP98242_DEVID_MASK }, { MCP_MANID, MCP98243_DEVID, MCP98243_DEVID_MASK }, { MCP_MANID, MCP9843_DEVID, MCP9843_DEVID_MASK }, @@ -133,6 +153,8 @@ static struct jc42_chips jc42_chips[] = { { NXP_MANID, SE98_DEVID, SE98_DEVID_MASK }, { STM_MANID, STTS424_DEVID, STTS424_DEVID_MASK }, { STM_MANID, STTS424E_DEVID, STTS424E_DEVID_MASK }, + { STM_MANID, STTS2002_DEVID, STTS2002_DEVID_MASK }, + { STM_MANID, STTS3000_DEVID, STTS3000_DEVID_MASK }, }; /* Each client has this additional data */ @@ -161,10 +183,12 @@ static struct jc42_data *jc42_update_device(struct device *dev); static const struct i2c_device_id jc42_id[] = { { "adt7408", 0 }, + { "at30ts00", 0 }, { "cat94ts02", 0 }, { "cat6095", 0 }, { "jc42", 0 }, { "max6604", 0 }, + { "mcp9804", 0 }, { "mcp9805", 0 }, { "mcp98242", 0 }, { "mcp98243", 0 }, @@ -173,8 +197,10 @@ static const struct i2c_device_id jc42_id[] = { { "se97b", 0 }, { "se98", 0 }, { "stts424", 0 }, - { "tse2002b3", 0 }, - { "ts3000b3", 0 }, + { "stts2002", 0 }, + { "stts3000", 0 }, + { "tse2002", 0 }, + { "ts3000", 0 }, { } }; MODULE_DEVICE_TABLE(i2c, jc42_id); diff --git a/drivers/hwmon/pmbus_core.c b/drivers/hwmon/pmbus_core.c index 8e31a8e2c74..ffa54dd7dbd 100644 --- a/drivers/hwmon/pmbus_core.c +++ b/drivers/hwmon/pmbus_core.c @@ -50,7 +50,8 @@ lcrit_alarm, crit_alarm */ #define PMBUS_IOUT_BOOLEANS_PER_PAGE 3 /* alarm, lcrit_alarm, crit_alarm */ -#define PMBUS_POUT_BOOLEANS_PER_PAGE 2 /* alarm, crit_alarm */ +#define PMBUS_POUT_BOOLEANS_PER_PAGE 3 /* cap_alarm, alarm, crit_alarm + */ #define PMBUS_MAX_BOOLEANS_PER_FAN 2 /* alarm, fault */ #define PMBUS_MAX_BOOLEANS_PER_TEMP 4 /* min_alarm, max_alarm, lcrit_alarm, crit_alarm */ diff --git a/drivers/i2c/busses/i2c-mxs.c b/drivers/i2c/busses/i2c-mxs.c index 7e78f7c8785..3d471d56bf1 100644 --- a/drivers/i2c/busses/i2c-mxs.c +++ b/drivers/i2c/busses/i2c-mxs.c @@ -72,6 +72,7 @@ #define MXS_I2C_QUEUESTAT (0x70) #define MXS_I2C_QUEUESTAT_RD_QUEUE_EMPTY 0x00002000 +#define MXS_I2C_QUEUESTAT_WRITE_QUEUE_CNT_MASK 0x0000001F #define MXS_I2C_QUEUECMD (0x80) @@ -219,14 +220,14 @@ static int mxs_i2c_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg, int ret; int flags; - init_completion(&i2c->cmd_complete); - dev_dbg(i2c->dev, "addr: 0x%04x, len: %d, flags: 0x%x, stop: %d\n", msg->addr, msg->len, msg->flags, stop); if (msg->len == 0) return -EINVAL; + init_completion(&i2c->cmd_complete); + flags = stop ? MXS_I2C_CTRL0_POST_SEND_STOP : 0; if (msg->flags & I2C_M_RD) @@ -286,6 +287,7 @@ static irqreturn_t mxs_i2c_isr(int this_irq, void *dev_id) { struct mxs_i2c_dev *i2c = dev_id; u32 stat = readl(i2c->regs + MXS_I2C_CTRL1) & MXS_I2C_IRQ_MASK; + bool is_last_cmd; if (!stat) return IRQ_NONE; @@ -300,9 +302,14 @@ static irqreturn_t mxs_i2c_isr(int this_irq, void *dev_id) else i2c->cmd_err = 0; - complete(&i2c->cmd_complete); + is_last_cmd = (readl(i2c->regs + MXS_I2C_QUEUESTAT) & + MXS_I2C_QUEUESTAT_WRITE_QUEUE_CNT_MASK) == 0; + + if (is_last_cmd || i2c->cmd_err) + complete(&i2c->cmd_complete); writel(stat, i2c->regs + MXS_I2C_CTRL1_CLR); + return IRQ_HANDLED; } diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index 99d58764ef0..0b9944346ec 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c @@ -426,7 +426,9 @@ static const struct alps_model_info *alps_get_model(struct psmouse *psmouse, int /* * First try "E6 report". - * ALPS should return 0,0,10 or 0,0,100 + * ALPS should return 0,0,10 or 0,0,100 if no buttons are pressed. + * The bits 0-2 of the first byte will be 1s if some buttons are + * pressed. */ param[0] = 0; if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES) || @@ -441,7 +443,8 @@ static const struct alps_model_info *alps_get_model(struct psmouse *psmouse, int dbg("E6 report: %2.2x %2.2x %2.2x", param[0], param[1], param[2]); - if (param[0] != 0 || param[1] != 0 || (param[2] != 10 && param[2] != 100)) + if ((param[0] & 0xf8) != 0 || param[1] != 0 || + (param[2] != 10 && param[2] != 100)) return NULL; /* diff --git a/drivers/md/dm-io.c b/drivers/md/dm-io.c index dc4433b13db..e0e775f41b7 100644 --- a/drivers/md/dm-io.c +++ b/drivers/md/dm-io.c @@ -296,6 +296,8 @@ static void do_region(int rw, unsigned region, struct dm_io_region *where, unsigned offset; unsigned num_bvecs; sector_t remaining = where->count; + struct request_queue *q = bdev_get_queue(where->bdev); + sector_t discard_sectors; /* * where->count may be zero if rw holds a flush and we need to @@ -305,9 +307,12 @@ static void do_region(int rw, unsigned region, struct dm_io_region *where, /* * Allocate a suitably sized-bio. */ - num_bvecs = dm_sector_div_up(remaining, - (PAGE_SIZE >> SECTOR_SHIFT)); - num_bvecs = min_t(int, bio_get_nr_vecs(where->bdev), num_bvecs); + if (rw & REQ_DISCARD) + num_bvecs = 1; + else + num_bvecs = min_t(int, bio_get_nr_vecs(where->bdev), + dm_sector_div_up(remaining, (PAGE_SIZE >> SECTOR_SHIFT))); + bio = bio_alloc_bioset(GFP_NOIO, num_bvecs, io->client->bios); if (!bio) { printk(KERN_WARNING "%s : %s() failed\n", __FILE__, __func__); @@ -319,10 +324,14 @@ static void do_region(int rw, unsigned region, struct dm_io_region *where, bio->bi_destructor = dm_bio_destructor; store_io_and_region_in_bio(bio, io, region); - /* - * Try and add as many pages as possible. - */ - while (remaining) { + if (rw & REQ_DISCARD) { + discard_sectors = min_t(sector_t, q->limits.max_discard_sectors, remaining); + bio->bi_size = discard_sectors << SECTOR_SHIFT; + remaining -= discard_sectors; + } else while (remaining) { + /* + * Try and add as many pages as possible. + */ dp->get_page(dp, &page, &len, &offset); len = min(len, to_bytes(remaining)); if (!bio_add_page(bio, page, len, offset)) diff --git a/drivers/md/dm-raid.c b/drivers/md/dm-raid.c index e5d8904fc8f..437ae1825f1 100644 --- a/drivers/md/dm-raid.c +++ b/drivers/md/dm-raid.c @@ -468,6 +468,7 @@ static int raid_ctr(struct dm_target *ti, unsigned argc, char **argv) INIT_WORK(&rs->md.event_work, do_table_event); ti->split_io = rs->md.chunk_sectors; ti->private = rs; + ti->num_flush_requests = 1; mutex_lock(&rs->md.reconfig_mutex); ret = md_run(&rs->md); diff --git a/drivers/mfd/cs5535-mfd.c b/drivers/mfd/cs5535-mfd.c index 155fa040788..e488a78a2fd 100644 --- a/drivers/mfd/cs5535-mfd.c +++ b/drivers/mfd/cs5535-mfd.c @@ -179,7 +179,7 @@ static struct pci_device_id cs5535_mfd_pci_tbl[] = { }; MODULE_DEVICE_TABLE(pci, cs5535_mfd_pci_tbl); -static struct pci_driver cs5535_mfd_drv = { +static struct pci_driver cs5535_mfd_driver = { .name = DRV_NAME, .id_table = cs5535_mfd_pci_tbl, .probe = cs5535_mfd_probe, @@ -188,12 +188,12 @@ static struct pci_driver cs5535_mfd_drv = { static int __init cs5535_mfd_init(void) { - return pci_register_driver(&cs5535_mfd_drv); + return pci_register_driver(&cs5535_mfd_driver); } static void __exit cs5535_mfd_exit(void) { - pci_unregister_driver(&cs5535_mfd_drv); + pci_unregister_driver(&cs5535_mfd_driver); } module_init(cs5535_mfd_init); diff --git a/drivers/mfd/mfd-core.c b/drivers/mfd/mfd-core.c index 0902523af62..acf9dad686a 100644 --- a/drivers/mfd/mfd-core.c +++ b/drivers/mfd/mfd-core.c @@ -122,7 +122,7 @@ static int mfd_add_device(struct device *parent, int id, } if (!cell->ignore_resource_conflicts) { - ret = acpi_check_resource_conflict(res); + ret = acpi_check_resource_conflict(&res[r]); if (ret) goto fail_res; } diff --git a/drivers/misc/cs5535-mfgpt.c b/drivers/misc/cs5535-mfgpt.c index bc685bfc4c3..87a390de054 100644 --- a/drivers/misc/cs5535-mfgpt.c +++ b/drivers/misc/cs5535-mfgpt.c @@ -262,7 +262,7 @@ static void __init reset_all_timers(void) * In other cases (such as with VSAless OpenFirmware), the system firmware * leaves timers available for us to use. */ -static int __init scan_timers(struct cs5535_mfgpt_chip *mfgpt) +static int __devinit scan_timers(struct cs5535_mfgpt_chip *mfgpt) { struct cs5535_mfgpt_timer timer = { .chip = mfgpt }; unsigned long flags; diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index ba31abee948..92e54370183 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -139,8 +139,9 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg) imx_data->scratchpad = val; return; case SDHCI_COMMAND: - if ((host->cmd->opcode == MMC_STOP_TRANSMISSION) - && (imx_data->flags & ESDHC_FLAG_MULTIBLK_NO_INT)) + if ((host->cmd->opcode == MMC_STOP_TRANSMISSION || + host->cmd->opcode == MMC_SET_BLOCK_COUNT) && + (imx_data->flags & ESDHC_FLAG_MULTIBLK_NO_INT)) val |= SDHCI_CMD_ABORTCMD; writel(val << 16 | imx_data->scratchpad, host->ioaddr + SDHCI_TRANSFER_MODE); diff --git a/drivers/net/usb/cdc_ether.c b/drivers/net/usb/cdc_ether.c index c924ea2bce0..13919ddb6f5 100644 --- a/drivers/net/usb/cdc_ether.c +++ b/drivers/net/usb/cdc_ether.c @@ -570,6 +570,13 @@ static const struct usb_device_id products [] = { .driver_info = (unsigned long)&wwan_info, }, +/* Logitech Harmony 900 - uses the pseudo-MDLM (BLAN) driver */ +{ + USB_DEVICE_AND_INTERFACE_INFO(0x046d, 0xc11f, USB_CLASS_COMM, + USB_CDC_SUBCLASS_MDLM, USB_CDC_PROTO_NONE), + .driver_info = 0, +}, + /* * WHITELIST!!! * diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c index ce395fe5de2..4918cf54c47 100644 --- a/drivers/net/usb/usbnet.c +++ b/drivers/net/usb/usbnet.c @@ -585,6 +585,7 @@ static int unlink_urbs (struct usbnet *dev, struct sk_buff_head *q) entry = (struct skb_data *) skb->cb; urb = entry->urb; + spin_unlock_irqrestore(&q->lock, flags); // during some PM-driven resume scenarios, // these (async) unlinks complete immediately retval = usb_unlink_urb (urb); @@ -592,6 +593,7 @@ static int unlink_urbs (struct usbnet *dev, struct sk_buff_head *q) netdev_dbg(dev->net, "unlink urb err, %d\n", retval); else count++; + spin_lock_irqsave(&q->lock, flags); } spin_unlock_irqrestore (&q->lock, flags); return count; diff --git a/drivers/net/usb/zaurus.c b/drivers/net/usb/zaurus.c index 1a2234c2051..246b3bb14f2 100644 --- a/drivers/net/usb/zaurus.c +++ b/drivers/net/usb/zaurus.c @@ -349,6 +349,13 @@ static const struct usb_device_id products [] = { ZAURUS_MASTER_INTERFACE, .driver_info = OLYMPUS_MXL_INFO, }, + +/* Logitech Harmony 900 - uses the pseudo-MDLM (BLAN) driver */ +{ + USB_DEVICE_AND_INTERFACE_INFO(0x046d, 0xc11f, USB_CLASS_COMM, + USB_CDC_SUBCLASS_MDLM, USB_CDC_PROTO_NONE), + .driver_info = (unsigned long) &bogus_mdlm_info, +}, { }, // END }; MODULE_DEVICE_TABLE(usb, products); diff --git a/drivers/net/wireless/ath/ath9k/ar5008_phy.c b/drivers/net/wireless/ath/ath9k/ar5008_phy.c index 441bb33f17a..0f23b1a789e 100644 --- a/drivers/net/wireless/ath/ath9k/ar5008_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar5008_phy.c @@ -489,8 +489,6 @@ static int ar5008_hw_rf_alloc_ext_banks(struct ath_hw *ah) ATH_ALLOC_BANK(ah->analogBank6Data, ah->iniBank6.ia_rows); ATH_ALLOC_BANK(ah->analogBank6TPCData, ah->iniBank6TPC.ia_rows); ATH_ALLOC_BANK(ah->analogBank7Data, ah->iniBank7.ia_rows); - ATH_ALLOC_BANK(ah->addac5416_21, - ah->iniAddac.ia_rows * ah->iniAddac.ia_columns); ATH_ALLOC_BANK(ah->bank6Temp, ah->iniBank6.ia_rows); return 0; @@ -519,7 +517,6 @@ static void ar5008_hw_rf_free_ext_banks(struct ath_hw *ah) ATH_FREE_BANK(ah->analogBank6Data); ATH_FREE_BANK(ah->analogBank6TPCData); ATH_FREE_BANK(ah->analogBank7Data); - ATH_FREE_BANK(ah->addac5416_21); ATH_FREE_BANK(ah->bank6Temp); #undef ATH_FREE_BANK @@ -799,27 +796,7 @@ static int ar5008_hw_process_ini(struct ath_hw *ah, REG_WRITE(ah, AR_PHY_ADC_SERIAL_CTL, AR_PHY_SEL_EXTERNAL_RADIO); ah->eep_ops->set_addac(ah, chan); - if (AR_SREV_5416_22_OR_LATER(ah)) { - REG_WRITE_ARRAY(&ah->iniAddac, 1, regWrites); - } else { - struct ar5416IniArray temp; - u32 addacSize = - sizeof(u32) * ah->iniAddac.ia_rows * - ah->iniAddac.ia_columns; - - /* For AR5416 2.0/2.1 */ - memcpy(ah->addac5416_21, - ah->iniAddac.ia_array, addacSize); - - /* override CLKDRV value at [row, column] = [31, 1] */ - (ah->addac5416_21)[31 * ah->iniAddac.ia_columns + 1] = 0; - - temp.ia_array = ah->addac5416_21; - temp.ia_columns = ah->iniAddac.ia_columns; - temp.ia_rows = ah->iniAddac.ia_rows; - REG_WRITE_ARRAY(&temp, 1, regWrites); - } - + REG_WRITE_ARRAY(&ah->iniAddac, 1, regWrites); REG_WRITE(ah, AR_PHY_ADC_SERIAL_CTL, AR_PHY_SEL_INTERNAL_ADDAC); ENABLE_REGWRITE_BUFFER(ah); diff --git a/drivers/net/wireless/ath/ath9k/ar9002_hw.c b/drivers/net/wireless/ath/ath9k/ar9002_hw.c index c32f9d1b215..30bf703c044 100644 --- a/drivers/net/wireless/ath/ath9k/ar9002_hw.c +++ b/drivers/net/wireless/ath/ath9k/ar9002_hw.c @@ -179,6 +179,25 @@ static void ar9002_hw_init_mode_regs(struct ath_hw *ah) INIT_INI_ARRAY(&ah->iniAddac, ar5416Addac, ARRAY_SIZE(ar5416Addac), 2); } + + /* iniAddac needs to be modified for these chips */ + if (AR_SREV_9160(ah) || !AR_SREV_5416_22_OR_LATER(ah)) { + struct ar5416IniArray *addac = &ah->iniAddac; + u32 size = sizeof(u32) * addac->ia_rows * addac->ia_columns; + u32 *data; + + data = kmalloc(size, GFP_KERNEL); + if (!data) + return; + + memcpy(data, addac->ia_array, size); + addac->ia_array = data; + + if (!AR_SREV_5416_22_OR_LATER(ah)) { + /* override CLKDRV value */ + INI_RA(addac, 31,1) = 0; + } + } } /* Support for Japan ch.14 (2484) spread */ diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index 939cc9d76c2..9dc2666fd1c 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -763,7 +763,6 @@ struct ath_hw { u32 *analogBank6Data; u32 *analogBank6TPCData; u32 *analogBank7Data; - u32 *addac5416_21; u32 *bank6Temp; u8 txpower_limit; diff --git a/drivers/net/wireless/ath/carl9170/tx.c b/drivers/net/wireless/ath/carl9170/tx.c index e94084fcf6f..f190f3219fc 100644 --- a/drivers/net/wireless/ath/carl9170/tx.c +++ b/drivers/net/wireless/ath/carl9170/tx.c @@ -1245,6 +1245,7 @@ static bool carl9170_tx_ps_drop(struct ar9170 *ar, struct sk_buff *skb) atomic_dec(&ar->tx_ampdu_upload); tx_info->flags |= IEEE80211_TX_STAT_TX_FILTERED; + carl9170_release_dev_space(ar, skb); carl9170_tx_status(ar, skb, false); return true; } diff --git a/drivers/regulator/88pm8607.c b/drivers/regulator/88pm8607.c index d63fddb0fbb..acda58e6ef0 100644 --- a/drivers/regulator/88pm8607.c +++ b/drivers/regulator/88pm8607.c @@ -195,7 +195,7 @@ static const unsigned int LDO12_suspend_table[] = { }; static const unsigned int LDO13_table[] = { - 1300000, 1800000, 2000000, 2500000, 2800000, 3000000, 0, 0, + 1200000, 1300000, 1800000, 2000000, 2500000, 2800000, 3000000, 0, }; static const unsigned int LDO13_suspend_table[] = { @@ -388,10 +388,10 @@ static struct pm8607_regulator_info pm8607_regulator_info[] = { PM8607_LDO( 7, LDO7, 0, 3, SUPPLIES_EN12, 1), PM8607_LDO( 8, LDO8, 0, 3, SUPPLIES_EN12, 2), PM8607_LDO( 9, LDO9, 0, 3, SUPPLIES_EN12, 3), - PM8607_LDO(10, LDO10, 0, 3, SUPPLIES_EN12, 4), + PM8607_LDO(10, LDO10, 0, 4, SUPPLIES_EN12, 4), PM8607_LDO(12, LDO12, 0, 4, SUPPLIES_EN12, 5), PM8607_LDO(13, VIBRATOR_SET, 1, 3, VIBRATOR_SET, 0), - PM8607_LDO(14, LDO14, 0, 4, SUPPLIES_EN12, 6), + PM8607_LDO(14, LDO14, 0, 3, SUPPLIES_EN12, 6), }; static int __devinit pm8607_regulator_probe(struct platform_device *pdev) diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index 30fb979d684..cc2dd7fb289 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -18,12 +18,12 @@ #include /* HDIO_GETGEO */ #include #include +#include #include #include #include #include -#include #include #include #include diff --git a/drivers/s390/block/dasd_ioctl.c b/drivers/s390/block/dasd_ioctl.c index 72261e4c516..9caeaea5d09 100644 --- a/drivers/s390/block/dasd_ioctl.c +++ b/drivers/s390/block/dasd_ioctl.c @@ -13,6 +13,7 @@ #define KMSG_COMPONENT "dasd" #include +#include #include #include #include diff --git a/drivers/s390/char/fs3270.c b/drivers/s390/char/fs3270.c index f6489eb7e97..e197fd7e1c0 100644 --- a/drivers/s390/char/fs3270.c +++ b/drivers/s390/char/fs3270.c @@ -14,8 +14,8 @@ #include #include #include +#include -#include #include #include #include diff --git a/drivers/s390/char/vmcp.c b/drivers/s390/char/vmcp.c index 31a3ccbb649..84e569c9c15 100644 --- a/drivers/s390/char/vmcp.c +++ b/drivers/s390/char/vmcp.c @@ -13,6 +13,7 @@ #include #include +#include #include #include #include diff --git a/drivers/s390/cio/chsc_sch.c b/drivers/s390/cio/chsc_sch.c index e950f1ad4dd..ec760297dd3 100644 --- a/drivers/s390/cio/chsc_sch.c +++ b/drivers/s390/cio/chsc_sch.c @@ -8,6 +8,7 @@ */ #include +#include #include #include #include diff --git a/drivers/s390/scsi/zfcp_cfdc.c b/drivers/s390/scsi/zfcp_cfdc.c index 303dde09d29..fab2c2592a9 100644 --- a/drivers/s390/scsi/zfcp_cfdc.c +++ b/drivers/s390/scsi/zfcp_cfdc.c @@ -11,6 +11,7 @@ #define KMSG_COMPONENT "zfcp" #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt +#include #include #include #include diff --git a/drivers/scsi/osd/osd_uld.c b/drivers/scsi/osd/osd_uld.c index b31a8e3841d..d4ed9eb5265 100644 --- a/drivers/scsi/osd/osd_uld.c +++ b/drivers/scsi/osd/osd_uld.c @@ -69,10 +69,10 @@ #ifndef SCSI_OSD_MAJOR # define SCSI_OSD_MAJOR 260 #endif -#define SCSI_OSD_MAX_MINOR 64 +#define SCSI_OSD_MAX_MINOR MINORMASK static const char osd_name[] = "osd"; -static const char *osd_version_string = "open-osd 0.2.0"; +static const char *osd_version_string = "open-osd 0.2.1"; MODULE_AUTHOR("Boaz Harrosh "); MODULE_DESCRIPTION("open-osd Upper-Layer-Driver osd.ko"); diff --git a/drivers/staging/lirc/lirc_serial.c b/drivers/staging/lirc/lirc_serial.c index 805df913bb6..21cbc9ae79c 100644 --- a/drivers/staging/lirc/lirc_serial.c +++ b/drivers/staging/lirc/lirc_serial.c @@ -836,25 +836,22 @@ static int hardware_init_port(void) return 0; } -static int init_port(void) +static int __devinit lirc_serial_probe(struct platform_device *dev) { int i, nlow, nhigh, result; result = request_irq(irq, irq_handler, IRQF_DISABLED | (share_irq ? IRQF_SHARED : 0), LIRC_DRIVER_NAME, (void *)&hardware); - - switch (result) { - case -EBUSY: - printk(KERN_ERR LIRC_DRIVER_NAME ": IRQ %d busy\n", irq); - return -EBUSY; - case -EINVAL: - printk(KERN_ERR LIRC_DRIVER_NAME - ": Bad irq number or handler\n"); - return -EINVAL; - default: - break; - }; + if (result < 0) { + if (result == -EBUSY) + printk(KERN_ERR LIRC_DRIVER_NAME ": IRQ %d busy\n", + irq); + else if (result == -EINVAL) + printk(KERN_ERR LIRC_DRIVER_NAME + ": Bad irq number or handler\n"); + return result; + } /* Reserve io region. */ /* @@ -875,11 +872,14 @@ static int init_port(void) ": or compile the serial port driver as module and\n"); printk(KERN_WARNING LIRC_DRIVER_NAME ": make sure this module is loaded first\n"); - return -EBUSY; + result = -EBUSY; + goto exit_free_irq; } - if (hardware_init_port() < 0) - return -EINVAL; + if (hardware_init_port() < 0) { + result = -EINVAL; + goto exit_release_region; + } /* Initialize pulse/space widths */ init_timing_params(duty_cycle, freq); @@ -911,6 +911,28 @@ static int init_port(void) dprintk("Interrupt %d, port %04x obtained\n", irq, io); return 0; + +exit_release_region: + if (iommap != 0) + release_mem_region(iommap, 8 << ioshift); + else + release_region(io, 8); +exit_free_irq: + free_irq(irq, (void *)&hardware); + + return result; +} + +static int __devexit lirc_serial_remove(struct platform_device *dev) +{ + free_irq(irq, (void *)&hardware); + + if (iommap != 0) + release_mem_region(iommap, 8 << ioshift); + else + release_region(io, 8); + + return 0; } static int set_use_inc(void *data) @@ -1076,16 +1098,6 @@ static struct lirc_driver driver = { static struct platform_device *lirc_serial_dev; -static int __devinit lirc_serial_probe(struct platform_device *dev) -{ - return 0; -} - -static int __devexit lirc_serial_remove(struct platform_device *dev) -{ - return 0; -} - static int lirc_serial_suspend(struct platform_device *dev, pm_message_t state) { @@ -1112,10 +1124,8 @@ static int lirc_serial_resume(struct platform_device *dev) { unsigned long flags; - if (hardware_init_port() < 0) { - lirc_serial_exit(); + if (hardware_init_port() < 0) return -EINVAL; - } spin_lock_irqsave(&hardware[type].lock, flags); /* Enable Interrupt */ @@ -1188,10 +1198,6 @@ static int __init lirc_serial_init_module(void) { int result; - result = lirc_serial_init(); - if (result) - return result; - switch (type) { case LIRC_HOMEBREW: case LIRC_IRDEO: @@ -1211,8 +1217,7 @@ static int __init lirc_serial_init_module(void) break; #endif default: - result = -EINVAL; - goto exit_serial_exit; + return -EINVAL; } if (!softcarrier) { switch (type) { @@ -1228,37 +1233,26 @@ static int __init lirc_serial_init_module(void) } } - result = init_port(); - if (result < 0) - goto exit_serial_exit; + result = lirc_serial_init(); + if (result) + return result; + driver.features = hardware[type].features; driver.dev = &lirc_serial_dev->dev; driver.minor = lirc_register_driver(&driver); if (driver.minor < 0) { printk(KERN_ERR LIRC_DRIVER_NAME ": register_chrdev failed!\n"); - result = -EIO; - goto exit_release; + lirc_serial_exit(); + return -EIO; } return 0; -exit_release: - release_region(io, 8); -exit_serial_exit: - lirc_serial_exit(); - return result; } static void __exit lirc_serial_exit_module(void) { - lirc_serial_exit(); - - free_irq(irq, (void *)&hardware); - - if (iommap != 0) - release_mem_region(iommap, 8 << ioshift); - else - release_region(io, 8); lirc_unregister_driver(driver.minor); + lirc_serial_exit(); dprintk("cleaned up module\n"); } diff --git a/drivers/video/omap2/dss/hdmi.c b/drivers/video/omap2/dss/hdmi.c index b0555f4f0a7..fadd6a0836c 100644 --- a/drivers/video/omap2/dss/hdmi.c +++ b/drivers/video/omap2/dss/hdmi.c @@ -29,6 +29,7 @@ #include #include #include +#include #include