summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNitin Kumbhar <nkumbhar@nvidia.com>2010-11-23 12:01:55 +0530
committerNitin Kumbhar <nkumbhar@nvidia.com>2010-11-23 12:01:55 +0530
commit99021ee71173b95afdd9b2af6bcdafdb53eb82c1 (patch)
tree74685f62d42eadd741c41a5f371eeb2eecc9cdea
parent5d3ad2e2d95632f31b2cb0ae045fe53f85d7565a (diff)
parentb4d50aa63c58b8affa08f60f4dba415040845371 (diff)
merging android-tegra-2.6.36 into git-master/linux-2.6/android-tegra-2.6.36
Change-Id: Ibd208fc6b803bbf729d1f554839ebaee8c8461ce
-rw-r--r--arch/arm/common/fiq_debugger.c27
-rw-r--r--arch/arm/mach-tegra/dma.c149
-rw-r--r--arch/arm/mach-tegra/include/mach/dc.h3
-rw-r--r--arch/arm/mach-tegra/include/mach/dma.h6
-rw-r--r--arch/arm/mach-tegra/include/mach/usb_phy.h4
-rw-r--r--arch/arm/mach-tegra/tegra_i2s_audio.c1408
-rw-r--r--arch/arm/mach-tegra/tegra_spdif_audio.c193
-rw-r--r--arch/arm/mach-tegra/usb_phy.c35
-rw-r--r--drivers/mmc/host/sdhci-tegra.c1
-rw-r--r--drivers/net/wireless/bcm4329/Makefile2
-rw-r--r--drivers/net/wireless/bcm4329/dhd.h4
-rw-r--r--drivers/net/wireless/bcm4329/dhd_common.c62
-rw-r--r--drivers/net/wireless/bcm4329/dhd_linux.c18
-rw-r--r--drivers/net/wireless/bcm4329/include/epivers.h10
-rw-r--r--drivers/net/wireless/bcm4329/include/wlioctl.h3
-rw-r--r--drivers/net/wireless/bcm4329/wl_iw.c203
-rw-r--r--drivers/net/wireless/bcm4329/wl_iw.h6
-rw-r--r--drivers/usb/gadget/rndis.c23
-rw-r--r--drivers/usb/host/ehci-tegra.c85
-rw-r--r--drivers/video/tegra/dc/dc.c117
-rw-r--r--drivers/video/tegra/dc/dc_reg.h31
-rw-r--r--drivers/video/tegra/fb.c17
-rw-r--r--drivers/video/tegra/host/dev.c2
-rw-r--r--include/linux/tegra_audio.h36
-rw-r--r--include/video/tegrafb.h3
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;