From: Chris Fries Date: Wed, 20 Oct 2010 18:10:29 +0000 (-0500) Subject: [ARM] tegra_i2s_audio: support mono capture X-Git-Tag: firefly_0821_release~9833^2~147^2~10 X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=1e7893b76b0774e7b94995dbfea40212f9780f8c;p=firefly-linux-kernel-4.4.55.git [ARM] tegra_i2s_audio: support mono capture Support mono data formats such as DSP PCM Mode with 16 bit mono capture. This patch also disables the in-driver downsampler. Signed-off-by: Iliyan Malchev --- diff --git a/arch/arm/mach-tegra/include/mach/audio.h b/arch/arm/mach-tegra/include/mach/audio.h index 1d631dbddc06..80f8b2c2d8cd 100644 --- a/arch/arm/mach-tegra/include/mach/audio.h +++ b/arch/arm/mach-tegra/include/mach/audio.h @@ -46,9 +46,8 @@ struct tegra_audio_platform_data { int bit_size; int i2s_bus_width; /* 32-bit for 16-bit packed I2S */ int dsp_bus_width; /* 16-bit for DSP data format */ - int mask; /* enable tx and rx? */ - + bool stereo_capture; /* True if hardware supports stereo */ void *driver_data; }; diff --git a/arch/arm/mach-tegra/include/mach/i2s.h b/arch/arm/mach-tegra/include/mach/i2s.h index ed8bc14c4ca4..42cce885cdac 100644 --- a/arch/arm/mach-tegra/include/mach/i2s.h +++ b/arch/arm/mach-tegra/include/mach/i2s.h @@ -263,5 +263,54 @@ #define I2S_I2S_PCM_CTRL_RCV_MODE (1<<0) +/* + * I2S_I2S_NW_CTRL_0 + */ + +#define I2S_TRM_TLPHY_SLOT_SEL_SLOT1 0 +#define I2S_TRM_TLPHY_SLOT_SEL_SLOT2 1 +#define I2S_TRM_TLPHY_SLOT_SEL_SLOT3 2 +#define I2S_TRM_TLPHY_SLOT_SEL_SLOT4 3 +#define I2S_I2S_NW_TRM_TLPHY_SLOT_SEL_SHIFT 4 + +#define I2S_I2S_NW_TRM_TLPHY_SLOT_SEL_MASK \ + (3 << I2S_TRM_TLPHY_SLOT_SEL_SHIFT) +#define I2S_I2S_TRM_TLPHY_SLOT_SEL_SLOT1 \ + (I2S_TRM_TLPHY_SLOT_SEL_SLOT1 \ + << I2S_I2S_NW_TRM_TLPHY_SLOT_SEL_SHIFT) +#define I2S_I2S_TRM_TLPHY_SLOT_SEL_SLOT2 \ + (I2S_TRM_TLPHY_SLOT_SEL_SLOT2 \ + << I2S_I2S_NW_TRM_TLPHY_SLOT_SEL_SHIFT) +#define I2S_I2S_TRM_TLPHY_SLOT_SEL_SLOT3 \ + (I2S_TRM_TLPHY_SLOT_SEL_SLOT3 \ + << I2S_I2S_NW_TRM_TLPHY_SLOT_SEL_SHIFT) +#define I2S_I2S_TRM_TLPHY_SLOT_SEL_SLOT4 \ + (I2S_TRM_TLPHY_SLOT_SEL_SLOT4 \ + << I2S_I2S_NW_TRM_TLPHY_SLOT_SEL_SHIFT) + +#define I2S_I2S_NW_CTRL_TRM_TLPHY_MODE (1<<3) + +#define I2S_RCV_TLPHY_SLOT_SEL_SLOT1 0 +#define I2S_RCV_TLPHY_SLOT_SEL_SLOT2 1 +#define I2S_RCV_TLPHY_SLOT_SEL_SLOT3 2 +#define I2S_RCV_TLPHY_SLOT_SEL_SLOT4 3 +#define I2S_I2S_NW_RCV_TLPHY_SLOT_SEL_SHIFT 1 + +#define I2S_I2S_NW_RCV_TLPHY_SLOT_SEL_MASK \ + (3 << I2S_RCV_TLPHY_SLOT_SEL_SHIFT) +#define I2S_I2S_RCV_TLPHY_SLOT_SEL_SLOT1 \ + (I2S_RCV_TLPHY_SLOT_SEL_SLOT1 \ + << I2S_I2S_NW_RCV_TLPHY_SLOT_SEL_SHIFT) +#define I2S_I2S_RCV_TLPHY_SLOT_SEL_SLOT2 \ + (I2S_RCV_TLPHY_SLOT_SEL_SLOT2 \ + << I2S_I2S_NW_RCV_TLPHY_SLOT_SEL_SHIFT) +#define I2S_I2S_RCV_TLPHY_SLOT_SEL_SLOT3 \ + (I2S_RCV_TLPHY_SLOT_SEL_SLOT3 \ + << I2S_I2S_NW_RCV_TLPHY_SLOT_SEL_SHIFT) +#define I2S_I2S_RCV_TLPHY_SLOT_SEL_SLOT4 \ + (I2S_RCV_TLPHY_SLOT_SEL_SLOT4 \ + << I2S_I2S_NW_RCV_TLPHY_SLOT_SEL_SHIFT) + +#define I2S_I2S_NW_CTRL_RCV_TLPHY_MODE (1<<0) #endif /* __ARCH_ARM_MACH_TEGRA_I2S_H */ diff --git a/arch/arm/mach-tegra/tegra_i2s_audio.c b/arch/arm/mach-tegra/tegra_i2s_audio.c index 583802dd258d..bca3828f3c6a 100644 --- a/arch/arm/mach-tegra/tegra_i2s_audio.c +++ b/arch/arm/mach-tegra/tegra_i2s_audio.c @@ -59,6 +59,11 @@ #define PCM_IN_BUFFER_PADDING (1<<6) /* bytes */ +#define TEGRA_AUDIO_DSP_NONE 0 +#define TEGRA_AUDIO_DSP_PCM 1 +#define TEGRA_AUDIO_DSP_NETWORK 2 +#define TEGRA_AUDIO_DSP_TDM 3 + /* per stream (input/output) */ struct audio_stream { int opened; @@ -365,6 +370,51 @@ static void i2s_set_master(unsigned long base, int master) i2s_writel(base, val, I2S_I2S_CTRL_0); } +static int i2s_set_dsp_mode(unsigned long base, unsigned int mode) +{ + u32 val; + if (mode > TEGRA_AUDIO_DSP_TDM) { + pr_err("%s: invalid mode %d.\n", __func__, mode); + return -EINVAL; + } + if (mode == TEGRA_AUDIO_DSP_TDM) { + pr_err("TEGRA_AUDIO_DSP_TDM not implemented.\n"); + return -EINVAL; + } + + /* Disable unused modes */ + if (mode != TEGRA_AUDIO_DSP_PCM) { + /* Disable PCM mode */ + val = i2s_readl(base, I2S_I2S_PCM_CTRL_0); + val &= ~(I2S_I2S_PCM_CTRL_TRM_MODE|I2S_I2S_PCM_CTRL_RCV_MODE); + i2s_writel(base, val, I2S_I2S_PCM_CTRL_0); + } + if (mode != TEGRA_AUDIO_DSP_NETWORK) { + /* Disable Network mode */ + val = i2s_readl(base, I2S_I2S_NW_CTRL_0); + val &= ~(I2S_I2S_NW_CTRL_TRM_TLPHY_MODE|I2S_I2S_NW_CTRL_RCV_TLPHY_MODE); + i2s_writel(base, val, I2S_I2S_NW_CTRL_0); + } + + /* Enable the selected mode. */ + switch (mode) { + case TEGRA_AUDIO_DSP_NETWORK: + /* Set DSP Network (Telephony) Mode */ + val = i2s_readl(base, I2S_I2S_NW_CTRL_0); + val |= I2S_I2S_NW_CTRL_TRM_TLPHY_MODE|I2S_I2S_NW_CTRL_RCV_TLPHY_MODE; + i2s_writel(base, val, I2S_I2S_NW_CTRL_0); + break; + case TEGRA_AUDIO_DSP_PCM: + /* Set DSP PCM Mode */ + val = i2s_readl(base, I2S_I2S_PCM_CTRL_0); + val |= I2S_I2S_PCM_CTRL_TRM_MODE|I2S_I2S_PCM_CTRL_RCV_MODE; + i2s_writel(base, val, I2S_I2S_PCM_CTRL_0); + break; + } + + return 0; +} + static int i2s_set_bit_format(unsigned long base, unsigned fmt) { u32 val; @@ -378,12 +428,12 @@ static int i2s_set_bit_format(unsigned long base, unsigned fmt) val &= ~I2S_I2S_CTRL_BIT_FORMAT_MASK; val |= fmt << I2S_BIT_FORMAT_SHIFT; i2s_writel(base, val, I2S_I2S_CTRL_0); - - if (fmt == I2S_BIT_FORMAT_DSP) { - val = i2s_readl(base, I2S_I2S_PCM_CTRL_0); - val |= I2S_I2S_PCM_CTRL_TRM_MODE|I2S_I2S_PCM_CTRL_RCV_MODE; - i2s_writel(base, val, I2S_I2S_PCM_CTRL_0); - } + /* For DSP format, select DSP PCM mode. */ + /* PCM mode and Network Mode slot 0 are effectively identical. */ + if (fmt == I2S_BIT_FORMAT_DSP) + i2s_set_dsp_mode(base, TEGRA_AUDIO_DSP_PCM); + else + i2s_set_dsp_mode(base, TEGRA_AUDIO_DSP_NONE); return 0; } @@ -1481,6 +1531,7 @@ 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; @@ -1508,7 +1559,12 @@ static long tegra_audio_in_ioctl(struct file *file, rc = -EINVAL; break; } - +#endif + if(cfg.stereo && !ads->pdata->stereo_capture) { + pr_err("%s: not capable of stereo capture.", + __func__); + rc = -EINVAL; + } if (!rc) { pr_info("%s: setting input sampling rate to %d, %s\n", __func__, cfg.rate, @@ -1560,6 +1616,23 @@ 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. */ @@ -1570,6 +1643,7 @@ static int downsample(const s16 *in, int in_len, const int *divs, int divs_len, bool out_stereo) { + /* Todo: Handle mono source streams */ int i, j; int lsum, rsum; int di, div; @@ -1632,6 +1706,7 @@ static ssize_t __downsample_to_user(struct audio_driver_state *ads, return bytes_ds; } +#endif /*SAMPLE_RATE_CONVERTER_IN_DRIVER*/ static ssize_t downsample_to_user(struct audio_driver_state *ads, void __user *buf, @@ -1668,7 +1743,12 @@ static ssize_t downsample_to_user(struct audio_driver_state *ads, BUG_ON(!sgl[i].length); again: - ds_now = __downsample_to_user(ads, +#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);