diff options
author | Nitin Kumbhar <nkumbhar@nvidia.com> | 2010-11-23 12:01:55 +0530 |
---|---|---|
committer | Nitin Kumbhar <nkumbhar@nvidia.com> | 2010-11-23 12:01:55 +0530 |
commit | 99021ee71173b95afdd9b2af6bcdafdb53eb82c1 (patch) | |
tree | 74685f62d42eadd741c41a5f371eeb2eecc9cdea | |
parent | 5d3ad2e2d95632f31b2cb0ae045fe53f85d7565a (diff) | |
parent | b4d50aa63c58b8affa08f60f4dba415040845371 (diff) |
merging android-tegra-2.6.36 into git-master/linux-2.6/android-tegra-2.6.36
Change-Id: Ibd208fc6b803bbf729d1f554839ebaee8c8461ce
25 files changed, 1127 insertions, 1321 deletions
diff --git a/arch/arm/common/fiq_debugger.c b/arch/arm/common/fiq_debugger.c index a97c9b2c197a..f172c15f1d3e 100644 --- a/arch/arm/common/fiq_debugger.c +++ b/arch/arm/common/fiq_debugger.c @@ -447,10 +447,35 @@ void dump_stacktrace(struct fiq_debugger_state *state, tail = user_backtrace(state, tail); } +static void debug_help(struct fiq_debugger_state *state) +{ + debug_printf(state, "FIQ Debugger commands:\n" + " pc PC status\n" + " regs Register dump\n" + " allregs Extended Register dump\n" + " bt Stack trace\n" + " reboot Reboot\n" + " irqs Interupt status\n" + " kmsg Kernel log\n" + " version Kernel version\n"); + debug_printf(state, " sleep Allow sleep while in FIQ\n" + " nosleep Disable sleep while in FIQ\n" + " console Switch terminal to console\n" + " cpu Current CPU\n" + " cpu <number> Switch to CPU<number>\n"); + if (!state->debug_busy) { + strcpy(state->debug_cmd, "help"); + state->debug_busy = 1; + debug_force_irq(state); + } +} + static void debug_exec(struct fiq_debugger_state *state, const char *cmd, unsigned *regs, void *svc_sp) { - if (!strcmp(cmd, "pc")) { + if (!strcmp(cmd, "help") || !strcmp(cmd, "?")) { + debug_help(state); + } else if (!strcmp(cmd, "pc")) { debug_printf(state, " pc %08x cpsr %08x mode %s\n", regs[15], regs[16], mode_name(regs[16])); } else if (!strcmp(cmd, "regs")) { diff --git a/arch/arm/mach-tegra/dma.c b/arch/arm/mach-tegra/dma.c index d8d47ed610ab..97335c0f93fb 100644 --- a/arch/arm/mach-tegra/dma.c +++ b/arch/arm/mach-tegra/dma.c @@ -170,6 +170,11 @@ void tegra_dma_stop(struct tegra_dma_channel *ch) writel(status, ch->addr + APB_DMA_CHAN_STA); } +bool tegra_dma_is_stopped(struct tegra_dma_channel *ch) +{ + return !!(readl(ch->addr + APB_DMA_CHAN_STA) & CSR_ENB); +} + int tegra_dma_cancel(struct tegra_dma_channel *ch) { unsigned long irq_flags; @@ -207,7 +212,7 @@ static unsigned int dma_active_count(struct tegra_dma_channel *ch, * half DMA buffer. So, if the DMA already finished half the DMA * then add the half buffer to the completed count. */ - if (ch->mode & TEGRA_DMA_MODE_CONTINUOUS) + if (ch->mode & TEGRA_DMA_MODE_CONTINUOUS_DOUBLE) if (req->buffer_status == TEGRA_DMA_REQ_BUF_STATUS_HALF_FULL) bytes_transferred += req_transfer_count; @@ -334,7 +339,8 @@ int tegra_dma_enqueue_req(struct tegra_dma_channel *ch, req->bytes_transferred = 0; req->status = 0; - req->buffer_status = 0; + /* STATUS_EMPTY just means the DMA hasn't processed the buf yet. */ + req->buffer_status = TEGRA_DMA_REQ_BUF_STATUS_EMPTY; if (list_empty(&ch->list)) start_dma = 1; @@ -342,6 +348,34 @@ int tegra_dma_enqueue_req(struct tegra_dma_channel *ch, if (start_dma) tegra_dma_update_hw(ch, req); + /* Check to see if this request needs to be pushed immediately. + * For continuous single-buffer DMA: + * The first buffer is always in-flight. The 2nd buffer should + * also be in-flight. The 3rd buffer becomes in-flight when the + * first is completed in the interrupt. + */ + else if (ch->mode & TEGRA_DMA_MODE_CONTINUOUS_SINGLE) { + struct tegra_dma_req *first_req, *second_req; + first_req = list_entry(ch->list.next, + typeof(*first_req), node); + second_req = list_entry(first_req->node.next, + typeof(*second_req), node); + if (second_req == req) { + unsigned long status = + readl(ch->addr + APB_DMA_CHAN_STA); + if (!(status & STA_ISE_EOC)) + tegra_dma_update_hw_partial(ch, req); + /* Handle the case where the IRQ fired while we're + * writing the interrupts. + */ + if (status & STA_ISE_EOC) { + /* Interrupt fired, let the IRQ stop/restart + * the DMA with this buffer in a clean way. + */ + req->status = TEGRA_DMA_REQ_SUCCESS; + } + } + } spin_unlock_irqrestore(&ch->lock, irq_flags); @@ -394,6 +428,7 @@ static void tegra_dma_update_hw_partial(struct tegra_dma_channel *ch, { u32 apb_ptr; u32 ahb_ptr; + u32 csr; if (req->to_memory) { apb_ptr = req->source_addr; @@ -405,6 +440,15 @@ static void tegra_dma_update_hw_partial(struct tegra_dma_channel *ch, writel(apb_ptr, ch->addr + APB_DMA_CHAN_APB_PTR); writel(ahb_ptr, ch->addr + APB_DMA_CHAN_AHB_PTR); + if (ch->mode & TEGRA_DMA_MODE_CONTINUOUS_DOUBLE) + ch->req_transfer_count = (req->size >> 3) - 1; + else + ch->req_transfer_count = (req->size >> 2) - 1; + csr = readl(ch->addr + APB_DMA_CHAN_CSR); + csr &= ~CSR_WCOUNT_MASK; + csr |= ch->req_transfer_count << CSR_WCOUNT_SHIFT; + writel(csr, ch->addr + APB_DMA_CHAN_CSR); + req->status = TEGRA_DMA_REQ_INFLIGHT; return; } @@ -430,21 +474,21 @@ static void tegra_dma_update_hw(struct tegra_dma_channel *ch, csr |= req->req_sel << CSR_REQ_SEL_SHIFT; - /* One shot mode is always single buffered, - * continuous mode is always double buffered - * */ + ch->req_transfer_count = (req->size >> 2) - 1; + + /* One shot mode is always single buffered. Continuous mode could + * support either. + */ if (ch->mode & TEGRA_DMA_MODE_ONESHOT) { csr |= CSR_ONCE; - ch->req_transfer_count = (req->size >> 2) - 1; - } else { + } else if (ch->mode & TEGRA_DMA_MODE_CONTINUOUS_DOUBLE) { ahb_seq |= AHB_SEQ_DBL_BUF; - - /* In double buffered mode, we set the size to half the - * requested size and interrupt when half the buffer - * is full */ + /* We want an interrupt halfway through, then on the + * completion. The double buffer means 2 interrupts + * pass before the DMA HW latches a new AHB_PTR etc. + */ ch->req_transfer_count = (req->size >> 3) - 1; } - csr |= ch->req_transfer_count << CSR_WCOUNT_SHIFT; if (req->to_memory) { @@ -529,14 +573,8 @@ static void handle_oneshot_dma(struct tegra_dma_channel *ch) req = list_entry(ch->list.next, typeof(*req), node); if (req) { - int bytes_transferred; - - bytes_transferred = ch->req_transfer_count; - bytes_transferred += 1; - bytes_transferred <<= 2; - list_del(&req->node); - req->bytes_transferred = bytes_transferred; + req->bytes_transferred = req->size; req->status = TEGRA_DMA_REQ_SUCCESS; spin_unlock_irqrestore(&ch->lock, irq_flags); @@ -557,7 +595,7 @@ static void handle_oneshot_dma(struct tegra_dma_channel *ch) spin_unlock_irqrestore(&ch->lock, irq_flags); } -static void handle_continuous_dma(struct tegra_dma_channel *ch) +static void handle_continuous_dbl_dma(struct tegra_dma_channel *ch) { struct tegra_dma_req *req; struct tegra_dma_req *next_req; @@ -580,14 +618,9 @@ static void handle_continuous_dma(struct tegra_dma_channel *ch) is_dma_ping_complete = !is_dma_ping_complete; /* Out of sync - Release current buffer */ if (!is_dma_ping_complete) { - int bytes_transferred; - - bytes_transferred = ch->req_transfer_count; - bytes_transferred += 1; - bytes_transferred <<= 3; req->buffer_status = TEGRA_DMA_REQ_BUF_STATUS_FULL; - req->bytes_transferred = bytes_transferred; + req->bytes_transferred = req->size; req->status = TEGRA_DMA_REQ_SUCCESS; tegra_dma_stop(ch); @@ -624,14 +657,9 @@ static void handle_continuous_dma(struct tegra_dma_channel *ch) TEGRA_DMA_REQ_BUF_STATUS_HALF_FULL) { /* Callback when the buffer is completely full (i.e on * the second interrupt */ - int bytes_transferred; - - bytes_transferred = ch->req_transfer_count; - bytes_transferred += 1; - bytes_transferred <<= 3; req->buffer_status = TEGRA_DMA_REQ_BUF_STATUS_FULL; - req->bytes_transferred = bytes_transferred; + req->bytes_transferred = req->size; req->status = TEGRA_DMA_REQ_SUCCESS; if (list_is_last(&req->node, &ch->list)) tegra_dma_stop(ch); @@ -664,6 +692,57 @@ static void handle_continuous_dma(struct tegra_dma_channel *ch) spin_unlock_irqrestore(&ch->lock, irq_flags); } +static void handle_continuous_sngl_dma(struct tegra_dma_channel *ch) +{ + struct tegra_dma_req *req; + struct tegra_dma_req *next_req; + struct tegra_dma_req *next_next_req; + unsigned long irq_flags; + + spin_lock_irqsave(&ch->lock, irq_flags); + if (list_empty(&ch->list)) { + tegra_dma_stop(ch); + spin_unlock_irqrestore(&ch->lock, irq_flags); + pr_err("%s: No requests in the list.\n", __func__); + return; + } + req = list_entry(ch->list.next, typeof(*req), node); + if (!req || (req->buffer_status == TEGRA_DMA_REQ_BUF_STATUS_FULL)) { + tegra_dma_stop(ch); + spin_unlock_irqrestore(&ch->lock, irq_flags); + pr_err("%s: DMA complete irq without corresponding req\n", + __func__); + return; + } + + /* Handle the case when buffer is completely full */ + req->bytes_transferred = req->size; + req->buffer_status = TEGRA_DMA_REQ_BUF_STATUS_FULL; + req->status = TEGRA_DMA_REQ_SUCCESS; + if (list_is_last(&req->node, &ch->list)) { + pr_debug("%s: stop\n", __func__); + tegra_dma_stop(ch); + } else { + /* The next entry should have already been queued and is now + * in the middle of xfer. We can then write the next->next one + * if it exists. + */ + next_req = list_entry(req->node.next, typeof(*next_req), node); + if (next_req->status != TEGRA_DMA_REQ_INFLIGHT) { + pr_warning("%s: interrupt during enqueue\n", __func__); + tegra_dma_stop(ch); + tegra_dma_update_hw(ch, next_req); + } else if (!list_is_last(&next_req->node, &ch->list)) { + next_next_req = list_entry(next_req->node.next, + typeof(*next_next_req), node); + tegra_dma_update_hw_partial(ch, next_next_req); + } + } + list_del(&req->node); + spin_unlock_irqrestore(&ch->lock, irq_flags); + req->complete(req); +} + static irqreturn_t dma_isr(int irq, void *data) { struct tegra_dma_channel *ch = data; @@ -679,8 +758,12 @@ static irqreturn_t dma_isr(int irq, void *data) if (ch->mode & TEGRA_DMA_MODE_ONESHOT) handle_oneshot_dma(ch); + else if (ch->mode & TEGRA_DMA_MODE_CONTINUOUS_DOUBLE) + handle_continuous_dbl_dma(ch); + else if (ch->mode & TEGRA_DMA_MODE_CONTINUOUS_SINGLE) + handle_continuous_sngl_dma(ch); else - handle_continuous_dma(ch); + pr_err("Bad channel mode for DMA ISR to handle\n"); return IRQ_HANDLED; } diff --git a/arch/arm/mach-tegra/include/mach/dc.h b/arch/arm/mach-tegra/include/mach/dc.h index 7b674220c3ee..77a9f15bc0bf 100644 --- a/arch/arm/mach-tegra/include/mach/dc.h +++ b/arch/arm/mach-tegra/include/mach/dc.h @@ -86,7 +86,10 @@ struct tegra_dc_win { void *virt_addr; dma_addr_t phys_addr; + unsigned offset_u; + unsigned offset_v; unsigned stride; + unsigned stride_uv; unsigned x; unsigned y; unsigned w; diff --git a/arch/arm/mach-tegra/include/mach/dma.h b/arch/arm/mach-tegra/include/mach/dma.h index 243050693eec..f9b09cbd8166 100644 --- a/arch/arm/mach-tegra/include/mach/dma.h +++ b/arch/arm/mach-tegra/include/mach/dma.h @@ -61,7 +61,9 @@ struct tegra_dma_channel; enum tegra_dma_mode { TEGRA_DMA_SHARED = 1, TEGRA_DMA_MODE_CONTINUOUS = 2, - TEGRA_DMA_MODE_ONESHOT = 4, + TEGRA_DMA_MODE_CONTINUOUS_DOUBLE = TEGRA_DMA_MODE_CONTINUOUS, + TEGRA_DMA_MODE_CONTINUOUS_SINGLE = 4, + TEGRA_DMA_MODE_ONESHOT = 8, }; enum tegra_dma_req_error { @@ -146,9 +148,11 @@ void tegra_dma_flush(struct tegra_dma_channel *ch); bool tegra_dma_is_req_inflight(struct tegra_dma_channel *ch, struct tegra_dma_req *req); bool tegra_dma_is_empty(struct tegra_dma_channel *ch); +bool tegra_dma_is_stopped(struct tegra_dma_channel *ch); struct tegra_dma_channel *tegra_dma_allocate_channel(int mode); void tegra_dma_free_channel(struct tegra_dma_channel *ch); +int tegra_dma_cancel(struct tegra_dma_channel *ch); int __init tegra_dma_init(void); diff --git a/arch/arm/mach-tegra/include/mach/usb_phy.h b/arch/arm/mach-tegra/include/mach/usb_phy.h index 71f1b9fa76db..8b4beae773a7 100644 --- a/arch/arm/mach-tegra/include/mach/usb_phy.h +++ b/arch/arm/mach-tegra/include/mach/usb_phy.h @@ -79,6 +79,10 @@ int tegra_usb_phy_clk_enable(struct tegra_usb_phy *phy); int tegra_usb_phy_power_off(struct tegra_usb_phy *phy); +int tegra_usb_phy_preresume(struct tegra_usb_phy *phy); + +int tegra_usb_phy_postresume(struct tegra_usb_phy *phy); + int tegra_usb_phy_close(struct tegra_usb_phy *phy); #endif //__MACH_USB_PHY_H diff --git a/arch/arm/mach-tegra/tegra_i2s_audio.c b/arch/arm/mach-tegra/tegra_i2s_audio.c index f8a67f1e2043..39c6c0290f25 100644 --- a/arch/arm/mach-tegra/tegra_i2s_audio.c +++ b/arch/arm/mach-tegra/tegra_i2s_audio.c @@ -17,6 +17,10 @@ * */ +/* TODO: + -- replace make I2S_MAX_NUM_BUFS configurable through an ioctl +*/ + #include <linux/module.h> #include <linux/kernel.h> #include <linux/miscdevice.h> @@ -40,7 +44,6 @@ #include <linux/io.h> #include <linux/ktime.h> #include <linux/sysfs.h> -#include <linux/pm_qos_params.h> #include <linux/wakelock.h> #include <linux/delay.h> #include <linux/tegra_audio.h> @@ -55,63 +58,42 @@ #include "clock.h" -#define PCM_BUFFER_MAX_SIZE_ORDER (PAGE_SHIFT + 2) -#define PCM_BUFFER_DMA_CHUNK_SIZE_ORDER PAGE_SHIFT -#define PCM_BUFFER_THRESHOLD_ORDER (PCM_BUFFER_MAX_SIZE_ORDER - 1) -#define PCM_DMA_CHUNK_MIN_SIZE_ORDER 3 - -#define PCM_IN_BUFFER_PADDING (1<<6) /* bytes */ +#define PCM_BUFFER_MAX_SIZE_ORDER PAGE_SHIFT #define TEGRA_AUDIO_DSP_NONE 0 #define TEGRA_AUDIO_DSP_PCM 1 #define TEGRA_AUDIO_DSP_NETWORK 2 #define TEGRA_AUDIO_DSP_TDM 3 +#define I2S_MAX_NUM_BUFS 4 +#define I2S_DEFAULT_TX_NUM_BUFS 2 +#define I2S_DEFAULT_RX_NUM_BUFS 2 + /* per stream (input/output) */ struct audio_stream { int opened; struct mutex lock; - struct tegra_audio_buf_config buf_config; - bool active; /* is DMA or PIO in progress? */ - void *buffer; - dma_addr_t buf_phys; - struct kfifo fifo; - struct completion fifo_completion; - struct scatterlist sg; - - struct tegra_audio_error_counts errors; + bool active; /* is DMA in progress? */ + int num_bufs; + void *buffer[I2S_MAX_NUM_BUFS]; + dma_addr_t buf_phy[I2S_MAX_NUM_BUFS]; + struct completion comp[I2S_MAX_NUM_BUFS]; + struct tegra_dma_req dma_req[I2S_MAX_NUM_BUFS]; + int last_queued; int i2s_fifo_atn_level; - ktime_t last_dma_ts; struct tegra_dma_channel *dma_chan; bool stop; struct completion stop_completion; - spinlock_t dma_req_lock; /* guards dma_has_it */ - int dma_has_it; - struct tegra_dma_req dma_req; + spinlock_t dma_req_lock; - struct pm_qos_request_list pm_qos; struct work_struct allow_suspend_work; struct wake_lock wake_lock; char wake_lock_name[100]; }; -struct i2s_pio_stats { - u32 i2s_interrupt_count; - u32 tx_fifo_errors; - u32 rx_fifo_errors; - u32 tx_fifo_written; - u32 rx_fifo_read; -}; - -static const int divs_8000[] = { 5, 6, 6, 5 }; /* 8018.(18) Hz */ -static const int divs_11025[] = { 4 }; -static const int divs_22050[] = { 2 }; -static const int divs_44100[] = { 1 }; -static const int divs_16000[] = { 2, 3, 3, 3, 3, 3, 3, 2 }; /* 16036.(36) Hz */ - /* per i2s controller */ struct audio_driver_state { struct list_head next; @@ -121,14 +103,10 @@ struct audio_driver_state { phys_addr_t i2s_phys; unsigned long i2s_base; - bool using_dma; unsigned long dma_req_sel; - int irq; /* for pio mode */ - struct i2s_pio_stats pio_stats; + int irq; struct tegra_audio_in_config in_config; - const int *in_divs; - int in_divs_len; struct miscdevice misc_out; struct miscdevice misc_out_ctl; @@ -143,19 +121,18 @@ struct audio_driver_state { unsigned int bit_format; }; -static inline int buf_size(struct audio_stream *s) +static inline bool pending_buffer_requests(struct audio_stream *stream) { - return 1 << s->buf_config.size; -} - -static inline int chunk_size(struct audio_stream *s) -{ - return 1 << s->buf_config.chunk; + int i; + for (i = 0; i < stream->num_bufs; i++) + if (!completion_done(&stream->comp[i])) + return true; + return false; } -static inline int threshold_size(struct audio_stream *s) +static inline int buf_size(struct audio_stream *s __attribute__((unused))) { - return 1 << s->buf_config.threshold; + return 1 << PCM_BUFFER_MAX_SIZE_ORDER; } static inline struct audio_driver_state *ads_from_misc_out(struct file *file) @@ -226,16 +203,13 @@ static inline void prevent_suspend(struct audio_stream *as) pr_debug("%s\n", __func__); cancel_work_sync(&as->allow_suspend_work); wake_lock(&as->wake_lock); - pm_qos_update_request(&as->pm_qos, 0); } static void allow_suspend_worker(struct work_struct *w) { struct audio_stream *as = container_of(w, struct audio_stream, allow_suspend_work); - pr_debug("%s\n", __func__); - pm_qos_update_request(&as->pm_qos, PM_QOS_DEFAULT_VALUE); wake_unlock(&as->wake_lock); } @@ -349,6 +323,7 @@ static void i2s_fifo_enable(unsigned long base, int fifo, int on) i2s_writel(base, val, I2S_I2S_CTRL_0); } +#if 0 static bool i2s_is_fifo_enabled(unsigned long base, int fifo) { u32 val = i2s_readl(base, I2S_I2S_CTRL_0); @@ -356,6 +331,7 @@ static bool i2s_is_fifo_enabled(unsigned long base, int fifo) return !!(val & I2S_I2S_CTRL_FIFO1_ENABLE); return !!(val & I2S_I2S_CTRL_FIFO2_ENABLE); } +#endif static void i2s_fifo_clear(unsigned long base, int fifo) { @@ -501,6 +477,7 @@ static void i2s_set_left_right_control_polarity(unsigned long base, i2s_writel(base, val, I2S_I2S_CTRL_0); } +#if 0 static void i2s_set_fifo_irq_on_err(unsigned long base, int fifo, int on) { u32 val = i2s_readl(base, I2S_I2S_CTRL_0); @@ -526,6 +503,7 @@ static void i2s_set_fifo_irq_on_qe(unsigned long base, int fifo, int on) } i2s_writel(base, val, I2S_I2S_CTRL_0); } +#endif static void i2s_enable_fifos(unsigned long base, int on) { @@ -612,10 +590,10 @@ static int i2s_configure(struct platform_device *pdev) master_clk = state->bit_format == TEGRA_AUDIO_BIT_FORMAT_DSP ? state->pdata->dsp_master_clk : state->pdata->i2s_master_clk; -#define I2S_CLK_FUDGE_FACTOR 2 /* Todo, fix this! */ +#define I2S_CLK_TO_BITCLK_RATIO 2 /* Todo, Bitclk based on 2X clock? */ if (master) i2s_set_channel_bit_count(state->i2s_base, master_clk, - clk_get_rate(i2s_clk)*I2S_CLK_FUDGE_FACTOR); + clk_get_rate(i2s_clk)*I2S_CLK_TO_BITCLK_RATIO); i2s_set_master(state->i2s_base, master); i2s_set_fifo_mode(state->i2s_base, I2S_FIFO_TX, 1); @@ -631,81 +609,47 @@ static int i2s_configure(struct platform_device *pdev) return 0; } -static int init_stream_buffer(struct audio_stream *, - struct tegra_audio_buf_config *cfg, unsigned); +static int init_stream_buffer(struct audio_stream *, int); static int setup_dma(struct audio_driver_state *); static void tear_down_dma(struct audio_driver_state *); -static int start_dma_playback(struct audio_stream *); static void stop_dma_playback(struct audio_stream *); -static int start_dma_recording(struct audio_stream *); -static int resume_dma_recording(struct audio_stream *); +static int start_dma_recording(struct audio_stream *, int); static void stop_dma_recording(struct audio_stream *); -static int setup_pio(struct audio_driver_state *); -static void tear_down_pio(struct audio_driver_state *); -static int start_pio_playback(struct audio_stream *); -static void stop_pio_playback(struct audio_stream *); -static int start_pio_recording(struct audio_stream *); -static void stop_pio_recording(struct audio_stream *); - struct sound_ops { int (*setup)(struct audio_driver_state *); void (*tear_down)(struct audio_driver_state *); - int (*start_playback)(struct audio_stream *); void (*stop_playback)(struct audio_stream *); - int (*start_recording)(struct audio_stream *); + int (*start_recording)(struct audio_stream *, int); void (*stop_recording)(struct audio_stream *); }; static const struct sound_ops dma_sound_ops = { .setup = setup_dma, .tear_down = tear_down_dma, - .start_playback = start_dma_playback, .stop_playback = stop_dma_playback, .start_recording = start_dma_recording, .stop_recording = stop_dma_recording, }; -static const struct sound_ops pio_sound_ops = { - .setup = setup_pio, - .tear_down = tear_down_pio, - .start_playback = start_pio_playback, - .stop_playback = stop_pio_playback, - .start_recording = start_pio_recording, - .stop_recording = stop_pio_recording, -}; - static const struct sound_ops *sound_ops = &dma_sound_ops; -static int start_playback(struct audio_stream *aos) -{ - int rc; - unsigned long flags; - spin_lock_irqsave(&aos->dma_req_lock, flags); - pr_debug("%s: starting playback\n", __func__); - rc = sound_ops->start_playback(aos); - spin_unlock_irqrestore(&aos->dma_req_lock, flags); - if (!rc) - prevent_suspend(aos); - return rc; -} - -static int start_recording_if_necessary(struct audio_stream *ais) +static int start_recording_if_necessary(struct audio_stream *ais, int size) { int rc = 0; bool started = false; unsigned long flags; - + prevent_suspend(ais); spin_lock_irqsave(&ais->dma_req_lock, flags); - if (!ais->stop && !kfifo_is_full(&ais->fifo)) { - pr_debug("%s: starting recording\n", __func__); - rc = sound_ops->start_recording(ais); + if (!ais->stop && !pending_buffer_requests(ais)) { + /* pr_debug("%s: starting recording\n", __func__); */ + rc = sound_ops->start_recording(ais, size); started = !rc; } spin_unlock_irqrestore(&ais->dma_req_lock, flags); - if (started) - prevent_suspend(ais); + if (!started) + allow_suspend(ais); return rc; } @@ -713,7 +657,9 @@ static bool stop_playback_if_necessary(struct audio_stream *aos) { unsigned long flags; spin_lock_irqsave(&aos->dma_req_lock, flags); - if (kfifo_is_empty(&aos->fifo)) { + pr_debug("%s\n", __func__); + if (!pending_buffer_requests(aos)) { + pr_debug("%s: no more data to play back\n", __func__); sound_ops->stop_playback(aos); spin_unlock_irqrestore(&aos->dma_req_lock, flags); allow_suspend(aos); @@ -724,27 +670,17 @@ static bool stop_playback_if_necessary(struct audio_stream *aos) return false; } -static bool stop_recording_if_necessary_nosync(struct audio_stream *ais) -{ - if (ais->stop || kfifo_is_full(&ais->fifo)) { - if (kfifo_is_full(&ais->fifo)) - ais->errors.full_empty++; /* overflow */ - sound_ops->stop_recording(ais); - return true; - } - - return false; -} - /* playback and recording */ static bool wait_till_stopped(struct audio_stream *as) { int rc; pr_debug("%s: wait for completion\n", __func__); - rc = wait_for_completion_interruptible_timeout( + rc = wait_for_completion_timeout( &as->stop_completion, HZ); if (!rc) pr_err("%s: wait timed out", __func__); + if (rc < 0) + pr_err("%s: wait error %d\n", __func__, rc); allow_suspend(as); pr_debug("%s: done: %d\n", __func__, rc); return true; @@ -755,33 +691,20 @@ static bool wait_till_stopped(struct audio_stream *as) */ static void request_stop_nosync(struct audio_stream *as) { + int i; pr_debug("%s\n", __func__); if (!as->stop) { as->stop = true; wait_till_stopped(as); - if (!completion_done(&as->fifo_completion)) { - pr_debug("%s: complete\n", __func__); - complete(&as->fifo_completion); + for (i = 0; i < as->num_bufs; i++) { + init_completion(&as->comp[i]); + complete(&as->comp[i]); } } - kfifo_reset(&as->fifo); as->active = false; /* applies to recording only */ pr_debug("%s: done\n", __func__); } -static void toggle_dma(struct audio_driver_state *ads) -{ - pr_info("%s: %s\n", __func__, ads->using_dma ? "pio" : "dma"); - sound_ops->tear_down(ads); - sound_ops = ads->using_dma ? &pio_sound_ops : &dma_sound_ops; - sound_ops->setup(ads); - ads->using_dma = !ads->using_dma; -} - -/* DMA */ - -static int resume_dma_playback(struct audio_stream *aos); - static void setup_dma_tx_request(struct tegra_dma_req *req, struct audio_stream *aos); @@ -790,21 +713,24 @@ static void setup_dma_rx_request(struct tegra_dma_req *req, static int setup_dma(struct audio_driver_state *ads) { - int rc; + int rc, i; pr_info("%s\n", __func__); if ((ads->pdata->mask & TEGRA_AUDIO_ENABLE_TX)) { /* setup audio playback */ - ads->out.buf_phys = dma_map_single(&ads->pdev->dev, - ads->out.buffer, + for (i = 0; i < ads->out.num_bufs; i++) { + ads->out.buf_phy[i] = dma_map_single(&ads->pdev->dev, + ads->out.buffer[i], 1 << PCM_BUFFER_MAX_SIZE_ORDER, DMA_TO_DEVICE); - BUG_ON(!ads->out.buf_phys); - setup_dma_tx_request(&ads->out.dma_req, &ads->out); - ads->out.dma_chan = - tegra_dma_allocate_channel(TEGRA_DMA_MODE_ONESHOT); + BUG_ON(!ads->out.buf_phy[i]); + setup_dma_tx_request(&ads->out.dma_req[i], &ads->out); + ads->out.dma_req[i].source_addr = ads->out.buf_phy[i]; + } + ads->out.dma_chan = tegra_dma_allocate_channel( + TEGRA_DMA_MODE_CONTINUOUS_SINGLE); if (!ads->out.dma_chan) { - pr_err("%s: error allocating output DMA channel: %ld\n", + pr_err("%s: error alloc output DMA channel: %ld\n", __func__, PTR_ERR(ads->out.dma_chan)); rc = -ENODEV; goto fail_tx; @@ -813,14 +739,17 @@ static int setup_dma(struct audio_driver_state *ads) if ((ads->pdata->mask & TEGRA_AUDIO_ENABLE_RX)) { /* setup audio recording */ - ads->in.buf_phys = dma_map_single(&ads->pdev->dev, - ads->in.buffer, + for (i = 0; i < ads->in.num_bufs; i++) { + ads->in.buf_phy[i] = dma_map_single(&ads->pdev->dev, + ads->in.buffer[i], 1 << PCM_BUFFER_MAX_SIZE_ORDER, DMA_FROM_DEVICE); - BUG_ON(!ads->in.buf_phys); - setup_dma_rx_request(&ads->in.dma_req, &ads->in); - ads->in.dma_chan = - tegra_dma_allocate_channel(TEGRA_DMA_MODE_ONESHOT); + BUG_ON(!ads->in.buf_phy[i]); + setup_dma_rx_request(&ads->in.dma_req[i], &ads->in); + ads->in.dma_req[i].dest_addr = ads->in.buf_phy[i]; + } + ads->in.dma_chan = tegra_dma_allocate_channel( + TEGRA_DMA_MODE_CONTINUOUS_SINGLE); if (!ads->in.dma_chan) { pr_err("%s: error allocating input DMA channel: %ld\n", __func__, PTR_ERR(ads->in.dma_chan)); @@ -832,16 +761,24 @@ static int setup_dma(struct audio_driver_state *ads) return 0; fail_rx: - if ((ads->pdata->mask & TEGRA_AUDIO_ENABLE_RX)) { - dma_unmap_single(&ads->pdev->dev, ads->in.buf_phys, - 1 << PCM_BUFFER_MAX_SIZE_ORDER, DMA_FROM_DEVICE); + if (ads->pdata->mask & TEGRA_AUDIO_ENABLE_RX) { + for (i = 0; i < ads->in.num_bufs; i++) { + dma_unmap_single(&ads->pdev->dev, ads->in.buf_phy[i], + 1 << PCM_BUFFER_MAX_SIZE_ORDER, + DMA_FROM_DEVICE); + ads->in.buf_phy[i] = 0; + } tegra_dma_free_channel(ads->in.dma_chan); ads->in.dma_chan = 0; } fail_tx: - if ((ads->pdata->mask & TEGRA_AUDIO_ENABLE_TX)) { - dma_unmap_single(&ads->pdev->dev, ads->out.buf_phys, - 1 << PCM_BUFFER_MAX_SIZE_ORDER, DMA_TO_DEVICE); + if (ads->pdata->mask & TEGRA_AUDIO_ENABLE_TX) { + for (i = 0; i < ads->out.num_bufs; i++) { + dma_unmap_single(&ads->pdev->dev, ads->out.buf_phy[i], + 1 << PCM_BUFFER_MAX_SIZE_ORDER, + DMA_TO_DEVICE); + ads->out.buf_phy[i] = 0; + } tegra_dma_free_channel(ads->out.dma_chan); ads->out.dma_chan = 0; } @@ -851,50 +788,43 @@ fail_tx: static void tear_down_dma(struct audio_driver_state *ads) { + int i; pr_info("%s\n", __func__); - tegra_dma_free_channel(ads->out.dma_chan); + if (ads->pdata->mask & TEGRA_AUDIO_ENABLE_TX) { + tegra_dma_free_channel(ads->out.dma_chan); + for (i = 0; i < ads->out.num_bufs; i++) { + dma_unmap_single(&ads->pdev->dev, ads->out.buf_phy[i], + buf_size(&ads->out), + DMA_TO_DEVICE); + ads->out.buf_phy[i] = 0; + } + } ads->out.dma_chan = NULL; - dma_unmap_single(&ads->pdev->dev, ads->out.buf_phys, - buf_size(&ads->out), - DMA_TO_DEVICE); - ads->out.buf_phys = 0; - tegra_dma_free_channel(ads->in.dma_chan); + if (ads->pdata->mask & TEGRA_AUDIO_ENABLE_RX) { + tegra_dma_free_channel(ads->in.dma_chan); + for (i = 0; i < ads->in.num_bufs; i++) { + dma_unmap_single(&ads->pdev->dev, ads->in.buf_phy[i], + buf_size(&ads->in), + DMA_FROM_DEVICE); + ads->in.buf_phy[i] = 0; + } + } ads->in.dma_chan = NULL; - dma_unmap_single(&ads->pdev->dev, ads->in.buf_phys, - buf_size(&ads->in), - DMA_FROM_DEVICE); - ads->in.buf_phys = 0; } static void dma_tx_complete_callback(struct tegra_dma_req *req) { - unsigned long flags; struct audio_stream *aos = req->dev; - int count = req->bytes_transferred; - u64 delta_us; - u64 max_delay_us = count * 10000 / (4 * 441); - - pr_debug("%s bytes transferred %d\n", __func__, count); + unsigned req_num; - aos->dma_has_it = false; - delta_us = ktime_to_us(ktime_sub(ktime_get_real(), aos->last_dma_ts)); - - if (delta_us > max_delay_us) { - pr_debug("%s: too late by %lld us\n", __func__, - delta_us - max_delay_us); - aos->errors.late_dma++; - } + req_num = req - aos->dma_req; + pr_debug("%s: completed buffer %d size %d\n", __func__, + req_num, req->bytes_transferred); + BUG_ON(req_num >= aos->num_bufs); - kfifo_dma_out_finish(&aos->fifo, count); - dma_unmap_sg(NULL, &aos->sg, 1, DMA_TO_DEVICE); - - if (!completion_done(&aos->fifo_completion)) { - pr_debug("%s: complete (%d avail)\n", __func__, - kfifo_avail(&aos->fifo)); - complete(&aos->fifo_completion); - } + complete(&aos->comp[req_num]); if (stop_playback_if_necessary(aos)) { pr_debug("%s: done (stopped)\n", __func__); @@ -904,60 +834,24 @@ static void dma_tx_complete_callback(struct tegra_dma_req *req) } return; } - - spin_lock_irqsave(&aos->dma_req_lock, flags); - resume_dma_playback(aos); - spin_unlock_irqrestore(&aos->dma_req_lock, flags); -} - -static void dma_rx_complete_threshold(struct tegra_dma_req *req) -{ - pr_debug("%s\n", __func__); } static void dma_rx_complete_callback(struct tegra_dma_req *req) { unsigned long flags; struct audio_stream *ais = req->dev; - int count = req->bytes_transferred; + unsigned req_num; spin_lock_irqsave(&ais->dma_req_lock, flags); - ais->dma_has_it = false; - - pr_debug("%s(%d): transferred %d bytes (%d available in fifo)\n", - __func__, - smp_processor_id(), - count, kfifo_avail(&ais->fifo)); + req_num = req - ais->dma_req; + pr_debug("%s: completed buffer %d size %d\n", __func__, + req_num, req->bytes_transferred); + BUG_ON(req_num >= ais->num_bufs); - BUG_ON(kfifo_avail(&ais->fifo) < count); - kfifo_dma_in_finish(&ais->fifo, count); - dma_unmap_sg(NULL, &ais->sg, 1, DMA_FROM_DEVICE); + complete(&ais->comp[req_num]); - if (!completion_done(&ais->fifo_completion)) { - pr_debug("%s: signalling fifo completion\n", __func__); - complete(&ais->fifo_completion); - } - - if (stop_recording_if_necessary_nosync(ais)) { - spin_unlock_irqrestore(&ais->dma_req_lock, flags); - pr_debug("%s: done (stopped)\n", __func__); - if (!completion_done(&ais->stop_completion)) { - pr_debug("%s: signalling stop completion\n", __func__); - complete(&ais->stop_completion); - } - return; - } - - pr_debug("%s: resuming dma recording\n", __func__); - - /* This call will fail if we try to set up a DMA request that's - * too small. - */ - (void)resume_dma_recording(ais); spin_unlock_irqrestore(&ais->dma_req_lock, flags); - - pr_debug("%s: done\n", __func__); } static void setup_dma_tx_request(struct tegra_dma_req *req, @@ -989,7 +883,6 @@ static void setup_dma_rx_request(struct tegra_dma_req *req, memset(req, 0, sizeof(*req)); req->complete = dma_rx_complete_callback; - req->threshold = dma_rx_complete_threshold; req->dev = ais; req->to_memory = true; req->source_addr = i2s_get_fifo_phy_base(ads->i2s_phys, I2S_FIFO_RX); @@ -1003,65 +896,34 @@ static void setup_dma_rx_request(struct tegra_dma_req *req, req->req_sel = ads->dma_req_sel; } -/* Called with aos->dma_req_lock taken. */ -static int resume_dma_playback(struct audio_stream *aos) +static int start_playback(struct audio_stream *aos, + struct tegra_dma_req *req) { int rc; + unsigned long flags; struct audio_driver_state *ads = ads_from_out(aos); - struct tegra_dma_req *req = &aos->dma_req; - - if (aos->dma_has_it) { - pr_debug("%s: playback already in progress\n", __func__); - return -EALREADY; - } - rc = kfifo_dma_out_prepare(&aos->fifo, &aos->sg, - 1, kfifo_len(&aos->fifo)); - /* stop_playback_if_necessary() already checks to see if the fifo is - * empty. - */ - BUG_ON(!rc); - rc = dma_map_sg(NULL, &aos->sg, 1, DMA_TO_DEVICE); - if (rc < 0) { - pr_err("%s: could not map dma memory: %d\n", __func__, rc); - return rc; - } + pr_debug("%s: (writing %d)\n", + __func__, req->size); + spin_lock_irqsave(&aos->dma_req_lock, flags); #if 0 i2s_fifo_clear(ads->i2s_base, I2S_FIFO_TX); #endif i2s_fifo_set_attention_level(ads->i2s_base, I2S_FIFO_TX, aos->i2s_fifo_atn_level); - - req->source_addr = sg_dma_address(&aos->sg); - req->size = sg_dma_len(&aos->sg); - dma_sync_single_for_device(NULL, - req->source_addr, req->size, DMA_TO_DEVICE); - - /* Don't send all the data yet. */ - if (req->size > chunk_size(aos)) - req->size = chunk_size(aos); - pr_debug("%s resume playback (%d in fifo, writing %d)\n", - __func__, kfifo_len(&aos->fifo), req->size); - i2s_fifo_enable(ads->i2s_base, I2S_FIFO_TX, 1); - aos->last_dma_ts = ktime_get_real(); rc = tegra_dma_enqueue_req(aos->dma_chan, req); - aos->dma_has_it = !rc; - if (!aos->dma_has_it) + spin_unlock_irqrestore(&aos->dma_req_lock, flags); + + if (rc) pr_err("%s: could not enqueue TX DMA req\n", __func__); return rc; } /* Called with aos->dma_req_lock taken. */ -static int start_dma_playback(struct audio_stream *aos) -{ - return resume_dma_playback(aos); -} - -/* Called with aos->dma_req_lock taken. */ static void stop_dma_playback(struct audio_stream *aos) { int spin = 0; @@ -1080,48 +942,23 @@ static void stop_dma_playback(struct audio_stream *aos) /* This function may be called from either interrupt or process context. */ /* Called with ais->dma_req_lock taken. */ -static int resume_dma_recording(struct audio_stream *ais) +static int start_dma_recording(struct audio_stream *ais, int size) { - int rc; + int i; struct audio_driver_state *ads = ads_from_in(ais); - struct tegra_dma_req *req = &ais->dma_req; - BUG_ON(kfifo_is_full(&ais->fifo)); - - if (ais->dma_has_it) { - pr_debug("%s: recording already in progress\n", __func__); - return -EALREADY; - } - - /* Don't send all the data yet. */ - if (req->size > chunk_size(ais)) - req->size = chunk_size(ais); - rc = kfifo_dma_in_prepare(&ais->fifo, &ais->sg, 1, - kfifo_avail(&ais->fifo)); - BUG_ON(!rc); - rc = dma_map_sg(NULL, &ais->sg, 1, DMA_FROM_DEVICE); - if (rc < 0) { - pr_err("%s: coult not map dma for recording: %d\n", - __func__, rc); - return rc; - } + pr_debug("%s\n", __func__); - req->dest_addr = sg_dma_address(&ais->sg); - req->size = round_down(sg_dma_len(&ais->sg), 4); + BUG_ON(pending_buffer_requests(ais)); - if (!req->size) { - pr_err("%s: invalid request size %d\n", __func__, req->size); - return -EIO; + for (i = 0; i < ais->num_bufs; i++) { + init_completion(&ais->comp[i]); + ais->dma_req[i].dest_addr = ais->buf_phy[i]; + ais->dma_req[i].size = size; + tegra_dma_enqueue_req(ais->dma_chan, &ais->dma_req[i]); } - dma_sync_single_for_device(NULL, - req->dest_addr, req->size, DMA_FROM_DEVICE); - - ais->dma_has_it = !tegra_dma_enqueue_req(ais->dma_chan, &ais->dma_req); - if (!ais->dma_has_it) { - pr_err("%s: could not enqueue RX DMA req\n", __func__); - return -EINVAL; - } + ais->last_queued = ais->num_bufs - 1; #if 0 i2s_fifo_clear(ads->i2s_base, I2S_FIFO_RX); @@ -1132,19 +969,12 @@ static int resume_dma_recording(struct audio_stream *ais) return 0; } -/* Called with ais->dma_req_lock taken. */ -static int start_dma_recording(struct audio_stream *ais) -{ - pr_debug("%s\n", __func__); - return resume_dma_recording(ais); -} - -/* Called with ais->dma_req_lock taken. */ static void stop_dma_recording(struct audio_stream *ais) { int spin = 0; struct audio_driver_state *ads = ads_from_in(ais); pr_debug("%s\n", __func__); + tegra_dma_cancel(ais->dma_chan); i2s_fifo_enable(ads->i2s_base, I2S_FIFO_RX, 0); i2s_fifo_clear(ads->i2s_base, I2S_FIFO_RX); while ((i2s_get_status(ads->i2s_base) & I2S_I2S_FIFO_RX_BUSY) && @@ -1157,112 +987,6 @@ static void stop_dma_recording(struct audio_stream *ais) pr_warn("%s: spinny\n", __func__); } -/* PIO (non-DMA) */ - -static int setup_pio(struct audio_driver_state *ads) -{ - pr_info("%s\n", __func__); - enable_irq(ads->irq); - return 0; -} - -static void tear_down_pio(struct audio_driver_state *ads) -{ - pr_info("%s\n", __func__); - disable_irq(ads->irq); -} - -static int start_pio_playback(struct audio_stream *aos) -{ - struct audio_driver_state *ads = ads_from_out(aos); - - if (i2s_is_fifo_enabled(ads->i2s_base, I2S_FIFO_TX)) { - pr_debug("%s: playback is already in progress\n", __func__); - return -EALREADY; - } - - pr_debug("%s\n", __func__); - - i2s_fifo_set_attention_level(ads->i2s_base, - I2S_FIFO_TX, aos->i2s_fifo_atn_level); -#if 0 - i2s_fifo_clear(ads->i2s_base, I2S_FIFO_TX); -#endif - - i2s_fifo_enable(ads->i2s_base, I2S_FIFO_TX, 1); - - i2s_set_fifo_irq_on_err(ads->i2s_base, I2S_FIFO_TX, 1); - i2s_set_fifo_irq_on_qe(ads->i2s_base, I2S_FIFO_TX, 1); - - return 0; -} - -static void stop_pio_playback(struct audio_stream *aos) -{ - struct audio_driver_state *ads = ads_from_out(aos); - - i2s_set_fifo_irq_on_err(ads->i2s_base, I2S_FIFO_TX, 0); - i2s_set_fifo_irq_on_qe(ads->i2s_base, I2S_FIFO_TX, 0); - i2s_fifo_enable(ads->i2s_base, I2S_FIFO_TX, 0); - while (i2s_get_status(ads->i2s_base) & I2S_I2S_FIFO_TX_BUSY) - /* spin */; - - pr_debug("%s: interrupts %d\n", __func__, - ads->pio_stats.i2s_interrupt_count); - pr_info("%s: sent %d\n", __func__, - ads->pio_stats.tx_fifo_written); - pr_info("%s: tx errors %d\n", __func__, - ads->pio_stats.tx_fifo_errors); - - memset(&ads->pio_stats, 0, sizeof(ads->pio_stats)); -} - -static int start_pio_recording(struct audio_stream *ais) -{ - struct audio_driver_state *ads = ads_from_in(ais); - - if (i2s_is_fifo_enabled(ads->i2s_base, I2S_FIFO_RX)) { - pr_debug("%s: already started\n", __func__); - return -EALREADY; - } - - pr_debug("%s: start\n", __func__); - - i2s_fifo_set_attention_level(ads->i2s_base, - I2S_FIFO_RX, ais->i2s_fifo_atn_level); -#if 0 - i2s_fifo_clear(ads->i2s_base, I2S_FIFO_RX); -#endif - - i2s_fifo_enable(ads->i2s_base, I2S_FIFO_RX, 1); - - i2s_set_fifo_irq_on_err(ads->i2s_base, I2S_FIFO_RX, 1); - i2s_set_fifo_irq_on_qe(ads->i2s_base, I2S_FIFO_RX, 1); - - return 0; -} - -static void stop_pio_recording(struct audio_stream *ais) -{ - struct audio_driver_state *ads = ads_from_in(ais); - - pr_debug("%s\n", __func__); - - i2s_set_fifo_irq_on_err(ads->i2s_base, I2S_FIFO_RX, 0); - i2s_set_fifo_irq_on_qe(ads->i2s_base, I2S_FIFO_RX, 0); - i2s_fifo_enable(ads->i2s_base, I2S_FIFO_RX, 0); - i2s_fifo_clear(ads->i2s_base, I2S_FIFO_RX); - - pr_debug("%s: interrupts %d\n", __func__, - ads->pio_stats.i2s_interrupt_count); - pr_debug("%s: received %d\n", __func__, - ads->pio_stats.rx_fifo_read); - pr_debug("%s: rx errors %d\n", __func__, - ads->pio_stats.rx_fifo_errors); - - memset(&ads->pio_stats, 0, sizeof(ads->pio_stats)); -} - static irqreturn_t i2s_interrupt(int irq, void *data) { struct audio_driver_state *ads = data; @@ -1270,126 +994,9 @@ static irqreturn_t i2s_interrupt(int irq, void *data) pr_debug("%s: %08x\n", __func__, status); - ads->pio_stats.i2s_interrupt_count++; - - if (status & I2S_I2S_FIFO_TX_ERR) - ads->pio_stats.tx_fifo_errors++; - - if (status & I2S_I2S_FIFO_RX_ERR) { - ads->pio_stats.rx_fifo_errors++; - ads->in.errors.full_empty++; - } - if (status & I2S_FIFO_ERR) i2s_ack_status(ads->i2s_base); - if (status & I2S_I2S_FIFO_TX_QS) { - int written; - int empty; - int len; - u16 fifo_buffer[32]; - - struct audio_stream *out = &ads->out; - - if (!i2s_is_fifo_enabled(ads->i2s_base, I2S_FIFO_TX)) { - pr_debug("%s: tx fifo not enabled, skipping\n", - __func__); - goto check_rx; - } - - pr_debug("%s tx fifo is ready\n", __func__); - - if (!completion_done(&out->fifo_completion)) { - pr_debug("%s: tx complete (%d avail)\n", __func__, - kfifo_avail(&out->fifo)); - complete(&out->fifo_completion); - } - - if (stop_playback_if_necessary(out)) { - pr_debug("%s: done (stopped)\n", __func__); - if (!completion_done(&out->stop_completion)) { - pr_debug("%s: signalling stop completion\n", - __func__); - complete(&out->stop_completion); - } - goto check_rx; - } - - empty = i2s_get_fifo_full_empty_count(ads->i2s_base, - I2S_FIFO_TX); - - len = kfifo_out(&out->fifo, fifo_buffer, - empty * sizeof(u16)); - len /= sizeof(u16); - - written = 0; - while (empty-- && written < len) { - ads->pio_stats.tx_fifo_written += written * sizeof(u16); - i2s_fifo_write(ads->i2s_base, - I2S_FIFO_TX, fifo_buffer[written++]); - } - - /* TODO: Should we check to see if we wrote less than the - * FIFO threshold and adjust it if so? - */ - - if (written) { - /* start the transaction */ - pr_debug("%s: enabling fifo (%d samples written)\n", - __func__, written); - i2s_fifo_enable(ads->i2s_base, I2S_FIFO_TX, 1); - } - } - -check_rx: - if (status & I2S_I2S_FIFO_RX_QS) { - int nr; - int full; - - struct audio_stream *in = &ads->in; - - if (!i2s_is_fifo_enabled(ads->i2s_base, I2S_FIFO_RX)) { - pr_debug("%s: rx fifo not enabled, skipping\n", - __func__); - goto done; - } - - i2s_fifo_enable(ads->i2s_base, I2S_FIFO_RX, 0); - - full = i2s_get_fifo_full_empty_count(ads->i2s_base, - I2S_FIFO_RX); - - pr_debug("%s rx fifo is ready (%d samples)\n", __func__, full); - - nr = full; - while (nr--) { - u16 sample = i2s_fifo_read(ads->i2s_base, I2S_FIFO_RX); - kfifo_in(&in->fifo, &sample, sizeof(sample)); - } - - ads->pio_stats.rx_fifo_read += full * sizeof(u16); - - if (!completion_done(&in->fifo_completion)) { - pr_debug("%s: rx complete (%d avail)\n", __func__, - kfifo_avail(&in->fifo)); - complete(&in->fifo_completion); - } - - if (stop_recording_if_necessary_nosync(&ads->in)) { - pr_debug("%s: recording cancelled or fifo full\n", - __func__); - if (!completion_done(&ads->in.stop_completion)) { - pr_debug("%s: signalling stop completion\n", - __func__); - complete(&ads->in.stop_completion); - } - goto done; - } - - i2s_fifo_enable(ads->i2s_base, I2S_FIFO_RX, 1); - } - -done: pr_debug("%s: done %08x\n", __func__, i2s_get_status(ads->i2s_base)); return IRQ_HANDLED; } @@ -1397,60 +1004,68 @@ done: static ssize_t tegra_audio_write(struct file *file, const char __user *buf, size_t size, loff_t *off) { - ssize_t rc = 0, total = 0; - unsigned nw = 0; - + ssize_t rc = 0; + int out_buf; + struct tegra_dma_req *req; struct audio_driver_state *ads = ads_from_misc_out(file); mutex_lock(&ads->out.lock); - if (!IS_ALIGNED(size, 4)) { - pr_err("%s: user size request %d not aligned to 4\n", - __func__, size); + if (!IS_ALIGNED(size, 4) || size < 4 || size > buf_size(&ads->out)) { + pr_err("%s: invalid user size %d\n", __func__, size); rc = -EINVAL; goto done; } - pr_debug("%s: write %d bytes, %d available\n", __func__, - size, kfifo_avail(&ads->out.fifo)); + pr_debug("%s: write %d bytes\n", __func__, size); -again: if (ads->out.stop) { - pr_info("%s: playback has been cancelled (%d/%d bytes)\n", - __func__, total, size); + pr_debug("%s: playback has been cancelled\n", __func__); goto done; } - rc = kfifo_from_user(&ads->out.fifo, buf + total, size - total, &nw); - if (rc < 0) { - pr_err("%s: error copying from user\n", __func__); + /* Decide which buf is next. */ + out_buf = (ads->out.last_queued + 1) % ads->out.num_bufs; + req = &ads->out.dma_req[out_buf]; + + /* Wait for the buffer to be emptied (complete). The maximum timeout + * value could be calculated dynamically based on buf_size(&ads->out). + * For a buffer size of 16k, at 44.1kHz/stereo/16-bit PCM, you would + * have ~93ms. + */ + pr_debug("%s: waiting for buffer %d\n", __func__, out_buf); + rc = wait_for_completion_interruptible_timeout( + &ads->out.comp[out_buf], HZ); + if (!rc) { + pr_err("%s: timeout", __func__); + rc = -ETIMEDOUT; + goto done; + } else if (rc < 0) { + pr_err("%s: wait error %d", __func__, rc); goto done; } - rc = start_playback(&ads->out); - if (rc < 0 && rc != -EALREADY) { - pr_err("%s: could not start playback: %d\n", __func__, rc); + /* Fill the buffer and enqueue it. */ + pr_debug("%s: acquired buffer %d, copying data\n", __func__, out_buf); + rc = copy_from_user(ads->out.buffer[out_buf], buf, size); + if (rc) { + rc = -EFAULT; goto done; } - total += nw; - if (total < size) { - pr_debug("%s: sleep (user %d total %d nw %d)\n", __func__, - size, total, nw); - mutex_unlock(&ads->out.lock); - rc = wait_for_completion_interruptible( - &ads->out.fifo_completion); - mutex_lock(&ads->out.lock); - if (rc == -ERESTARTSYS) { - pr_warn("%s: interrupted\n", __func__); - goto done; - } - pr_debug("%s: awake\n", __func__); - goto again; - } + prevent_suspend(&ads->out); + + req->size = size; + dma_sync_single_for_device(NULL, + req->source_addr, req->size, DMA_TO_DEVICE); + ads->out.last_queued = out_buf; + init_completion(&ads->out.stop_completion); - rc = total; - *off += total; + rc = start_playback(&ads->out, req); + if (!rc) + rc = size; + else + allow_suspend(&ads->out); done: mutex_unlock(&ads->out.lock); @@ -1467,43 +1082,42 @@ static long tegra_audio_out_ioctl(struct file *file, mutex_lock(&aos->lock); switch (cmd) { - case TEGRA_AUDIO_OUT_SET_BUF_CONFIG: { - struct tegra_audio_buf_config cfg; - if (copy_from_user(&cfg, (void __user *)arg, sizeof(cfg))) { + case TEGRA_AUDIO_OUT_FLUSH: + if (pending_buffer_requests(aos)) { + pr_debug("%s: flushing\n", __func__); + request_stop_nosync(aos); + pr_debug("%s: flushed\n", __func__); + } + aos->stop = false; + break; + case TEGRA_AUDIO_OUT_SET_NUM_BUFS: { + unsigned int num; + if (copy_from_user(&num, (const void __user *)arg, + sizeof(num))) { rc = -EFAULT; break; } - if (kfifo_len(&aos->fifo)) { + if (!num || num > I2S_MAX_NUM_BUFS) { + pr_err("%s: invalid buffer count %d\n", __func__, num); + rc = -EINVAL; + break; + } + if (pending_buffer_requests(aos)) { pr_err("%s: playback in progress\n", __func__); rc = -EBUSY; break; } - rc = init_stream_buffer(aos, &cfg, 0); + rc = init_stream_buffer(aos, num); if (rc < 0) break; - aos->buf_config = cfg; + aos->num_bufs = num; } break; - case TEGRA_AUDIO_OUT_GET_BUF_CONFIG: - if (copy_to_user((void __user *)arg, &aos->buf_config, - sizeof(aos->buf_config))) + case TEGRA_AUDIO_OUT_GET_NUM_BUFS: + if (copy_to_user((void __user *)arg, + &aos->num_bufs, sizeof(aos->num_bufs))) rc = -EFAULT; break; - case TEGRA_AUDIO_OUT_GET_ERROR_COUNT: - if (copy_to_user((void __user *)arg, &aos->errors, - sizeof(aos->errors))) - rc = -EFAULT; - if (!rc) - memset(&aos->errors, 0, sizeof(aos->errors)); - break; - case TEGRA_AUDIO_OUT_FLUSH: - if (kfifo_len(&aos->fifo)) { - pr_debug("%s: flushing\n", __func__); - request_stop_nosync(aos); - pr_debug("%s: flushed\n", __func__); - } - aos->stop = false; - break; default: rc = -EINVAL; } @@ -1553,10 +1167,10 @@ static long tegra_audio_ioctl(struct file *file, goto done; } - if (dma_restart && ads->using_dma) { + if (dma_restart) { pr_debug("%s: Restarting DMA due to configuration change.\n", __func__); - if (kfifo_len(&ads->out.fifo) || ads->in.active) { + if (pending_buffer_requests(&ads->out) || ads->in.active) { pr_err("%s: dma busy, cannot restart.\n", __func__); rc = -EBUSY; goto done; @@ -1585,13 +1199,16 @@ static long tegra_audio_in_ioctl(struct file *file, case TEGRA_AUDIO_IN_START: pr_debug("%s: start recording\n", __func__); ais->stop = false; - rc = start_recording_if_necessary(ais); - ais->active = !rc || rc == -EALREADY; break; case TEGRA_AUDIO_IN_STOP: - pr_debug("%s: start recording\n", __func__); - if (ais->active) + pr_debug("%s: stop recording\n", __func__); + if (ais->active) { + /* Clean up DMA/I2S, and complete the completion */ + sound_ops->stop_recording(ais); + complete(&ais->stop_completion); + /* Set stop flag and allow suspend. */ request_stop_nosync(ais); + } break; case TEGRA_AUDIO_IN_SET_CONFIG: { struct tegra_audio_in_config cfg; @@ -1607,35 +1224,6 @@ static long tegra_audio_in_ioctl(struct file *file, break; } -#ifdef SAMPLE_RATE_CONVERTER_IN_DRIVER - switch (cfg.rate) { - case 8000: - ads->in_divs = divs_8000; - ads->in_divs_len = ARRAY_SIZE(divs_8000); - break; - case 11025: - ads->in_divs = divs_11025; - ads->in_divs_len = ARRAY_SIZE(divs_11025); - break; - case 16000: - ads->in_divs = divs_16000; - ads->in_divs_len = ARRAY_SIZE(divs_16000); - break; - case 22050: - ads->in_divs = divs_22050; - ads->in_divs_len = ARRAY_SIZE(divs_22050); - break; - case 44100: - ads->in_divs = divs_44100; - ads->in_divs_len = ARRAY_SIZE(divs_44100); - break; - default: - pr_err("%s: invalid sampling rate %d\n", __func__, - cfg.rate); - rc = -EINVAL; - break; - } -#endif if (cfg.stereo && !ads->pdata->stereo_capture) { pr_err("%s: not capable of stereo capture.", __func__); @@ -1655,34 +1243,34 @@ static long tegra_audio_in_ioctl(struct file *file, sizeof(ads->in_config))) rc = -EFAULT; break; - case TEGRA_AUDIO_IN_SET_BUF_CONFIG: { - struct tegra_audio_buf_config cfg; - if (copy_from_user(&cfg, (void __user *)arg, sizeof(cfg))) { + case TEGRA_AUDIO_IN_SET_NUM_BUFS: { + unsigned int num; + if (copy_from_user(&num, (const void __user *)arg, + sizeof(num))) { rc = -EFAULT; break; } - if (ais->active) { + if (!num || num > I2S_MAX_NUM_BUFS) { + pr_err("%s: invalid buffer count %d\n", __func__, + num); + rc = -EINVAL; + break; + } + if (ais->active || pending_buffer_requests(ais)) { pr_err("%s: recording in progress\n", __func__); rc = -EBUSY; break; } - rc = init_stream_buffer(ais, &cfg, PCM_IN_BUFFER_PADDING); + rc = init_stream_buffer(ais, num); if (rc < 0) break; - ais->buf_config = cfg; + ais->num_bufs = num; } break; - case TEGRA_AUDIO_IN_GET_BUF_CONFIG: - if (copy_to_user((void __user *)arg, &ais->buf_config, - sizeof(ais->buf_config))) - rc = -EFAULT; - break; - case TEGRA_AUDIO_IN_GET_ERROR_COUNT: - if (copy_to_user((void __user *)arg, &ais->errors, - sizeof(ais->errors))) + case TEGRA_AUDIO_IN_GET_NUM_BUFS: + if (copy_from_user((void __user *)arg, + &ais->num_bufs, sizeof(ais->num_bufs))) rc = -EFAULT; - if (!rc) - memset(&ais->errors, 0, sizeof(ais->errors)); break; default: rc = -EINVAL; @@ -1692,293 +1280,119 @@ static long tegra_audio_in_ioctl(struct file *file, return rc; } -static ssize_t __i2s_copy_to_user(struct audio_driver_state *ads, - void __user *dst, int dst_size, - void *src, int src_size, - int *num_consumed) -{ - int bytes_written = dst_size < src_size ? dst_size : src_size; - *num_consumed = bytes_written; - if (copy_to_user(dst, src, bytes_written)) { - pr_err("%s: error copying %d bytes to user\n", __func__, - bytes_written); - return -EFAULT; - } - return bytes_written; -} - -#ifdef SAMPLE_RATE_CONVERTER_IN_DRIVER - -/* downsample a 16-bit 44.1kHz PCM stereo stream to stereo or mono 16-bit PCM - * stream. - */ - -static int downsample(const s16 *in, int in_len, - s16 *out, int out_len, - int *consumed, /* from input */ - const int *divs, int divs_len, - bool out_stereo) -{ - /* Todo: Handle mono source streams */ - int i, j; - int lsum, rsum; - int di, div; - int oi; - - i = 0; - oi = 0; - di = 0; - div = divs[0]; - while (i + div * 2 <= in_len && oi + out_stereo < out_len) { - for (j = 0, lsum = 0, rsum = 0; j < div; j++) { - lsum += in[i + j * 2]; - rsum += in[i + j * 2 + 1]; - } - if (!out_stereo) - out[oi] = (lsum + rsum) / (div * 2); - else { - out[oi] = lsum / div; - out[oi + 1] = rsum / div; - } - - oi += out_stereo + 1; - i += div * 2; - div = divs[++di % divs_len]; - } - - *consumed = i; - - pr_debug("%s: in_len %d out_len %d consumed %d generated %d\n", - __func__, in_len, out_len, *consumed, oi); - return oi; -} - -static ssize_t __downsample_to_user(struct audio_driver_state *ads, - void __user *dst, int dst_size, - void *src, int src_size, - int *num_consumed) -{ - int bytes_ds; - - pr_debug("%s\n", __func__); - - bytes_ds = downsample(src, src_size / sizeof(s16), - src, dst_size / sizeof(s16), - num_consumed, - ads->in_divs, ads->in_divs_len, - ads->in_config.stereo) * sizeof(s16); - - if (copy_to_user(dst, src, bytes_ds)) { - pr_err("%s: error copying %d bytes to user\n", __func__, - bytes_ds); - return -EFAULT; - } - - *num_consumed *= sizeof(s16); - BUG_ON(*num_consumed > src_size); - - pr_debug("%s: generated %d, skipped %d, original in fifo %d\n", - __func__, bytes_ds, *num_consumed, src_size); - - return bytes_ds; -} -#endif /*SAMPLE_RATE_CONVERTER_IN_DRIVER*/ - -static ssize_t downsample_to_user(struct audio_driver_state *ads, - void __user *buf, - size_t size) /* bytes to write to user buffer */ -{ - int i, nr_sg; - int bytes_consumed_from_fifo, bc_now; - int bytes_ds, ds_now; - bool take_two = false; - - struct scatterlist sgl[PCM_BUFFER_MAX_SIZE_ORDER - PAGE_SHIFT]; - sg_init_table(sgl, ARRAY_SIZE(sgl)); - - if (size == 0) { - pr_debug("%s: user buffer is full\n", __func__); - return 0; - } - - if (kfifo_is_empty(&ads->in.fifo)) { - pr_debug("%s: input fifo is empty\n", __func__); - return 0; - } - - nr_sg = kfifo_dma_out_prepare(&ads->in.fifo, - sgl, ARRAY_SIZE(sgl), - kfifo_len(&ads->in.fifo)); - BUG_ON(!nr_sg); - - pr_debug("%s (fifo size %d)\n", __func__, size); - - bytes_ds = 0; - bytes_consumed_from_fifo = 0; - for (bytes_ds = 0, i = 0; i < nr_sg; i++) { - BUG_ON(!sgl[i].length); - -again: -#ifdef SAMPLE_RATE_CONVERTER_IN_DRIVER - ds_now = __downsample_to_user( -#else - ds_now = __i2s_copy_to_user( -#endif - ads, - buf, size, - sg_virt(&sgl[i]), sgl[i].length, - &bc_now); - - if (!ds_now && !sg_is_last(sgl + i)) { - - BUG_ON(bc_now); - BUG_ON(take_two); - take_two = true; - - /* The assumption is that this sgl entry is at the end - * of the fifo, and there isn't enough space till the - * end of the fifo for at least one target sample to be - * generated. When this happens, we copy enough bytes - * from the next sgl entry to the end of the buffer of - * the current one, knowing that the copied bytes will - * cause the fifo to wrap around. We adjust the next - * entry, and continue with the loop. - */ - - BUG_ON(sg_virt(&sgl[i]) + sgl[i].length != - ads->in.buffer + kfifo_size(&ads->in.fifo)); - - if (sgl[i + 1].length < PCM_IN_BUFFER_PADDING) { - pr_debug("%s: not enough data till end of fifo\n", - __func__); - return 0; - } - - memcpy(sg_virt(&sgl[i]) + sgl[i].length, - sg_virt(&sgl[i + 1]), - PCM_IN_BUFFER_PADDING); - sgl[i].length += PCM_IN_BUFFER_PADDING; - - sg_set_buf(&sgl[i + 1], - sg_virt(&sgl[i + 1]) + PCM_IN_BUFFER_PADDING, - sgl[i + 1].length - PCM_IN_BUFFER_PADDING); - - goto again; - } - - bytes_ds += ds_now; - buf += ds_now; - BUG_ON(ds_now > size); - size -= ds_now; - bytes_consumed_from_fifo += bc_now; - pr_debug("%s: downsampled (%d req, %d actual)" \ - " -> total ds %d (size %d)\n", __func__, - sgl[i].length, bytes_consumed_from_fifo, - bytes_ds, size); - if (sg_is_last(sgl + i)) - break; - } - - kfifo_dma_out_finish(&ads->in.fifo, bytes_consumed_from_fifo); - - return bytes_ds; -} - static ssize_t tegra_audio_read(struct file *file, char __user *buf, size_t size, loff_t *off) { - ssize_t rc, total = 0; - ssize_t nr; - + ssize_t rc; + ssize_t nr = 0; + int in_buf; + struct tegra_dma_req *req; struct audio_driver_state *ads = ads_from_misc_in(file); mutex_lock(&ads->in.lock); - if (!IS_ALIGNED(size, 4)) { - pr_err("%s: user size request %d not aligned to 4\n", - __func__, size); + if (!IS_ALIGNED(size, 4) || size < 4 || size > buf_size(&ads->in)) { + pr_err("%s: invalid size %d.\n", __func__, size); rc = -EINVAL; - goto done_err; + goto done; } - pr_debug("%s:%d: read %d bytes, %d available\n", __func__, - smp_processor_id(), - size, kfifo_len(&ads->in.fifo)); + pr_debug("%s: size %d\n", __func__, size); -again: /* If we want recording to stop immediately after it gets cancelled, * then we do not want to wait for the fifo to get drained. */ - if (ads->in.stop /* && kfifo_is_empty(&ads->in.fifo) */) { - pr_debug("%s: recording has been cancelled (%d/%d bytes)\n", - __func__, total, size); - goto done_ok; + if (ads->in.stop) { + pr_debug("%s: recording has been cancelled\n", __func__); + rc = 0; + goto done; } - rc = start_recording_if_necessary(&ads->in); + /* This function calls prevent_suspend() internally */ + rc = start_recording_if_necessary(&ads->in, size); if (rc < 0 && rc != -EALREADY) { pr_err("%s: could not start recording\n", __func__); - goto done_err; + goto done; } ads->in.active = true; - nr = 0; - do { - nr = downsample_to_user(ads, buf + total, size - total); - if (nr < 0) { - rc = nr; - goto done_err; - } - total += nr; - } while (nr); - - pr_debug("%s: copied %d bytes to user, total %d/%d\n", - __func__, nr, total, size); - - if (total < size) { - mutex_unlock(&ads->in.lock); - pr_debug("%s: sleep (user %d total %d nr %d)\n", __func__, - size, total, nr); - rc = wait_for_completion_interruptible( - &ads->in.fifo_completion); - pr_debug("%s: awake\n", __func__); - mutex_lock(&ads->in.lock); - if (rc == -ERESTARTSYS) { - pr_warn("%s: interrupted\n", __func__); - goto done_err; - } - goto again; + /* Note that when tegra_audio_read() is called for the first time (or + * when all the buffers are empty), then it queues up all + * ads->in.num_bufs buffers, and in_buf is set to zero below. + */ + in_buf = (ads->in.last_queued + 1) % ads->in.num_bufs; + + /* Wait for the buffer to be filled (complete). The maximum timeout + * value could be calculated dynamically based on buf_size(&ads->in). + * For a buffer size of 16k, at 44.1kHz/stereo/16-bit PCM, you would + * have ~93ms. + */ + rc = wait_for_completion_interruptible_timeout( + &ads->in.comp[in_buf], HZ); + if (!rc) { + pr_err("%s: timeout", __func__); + rc = -ETIMEDOUT; + goto done; + } else if (rc < 0) { + pr_err("%s: wait error %d", __func__, rc); + goto done; } - pr_debug("%s: done reading %d bytes, %d available\n", __func__, - total, kfifo_avail(&ads->in.fifo)); + req = &ads->in.dma_req[in_buf]; -done_ok: - rc = total; - *off += total; + nr = size > req->size ? req->size : size; + req->size = size; + dma_sync_single_for_cpu(NULL, ads->in.dma_req[in_buf].dest_addr, + ads->in.dma_req[in_buf].size, DMA_FROM_DEVICE); + rc = copy_to_user(buf, ads->in.buffer[in_buf], nr); + if (rc) { + rc = -EFAULT; + goto done; + } + + init_completion(&ads->in.stop_completion); -done_err: + ads->in.last_queued = in_buf; + rc = tegra_dma_enqueue_req(ads->in.dma_chan, req); + /* We've successfully enqueued this request before. */ + BUG_ON(rc); + + rc = nr; + *off += nr; +done: mutex_unlock(&ads->in.lock); + pr_debug("%s: done %d\n", __func__, rc); return rc; } static int tegra_audio_out_open(struct inode *inode, struct file *file) { + int rc = 0; + int i; struct audio_driver_state *ads = ads_from_misc_out(file); pr_debug("%s\n", __func__); mutex_lock(&ads->out.lock); - if (!ads->out.opened++) { - pr_debug("%s: resetting fifo and error count\n", __func__); - ads->out.stop = false; - memset(&ads->out.errors, 0, sizeof(ads->out.errors)); - kfifo_reset(&ads->out.fifo); + + if (ads->out.opened) { + rc = -EBUSY; + goto done; } - mutex_unlock(&ads->out.lock); - return 0; + ads->out.opened = 1; + ads->out.stop = false; + + for (i = 0; i < I2S_MAX_NUM_BUFS; i++) { + init_completion(&ads->out.comp[i]); + /* TX buf rest state is unqueued, complete. */ + complete(&ads->out.comp[i]); + } + +done: + mutex_unlock(&ads->out.lock); + return rc; } static int tegra_audio_out_release(struct inode *inode, struct file *file) @@ -1988,42 +1402,40 @@ static int tegra_audio_out_release(struct inode *inode, struct file *file) pr_debug("%s\n", __func__); mutex_lock(&ads->out.lock); - if (ads->out.opened) - ads->out.opened--; - if (!ads->out.opened) { - stop_playback_if_necessary(&ads->out); - if (wake_lock_active(&ads->out.wake_lock)) - pr_err("%s: wake lock is still held!\n", __func__); - if (kfifo_len(&ads->out.fifo)) - pr_err("%s: output fifo is not empty (%d bytes left)\n", - __func__, kfifo_len(&ads->out.fifo)); - allow_suspend(&ads->out); - } + ads->out.opened = 0; + request_stop_nosync(&ads->out); + allow_suspend(&ads->out); mutex_unlock(&ads->out.lock); - + pr_debug("%s: done\n", __func__); return 0; } static int tegra_audio_in_open(struct inode *inode, struct file *file) { + int rc = 0; + int i; struct audio_driver_state *ads = ads_from_misc_in(file); pr_debug("%s\n", __func__); mutex_lock(&ads->in.lock); - if (!ads->in.opened++) { - pr_debug("%s: resetting fifo\n", __func__); - /* By default, do not start recording when someone reads from - * input device. - */ - ads->in.stop = false; - memset(&ads->in.errors, 0, sizeof(ads->in.errors)); - kfifo_reset(&ads->in.fifo); + if (ads->in.opened) { + rc = -EBUSY; + goto done; } - mutex_unlock(&ads->in.lock); - pr_debug("%s: done\n", __func__); - return 0; + ads->in.opened = 1; + ads->in.stop = false; + + for (i = 0; i < I2S_MAX_NUM_BUFS; i++) { + init_completion(&ads->in.comp[i]); + /* RX buf rest state is unqueued, complete. */ + complete(&ads->in.comp[i]); + } + +done: + mutex_unlock(&ads->in.lock); + return rc; } static int tegra_audio_in_release(struct inode *inode, struct file *file) @@ -2033,20 +1445,13 @@ static int tegra_audio_in_release(struct inode *inode, struct file *file) pr_debug("%s\n", __func__); mutex_lock(&ads->in.lock); - if (ads->in.opened) - ads->in.opened--; - - if (!ads->in.opened) { - if (ads->in.active) - request_stop_nosync(&ads->in); - if (wake_lock_active(&ads->in.wake_lock)) - pr_err("%s: wake lock is still held!\n", __func__); - if (kfifo_len(&ads->in.fifo)) - pr_err("%s: input fifo is not empty (%d bytes left)\n", - __func__, kfifo_len(&ads->in.fifo)); - allow_suspend(&ads->in); + ads->in.opened = 0; + if (ads->in.active) { + sound_ops->stop_recording(&ads->in); + complete(&ads->in.stop_completion); + request_stop_nosync(&ads->in); } - + allow_suspend(&ads->in); mutex_unlock(&ads->in.lock); pr_debug("%s: done\n", __func__); return 0; @@ -2097,56 +1502,25 @@ static const struct file_operations tegra_audio_ctl_fops = { .unlocked_ioctl = tegra_audio_ioctl, }; -static int init_stream_buffer(struct audio_stream *s, - struct tegra_audio_buf_config *cfg, - unsigned padding) +static int init_stream_buffer(struct audio_stream *s, int num) { - pr_debug("%s (size %d threshold %d chunk %d)\n", __func__, - cfg->size, cfg->threshold, cfg->chunk); - - if (cfg->chunk < PCM_DMA_CHUNK_MIN_SIZE_ORDER) { - pr_err("%s: chunk %d too small (%d min)\n", __func__, - cfg->chunk, PCM_DMA_CHUNK_MIN_SIZE_ORDER); - return -EINVAL; - } - - if (cfg->chunk > cfg->size) { - pr_err("%s: chunk %d > size %d\n", __func__, - cfg->chunk, cfg->size); - return -EINVAL; - } - - if (cfg->threshold > cfg->size) { - pr_err("%s: threshold %d > size %d\n", __func__, - cfg->threshold, cfg->size); - return -EINVAL; - } - - if ((1 << cfg->size) < padding) { - pr_err("%s: size %d < buffer padding %d (bytes)\n", __func__, - cfg->size, padding); - return -EINVAL; - } - - if (cfg->size > PCM_BUFFER_MAX_SIZE_ORDER) { - pr_err("%s: size %d exceeds max %d\n", __func__, - cfg->size, PCM_BUFFER_MAX_SIZE_ORDER); - return -EINVAL; - } - - if (!s->buffer) { - pr_debug("%s: allocating buffer (size %d, padding %d)\n", - __func__, 1 << cfg->size, padding); - s->buffer = kmalloc((1 << cfg->size) + padding, - GFP_KERNEL | GFP_DMA); - } - if (!s->buffer) { - pr_err("%s: could not allocate output buffer\n", __func__); - return -ENOMEM; + int i, j; + pr_debug("%s (num %d)\n", __func__, num); + + for (i = 0; i < num; i++) { + kfree(s->buffer[i]); + s->buffer[i] = + kmalloc((1 << PCM_BUFFER_MAX_SIZE_ORDER), + GFP_KERNEL | GFP_DMA); + if (!s->buffer[i]) { + pr_err("%s: could not allocate buffer\n", __func__); + for (j = i - 1; j >= 0; j--) { + kfree(s->buffer[j]); + s->buffer[j] = 0; + } + return -ENOMEM; + } } - - kfifo_init(&s->fifo, s->buffer, 1 << cfg->size); - sg_init_table(&s->sg, 1); return 0; } @@ -2187,47 +1561,15 @@ static ssize_t dma_toggle_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct tegra_audio_platform_data *pdata = dev->platform_data; - struct audio_driver_state *ads = pdata->driver_data; - return sprintf(buf, "%s\n", ads->using_dma ? "dma" : "pio"); + return sprintf(buf, "dma\n"); } static ssize_t dma_toggle_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - int use_dma; - struct tegra_audio_platform_data *pdata = dev->platform_data; - struct audio_driver_state *ads = pdata->driver_data; - - if (count < 4) - return -EINVAL; - - use_dma = 0; - if (!strncmp(buf, "dma", 3)) - use_dma = 1; - else if (strncmp(buf, "pio", 3)) { - dev_err(dev, "%s: invalid string [%s]\n", __func__, buf); - return -EINVAL; - } - - mutex_lock(&ads->out.lock); - mutex_lock(&ads->in.lock); - if (kfifo_len(&ads->out.fifo) || ads->in.active) { - dev_err(dev, "%s: playback or recording in progress.\n", - __func__); - mutex_unlock(&ads->in.lock); - mutex_unlock(&ads->out.lock); - return -EBUSY; - } - if (!!use_dma ^ !!ads->using_dma) - toggle_dma(ads); - else - dev_info(dev, "%s: no change\n", __func__); - mutex_unlock(&ads->in.lock); - mutex_unlock(&ads->out.lock); - - return count; + pr_err("%s: Not implemented.", __func__); + return 0; } static DEVICE_ATTR(dma_toggle, 0644, dma_toggle_show, dma_toggle_store); @@ -2311,7 +1653,7 @@ static ssize_t tx_fifo_atn_store(struct device *dev, struct tegra_audio_platform_data *pdata = dev->platform_data; struct audio_driver_state *ads = pdata->driver_data; mutex_lock(&ads->out.lock); - if (kfifo_len(&ads->out.fifo)) { + if (pending_buffer_requests(&ads->out)) { pr_err("%s: playback in progress.\n", __func__); rc = -EBUSY; goto done; @@ -2360,7 +1702,7 @@ static DEVICE_ATTR(rx_fifo_atn, 0644, rx_fifo_atn_show, rx_fifo_atn_store); static int tegra_audio_probe(struct platform_device *pdev) { - int rc; + int rc, i; struct resource *res; struct clk *i2s_clk, *audio_sync_clk, *dap_mclk; struct audio_driver_state *state; @@ -2415,8 +1757,6 @@ static int tegra_audio_probe(struct platform_device *pdev) } state->irq = res->start; - memset(&state->pio_stats, 0, sizeof(state->pio_stats)); - i2s_clk = clk_get(&pdev->dev, NULL); if (!i2s_clk) { dev_err(&pdev->dev, "%s: could not get i2s clock\n", @@ -2456,25 +1796,24 @@ static int tegra_audio_probe(struct platform_device *pdev) state->out.opened = 0; state->out.active = false; mutex_init(&state->out.lock); - init_completion(&state->out.fifo_completion); init_completion(&state->out.stop_completion); spin_lock_init(&state->out.dma_req_lock); - state->out.buf_phys = 0; state->out.dma_chan = NULL; - state->out.dma_has_it = false; - state->out.i2s_fifo_atn_level = I2S_FIFO_ATN_LVL_FOUR_SLOTS; - state->out.buffer = 0; - state->out.buf_config.size = PCM_BUFFER_MAX_SIZE_ORDER; - state->out.buf_config.threshold = PCM_BUFFER_THRESHOLD_ORDER; - state->out.buf_config.chunk = PCM_BUFFER_DMA_CHUNK_SIZE_ORDER; - rc = init_stream_buffer(&state->out, &state->out.buf_config, 0); + state->out.num_bufs = I2S_DEFAULT_TX_NUM_BUFS; + for (i = 0; i < I2S_MAX_NUM_BUFS; i++) { + init_completion(&state->out.comp[i]); + /* TX buf rest state is unqueued, complete. */ + complete(&state->out.comp[i]); + state->out.buffer[i] = 0; + state->out.buf_phy[i] = 0; + } + state->out.last_queued = 0; + rc = init_stream_buffer(&state->out, state->out.num_bufs); if (rc < 0) return rc; INIT_WORK(&state->out.allow_suspend_work, allow_suspend_worker); - pm_qos_add_request(&state->out.pm_qos, PM_QOS_CPU_DMA_LATENCY, - PM_QOS_DEFAULT_VALUE); snprintf(state->out.wake_lock_name, sizeof(state->out.wake_lock_name), @@ -2499,26 +1838,24 @@ static int tegra_audio_probe(struct platform_device *pdev) state->in.opened = 0; state->in.active = false; mutex_init(&state->in.lock); - init_completion(&state->in.fifo_completion); init_completion(&state->in.stop_completion); spin_lock_init(&state->in.dma_req_lock); - state->in.buf_phys = 0; state->in.dma_chan = NULL; - state->in.dma_has_it = false; - state->in.i2s_fifo_atn_level = I2S_FIFO_ATN_LVL_FOUR_SLOTS; - state->in.buffer = 0; - state->in.buf_config.size = PCM_BUFFER_MAX_SIZE_ORDER; - state->in.buf_config.threshold = PCM_BUFFER_THRESHOLD_ORDER; - state->in.buf_config.chunk = PCM_BUFFER_DMA_CHUNK_SIZE_ORDER; - rc = init_stream_buffer(&state->in, &state->in.buf_config, - PCM_IN_BUFFER_PADDING); + state->in.num_bufs = I2S_DEFAULT_RX_NUM_BUFS; + for (i = 0; i < I2S_MAX_NUM_BUFS; i++) { + init_completion(&state->in.comp[i]); + /* RX buf rest state is unqueued, complete. */ + complete(&state->in.comp[i]); + state->in.buffer[i] = 0; + state->in.buf_phy[i] = 0; + } + state->in.last_queued = 0; + rc = init_stream_buffer(&state->in, state->in.num_bufs); if (rc < 0) return rc; INIT_WORK(&state->in.allow_suspend_work, allow_suspend_worker); - pm_qos_add_request(&state->in.pm_qos, PM_QOS_CPU_DMA_LATENCY, - PM_QOS_DEFAULT_VALUE); snprintf(state->in.wake_lock_name, sizeof(state->in.wake_lock_name), @@ -2553,9 +1890,6 @@ static int tegra_audio_probe(struct platform_device *pdev) if (rc < 0) return rc; - state->using_dma = state->pdata->dma_on; - if (!state->using_dma) - sound_ops = &pio_sound_ops; sound_ops->setup(state); rc = device_create_file(&pdev->dev, &dev_attr_dma_toggle); @@ -2581,8 +1915,6 @@ static int tegra_audio_probe(struct platform_device *pdev) state->in_config.rate = 11025; state->in_config.stereo = false; - state->in_divs = divs_11025; - state->in_divs_len = ARRAY_SIZE(divs_11025); return 0; } diff --git a/arch/arm/mach-tegra/tegra_spdif_audio.c b/arch/arm/mach-tegra/tegra_spdif_audio.c index fd96f2abf5eb..faa53a24e219 100644 --- a/arch/arm/mach-tegra/tegra_spdif_audio.c +++ b/arch/arm/mach-tegra/tegra_spdif_audio.c @@ -48,6 +48,7 @@ #include <linux/delay.h> #include <linux/tegra_audio.h> #include <linux/workqueue.h> +#include <linux/pm.h> #include <mach/dma.h> #include <mach/iomap.h> @@ -69,7 +70,6 @@ struct audio_stream { int opened; struct mutex lock; - struct tegra_audio_buf_config buf_config; bool active; /* is DMA or PIO in progress? */ void *buffer; dma_addr_t buf_phys; @@ -77,8 +77,6 @@ struct audio_stream { struct completion fifo_completion; struct scatterlist sg; - struct tegra_audio_error_counts errors; - int spdif_fifo_atn_level; ktime_t last_dma_ts; @@ -112,6 +110,7 @@ struct audio_driver_state { bool using_dma; unsigned long dma_req_sel; + bool fifo_init; int irq; /* for pio mode */ struct spdif_pio_stats pio_stats; @@ -125,17 +124,17 @@ struct audio_driver_state { static inline int buf_size(struct audio_stream *s) { - return 1 << s->buf_config.size; + return 1 << PCM_BUFFER_MAX_SIZE_ORDER; } static inline int chunk_size(struct audio_stream *s) { - return 1 << s->buf_config.chunk; + return 1 << PCM_BUFFER_DMA_CHUNK_SIZE_ORDER; } static inline int threshold_size(struct audio_stream *s) { - return 1 << s->buf_config.threshold; + return 1 << PCM_BUFFER_THRESHOLD_ORDER; } static inline struct audio_driver_state *ads_from_misc_out(struct file *file) @@ -237,7 +236,11 @@ static int spdif_fifo_set_attention_level(unsigned long base, static void spdif_fifo_enable(unsigned long base, int on) { u32 val = spdif_readl(base, SPDIF_CTRL_0); - val &= ~(SPDIF_CTRL_0_TX_EN | SPDIF_CTRL_0_TC_EN); + + if (on && (val & SPDIF_CTRL_0_TX_EN)) + return; + + val &= ~(SPDIF_CTRL_0_TX_EN | SPDIF_CTRL_0_TC_EN | SPDIF_CTRL_0_TU_EN); val |= on ? (SPDIF_CTRL_0_TX_EN) : 0; val |= on ? (SPDIF_CTRL_0_TC_EN) : 0; @@ -347,12 +350,11 @@ static int spdif_set_sample_rate(struct audio_driver_state *state, unsigned int sample_rate) { unsigned int clock_freq = 0; - unsigned int parent_clock_freq = 0; struct clk *spdif_clk; unsigned int ch_sta[] = { 0x0, /* 44.1, default values */ - 0xf << 4, /* bits 36-39, original sample freq -- 44.1 */ + 0x0, 0x0, 0x0, 0x0, @@ -362,37 +364,30 @@ static int spdif_set_sample_rate(struct audio_driver_state *state, switch (sample_rate) { case 32000: clock_freq = 4096000; /* 4.0960 MHz */ - parent_clock_freq = 12288000; ch_sta[0] = 0x3 << 24; - ch_sta[1] = 0xC << 4; + /*ch_sta[1] = 0xC << 4;*/ break; case 44100: clock_freq = 5644800; /* 5.6448 MHz */ - parent_clock_freq = 11289600; ch_sta[0] = 0x0; - ch_sta[1] = 0xF << 4; + /*ch_sta[1] = 0xF << 4;*/ break; case 48000: clock_freq = 6144000; /* 6.1440MHz */ - parent_clock_freq = 12288000; ch_sta[0] = 0x2 << 24; - ch_sta[1] = 0xD << 4; + /*ch_sta[1] = 0xD << 4;*/ break; case 88200: clock_freq = 11289600; /* 11.2896 MHz */ - parent_clock_freq = 11289600; break; case 96000: clock_freq = 12288000; /* 12.288 MHz */ - parent_clock_freq = 12288000; break; case 176400: clock_freq = 22579200; /* 22.5792 MHz */ - parent_clock_freq = 11289600; break; case 192000: clock_freq = 24576000; /* 24.5760 MHz */ - parent_clock_freq = 12288000; break; default: return -1; @@ -423,8 +418,32 @@ static int spdif_set_sample_rate(struct audio_driver_state *state, return 0; } -static int init_stream_buffer(struct audio_stream *, - struct tegra_audio_buf_config *cfg, unsigned); +static int spdif_configure(struct platform_device *pdev) +{ + struct tegra_audio_platform_data *pdata = pdev->dev.platform_data; + struct audio_driver_state *state = pdata->driver_data; + + if (!state) + return -ENOMEM; + + /* disable interrupts from SPDIF */ + spdif_writel(state->spdif_base, 0x0, SPDIF_CTRL_0); + spdif_fifo_clear(state->spdif_base); + spdif_enable_fifos(state->spdif_base, 0); + + spdif_set_bit_mode(state->spdif_base, state->pdata->mode); + spdif_set_fifo_packed(state->spdif_base, state->pdata->fifo_fmt); + + spdif_fifo_set_attention_level(state->spdif_base, + state->out.spdif_fifo_atn_level); + + spdif_set_sample_rate(state, 44100); + + state->fifo_init = true; + return 0; +} + +static int init_stream_buffer(struct audio_stream *); static int setup_dma(struct audio_driver_state *); static void tear_down_dma(struct audio_driver_state *); @@ -595,7 +614,6 @@ static void dma_tx_complete_callback(struct tegra_dma_req *req) if (delta_us > max_delay_us) { pr_debug("%s: too late by %lld us\n", __func__, delta_us - max_delay_us); - aos->errors.late_dma++; } kfifo_dma_out_finish(&aos->fifo, count); @@ -633,9 +651,14 @@ static void setup_dma_tx_request(struct tegra_dma_req *req, req->to_memory = false; req->dest_addr = spdif_get_fifo_phy_base(ads->spdif_phys); req->dest_wrap = 4; - req->dest_bus_width = 16; - req->source_bus_width = 32; + if (ads->pdata->mode != SPDIF_BIT_MODE_MODE16BIT || + ads->pdata->fifo_fmt) + req->dest_bus_width = 32; + else + req->dest_bus_width = 16; + req->source_wrap = 0; + req->source_bus_width = 32; req->req_sel = ads->dma_req_sel; } @@ -667,8 +690,6 @@ static int resume_dma_playback(struct audio_stream *aos) #if 0 spdif_fifo_clear(ads->spdif_base); #endif - spdif_fifo_set_attention_level(ads->spdif_base, - aos->spdif_fifo_atn_level); req->source_addr = sg_dma_address(&aos->sg); req->size = sg_dma_len(&aos->sg); @@ -681,7 +702,12 @@ static int resume_dma_playback(struct audio_stream *aos) pr_debug("%s resume playback (%d in fifo, writing %d)\n", __func__, kfifo_len(&aos->fifo), req->size); - spdif_fifo_enable(ads->spdif_base, 1); + if (ads->fifo_init) { + spdif_set_bit_mode(ads->spdif_base, ads->pdata->mode); + spdif_set_fifo_packed(ads->spdif_base, ads->pdata->fifo_fmt); + spdif_fifo_enable(ads->spdif_base, 1); + ads->fifo_init = false; + } aos->last_dma_ts = ktime_get_real(); rc = tegra_dma_enqueue_req(aos->dma_chan, req); @@ -712,6 +738,7 @@ static void stop_dma_playback(struct audio_stream *aos) } if (spin == 100) pr_warn("%s: spinny\n", __func__); + ads->fifo_init = true; } /* PIO (non-DMA) */ @@ -925,35 +952,6 @@ static long tegra_spdif_out_ioctl(struct file *file, mutex_lock(&aos->lock); switch (cmd) { - case TEGRA_AUDIO_OUT_SET_BUF_CONFIG: { - struct tegra_audio_buf_config cfg; - if (copy_from_user(&cfg, (void __user *)arg, sizeof(cfg))) { - rc = -EFAULT; - break; - } - if (kfifo_len(&aos->fifo)) { - pr_err("%s: playback in progress\n", __func__); - rc = -EBUSY; - break; - } - rc = init_stream_buffer(aos, &cfg, 0); - if (rc < 0) - break; - aos->buf_config = cfg; - } - break; - case TEGRA_AUDIO_OUT_GET_BUF_CONFIG: - if (copy_to_user((void __user *)arg, &aos->buf_config, - sizeof(aos->buf_config))) - rc = -EFAULT; - break; - case TEGRA_AUDIO_OUT_GET_ERROR_COUNT: - if (copy_to_user((void __user *)arg, &aos->errors, - sizeof(aos->errors))) - rc = -EFAULT; - if (!rc) - memset(&aos->errors, 0, sizeof(aos->errors)); - break; case TEGRA_AUDIO_OUT_FLUSH: if (kfifo_len(&aos->fifo)) { pr_debug("%s: flushing\n", __func__); @@ -981,10 +979,9 @@ static int tegra_spdif_out_open(struct inode *inode, struct file *file) if (!ads->out.opened++) { pr_debug("%s: resetting fifo and error count\n", __func__); ads->out.stop = false; - memset(&ads->out.errors, 0, sizeof(ads->out.errors)); kfifo_reset(&ads->out.fifo); - rc = spdif_set_sample_rate(ads, 44100); + rc = spdif_set_sample_rate(ads, 44100); } mutex_unlock(&ads->out.lock); @@ -1049,55 +1046,19 @@ static const struct file_operations tegra_spdif_out_ctl_fops = { .unlocked_ioctl = tegra_spdif_out_ioctl, }; -static int init_stream_buffer(struct audio_stream *s, - struct tegra_audio_buf_config *cfg, - unsigned padding) +static int init_stream_buffer(struct audio_stream *s) { - pr_info("%s (size %d threshold %d chunk %d)\n", __func__, - cfg->size, cfg->threshold, cfg->chunk); - - if (cfg->chunk < PCM_DMA_CHUNK_MIN_SIZE_ORDER) { - pr_err("%s: chunk %d too small (%d min)\n", __func__, - cfg->chunk, PCM_DMA_CHUNK_MIN_SIZE_ORDER); - return -EINVAL; - } - - if (cfg->chunk > cfg->size) { - pr_err("%s: chunk %d > size %d\n", __func__, - cfg->chunk, cfg->size); - return -EINVAL; - } - - if (cfg->threshold > cfg->size) { - pr_err("%s: threshold %d > size %d\n", __func__, - cfg->threshold, cfg->size); - return -EINVAL; - } - - if ((1 << cfg->size) < padding) { - pr_err("%s: size %d < buffer padding %d (bytes)\n", __func__, - cfg->size, padding); - return -EINVAL; - } - - if (cfg->size > PCM_BUFFER_MAX_SIZE_ORDER) { - pr_err("%s: size %d exceeds max %d\n", __func__, - cfg->size, PCM_BUFFER_MAX_SIZE_ORDER); - return -EINVAL; - } + pr_info("%s\n", __func__); - if (!s->buffer) { - pr_debug("%s: allocating buffer (size %d, padding %d)\n", - __func__, 1 << cfg->size, padding); - s->buffer = kmalloc((1 << cfg->size) + padding, - GFP_KERNEL | GFP_DMA); - } + kfree(s->buffer); + s->buffer = kmalloc(1 << PCM_BUFFER_MAX_SIZE_ORDER, + GFP_KERNEL | GFP_DMA); if (!s->buffer) { pr_err("%s: could not allocate output buffer\n", __func__); return -ENOMEM; } - kfifo_init(&s->fifo, s->buffer, 1 << cfg->size); + kfifo_init(&s->fifo, s->buffer, 1 << PCM_BUFFER_MAX_SIZE_ORDER); sg_init_table(&s->sg, 1); return 0; } @@ -1312,8 +1273,6 @@ static int tegra_spdif_probe(struct platform_device *pdev) return -EIO; } - state->out.spdif_fifo_atn_level = SPDIF_FIFO_ATN_LVL_FOUR_SLOTS; - res = platform_get_resource(pdev, IORESOURCE_DMA, 0); if (!res) { dev_err(&pdev->dev, "no dma resource!\n"); @@ -1330,12 +1289,10 @@ static int tegra_spdif_probe(struct platform_device *pdev) memset(&state->pio_stats, 0, sizeof(state->pio_stats)); - /* disable interrupts from SPDIF */ - spdif_fifo_clear(state->spdif_base); - spdif_enable_fifos(state->spdif_base, 0); - spdif_set_bit_mode(state->spdif_base, state->pdata->mode); - spdif_set_fifo_packed(state->spdif_base, state->pdata->fifo_fmt); + rc = spdif_configure(pdev); + if (rc < 0) + return rc; state->out.opened = 0; state->out.active = false; @@ -1348,10 +1305,7 @@ static int tegra_spdif_probe(struct platform_device *pdev) state->out.dma_has_it = false; state->out.buffer = 0; - state->out.buf_config.size = PCM_BUFFER_MAX_SIZE_ORDER; - state->out.buf_config.threshold = PCM_BUFFER_THRESHOLD_ORDER; - state->out.buf_config.chunk = PCM_BUFFER_DMA_CHUNK_SIZE_ORDER; - rc = init_stream_buffer(&state->out, &state->out.buf_config, 0); + rc = init_stream_buffer(&state->out); if (rc < 0) return rc; @@ -1405,12 +1359,29 @@ static int tegra_spdif_probe(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM +static int tegra_spdif_suspend(struct platform_device *pdev, pm_message_t mesg) +{ + /* dev_info(&pdev->dev, "%s\n", __func__); */ + return 0; +} + +static int tegra_spdif_resume(struct platform_device *pdev) +{ + return spdif_configure(pdev); +} +#endif /* CONFIG_PM */ + static struct platform_driver tegra_spdif_driver = { .driver = { .name = "spdif_out", .owner = THIS_MODULE, }, .probe = tegra_spdif_probe, +#ifdef CONFIG_PM + .suspend = tegra_spdif_suspend, + .resume = tegra_spdif_resume, +#endif }; static int __init tegra_spdif_init(void) diff --git a/arch/arm/mach-tegra/usb_phy.c b/arch/arm/mach-tegra/usb_phy.c index 71e581512151..e7929aed1025 100644 --- a/arch/arm/mach-tegra/usb_phy.c +++ b/arch/arm/mach-tegra/usb_phy.c @@ -116,6 +116,7 @@ #define UTMIP_TX_CFG0 0x820 #define UTMIP_FS_PREABMLE_J (1 << 19) +#define UTMIP_HS_DISCON_DISABLE (1 << 8) #define UTMIP_MISC_CFG0 0x824 #define UTMIP_SUSPEND_EXIT_ON_EDGE (1 << 22) @@ -501,6 +502,26 @@ static void utmi_phy_power_off(struct tegra_usb_phy *phy) utmip_pad_power_off(phy); } +static void utmi_phy_preresume(struct tegra_usb_phy *phy) +{ + unsigned long val; + void __iomem *base = phy->regs; + + val = readl(base + UTMIP_TX_CFG0); + val |= UTMIP_HS_DISCON_DISABLE; + writel(val, base + UTMIP_TX_CFG0); +} + +static void utmi_phy_postresume(struct tegra_usb_phy *phy) +{ + unsigned long val; + void __iomem *base = phy->regs; + + val = readl(base + UTMIP_TX_CFG0); + val &= ~UTMIP_HS_DISCON_DISABLE; + writel(val, base + UTMIP_TX_CFG0); +} + static void ulpi_viewport_write(struct tegra_usb_phy *phy, u8 addr, u8 data) { unsigned long val; @@ -692,6 +713,20 @@ int tegra_usb_phy_power_off(struct tegra_usb_phy *phy) return 0; } +int tegra_usb_phy_preresume(struct tegra_usb_phy *phy) +{ + if (phy->instance == 2) + utmi_phy_preresume(phy); + return 0; +} + +int tegra_usb_phy_postresume(struct tegra_usb_phy *phy) +{ + if (phy->instance == 2) + utmi_phy_postresume(phy); + return 0; +} + int tegra_usb_phy_clk_disable(struct tegra_usb_phy *phy) { if (phy->instance != 1) diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c index 4493802a458e..f65bcd29d2e6 100644 --- a/drivers/mmc/host/sdhci-tegra.c +++ b/drivers/mmc/host/sdhci-tegra.c @@ -224,6 +224,7 @@ static int tegra_sdhci_suspend(struct platform_device *pdev, pm_message_t state) if (ret) pr_err("%s: failed, error = %d\n", __func__, ret); + tegra_sdhci_enable_clock(host, 0); return ret; } diff --git a/drivers/net/wireless/bcm4329/Makefile b/drivers/net/wireless/bcm4329/Makefile index bffe59160c54..20bdab258433 100644 --- a/drivers/net/wireless/bcm4329/Makefile +++ b/drivers/net/wireless/bcm4329/Makefile @@ -6,7 +6,7 @@ DHDCFLAGS = -DLINUX -DBCMDRIVER -DBCMDONGLEHOST -DDHDTHREAD -DBCMWPA2 \ -Wall -Wstrict-prototypes -Werror -DCUSTOMER_HW2 -DMMC_SDIO_ABORT \ -DDHD_DEBUG_TRAP -DSOFTAP -DEMBEDDED_PLATFORM -DARP_OFFLOAD_SUPPORT \ -DPKT_FILTER_SUPPORT -DSET_RANDOM_MAC_SOFTAP -DCSCAN \ - -DKEEP_ALIVE \ + -DKEEP_ALIVE -DCONFIG_US_NON_DFS_CHANNELS_ONLY \ -Idrivers/net/wireless/bcm4329 -Idrivers/net/wireless/bcm4329/include ifeq ($(CONFIG_BCM4329_WIFI_CONTROL_FUNC),y) diff --git a/drivers/net/wireless/bcm4329/dhd.h b/drivers/net/wireless/bcm4329/dhd.h index 8f95e576ca65..1ddf1ff61e70 100644 --- a/drivers/net/wireless/bcm4329/dhd.h +++ b/drivers/net/wireless/bcm4329/dhd.h @@ -24,7 +24,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: dhd.h,v 1.32.4.7.2.4.14.49.4.1 2010/09/23 02:33:19 Exp $ + * $Id: dhd.h,v 1.32.4.7.2.4.14.49.4.7 2010/11/12 22:48:36 Exp $ */ /**************** @@ -90,6 +90,7 @@ enum dhd_bus_wake_state { WAKE_LOCK_SOFTAP_SET, WAKE_LOCK_SOFTAP_STOP, WAKE_LOCK_SOFTAP_START, + WAKE_LOCK_SOFTAP_THREAD, WAKE_LOCK_MAX }; enum dhd_prealloc_index { @@ -351,6 +352,7 @@ typedef enum cust_gpio_modes { WLAN_POWER_ON, WLAN_POWER_OFF } cust_gpio_modes_t; + extern int wl_iw_iscan_set_scan_broadcast_prep(struct net_device *dev, uint flag); extern int wl_iw_send_priv_event(struct net_device *dev, char *flag); extern int net_os_send_hang_message(struct net_device *dev); diff --git a/drivers/net/wireless/bcm4329/dhd_common.c b/drivers/net/wireless/bcm4329/dhd_common.c index dbd4b922a262..b8bab3016115 100644 --- a/drivers/net/wireless/bcm4329/dhd_common.c +++ b/drivers/net/wireless/bcm4329/dhd_common.c @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: dhd_common.c,v 1.5.6.8.2.6.6.69.4.10 2010/10/29 19:58:08 Exp $ + * $Id: dhd_common.c,v 1.5.6.8.2.6.6.69.4.16 2010/11/18 03:53:32 Exp $ */ #include <typedefs.h> #include <osl.h> @@ -1230,8 +1230,9 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) uint bcn_timeout = 3; int scan_assoc_time = 40; int scan_unassoc_time = 40; + uint32 listen_interval = LISTEN_INTERVAL; /* Default Listen Interval in Beacons */ + int ret = 0; #ifdef GET_CUSTOM_MAC_ENABLE - int ret; struct ether_addr ea_addr; #endif /* GET_CUSTOM_MAC_ENABLE */ @@ -1258,7 +1259,6 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) #ifdef SET_RANDOM_MAC_SOFTAP if (strstr(fw_path, "apsta") != NULL) { uint rand_mac; - int ret; srandom32((uint)jiffies); rand_mac = random32(); @@ -1289,6 +1289,11 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) } } + /* Set Listen Interval */ + bcm_mkiovar("assoc_listen", (char *)&listen_interval, 4, iovbuf, sizeof(iovbuf)); + if ((ret = dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf))) < 0) + DHD_ERROR(("%s assoc_listen failed %d\n", __FUNCTION__, ret)); + /* query for 'ver' to get version info from firmware */ memset(buf, 0, sizeof(buf)); ptr = buf; @@ -1797,6 +1802,57 @@ fail: #endif +/* Function to estimate possible DTIM_SKIP value */ +int dhd_get_dtim_skip(dhd_pub_t *dhd) +{ + int bcn_li_dtim; + char buf[128]; + int ret; + int dtim_assoc = 0; + + if ((dhd->dtim_skip == 0) || (dhd->dtim_skip == 1)) + bcn_li_dtim = 3; + else + bcn_li_dtim = dhd->dtim_skip; + + /* Read DTIM value if associated */ + memset(buf, 0, sizeof(buf)); + bcm_mkiovar("dtim_assoc", 0, 0, buf, sizeof(buf)); + if ((ret = dhdcdc_query_ioctl(dhd, 0, WLC_GET_VAR, buf, sizeof(buf))) < 0) { + DHD_ERROR(("%s failed code %d\n", __FUNCTION__, ret)); + bcn_li_dtim = 1; + goto exit; + } + else + dtim_assoc = dtoh32(*(int *)buf); + + DHD_ERROR(("%s bcn_li_dtim=%d DTIM=%d Listen=%d\n", \ + __FUNCTION__, bcn_li_dtim, dtim_assoc, LISTEN_INTERVAL)); + + /* if not assocated just eixt */ + if (dtim_assoc == 0) { + goto exit; + } + + /* check if sta listen interval fits into AP dtim */ + if (dtim_assoc > LISTEN_INTERVAL) { + /* AP DTIM to big for our Listen Interval : no dtim skiping */ + bcn_li_dtim = 1; + DHD_ERROR(("%s DTIM=%d > Listen=%d : too big ...\n", \ + __FUNCTION__, dtim_assoc, LISTEN_INTERVAL)); + goto exit; + } + + if ((bcn_li_dtim * dtim_assoc) > LISTEN_INTERVAL) { + /* Round up dtim_skip to fit into STAs Listen Interval */ + bcn_li_dtim = (int)(LISTEN_INTERVAL / dtim_assoc); + DHD_TRACE(("%s agjust dtim_skip as %d\n", __FUNCTION__, bcn_li_dtim)); + } + +exit: + return bcn_li_dtim; +} + #ifdef PNO_SUPPORT int dhd_pno_clean(dhd_pub_t *dhd) { diff --git a/drivers/net/wireless/bcm4329/dhd_linux.c b/drivers/net/wireless/bcm4329/dhd_linux.c index 80dbbff9b772..472b992751e7 100644 --- a/drivers/net/wireless/bcm4329/dhd_linux.c +++ b/drivers/net/wireless/bcm4329/dhd_linux.c @@ -22,7 +22,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: dhd_linux.c,v 1.65.4.9.2.12.2.104.4.29 2010/11/04 01:14:41 Exp $ + * $Id: dhd_linux.c,v 1.65.4.9.2.12.2.104.4.35 2010/11/17 03:13:21 Exp $ */ #ifdef CONFIG_WIFI_CONTROL_FUNC @@ -555,10 +555,7 @@ static int dhd_set_suspend(int value, dhd_pub_t *dhd) * for better power saving. * Note that side effect is chance to miss BC/MC packet */ - if ((dhd->dtim_skip == 0) || (dhd->dtim_skip == 1)) - bcn_li_dtim = 3; - else - bcn_li_dtim = dhd->dtim_skip; + bcn_li_dtim = dhd_get_dtim_skip(dhd); bcm_mkiovar("bcn_li_dtim", (char *)&bcn_li_dtim, 4, iovbuf, sizeof(iovbuf)); dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf)); @@ -1867,7 +1864,7 @@ dhd_stop(struct net_device *net) #if !defined(IGNORE_ETH0_DOWN) dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(net); - DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + DHD_TRACE(("%s: Enter %s\n", __FUNCTION__, net->name)); if (dhd->pub.up == 0) { return 0; } @@ -3078,6 +3075,15 @@ void dhd_bus_country_set(struct net_device *dev, char *country_code) strncpy(dhd->pub.country_code, country_code, WLC_CNTRY_BUF_SZ); } +char *dhd_bus_country_get(struct net_device *dev) +{ + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); + + if (dhd && (dhd->pub.country_code[0] != 0)) + return dhd->pub.country_code; + return NULL; +} + void dhd_os_start_lock(dhd_pub_t *pub) { #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) diff --git a/drivers/net/wireless/bcm4329/include/epivers.h b/drivers/net/wireless/bcm4329/include/epivers.h index 9b5a2f1f20d7..062df0480357 100644 --- a/drivers/net/wireless/bcm4329/include/epivers.h +++ b/drivers/net/wireless/bcm4329/include/epivers.h @@ -33,16 +33,16 @@ #define EPI_RC_NUMBER 248 -#define EPI_INCREMENTAL_NUMBER 13 +#define EPI_INCREMENTAL_NUMBER 15 #define EPI_BUILD_NUMBER 0 -#define EPI_VERSION 4, 218, 248, 13 +#define EPI_VERSION 4, 218, 248, 15 -#define EPI_VERSION_NUM 0x04daf80d +#define EPI_VERSION_NUM 0x04daf80f -#define EPI_VERSION_STR "4.218.248.13" -#define EPI_ROUTER_VERSION_STR "4.219.248.13" +#define EPI_VERSION_STR "4.218.248.15" +#define EPI_ROUTER_VERSION_STR "4.219.248.15" #endif diff --git a/drivers/net/wireless/bcm4329/include/wlioctl.h b/drivers/net/wireless/bcm4329/include/wlioctl.h index 345ba34b94c0..cd7725a70db4 100644 --- a/drivers/net/wireless/bcm4329/include/wlioctl.h +++ b/drivers/net/wireless/bcm4329/include/wlioctl.h @@ -24,7 +24,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: wlioctl.h,v 1.601.4.15.2.14.2.62 2010/08/19 01:20:12 Exp $ + * $Id: wlioctl.h,v 1.601.4.15.2.14.2.62.4.1 2010/11/17 03:09:28 Exp $ */ @@ -857,6 +857,7 @@ typedef struct wl_ioctl { #define PM_MAX 1 #define PM_FAST 2 +#define LISTEN_INTERVAL 20 #define INTERFERE_NONE 0 #define NON_WLAN 1 diff --git a/drivers/net/wireless/bcm4329/wl_iw.c b/drivers/net/wireless/bcm4329/wl_iw.c index 547c6cee0082..e6a8c61aff9d 100644 --- a/drivers/net/wireless/bcm4329/wl_iw.c +++ b/drivers/net/wireless/bcm4329/wl_iw.c @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: wl_iw.c,v 1.51.4.9.2.6.4.142.4.45 2010/11/04 21:08:09 Exp $ + * $Id: wl_iw.c,v 1.51.4.9.2.6.4.142.4.58 2010/11/18 02:08:30 Exp $ */ @@ -115,6 +115,10 @@ static int g_onoff = G_WLAN_SET_ON; wl_iw_extra_params_t g_wl_iw_params; static struct mutex wl_cache_lock; +#ifdef CONFIG_US_NON_DFS_CHANNELS_ONLY +static bool use_non_dfs_channels = true; +#endif + extern bool wl_iw_conn_status_str(uint32 event_type, uint32 status, uint32 reason, char* stringBuf, uint buflen); #include <bcmsdbus.h> @@ -611,6 +615,31 @@ wl_iw_get_macaddr( return error; } +static int +wl_iw_set_country_code(struct net_device *dev, char *ccode) +{ + char country_code[WLC_CNTRY_BUF_SZ]; + int ret = -1; + + WL_TRACE(("%s\n", __FUNCTION__)); + if (!ccode) + ccode = dhd_bus_country_get(dev); + strncpy(country_code, ccode, sizeof(country_code)); + if (ccode && (country_code[0] != 0)) { +#ifdef CONFIG_US_NON_DFS_CHANNELS_ONLY + if (use_non_dfs_channels && !strncmp(country_code, "US", 2)) + strncpy(country_code, "Q2", WLC_CNTRY_BUF_SZ); + if (!use_non_dfs_channels && !strncmp(country_code, "Q2", 2)) + strncpy(country_code, "US", WLC_CNTRY_BUF_SZ); +#endif + ret = dev_wlc_ioctl(dev, WLC_SET_COUNTRY, &country_code, sizeof(country_code)); + if (ret >= 0) { + WL_TRACE(("%s: set country %s OK\n", __FUNCTION__, country_code)); + dhd_bus_country_set(dev, &country_code[0]); + } + } + return ret; +} static int wl_iw_set_country( @@ -632,16 +661,13 @@ wl_iw_set_country( country_offset = strcspn(extra, " "); country_code_size = strlen(extra) - country_offset; - if (country_offset != 0) { strncpy(country_code, extra + country_offset + 1, MIN(country_code_size, sizeof(country_code))); - - if ((error = dev_wlc_ioctl(dev, WLC_SET_COUNTRY, - &country_code, sizeof(country_code))) >= 0) { + error = wl_iw_set_country_code(dev, country_code); + if (error >= 0) { p += snprintf(p, MAX_WX_STRING, "OK"); WL_TRACE(("%s: set country %s OK\n", __FUNCTION__, country_code)); - dhd_bus_country_set(dev, &country_code[0]); goto exit; } } @@ -708,6 +734,29 @@ wl_iw_set_power_mode( #endif +static bool btcoex_is_sco_active(struct net_device *dev) +{ + int ioc_res = 0; + bool res = false; + int temp = 0; + + ioc_res = dev_wlc_intvar_get_reg(dev, "btc_params", 4, &temp); + + if (ioc_res == 0) { + WL_TRACE_COEX(("%s: read btc_params[4] = %x\n", __FUNCTION__, temp)); + + if (temp > 0xea0) { + WL_TRACE_COEX(("%s: BT SCO/eSCO is ACTIVE\n", __FUNCTION__)); + res = true; + } else { + WL_TRACE_COEX(("%s: BT SCO/eSCO is NOT detected\n", __FUNCTION__)); + } + } else { + WL_ERROR(("%s ioc read btc params error\n", __FUNCTION__)); + } + return res; +} + #if defined(BT_DHCP_eSCO_FIX) static int set_btc_esco_params(struct net_device *dev, bool trump_sco) @@ -855,9 +904,6 @@ wl_iw_set_btcoex_dhcp( static bool saved_status = FALSE; char buf_flag7_default[8] = { 7, 00, 00, 00, 0x0, 0x00, 0x00, 0x00}; -#ifndef CUSTOMER_HW2 - uint32 temp1, temp2; -#endif #ifdef CUSTOMER_HW2 strncpy((char *)&powermode_val, extra + strlen("BTCOEXMODE") + 1, 1); @@ -871,40 +917,33 @@ wl_iw_set_btcoex_dhcp( if ((saved_status == FALSE) && #ifndef CUSTOMER_HW2 - (!dev_wlc_ioctl(dev, WLC_GET_PM, &pm, sizeof(pm))) && + (!dev_wlc_ioctl(dev, WLC_GET_PM, &pm, sizeof(pm))) && #endif - (!dev_wlc_intvar_get_reg(dev, "btc_params", 66, &saved_reg66)) && - (!dev_wlc_intvar_get_reg(dev, "btc_params", 41, &saved_reg41)) && - (!dev_wlc_intvar_get_reg(dev, "btc_params", 68, &saved_reg68))) { - saved_status = TRUE; - WL_TRACE_COEX(("save regs {66,41,68} ->: 0x%x 0x%x 0x%x\n", \ - saved_reg66, saved_reg41, saved_reg68)); + (!dev_wlc_intvar_get_reg(dev, "btc_params", 66, &saved_reg66)) && + (!dev_wlc_intvar_get_reg(dev, "btc_params", 41, &saved_reg41)) && + (!dev_wlc_intvar_get_reg(dev, "btc_params", 68, &saved_reg68))) { + saved_status = TRUE; + WL_TRACE_COEX(("save regs {66,41,68} ->: 0x%x 0x%x 0x%x\n", \ + saved_reg66, saved_reg41, saved_reg68)); #ifndef CUSTOMER_HW2 - dev_wlc_ioctl(dev, WLC_SET_PM, &pm_local, sizeof(pm_local)); + dev_wlc_ioctl(dev, WLC_SET_PM, &pm_local, sizeof(pm_local)); #endif - dev_wlc_bufvar_set(dev, "btc_params", \ - (char *)&buf_reg66va_dhcp_on[0], sizeof(buf_reg66va_dhcp_on)); - dev_wlc_bufvar_set(dev, "btc_params", \ - (char *)&buf_reg41va_dhcp_on[0], sizeof(buf_reg41va_dhcp_on)); - dev_wlc_bufvar_set(dev, "btc_params", \ - (char *)&buf_reg68va_dhcp_on[0], sizeof(buf_reg68va_dhcp_on)); -#ifndef CUSTOMER_HW2 - if ((!dev_wlc_intvar_get_reg(dev, "btc_params", 12, &temp1)) && - (!dev_wlc_intvar_get_reg(dev, "btc_params", 13, &temp2))) - { - if ((temp1 != 0) && (temp2 != 0)) { -#endif - g_bt->bt_state = BT_DHCP_START; - g_bt->timer_on = 1; - mod_timer(&g_bt->timer, g_bt->timer.expires); - WL_TRACE_COEX(("%s enable BT DHCP Timer\n", \ - __FUNCTION__)); -#ifndef CUSTOMER_HW2 - } - } -#endif + dev_wlc_bufvar_set(dev, "btc_params", \ + (char *)&buf_reg66va_dhcp_on[0], sizeof(buf_reg66va_dhcp_on)); + dev_wlc_bufvar_set(dev, "btc_params", \ + (char *)&buf_reg41va_dhcp_on[0], sizeof(buf_reg41va_dhcp_on)); + dev_wlc_bufvar_set(dev, "btc_params", \ + (char *)&buf_reg68va_dhcp_on[0], sizeof(buf_reg68va_dhcp_on)); + + if (btcoex_is_sco_active(dev)) { + g_bt->bt_state = BT_DHCP_START; + g_bt->timer_on = 1; + mod_timer(&g_bt->timer, g_bt->timer.expires); + WL_TRACE_COEX(("%s enable BT DHCP Timer\n", \ + __FUNCTION__)); + } } else if (saved_status == TRUE) { WL_ERROR(("%s was called w/o DHCP OFF. Continue\n", __FUNCTION__)); @@ -994,6 +1033,22 @@ wl_iw_set_suspend( return ret; } +#ifdef CONFIG_US_NON_DFS_CHANNELS_ONLY +static int +wl_iw_set_dfs_channels( + struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra +) +{ + use_non_dfs_channels = *(extra + strlen(SETDFSCHANNELS_CMD) + 1) - '0'; + use_non_dfs_channels = (use_non_dfs_channels != 0) ? false : true; + wl_iw_set_country_code(dev, NULL); + return 0; +} +#endif + int wl_format_ssid(char* ssid_buf, uint8* ssid, int ssid_len) { @@ -1686,6 +1741,8 @@ int init_ap_profile_from_string(char *param_str, struct ap_profile *ap_cfg) get_parmeter_from_string(&str_ptr, "HIDDEN=", PTYPE_INTDEC, &ap_cfg->closednet, 5); + get_parmeter_from_string(&str_ptr, "COUNTRY=", PTYPE_STRING, &ap_cfg->country_code, 3); + return ret; } #endif @@ -2744,6 +2801,8 @@ wl_iw_iscan_prep(wl_scan_params_t *params, wlc_ssid_t *ssid) params->passive_time = -1; params->home_time = -1; params->channel_num = 0; + if (g_first_broadcast_scan == BROADCAST_SCAN_FIRST_STARTED) + params->passive_time = 30; params->nprobes = htod32(params->nprobes); params->active_time = htod32(params->active_time); @@ -2771,7 +2830,6 @@ wl_iw_iscan(iscan_info_t *iscan, wlc_ssid_t *ssid, uint16 action) WL_SCAN(("scan_type=%d\n", iscan->iscan_ex_params_p->params.scan_type)); WL_SCAN(("bss_type=%d\n", iscan->iscan_ex_params_p->params.bss_type)); - if ((err = dev_iw_iovar_setbuf(iscan->dev, "iscan", iscan->iscan_ex_params_p, \ iscan->iscan_ex_param_size, iscan->ioctlbuf, sizeof(iscan->ioctlbuf)))) { WL_ERROR(("Set ISCAN for %s failed with %d\n", __FUNCTION__, err)); @@ -5955,15 +6013,22 @@ static int thr_wait_for_2nd_eth_dev(void *data) DAEMONIZE("wl0_eth_wthread"); - WL_TRACE(("\n>%s threda started:, PID:%x\n", __FUNCTION__, current->pid)); + WL_TRACE(("\n>%s thread started:, PID:%x\n", __FUNCTION__, current->pid)); + iw = *(wl_iw_t **)netdev_priv(dev); + if (!iw) { + WL_ERROR(("%s: dev is null\n", __FUNCTION__)); + ret = -1; + goto fail; + } +#ifndef BCMSDIOH_STD if (down_timeout(&ap_eth_sema, msecs_to_jiffies(5000)) != 0) { WL_ERROR(("\n%s: sap_eth_sema timeout \n", __FUNCTION__)); ret = -1; goto fail; } +#endif - iw = *(wl_iw_t **)netdev_priv(dev); flags = dhd_os_spin_lock(iw->pub); if (!ap_net_dev) { WL_ERROR((" ap_net_dev is null !!!")); @@ -6154,15 +6219,6 @@ static int set_ap_cfg(struct net_device *dev, struct ap_profile *ap) WL_TRACE(("\n>in %s: apsta set result: %d \n", __FUNCTION__, res)); #endif - iolen = wl_bssiovar_mkbuf("closednet", - bsscfg_index, &ap->closednet, sizeof(ap->closednet)+4, - buf, sizeof(buf), &mkvar_err); - ASSERT(iolen); - if ((res = dev_wlc_ioctl(dev, WLC_SET_VAR, buf, iolen)) < 0) { - WL_ERROR(("%s failed to set 'closednet'for apsta \n", __FUNCTION__)); - goto fail; - } - updown = 1; if ((res = dev_wlc_ioctl(dev, WLC_UP, &updown, sizeof(updown))) < 0) { WL_ERROR(("%s fail to set apsta \n", __FUNCTION__)); @@ -6185,6 +6241,32 @@ static int set_ap_cfg(struct net_device *dev, struct ap_profile *ap) } } + if (strlen(ap->country_code)) { + int error = 0; + if ((error = dev_wlc_ioctl(dev, WLC_SET_COUNTRY, + ap->country_code, sizeof(ap->country_code))) >= 0) { + WL_SOFTAP(("%s: set country %s OK\n", + __FUNCTION__, ap->country_code)); + dhd_bus_country_set(dev, &ap->country_code[0]); + } else { + WL_ERROR(("%s: ERROR:%d setting country %s\n", + __FUNCTION__, error, ap->country_code)); + } + } else { + WL_SOFTAP(("%s: Country code is not specified," + " will use Radio's default\n", + __FUNCTION__)); + } + + iolen = wl_bssiovar_mkbuf("closednet", + bsscfg_index, &ap->closednet, sizeof(ap->closednet)+4, + buf, sizeof(buf), &mkvar_err); + ASSERT(iolen); + if ((res = dev_wlc_ioctl(dev, WLC_SET_VAR, buf, iolen)) < 0) { + WL_ERROR(("%s failed to set 'closednet'for apsta \n", __FUNCTION__)); + goto fail; + } + if ((ap->channel == 0) && (get_softap_auto_channel(dev, ap) < 0)) { ap->channel = 1; @@ -6966,22 +7048,26 @@ static int wl_iw_set_priv( ret = wl_iw_set_country(dev, info, (union iwreq_data *)dwrq, extra); else if (strnicmp(extra, "STOP", strlen("STOP")) == 0) ret = wl_iw_control_wl_off(dev, info); - else if (strnicmp(extra, BAND_GET_CMD, strlen(BAND_GET_CMD)) == 0) + else if (strnicmp(extra, BAND_GET_CMD, strlen(BAND_GET_CMD)) == 0) ret = wl_iw_get_band(dev, info, (union iwreq_data *)dwrq, extra); - else if (strnicmp(extra, BAND_SET_CMD, strlen(BAND_SET_CMD)) == 0) + else if (strnicmp(extra, BAND_SET_CMD, strlen(BAND_SET_CMD)) == 0) ret = wl_iw_set_band(dev, info, (union iwreq_data *)dwrq, extra); - else if (strnicmp(extra, DTIM_SKIP_GET_CMD, strlen(DTIM_SKIP_GET_CMD)) == 0) + else if (strnicmp(extra, DTIM_SKIP_GET_CMD, strlen(DTIM_SKIP_GET_CMD)) == 0) ret = wl_iw_get_dtim_skip(dev, info, (union iwreq_data *)dwrq, extra); - else if (strnicmp(extra, DTIM_SKIP_SET_CMD, strlen(DTIM_SKIP_SET_CMD)) == 0) + else if (strnicmp(extra, DTIM_SKIP_SET_CMD, strlen(DTIM_SKIP_SET_CMD)) == 0) ret = wl_iw_set_dtim_skip(dev, info, (union iwreq_data *)dwrq, extra); - else if (strnicmp(extra, SETSUSPEND_CMD, strlen(SETSUSPEND_CMD)) == 0) + else if (strnicmp(extra, SETSUSPEND_CMD, strlen(SETSUSPEND_CMD)) == 0) ret = wl_iw_set_suspend(dev, info, (union iwreq_data *)dwrq, extra); +#ifdef CONFIG_US_NON_DFS_CHANNELS_ONLY + else if (strnicmp(extra, SETDFSCHANNELS_CMD, strlen(SETDFSCHANNELS_CMD)) == 0) + ret = wl_iw_set_dfs_channels(dev, info, (union iwreq_data *)dwrq, extra); +#endif #if defined(PNO_SUPPORT) - else if (strnicmp(extra, PNOSSIDCLR_SET_CMD, strlen(PNOSSIDCLR_SET_CMD)) == 0) + else if (strnicmp(extra, PNOSSIDCLR_SET_CMD, strlen(PNOSSIDCLR_SET_CMD)) == 0) ret = wl_iw_set_pno_reset(dev, info, (union iwreq_data *)dwrq, extra); - else if (strnicmp(extra, PNOSETUP_SET_CMD, strlen(PNOSETUP_SET_CMD)) == 0) + else if (strnicmp(extra, PNOSETUP_SET_CMD, strlen(PNOSETUP_SET_CMD)) == 0) ret = wl_iw_set_pno_set(dev, info, (union iwreq_data *)dwrq, extra); - else if (strnicmp(extra, PNOENABLE_SET_CMD, strlen(PNOENABLE_SET_CMD)) == 0) + else if (strnicmp(extra, PNOENABLE_SET_CMD, strlen(PNOENABLE_SET_CMD)) == 0) ret = wl_iw_set_pno_enable(dev, info, (union iwreq_data *)dwrq, extra); #endif #if defined(CSCAN) @@ -8173,5 +8259,4 @@ void wl_iw_detach(void) wl_iw_send_priv_event(priv_dev, "AP_DOWN"); } #endif - } diff --git a/drivers/net/wireless/bcm4329/wl_iw.h b/drivers/net/wireless/bcm4329/wl_iw.h index 0f5f4db32eff..928291fe589a 100644 --- a/drivers/net/wireless/bcm4329/wl_iw.h +++ b/drivers/net/wireless/bcm4329/wl_iw.h @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: wl_iw.h,v 1.5.34.1.6.36.4.12 2010/11/03 03:15:49 Exp $ + * $Id: wl_iw.h,v 1.5.34.1.6.36.4.15 2010/11/17 03:13:51 Exp $ */ @@ -52,6 +52,7 @@ #define PNOSETUP_SET_CMD "PNOSETUP " #define PNOENABLE_SET_CMD "PNOFORCE" #define PNODEBUG_SET_CMD "PNODEBUG" +#define SETDFSCHANNELS_CMD "SETDFSCHANNELS" #define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5] #define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x" @@ -160,6 +161,7 @@ struct ap_profile { uint32 preamble; uint32 max_scb; uint32 closednet; + char country_code[WLC_CNTRY_BUF_SZ]; }; @@ -198,6 +200,8 @@ extern int net_os_set_suspend(struct net_device *dev, int val); extern int net_os_set_dtim_skip(struct net_device *dev, int val); extern int net_os_set_packet_filter(struct net_device *dev, int val); extern void dhd_bus_country_set(struct net_device *dev, char *country_code); +extern char *dhd_bus_country_get(struct net_device *dev); +extern int dhd_get_dtim_skip(dhd_pub_t *dhd); #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27) #define IWE_STREAM_ADD_EVENT(info, stream, ends, iwe, extra) \ diff --git a/drivers/usb/gadget/rndis.c b/drivers/usb/gadget/rndis.c index 972d5ddd1e18..116347b0bf13 100644 --- a/drivers/usb/gadget/rndis.c +++ b/drivers/usb/gadget/rndis.c @@ -159,6 +159,25 @@ static const u32 oid_supported_list [] = #endif /* RNDIS_PM */ }; +/* HACK: copied from net/core/dev.c to replace dev_get_stats since + * dev_get_stats cannot be called from atomic context */ +static void netdev_stats_to_stats64(struct rtnl_link_stats64 *stats64, + const struct net_device_stats *netdev_stats) +{ +#if BITS_PER_LONG == 64 + BUILD_BUG_ON(sizeof(*stats64) != sizeof(*netdev_stats)); + memcpy(stats64, netdev_stats, sizeof(*stats64)); +#else + size_t i, n = sizeof(*stats64) / sizeof(u64); + const unsigned long *src = (const unsigned long *)netdev_stats; + u64 *dst = (u64 *)stats64; + + BUILD_BUG_ON(sizeof(*netdev_stats) / sizeof(unsigned long) != + sizeof(*stats64) / sizeof(u64)); + for (i = 0; i < n; i++) + dst[i] = src[i]; +#endif +} /* NDIS Functions */ static int @@ -172,7 +191,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, rndis_query_cmplt_type *resp; struct net_device *net; struct rtnl_link_stats64 temp; - const struct rtnl_link_stats64 *stats; + struct rtnl_link_stats64 *stats = &temp; if (!r) return -ENOMEM; resp = (rndis_query_cmplt_type *) r->buf; @@ -195,7 +214,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, resp->InformationBufferOffset = cpu_to_le32 (16); net = rndis_per_dev_params[configNr].dev; - stats = dev_get_stats(net, &temp); + netdev_stats_to_stats64(stats, &net->stats); switch (OID) { diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index 003ae5f20e3a..8193c6edfca9 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -50,6 +50,7 @@ struct tegra_ehci_hcd { struct otg_transceiver *transceiver; int host_resumed; int bus_suspended; + int port_resuming; struct tegra_ehci_context context; int power_down_on_bus_suspend; }; @@ -82,6 +83,7 @@ static int tegra_ehci_hub_control( ) { struct ehci_hcd *ehci = hcd_to_ehci(hcd); + struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller); u32 __iomem *status_reg; u32 temp; unsigned long flags; @@ -94,7 +96,7 @@ static int tegra_ehci_hub_control( * that are write on clear, by writing back the register read value, so * USB_PORT_FEAT_ENABLE is handled by masking the set on clear bits */ - if ((typeReq == ClearPortFeature) && (wValue == USB_PORT_FEAT_ENABLE)) { + if (typeReq == ClearPortFeature && wValue == USB_PORT_FEAT_ENABLE) { spin_lock_irqsave(&ehci->lock, flags); temp = ehci_readl(ehci, status_reg); ehci_writel(ehci, (temp & ~PORT_RWC_BITS) & ~PORT_PE, status_reg); @@ -102,6 +104,85 @@ static int tegra_ehci_hub_control( return retval; } + if (typeReq == GetPortStatus) { + spin_lock_irqsave(&ehci->lock, flags); + temp = ehci_readl(ehci, status_reg); + if (tegra->port_resuming && !(temp & PORT_SUSPEND)) { + /* resume completed */ + tegra->port_resuming = 0; + tegra_usb_phy_postresume(tegra->phy); + } + spin_unlock_irqrestore(&ehci->lock, flags); + } + + if (typeReq == SetPortFeature && wValue == USB_PORT_FEAT_SUSPEND) { + spin_lock_irqsave(&ehci->lock, flags); + temp = ehci_readl(ehci, status_reg); + if ((temp & PORT_PE) == 0 || (temp & PORT_RESET) != 0) + retval = -EPIPE; + + /* After above check the port must be connected. + * Set appropriate bit thus could put phy into low power + * mode if we have hostpc feature + */ + temp &= ~PORT_WKCONN_E; + temp |= PORT_WKDISC_E | PORT_WKOC_E; + ehci_writel(ehci, temp | PORT_SUSPEND, status_reg); + if (handshake(ehci, status_reg, PORT_SUSPEND, + PORT_SUSPEND, 5000)) + pr_err("%s: timeout waiting for PORT_SUSPEND\n", __func__); + spin_unlock_irqrestore(&ehci->lock, flags); + return retval; + } + + /* + * Tegra host controller will time the resume operation to clear the bit + * when the port control state switches to HS or FS Idle. This behavior + * is different from EHCI where the host controller driver is required + * to set this bit to a zero after the resume duration is timed in the + * driver. + */ + if (typeReq == ClearPortFeature && wValue == USB_PORT_FEAT_SUSPEND) { + spin_lock_irqsave(&ehci->lock, flags); + temp = ehci_readl(ehci, status_reg); + if ((temp & PORT_RESET) || !(temp & PORT_PE)) + retval = -EPIPE; + + if (!(temp & PORT_SUSPEND)) { + spin_unlock_irqrestore(&ehci->lock, flags); + return retval; + } + + tegra_usb_phy_preresume(tegra->phy); + + /* reschedule root hub polling during resume signaling */ + ehci->reset_done[wIndex-1] = jiffies + msecs_to_jiffies(25); + /* check the port again */ + mod_timer(&ehci_to_hcd(ehci)->rh_timer, + ehci->reset_done[wIndex-1]); + + temp &= ~(PORT_RWC_BITS | PORT_WAKE_BITS); + /* start resume signalling */ + ehci_writel(ehci, temp | PORT_RESUME, status_reg); + + /* polling PORT_RESUME until the controller clear this bit */ + if (handshake(ehci, status_reg, PORT_RESUME, 0, 20000)) + pr_err("%s: timeout waiting for PORT_RESUME\n", __func__); + + /* write PORT_RESUME to 0 to clear PORT_SUSPEND bit */ + temp &= ~(PORT_RESUME | PORT_SUSPEND); + ehci_writel(ehci, temp, status_reg); + + /* polling PORT_SUSPEND until the controller clear this bit */ + if (handshake(ehci, status_reg, PORT_SUSPEND, 0, 2000)) + pr_err("%s: timeout waiting for PORT_SUSPEND\n", __func__); + + tegra->port_resuming = 1; + + spin_unlock_irqrestore(&ehci->lock, flags); + return retval; + } + /* Handle the hub control events here */ return ehci_hub_control(hcd, typeReq, wValue, wIndex, buf, wLength); } @@ -360,6 +441,8 @@ static int tegra_ehci_bus_resume(struct usb_hcd *hcd) tegra->bus_suspended = 0; } + tegra_usb_phy_preresume(tegra->phy); + tegra->port_resuming = 1; return ehci_bus_resume(hcd); } diff --git a/drivers/video/tegra/dc/dc.c b/drivers/video/tegra/dc/dc.c index 1c54da6672a5..7ca78cd7c247 100644 --- a/drivers/video/tegra/dc/dc.c +++ b/drivers/video/tegra/dc/dc.c @@ -75,12 +75,15 @@ static inline int tegra_dc_fmt_bpp(int fmt) case TEGRA_WIN_FMT_R6x2G6x2B6x2A8: return 32; - case TEGRA_WIN_FMT_YCbCr422: - case TEGRA_WIN_FMT_YUV422: + /* for planar formats, size of the Y plane, 8bit */ case TEGRA_WIN_FMT_YCbCr420P: case TEGRA_WIN_FMT_YUV420P: case TEGRA_WIN_FMT_YCbCr422P: case TEGRA_WIN_FMT_YUV422P: + return 8; + + case TEGRA_WIN_FMT_YCbCr422: + case TEGRA_WIN_FMT_YUV422: case TEGRA_WIN_FMT_YCbCr422R: case TEGRA_WIN_FMT_YUV422R: case TEGRA_WIN_FMT_YCbCr422RA: @@ -91,6 +94,18 @@ static inline int tegra_dc_fmt_bpp(int fmt) return 0; } +static inline bool tegra_dc_is_yuv_planar(int fmt) +{ + switch (fmt) { + case TEGRA_WIN_FMT_YUV420P: + case TEGRA_WIN_FMT_YCbCr420P: + case TEGRA_WIN_FMT_YCbCr422P: + case TEGRA_WIN_FMT_YUV422P: + return true; + } + return false; +} + #define DUMP_REG(a) do { \ snprintf(buff, sizeof(buff), "%-32s\t%03x\t%08lx\n", \ #a, a, tegra_dc_readl(dc, a)); \ @@ -219,15 +234,26 @@ static void _dump_regs(struct tegra_dc *dc, void *data, DUMP_REG(DC_WIN_DDA_INCREMENT); DUMP_REG(DC_WIN_LINE_STRIDE); DUMP_REG(DC_WIN_BUF_STRIDE); + DUMP_REG(DC_WIN_UV_BUF_STRIDE); DUMP_REG(DC_WIN_BLEND_NOKEY); DUMP_REG(DC_WIN_BLEND_1WIN); DUMP_REG(DC_WIN_BLEND_2WIN_X); DUMP_REG(DC_WIN_BLEND_2WIN_Y); DUMP_REG(DC_WIN_BLEND_3WIN_XY); DUMP_REG(DC_WINBUF_START_ADDR); + DUMP_REG(DC_WINBUF_START_ADDR_U); + DUMP_REG(DC_WINBUF_START_ADDR_V); DUMP_REG(DC_WINBUF_ADDR_H_OFFSET); DUMP_REG(DC_WINBUF_ADDR_V_OFFSET); DUMP_REG(DC_WINBUF_UFLOW_STATUS); + DUMP_REG(DC_WIN_CSC_YOF); + DUMP_REG(DC_WIN_CSC_KYRGB); + DUMP_REG(DC_WIN_CSC_KUR); + DUMP_REG(DC_WIN_CSC_KVR); + DUMP_REG(DC_WIN_CSC_KUG); + DUMP_REG(DC_WIN_CSC_KVG); + DUMP_REG(DC_WIN_CSC_KUB); + DUMP_REG(DC_WIN_CSC_KVB); } tegra_dc_io_end(dc); @@ -414,6 +440,35 @@ static void tegra_dc_set_blending(struct tegra_dc *dc, struct tegra_dc_blend *bl } } +static void tegra_dc_set_csc(struct tegra_dc *dc) +{ + tegra_dc_writel(dc, 0x00f0, DC_WIN_CSC_YOF); + tegra_dc_writel(dc, 0x012a, DC_WIN_CSC_KYRGB); + tegra_dc_writel(dc, 0x0000, DC_WIN_CSC_KUR); + tegra_dc_writel(dc, 0x0198, DC_WIN_CSC_KVR); + tegra_dc_writel(dc, 0x039b, DC_WIN_CSC_KUG); + tegra_dc_writel(dc, 0x032f, DC_WIN_CSC_KVG); + tegra_dc_writel(dc, 0x0204, DC_WIN_CSC_KUB); + tegra_dc_writel(dc, 0x0000, DC_WIN_CSC_KVB); +} + +static void tegra_dc_set_scaling_filter(struct tegra_dc *dc) +{ + unsigned i; + unsigned v0 = 128; + unsigned v1 = 0; + /* linear horizontal and vertical filters */ + for (i = 0; i < 16; i++) { + tegra_dc_writel(dc, (v1 << 16) | (v0 << 8), + DC_WIN_H_FILTER_P(i)); + + tegra_dc_writel(dc, v0, + DC_WIN_V_FILTER_P(i)); + v0 -= 8; + v1 += 8; + } +} + /* does not support updating windows on multiple dcs in one call */ int tegra_dc_update_windows(struct tegra_dc_win *windows[], int n) { @@ -441,6 +496,7 @@ int tegra_dc_update_windows(struct tegra_dc_win *windows[], int n) struct tegra_dc_win *win = windows[i]; unsigned h_dda; unsigned v_dda; + bool yuvp = tegra_dc_is_yuv_planar(win->fmt); if (win->z != dc->blend.z[win->idx]) { dc->blend.z[win->idx] = win->z; @@ -467,10 +523,6 @@ int tegra_dc_update_windows(struct tegra_dc_win *windows[], int n) tegra_dc_writel(dc, win->fmt, DC_WIN_COLOR_DEPTH); tegra_dc_writel(dc, 0, DC_WIN_BYTE_SWAP); - /* TODO: implement filter on settings */ - h_dda = (win->w * 0x1000) / max_t(int, win->out_w - 1, 1); - v_dda = (win->h * 0x1000) / max_t(int, win->out_h - 1, 1); - tegra_dc_writel(dc, V_POSITION(win->out_y) | H_POSITION(win->out_x), DC_WIN_POSITION); @@ -479,24 +531,53 @@ int tegra_dc_update_windows(struct tegra_dc_win *windows[], int n) DC_WIN_SIZE); tegra_dc_writel(dc, V_PRESCALED_SIZE(win->h) | - H_PRESCALED_SIZE(win->w*tegra_dc_fmt_bpp(win->fmt)/8), + H_PRESCALED_SIZE(win->w * tegra_dc_fmt_bpp(win->fmt) / 8), DC_WIN_PRESCALED_SIZE); - tegra_dc_writel(dc, 0, DC_WIN_H_INITIAL_DDA); - tegra_dc_writel(dc, 0, DC_WIN_V_INITIAL_DDA); + + h_dda = ((win->w - 1) * 0x1000) / max_t(int, win->out_w - 1, 1); + v_dda = ((win->h - 1) * 0x1000) / max_t(int, win->out_h - 1, 1); tegra_dc_writel(dc, V_DDA_INC(v_dda) | H_DDA_INC(h_dda), DC_WIN_DDA_INCREMENT); - tegra_dc_writel(dc, win->stride, DC_WIN_LINE_STRIDE); - tegra_dc_writel(dc, 0, DC_WIN_BUF_STRIDE); - + tegra_dc_writel(dc, 0, DC_WIN_H_INITIAL_DDA); + tegra_dc_writel(dc, 0, DC_WIN_V_INITIAL_DDA); + tegra_dc_writel(dc, 0, DC_WIN_BUF_STRIDE); + tegra_dc_writel(dc, 0, DC_WIN_UV_BUF_STRIDE); tegra_dc_writel(dc, (unsigned long)win->phys_addr, DC_WINBUF_START_ADDR); - tegra_dc_writel(dc, win->x, DC_WINBUF_ADDR_H_OFFSET); + + if (!yuvp) { + tegra_dc_writel(dc, win->stride, DC_WIN_LINE_STRIDE); + } else { + tegra_dc_writel(dc, + (unsigned long)win->phys_addr + + (unsigned long)win->offset_u, + DC_WINBUF_START_ADDR_U); + tegra_dc_writel(dc, + (unsigned long)win->phys_addr + + (unsigned long)win->offset_v, + DC_WINBUF_START_ADDR_V); + tegra_dc_writel(dc, + LINE_STRIDE(win->stride) | + UV_LINE_STRIDE(win->stride_uv), + DC_WIN_LINE_STRIDE); + } + + tegra_dc_writel(dc, win->x * tegra_dc_fmt_bpp(win->fmt) / 8, + DC_WINBUF_ADDR_H_OFFSET); tegra_dc_writel(dc, win->y, DC_WINBUF_ADDR_V_OFFSET); val = WIN_ENABLE; - if (tegra_dc_fmt_bpp(win->fmt) < 24) + if (yuvp) + val |= CSC_ENABLE; + else if (tegra_dc_fmt_bpp(win->fmt) < 24) val |= COLOR_EXPAND; + + if (win->w != win->out_w) + val |= H_FILTER_ENABLE; + if (win->h != win->out_h) + val |= V_FILTER_ENABLE; + tegra_dc_writel(dc, val, DC_WIN_WIN_OPTIONS); win->dirty = no_vsync ? 0 : 1; @@ -812,6 +893,7 @@ static void tegra_dc_init(struct tegra_dc *dc) { u32 disp_syncpt; u32 vblank_syncpt; + int i; tegra_dc_writel(dc, 0x00000100, DC_CMD_GENERAL_INCR_SYNCPT_CNTRL); if (dc->ndev->id == 0) { @@ -855,6 +937,13 @@ static void tegra_dc_init(struct tegra_dc *dc) tegra_dc_writel(dc, 0x00000000, DC_DISP_BORDER_COLOR); tegra_dc_set_color_control(dc); + for (i = 0; i < DC_N_WINDOWS; i++) { + tegra_dc_writel(dc, WINDOW_A_SELECT << i, + DC_CMD_DISPLAY_WINDOW_HEADER); + tegra_dc_set_csc(dc); + tegra_dc_set_scaling_filter(dc); + } + dc->syncpt_id = disp_syncpt; diff --git a/drivers/video/tegra/dc/dc_reg.h b/drivers/video/tegra/dc/dc_reg.h index 360eda69e0dd..5ae3cc4c1dec 100644 --- a/drivers/video/tegra/dc/dc_reg.h +++ b/drivers/video/tegra/dc/dc_reg.h @@ -309,26 +309,29 @@ #define DC_DISP_DAC_CRT_CTRL 0x4c0 #define DC_DISP_DISP_MISC_CONTROL 0x4c1 -#define DC_WINC_COLOR_PALETTE(x) (0x500 + (x)) - -#define DC_WINC_PALETTE_COLOR_EXT 0x600 -#define DC_WINC_H_FILTER_P(x) (0x601 + (x)) -#define DC_WINC_CSC_YOF 0x611 -#define DC_WINC_CSC_KYRGB 0x612 -#define DC_WINC_CSC_KUR 0x613 -#define DC_WINC_CSC_KVR 0x614 -#define DC_WINC_CSC_KUG 0x615 -#define DC_WINC_CSC_KVG 0x616 -#define DC_WINC_CSC_KUB 0x617 -#define DC_WINC_CSC_KVB 0x618 -#define DC_WINC_V_FILTER_P(x) (0x619 + (x)) +#define DC_WIN_COLOR_PALETTE(x) (0x500 + (x)) + +#define DC_WIN_PALETTE_COLOR_EXT 0x600 +#define DC_WIN_H_FILTER_P(x) (0x601 + (x)) +#define DC_WIN_CSC_YOF 0x611 +#define DC_WIN_CSC_KYRGB 0x612 +#define DC_WIN_CSC_KUR 0x613 +#define DC_WIN_CSC_KVR 0x614 +#define DC_WIN_CSC_KUG 0x615 +#define DC_WIN_CSC_KVG 0x616 +#define DC_WIN_CSC_KUB 0x617 +#define DC_WIN_CSC_KVB 0x618 +#define DC_WIN_V_FILTER_P(x) (0x619 + (x)) #define DC_WIN_WIN_OPTIONS 0x700 #define H_DIRECTION_INCREMENT (0 << 0) #define H_DIRECTION_DECREMENTT (1 << 0) #define V_DIRECTION_INCREMENT (0 << 2) #define V_DIRECTION_DECREMENTT (1 << 2) #define COLOR_EXPAND (1 << 6) +#define H_FILTER_ENABLE (1 << 8) +#define V_FILTER_ENABLE (1 << 10) #define CP_ENABLE (1 << 16) +#define CSC_ENABLE (1 << 18) #define DV_ENABLE (1 << 20) #define WIN_ENABLE (1 << 30) @@ -366,6 +369,8 @@ #define V_DDA_INC(x) (((x) & 0xffff) << 16) #define DC_WIN_LINE_STRIDE 0x70a +#define LINE_STRIDE(x) (x) +#define UV_LINE_STRIDE(x) (((x) & 0xffff) << 16) #define DC_WIN_BUF_STRIDE 0x70b #define DC_WIN_UV_BUF_STRIDE 0x70c #define DC_WIN_BUFFER_ADDR_MODE 0x70d diff --git a/drivers/video/tegra/fb.c b/drivers/video/tegra/fb.c index 47756e4225bb..cc26c5977a20 100644 --- a/drivers/video/tegra/fb.c +++ b/drivers/video/tegra/fb.c @@ -149,6 +149,9 @@ static int tegra_fb_set_par(struct fb_info *info) } info->fix.line_length = var->xres * var->bits_per_pixel / 8; tegra_fb->win->stride = info->fix.line_length; + tegra_fb->win->stride_uv = 0; + tegra_fb->win->offset_u = 0; + tegra_fb->win->offset_v = 0; } if (var->pixclock) { @@ -176,9 +179,9 @@ static int tegra_fb_set_par(struct fb_info *info) tegra_dc_set_mode(tegra_fb->win->dc, &mode); tegra_fb->win->w = info->mode->xres; - tegra_fb->win->h = info->mode->xres; + tegra_fb->win->h = info->mode->yres; tegra_fb->win->out_w = info->mode->xres; - tegra_fb->win->out_h = info->mode->xres; + tegra_fb->win->out_h = info->mode->yres; } return 0; } @@ -195,6 +198,10 @@ static int tegra_fb_setcolreg(unsigned regno, unsigned red, unsigned green, if (regno >= 16) return -EINVAL; + red = (red >> (16 - info->var.red.length)); + green = (green >> (16 - info->var.green.length)); + blue = (blue >> (16 - info->var.blue.length)); + v = (red << var->red.offset) | (green << var->green.offset) | (blue << var->blue.offset); @@ -372,7 +379,10 @@ static int tegra_fb_set_windowattr(struct tegra_fb_info *tegra_fb, /* STOPSHIP verify that this won't read outside of the surface */ win->phys_addr = flip_win->phys_addr + flip_win->attr.offset; + win->offset_u = flip_win->attr.offset_u + flip_win->attr.offset; + win->offset_v = flip_win->attr.offset_v + flip_win->attr.offset; win->stride = flip_win->attr.stride; + win->stride_uv = flip_win->attr.stride_uv; if ((s32)flip_win->attr.pre_syncpt_id >= 0) { nvhost_syncpt_wait_timeout(&tegra_fb->ndev->host->syncpt, @@ -737,7 +747,10 @@ struct tegra_fb_info *tegra_fb_register(struct nvhost_device *ndev, win->z = 0; win->phys_addr = fb_phys; win->virt_addr = fb_base; + win->offset_u = 0; + win->offset_v = 0; win->stride = fb_data->xres * fb_data->bits_per_pixel / 8; + win->stride_uv = 0; win->flags = TEGRA_WIN_FLAG_ENABLED; if (fb_mem) diff --git a/drivers/video/tegra/host/dev.c b/drivers/video/tegra/host/dev.c index daed882be5a2..42d268e7da59 100644 --- a/drivers/video/tegra/host/dev.c +++ b/drivers/video/tegra/host/dev.c @@ -452,7 +452,7 @@ static int nvhost_ioctl_ctrl_module_mutex( struct nvhost_ctrl_module_mutex_args *args) { int err = 0; - if (args->id >= NV_HOST1X_SYNCPT_NB_PTS || + if (args->id >= NV_HOST1X_NB_MLOCKS || args->lock > 1) return -EINVAL; diff --git a/include/linux/tegra_audio.h b/include/linux/tegra_audio.h index 0ca6250beac4..db4661aacb4f 100644 --- a/include/linux/tegra_audio.h +++ b/include/linux/tegra_audio.h @@ -36,39 +36,21 @@ struct tegra_audio_in_config { #define TEGRA_AUDIO_IN_GET_CONFIG _IOR(TEGRA_AUDIO_MAGIC, 3, \ struct tegra_audio_in_config *) -struct tegra_audio_buf_config { - unsigned size; /* order */ - unsigned threshold; /* order */ - unsigned chunk; /* order */ -}; - -#define TEGRA_AUDIO_IN_SET_BUF_CONFIG _IOW(TEGRA_AUDIO_MAGIC, 4, \ - const struct tegra_audio_buf_config *) -#define TEGRA_AUDIO_IN_GET_BUF_CONFIG _IOR(TEGRA_AUDIO_MAGIC, 5, \ - struct tegra_audio_buf_config *) - -#define TEGRA_AUDIO_OUT_SET_BUF_CONFIG _IOW(TEGRA_AUDIO_MAGIC, 6, \ - const struct tegra_audio_buf_config *) -#define TEGRA_AUDIO_OUT_GET_BUF_CONFIG _IOR(TEGRA_AUDIO_MAGIC, 7, \ - struct tegra_audio_buf_config *) - -struct tegra_audio_error_counts { - unsigned late_dma; - unsigned full_empty; /* empty for playback, full for recording */ -}; - -#define TEGRA_AUDIO_IN_GET_ERROR_COUNT _IOR(TEGRA_AUDIO_MAGIC, 8, \ - struct tegra_audio_error_counts *) - -#define TEGRA_AUDIO_OUT_GET_ERROR_COUNT _IOR(TEGRA_AUDIO_MAGIC, 9, \ - struct tegra_audio_error_counts *) +#define TEGRA_AUDIO_IN_SET_NUM_BUFS _IOW(TEGRA_AUDIO_MAGIC, 4, \ + const unsigned int *) +#define TEGRA_AUDIO_IN_GET_NUM_BUFS _IOW(TEGRA_AUDIO_MAGIC, 5, \ + unsigned int *) +#define TEGRA_AUDIO_OUT_SET_NUM_BUFS _IOW(TEGRA_AUDIO_MAGIC, 6, \ + const unsigned int *) +#define TEGRA_AUDIO_OUT_GET_NUM_BUFS _IOW(TEGRA_AUDIO_MAGIC, 7, \ + unsigned int *) #define TEGRA_AUDIO_OUT_FLUSH _IO(TEGRA_AUDIO_MAGIC, 10) #define TEGRA_AUDIO_BIT_FORMAT_DEFAULT 0 #define TEGRA_AUDIO_BIT_FORMAT_DSP 1 #define TEGRA_AUDIO_SET_BIT_FORMAT _IOW(TEGRA_AUDIO_MAGIC, 11, \ - unsigned int *) + const unsigned int *) #define TEGRA_AUDIO_GET_BIT_FORMAT _IOR(TEGRA_AUDIO_MAGIC, 12, \ unsigned int *) diff --git a/include/video/tegrafb.h b/include/video/tegrafb.h index 79578c3d7bd4..b9861bc7953a 100644 --- a/include/video/tegrafb.h +++ b/include/video/tegrafb.h @@ -55,7 +55,10 @@ struct tegra_fb_windowattr { __u32 buff_id; __u32 blend; __u32 offset; + __u32 offset_u; + __u32 offset_v; __u32 stride; + __u32 stride_uv; __u32 pixformat; __u32 x; __u32 y; |