diff options
author | Takashi Iwai <tiwai@suse.de> | 2012-03-18 18:22:37 +0100 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2012-03-18 18:22:37 +0100 |
commit | cb3f2adc03ab055b19c677a6283523861fafebdd (patch) | |
tree | 59cfb6800f0635a4aec16c8e0da619f27e51ee79 /sound/soc/imx | |
parent | 44c76a960a62fcc46cbcaa0a22a34e666a729329 (diff) | |
parent | 828006de1bddf83b6ecf03ec459c15f7c7c22db7 (diff) |
Merge branch 'topic/asoc' into for-linus
Diffstat (limited to 'sound/soc/imx')
-rw-r--r-- | sound/soc/imx/Kconfig | 25 | ||||
-rw-r--r-- | sound/soc/imx/Makefile | 15 | ||||
-rw-r--r-- | sound/soc/imx/eukrea-tlv320.c | 40 | ||||
-rw-r--r-- | sound/soc/imx/imx-audmux.c | 314 | ||||
-rw-r--r-- | sound/soc/imx/imx-audmux.h | 60 | ||||
-rw-r--r-- | sound/soc/imx/imx-pcm-dma-mx2.c | 223 | ||||
-rw-r--r-- | sound/soc/imx/imx-pcm.c | 105 | ||||
-rw-r--r-- | sound/soc/imx/imx-pcm.h | 32 | ||||
-rw-r--r-- | sound/soc/imx/imx-ssi.c | 118 | ||||
-rw-r--r-- | sound/soc/imx/imx-ssi.h | 16 | ||||
-rw-r--r-- | sound/soc/imx/mx27vis-aic32x4.c | 159 | ||||
-rw-r--r-- | sound/soc/imx/phycore-ac97.c | 27 | ||||
-rw-r--r-- | sound/soc/imx/wm1133-ev1.c | 25 |
13 files changed, 802 insertions, 357 deletions
diff --git a/sound/soc/imx/Kconfig b/sound/soc/imx/Kconfig index 738391757f2c..810acaa09009 100644 --- a/sound/soc/imx/Kconfig +++ b/sound/soc/imx/Kconfig @@ -1,9 +1,6 @@ menuconfig SND_IMX_SOC tristate "SoC Audio for Freescale i.MX CPUs" depends on ARCH_MXC - select SND_PCM - select FIQ - select SND_SOC_AC97_BUS help Say Y or M if you want to add support for codecs attached to the i.MX SSI interface. @@ -11,10 +8,23 @@ menuconfig SND_IMX_SOC if SND_IMX_SOC +config SND_SOC_IMX_SSI + tristate + +config SND_SOC_IMX_PCM + tristate + config SND_MXC_SOC_FIQ tristate + select FIQ + select SND_SOC_IMX_PCM config SND_MXC_SOC_MX2 + select SND_SOC_DMAENGINE_PCM + tristate + select SND_SOC_IMX_PCM + +config SND_SOC_IMX_AUDMUX tristate config SND_MXC_SOC_WM1133_EV1 @@ -22,6 +32,8 @@ config SND_MXC_SOC_WM1133_EV1 depends on MACH_MX31ADS_WM1133_EV1 && EXPERIMENTAL select SND_SOC_WM8350 select SND_MXC_SOC_FIQ + select SND_SOC_IMX_AUDMUX + select SND_SOC_IMX_SSI help Enable support for audio on the i.MX31ADS with the WM1133-EV1 PMIC board with WM8835x fitted. @@ -31,6 +43,8 @@ config SND_SOC_MX27VIS_AIC32X4 depends on MACH_IMX27_VISSTRIM_M10 && I2C select SND_SOC_TLV320AIC32X4 select SND_MXC_SOC_MX2 + select SND_SOC_IMX_AUDMUX + select SND_SOC_IMX_SSI help Say Y if you want to add support for SoC audio on Visstrim SM10 board with TLV320AIC32X4 codec. @@ -38,8 +52,11 @@ config SND_SOC_MX27VIS_AIC32X4 config SND_SOC_PHYCORE_AC97 tristate "SoC Audio support for Phytec phyCORE (and phyCARD) boards" depends on MACH_PCM043 || MACH_PCA100 + select SND_SOC_AC97_BUS select SND_SOC_WM9712 select SND_MXC_SOC_FIQ + select SND_SOC_IMX_AUDMUX + select SND_SOC_IMX_SSI help Say Y if you want to add support for SoC audio on Phytec phyCORE and phyCARD boards in AC97 mode @@ -53,6 +70,8 @@ config SND_SOC_EUKREA_TLV320 depends on I2C select SND_SOC_TLV320AIC23 select SND_MXC_SOC_FIQ + select SND_SOC_IMX_AUDMUX + select SND_SOC_IMX_SSI help Enable I2S based access to the TLV320AIC23B codec attached to the SSI interface diff --git a/sound/soc/imx/Makefile b/sound/soc/imx/Makefile index d6d609ba7e24..f5db3e92d0d1 100644 --- a/sound/soc/imx/Makefile +++ b/sound/soc/imx/Makefile @@ -1,11 +1,14 @@ # i.MX Platform Support -snd-soc-imx-objs := imx-ssi.o -snd-soc-imx-fiq-objs := imx-pcm-fiq.o -snd-soc-imx-mx2-objs := imx-pcm-dma-mx2.o +snd-soc-imx-ssi-objs := imx-ssi.o +snd-soc-imx-audmux-objs := imx-audmux.o -obj-$(CONFIG_SND_IMX_SOC) += snd-soc-imx.o -obj-$(CONFIG_SND_MXC_SOC_FIQ) += snd-soc-imx-fiq.o -obj-$(CONFIG_SND_MXC_SOC_MX2) += snd-soc-imx-mx2.o +obj-$(CONFIG_SND_SOC_IMX_SSI) += snd-soc-imx-ssi.o +obj-$(CONFIG_SND_SOC_IMX_AUDMUX) += snd-soc-imx-audmux.o + +obj-$(CONFIG_SND_SOC_IMX_PCM) += snd-soc-imx-pcm.o +snd-soc-imx-pcm-y := imx-pcm.o +snd-soc-imx-pcm-$(CONFIG_SND_MXC_SOC_FIQ) += imx-pcm-fiq.o +snd-soc-imx-pcm-$(CONFIG_SND_MXC_SOC_MX2) += imx-pcm-dma-mx2.o # i.MX Machine Support snd-soc-eukrea-tlv320-objs := eukrea-tlv320.o diff --git a/sound/soc/imx/eukrea-tlv320.c b/sound/soc/imx/eukrea-tlv320.c index 1c1fdd10f73f..7d4475cfdb24 100644 --- a/sound/soc/imx/eukrea-tlv320.c +++ b/sound/soc/imx/eukrea-tlv320.c @@ -26,6 +26,7 @@ #include "../codecs/tlv320aic23.h" #include "imx-ssi.h" +#include "imx-audmux.h" #define CODEC_CLOCK 12000000 @@ -97,12 +98,43 @@ static struct platform_device *eukrea_tlv320_snd_device; static int __init eukrea_tlv320_init(void) { int ret; - - if (!machine_is_eukrea_cpuimx27() && !machine_is_eukrea_cpuimx25sd() - && !machine_is_eukrea_cpuimx35sd() - && !machine_is_eukrea_cpuimx51sd()) + int int_port = 0, ext_port; + + if (machine_is_eukrea_cpuimx27()) { + imx_audmux_v1_configure_port(MX27_AUDMUX_HPCR1_SSI0, + IMX_AUDMUX_V1_PCR_SYN | + IMX_AUDMUX_V1_PCR_TFSDIR | + IMX_AUDMUX_V1_PCR_TCLKDIR | + IMX_AUDMUX_V1_PCR_RFSDIR | + IMX_AUDMUX_V1_PCR_RCLKDIR | + IMX_AUDMUX_V1_PCR_TFCSEL(MX27_AUDMUX_HPCR3_SSI_PINS_4) | + IMX_AUDMUX_V1_PCR_RFCSEL(MX27_AUDMUX_HPCR3_SSI_PINS_4) | + IMX_AUDMUX_V1_PCR_RXDSEL(MX27_AUDMUX_HPCR3_SSI_PINS_4) + ); + imx_audmux_v1_configure_port(MX27_AUDMUX_HPCR3_SSI_PINS_4, + IMX_AUDMUX_V1_PCR_SYN | + IMX_AUDMUX_V1_PCR_RXDSEL(MX27_AUDMUX_HPCR1_SSI0) + ); + } else if (machine_is_eukrea_cpuimx25sd() || + machine_is_eukrea_cpuimx35sd() || + machine_is_eukrea_cpuimx51sd()) { + ext_port = machine_is_eukrea_cpuimx25sd() ? 4 : 3; + imx_audmux_v2_configure_port(int_port, + IMX_AUDMUX_V2_PTCR_SYN | + IMX_AUDMUX_V2_PTCR_TFSDIR | + IMX_AUDMUX_V2_PTCR_TFSEL(ext_port) | + IMX_AUDMUX_V2_PTCR_TCLKDIR | + IMX_AUDMUX_V2_PTCR_TCSEL(ext_port), + IMX_AUDMUX_V2_PDCR_RXDSEL(ext_port) + ); + imx_audmux_v2_configure_port(ext_port, + IMX_AUDMUX_V2_PTCR_SYN, + IMX_AUDMUX_V2_PDCR_RXDSEL(int_port) + ); + } else { /* return happy. We might run on a totally different machine */ return 0; + } eukrea_tlv320_snd_device = platform_device_alloc("soc-audio", -1); if (!eukrea_tlv320_snd_device) diff --git a/sound/soc/imx/imx-audmux.c b/sound/soc/imx/imx-audmux.c new file mode 100644 index 000000000000..a839494c5ea8 --- /dev/null +++ b/sound/soc/imx/imx-audmux.c @@ -0,0 +1,314 @@ +/* + * Copyright 2012 Freescale Semiconductor, Inc. + * Copyright 2012 Linaro Ltd. + * Copyright 2009 Pengutronix, Sascha Hauer <s.hauer@pengutronix.de> + * + * Initial development of this code was funded by + * Phytec Messtechnik GmbH, http://www.phytec.de + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/clk.h> +#include <linux/debugfs.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +#include "imx-audmux.h" + +#define DRIVER_NAME "imx-audmux" + +static struct clk *audmux_clk; +static void __iomem *audmux_base; + +#define IMX_AUDMUX_V2_PTCR(x) ((x) * 8) +#define IMX_AUDMUX_V2_PDCR(x) ((x) * 8 + 4) + +#ifdef CONFIG_DEBUG_FS +static struct dentry *audmux_debugfs_root; + +static int audmux_open_file(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +/* There is an annoying discontinuity in the SSI numbering with regard + * to the Linux number of the devices */ +static const char *audmux_port_string(int port) +{ + switch (port) { + case MX31_AUDMUX_PORT1_SSI0: + return "imx-ssi.0"; + case MX31_AUDMUX_PORT2_SSI1: + return "imx-ssi.1"; + case MX31_AUDMUX_PORT3_SSI_PINS_3: + return "SSI3"; + case MX31_AUDMUX_PORT4_SSI_PINS_4: + return "SSI4"; + case MX31_AUDMUX_PORT5_SSI_PINS_5: + return "SSI5"; + case MX31_AUDMUX_PORT6_SSI_PINS_6: + return "SSI6"; + default: + return "UNKNOWN"; + } +} + +static ssize_t audmux_read_file(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + ssize_t ret; + char *buf = kmalloc(PAGE_SIZE, GFP_KERNEL); + int port = (int)file->private_data; + u32 pdcr, ptcr; + + if (!buf) + return -ENOMEM; + + if (audmux_clk) + clk_enable(audmux_clk); + + ptcr = readl(audmux_base + IMX_AUDMUX_V2_PTCR(port)); + pdcr = readl(audmux_base + IMX_AUDMUX_V2_PDCR(port)); + + if (audmux_clk) + clk_disable(audmux_clk); + + ret = snprintf(buf, PAGE_SIZE, "PDCR: %08x\nPTCR: %08x\n", + pdcr, ptcr); + + if (ptcr & IMX_AUDMUX_V2_PTCR_TFSDIR) + ret += snprintf(buf + ret, PAGE_SIZE - ret, + "TxFS output from %s, ", + audmux_port_string((ptcr >> 27) & 0x7)); + else + ret += snprintf(buf + ret, PAGE_SIZE - ret, + "TxFS input, "); + + if (ptcr & IMX_AUDMUX_V2_PTCR_TCLKDIR) + ret += snprintf(buf + ret, PAGE_SIZE - ret, + "TxClk output from %s", + audmux_port_string((ptcr >> 22) & 0x7)); + else + ret += snprintf(buf + ret, PAGE_SIZE - ret, + "TxClk input"); + + ret += snprintf(buf + ret, PAGE_SIZE - ret, "\n"); + + if (ptcr & IMX_AUDMUX_V2_PTCR_SYN) { + ret += snprintf(buf + ret, PAGE_SIZE - ret, + "Port is symmetric"); + } else { + if (ptcr & IMX_AUDMUX_V2_PTCR_RFSDIR) + ret += snprintf(buf + ret, PAGE_SIZE - ret, + "RxFS output from %s, ", + audmux_port_string((ptcr >> 17) & 0x7)); + else + ret += snprintf(buf + ret, PAGE_SIZE - ret, + "RxFS input, "); + + if (ptcr & IMX_AUDMUX_V2_PTCR_RCLKDIR) + ret += snprintf(buf + ret, PAGE_SIZE - ret, + "RxClk output from %s", + audmux_port_string((ptcr >> 12) & 0x7)); + else + ret += snprintf(buf + ret, PAGE_SIZE - ret, + "RxClk input"); + } + + ret += snprintf(buf + ret, PAGE_SIZE - ret, + "\nData received from %s\n", + audmux_port_string((pdcr >> 13) & 0x7)); + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret); + + kfree(buf); + + return ret; +} + +static const struct file_operations audmux_debugfs_fops = { + .open = audmux_open_file, + .read = audmux_read_file, + .llseek = default_llseek, +}; + +static void __init audmux_debugfs_init(void) +{ + int i; + char buf[20]; + + audmux_debugfs_root = debugfs_create_dir("audmux", NULL); + if (!audmux_debugfs_root) { + pr_warning("Failed to create AUDMUX debugfs root\n"); + return; + } + + for (i = 1; i < 8; i++) { + snprintf(buf, sizeof(buf), "ssi%d", i); + if (!debugfs_create_file(buf, 0444, audmux_debugfs_root, + (void *)i, &audmux_debugfs_fops)) + pr_warning("Failed to create AUDMUX port %d debugfs file\n", + i); + } +} + +static void __devexit audmux_debugfs_remove(void) +{ + debugfs_remove_recursive(audmux_debugfs_root); +} +#else +static inline void audmux_debugfs_init(void) +{ +} + +static inline void audmux_debugfs_remove(void) +{ +} +#endif + +enum imx_audmux_type { + IMX21_AUDMUX, + IMX31_AUDMUX, +} audmux_type; + +static struct platform_device_id imx_audmux_ids[] = { + { + .name = "imx21-audmux", + .driver_data = IMX21_AUDMUX, + }, { + .name = "imx31-audmux", + .driver_data = IMX31_AUDMUX, + }, { + /* sentinel */ + } +}; +MODULE_DEVICE_TABLE(platform, imx_audmux_ids); + +static const struct of_device_id imx_audmux_dt_ids[] = { + { .compatible = "fsl,imx21-audmux", .data = &imx_audmux_ids[0], }, + { .compatible = "fsl,imx31-audmux", .data = &imx_audmux_ids[1], }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, imx_audmux_dt_ids); + +static const uint8_t port_mapping[] = { + 0x0, 0x4, 0x8, 0x10, 0x14, 0x1c, +}; + +int imx_audmux_v1_configure_port(unsigned int port, unsigned int pcr) +{ + if (audmux_type != IMX21_AUDMUX) + return -EINVAL; + + if (!audmux_base) + return -ENOSYS; + + if (port >= ARRAY_SIZE(port_mapping)) + return -EINVAL; + + writel(pcr, audmux_base + port_mapping[port]); + + return 0; +} +EXPORT_SYMBOL_GPL(imx_audmux_v1_configure_port); + +int imx_audmux_v2_configure_port(unsigned int port, unsigned int ptcr, + unsigned int pdcr) +{ + if (audmux_type != IMX31_AUDMUX) + return -EINVAL; + + if (!audmux_base) + return -ENOSYS; + + if (audmux_clk) + clk_enable(audmux_clk); + + writel(ptcr, audmux_base + IMX_AUDMUX_V2_PTCR(port)); + writel(pdcr, audmux_base + IMX_AUDMUX_V2_PDCR(port)); + + if (audmux_clk) + clk_disable(audmux_clk); + + return 0; +} +EXPORT_SYMBOL_GPL(imx_audmux_v2_configure_port); + +static int __devinit imx_audmux_probe(struct platform_device *pdev) +{ + struct resource *res; + const struct of_device_id *of_id = + of_match_device(imx_audmux_dt_ids, &pdev->dev); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + audmux_base = devm_request_and_ioremap(&pdev->dev, res); + if (!audmux_base) + return -EADDRNOTAVAIL; + + audmux_clk = clk_get(&pdev->dev, "audmux"); + if (IS_ERR(audmux_clk)) { + dev_dbg(&pdev->dev, "cannot get clock: %ld\n", + PTR_ERR(audmux_clk)); + audmux_clk = NULL; + } + + if (of_id) + pdev->id_entry = of_id->data; + audmux_type = pdev->id_entry->driver_data; + if (audmux_type == IMX31_AUDMUX) + audmux_debugfs_init(); + + return 0; +} + +static int __devexit imx_audmux_remove(struct platform_device *pdev) +{ + if (audmux_type == IMX31_AUDMUX) + audmux_debugfs_remove(); + clk_put(audmux_clk); + + return 0; +} + +static struct platform_driver imx_audmux_driver = { + .probe = imx_audmux_probe, + .remove = __devexit_p(imx_audmux_remove), + .id_table = imx_audmux_ids, + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + .of_match_table = imx_audmux_dt_ids, + } +}; + +static int __init imx_audmux_init(void) +{ + return platform_driver_register(&imx_audmux_driver); +} +subsys_initcall(imx_audmux_init); + +static void __exit imx_audmux_exit(void) +{ + platform_driver_unregister(&imx_audmux_driver); +} +module_exit(imx_audmux_exit); + +MODULE_DESCRIPTION("Freescale i.MX AUDMUX driver"); +MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" DRIVER_NAME); diff --git a/sound/soc/imx/imx-audmux.h b/sound/soc/imx/imx-audmux.h new file mode 100644 index 000000000000..04ebbab8d7b9 --- /dev/null +++ b/sound/soc/imx/imx-audmux.h @@ -0,0 +1,60 @@ +#ifndef __IMX_AUDMUX_H +#define __IMX_AUDMUX_H + +#define MX27_AUDMUX_HPCR1_SSI0 0 +#define MX27_AUDMUX_HPCR2_SSI1 1 +#define MX27_AUDMUX_HPCR3_SSI_PINS_4 2 +#define MX27_AUDMUX_PPCR1_SSI_PINS_1 3 +#define MX27_AUDMUX_PPCR2_SSI_PINS_2 4 +#define MX27_AUDMUX_PPCR3_SSI_PINS_3 5 + +#define MX31_AUDMUX_PORT1_SSI0 0 +#define MX31_AUDMUX_PORT2_SSI1 1 +#define MX31_AUDMUX_PORT3_SSI_PINS_3 2 +#define MX31_AUDMUX_PORT4_SSI_PINS_4 3 +#define MX31_AUDMUX_PORT5_SSI_PINS_5 4 +#define MX31_AUDMUX_PORT6_SSI_PINS_6 5 + +#define MX51_AUDMUX_PORT1_SSI0 0 +#define MX51_AUDMUX_PORT2_SSI1 1 +#define MX51_AUDMUX_PORT3 2 +#define MX51_AUDMUX_PORT4 3 +#define MX51_AUDMUX_PORT5 4 +#define MX51_AUDMUX_PORT6 5 +#define MX51_AUDMUX_PORT7 6 + +/* Register definitions for the i.MX21/27 Digital Audio Multiplexer */ +#define IMX_AUDMUX_V1_PCR_INMMASK(x) ((x) & 0xff) +#define IMX_AUDMUX_V1_PCR_INMEN (1 << 8) +#define IMX_AUDMUX_V1_PCR_TXRXEN (1 << 10) +#define IMX_AUDMUX_V1_PCR_SYN (1 << 12) +#define IMX_AUDMUX_V1_PCR_RXDSEL(x) (((x) & 0x7) << 13) +#define IMX_AUDMUX_V1_PCR_RFCSEL(x) (((x) & 0xf) << 20) +#define IMX_AUDMUX_V1_PCR_RCLKDIR (1 << 24) +#define IMX_AUDMUX_V1_PCR_RFSDIR (1 << 25) +#define IMX_AUDMUX_V1_PCR_TFCSEL(x) (((x) & 0xf) << 26) +#define IMX_AUDMUX_V1_PCR_TCLKDIR (1 << 30) +#define IMX_AUDMUX_V1_PCR_TFSDIR (1 << 31) + +/* Register definitions for the i.MX25/31/35/51 Digital Audio Multiplexer */ +#define IMX_AUDMUX_V2_PTCR_TFSDIR (1 << 31) +#define IMX_AUDMUX_V2_PTCR_TFSEL(x) (((x) & 0xf) << 27) +#define IMX_AUDMUX_V2_PTCR_TCLKDIR (1 << 26) +#define IMX_AUDMUX_V2_PTCR_TCSEL(x) (((x) & 0xf) << 22) +#define IMX_AUDMUX_V2_PTCR_RFSDIR (1 << 21) +#define IMX_AUDMUX_V2_PTCR_RFSEL(x) (((x) & 0xf) << 17) +#define IMX_AUDMUX_V2_PTCR_RCLKDIR (1 << 16) +#define IMX_AUDMUX_V2_PTCR_RCSEL(x) (((x) & 0xf) << 12) +#define IMX_AUDMUX_V2_PTCR_SYN (1 << 11) + +#define IMX_AUDMUX_V2_PDCR_RXDSEL(x) (((x) & 0x7) << 13) +#define IMX_AUDMUX_V2_PDCR_TXRXEN (1 << 12) +#define IMX_AUDMUX_V2_PDCR_MODE(x) (((x) & 0x3) << 8) +#define IMX_AUDMUX_V2_PDCR_INMMASK(x) ((x) & 0xff) + +int imx_audmux_v1_configure_port(unsigned int port, unsigned int pcr); + +int imx_audmux_v2_configure_port(unsigned int port, unsigned int ptcr, + unsigned int pdcr); + +#endif /* __IMX_AUDMUX_H */ diff --git a/sound/soc/imx/imx-pcm-dma-mx2.c b/sound/soc/imx/imx-pcm-dma-mx2.c index 5780c9b9d569..e43c8fa2788b 100644 --- a/sound/soc/imx/imx-pcm-dma-mx2.c +++ b/sound/soc/imx/imx-pcm-dma-mx2.c @@ -27,212 +27,54 @@ #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> +#include <sound/dmaengine_pcm.h> #include <mach/dma.h> -#include "imx-ssi.h" - -struct imx_pcm_runtime_data { - int period_bytes; - int periods; - int dma; - unsigned long offset; - unsigned long size; - void *buf; - int period_time; - struct dma_async_tx_descriptor *desc; - struct dma_chan *dma_chan; - struct imx_dma_data dma_data; -}; - -static void audio_dma_irq(void *data) -{ - struct snd_pcm_substream *substream = (struct snd_pcm_substream *)data; - struct snd_pcm_runtime *runtime = substream->runtime; - struct imx_pcm_runtime_data *iprtd = runtime->private_data; - - iprtd->offset += iprtd->period_bytes; - iprtd->offset %= iprtd->period_bytes * iprtd->periods; - - snd_pcm_period_elapsed(substream); -} +#include "imx-pcm.h" static bool filter(struct dma_chan *chan, void *param) { - struct imx_pcm_runtime_data *iprtd = param; - if (!imx_dma_is_general_purpose(chan)) return false; - chan->private = &iprtd->dma_data; + chan->private = param; - return true; + return true; } -static int imx_ssi_dma_alloc(struct snd_pcm_substream *substream, +static int snd_imx_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct dma_chan *chan = snd_dmaengine_pcm_get_chan(substream); struct imx_pcm_dma_params *dma_params; - struct snd_pcm_runtime *runtime = substream->runtime; - struct imx_pcm_runtime_data *iprtd = runtime->private_data; struct dma_slave_config slave_config; - dma_cap_mask_t mask; - enum dma_slave_buswidth buswidth; int ret; dma_params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); - iprtd->dma_data.peripheral_type = IMX_DMATYPE_SSI; - iprtd->dma_data.priority = DMA_PRIO_HIGH; - iprtd->dma_data.dma_request = dma_params->dma; - - /* Try to grab a DMA channel */ - if (!iprtd->dma_chan) { - dma_cap_zero(mask); - dma_cap_set(DMA_SLAVE, mask); - iprtd->dma_chan = dma_request_channel(mask, filter, iprtd); - if (!iprtd->dma_chan) - return -EINVAL; - } - - switch (params_format(params)) { - case SNDRV_PCM_FORMAT_S16_LE: - buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES; - break; - case SNDRV_PCM_FORMAT_S20_3LE: - case SNDRV_PCM_FORMAT_S24_LE: - buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES; - break; - default: - return 0; - } + ret = snd_hwparams_to_dma_slave_config(substream, params, &slave_config); + if (ret) + return ret; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - slave_config.direction = DMA_MEM_TO_DEV; slave_config.dst_addr = dma_params->dma_addr; - slave_config.dst_addr_width = buswidth; slave_config.dst_maxburst = dma_params->burstsize; } else { - slave_config.direction = DMA_DEV_TO_MEM; slave_config.src_addr = dma_params->dma_addr; - slave_config.src_addr_width = buswidth; slave_config.src_maxburst = dma_params->burstsize; } - ret = dmaengine_slave_config(iprtd->dma_chan, &slave_config); + ret = dmaengine_slave_config(chan, &slave_config); if (ret) return ret; - return 0; -} - -static int snd_imx_pcm_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_pcm_runtime *runtime = substream->runtime; - struct imx_pcm_runtime_data *iprtd = runtime->private_data; - unsigned long dma_addr; - struct dma_chan *chan; - struct imx_pcm_dma_params *dma_params; - int ret; - - dma_params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); - ret = imx_ssi_dma_alloc(substream, params); - if (ret) - return ret; - chan = iprtd->dma_chan; - - iprtd->size = params_buffer_bytes(params); - iprtd->periods = params_periods(params); - iprtd->period_bytes = params_period_bytes(params); - iprtd->offset = 0; - iprtd->period_time = HZ / (params_rate(params) / - params_period_size(params)); - snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); - dma_addr = runtime->dma_addr; - - iprtd->buf = (unsigned int *)substream->dma_buffer.area; - - iprtd->desc = chan->device->device_prep_dma_cyclic(chan, dma_addr, - iprtd->period_bytes * iprtd->periods, - iprtd->period_bytes, - substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? - DMA_MEM_TO_DEV : DMA_DEV_TO_MEM); - if (!iprtd->desc) { - dev_err(&chan->dev->device, "cannot prepare slave dma\n"); - return -EINVAL; - } - - iprtd->desc->callback = audio_dma_irq; - iprtd->desc->callback_param = substream; - - return 0; -} - -static int snd_imx_pcm_hw_free(struct snd_pcm_substream *substream) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - struct imx_pcm_runtime_data *iprtd = runtime->private_data; - - if (iprtd->dma_chan) { - dma_release_channel(iprtd->dma_chan); - iprtd->dma_chan = NULL; - } - - return 0; -} - -static int snd_imx_pcm_prepare(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct imx_pcm_dma_params *dma_params; - - dma_params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); - return 0; } -static int snd_imx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - struct imx_pcm_runtime_data *iprtd = runtime->private_data; - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - case SNDRV_PCM_TRIGGER_RESUME: - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - dmaengine_submit(iprtd->desc); - - break; - - case SNDRV_PCM_TRIGGER_STOP: - case SNDRV_PCM_TRIGGER_SUSPEND: - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - dmaengine_terminate_all(iprtd->dma_chan); - - break; - default: - return -EINVAL; - } - - return 0; -} - -static snd_pcm_uframes_t snd_imx_pcm_pointer(struct snd_pcm_substream *substream) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - struct imx_pcm_runtime_data *iprtd = runtime->private_data; - - pr_debug("%s: %ld %ld\n", __func__, iprtd->offset, - bytes_to_frames(substream->runtime, iprtd->offset)); - - return bytes_to_frames(substream->runtime, iprtd->offset); -} - static struct snd_pcm_hardware snd_imx_hardware = { .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | @@ -254,33 +96,37 @@ static struct snd_pcm_hardware snd_imx_hardware = { static int snd_imx_open(struct snd_pcm_substream *substream) { - struct snd_pcm_runtime *runtime = substream->runtime; - struct imx_pcm_runtime_data *iprtd; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct imx_pcm_dma_params *dma_params; + struct imx_dma_data *dma_data; int ret; - iprtd = kzalloc(sizeof(*iprtd), GFP_KERNEL); - if (iprtd == NULL) - return -ENOMEM; - runtime->private_data = iprtd; + snd_soc_set_runtime_hwparams(substream, &snd_imx_hardware); - ret = snd_pcm_hw_constraint_integer(substream->runtime, - SNDRV_PCM_HW_PARAM_PERIODS); - if (ret < 0) { - kfree(iprtd); - return ret; + dma_params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); + + dma_data = kzalloc(sizeof(*dma_data), GFP_KERNEL); + dma_data->peripheral_type = IMX_DMATYPE_SSI; + dma_data->priority = DMA_PRIO_HIGH; + dma_data->dma_request = dma_params->dma; + + ret = snd_dmaengine_pcm_open(substream, filter, dma_data); + if (ret) { + kfree(dma_data); + return 0; } - snd_soc_set_runtime_hwparams(substream, &snd_imx_hardware); + snd_dmaengine_pcm_set_data(substream, dma_data); return 0; } static int snd_imx_close(struct snd_pcm_substream *substream) { - struct snd_pcm_runtime *runtime = substream->runtime; - struct imx_pcm_runtime_data *iprtd = runtime->private_data; + struct imx_dma_data *dma_data = snd_dmaengine_pcm_get_data(substream); - kfree(iprtd); + snd_dmaengine_pcm_close(substream); + kfree(dma_data); return 0; } @@ -290,10 +136,8 @@ static struct snd_pcm_ops imx_pcm_ops = { .close = snd_imx_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_imx_pcm_hw_params, - .hw_free = snd_imx_pcm_hw_free, - .prepare = snd_imx_pcm_prepare, - .trigger = snd_imx_pcm_trigger, - .pointer = snd_imx_pcm_pointer, + .trigger = snd_dmaengine_pcm_trigger, + .pointer = snd_dmaengine_pcm_pointer, .mmap = snd_imx_pcm_mmap, }; @@ -305,11 +149,6 @@ static struct snd_soc_platform_driver imx_soc_platform_mx2 = { static int __devinit imx_soc_platform_probe(struct platform_device *pdev) { - struct imx_ssi *ssi = platform_get_drvdata(pdev); - - ssi->dma_params_tx.burstsize = 6; - ssi->dma_params_rx.burstsize = 4; - return snd_soc_register_platform(&pdev->dev, &imx_soc_platform_mx2); } diff --git a/sound/soc/imx/imx-pcm.c b/sound/soc/imx/imx-pcm.c new file mode 100644 index 000000000000..93dc360b1777 --- /dev/null +++ b/sound/soc/imx/imx-pcm.c @@ -0,0 +1,105 @@ +/* + * Copyright 2009 Sascha Hauer <s.hauer@pengutronix.de> + * + * This code is based on code copyrighted by Freescale, + * Liam Girdwood, Javier Martin and probably others. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include <linux/dma-mapping.h> +#include <linux/module.h> +#include <sound/pcm.h> +#include <sound/soc.h> +#include "imx-pcm.h" + +int snd_imx_pcm_mmap(struct snd_pcm_substream *substream, + struct vm_area_struct *vma) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + int ret; + + ret = dma_mmap_writecombine(substream->pcm->card->dev, vma, + runtime->dma_area, runtime->dma_addr, runtime->dma_bytes); + + pr_debug("%s: ret: %d %p 0x%08x 0x%08x\n", __func__, ret, + runtime->dma_area, + runtime->dma_addr, + runtime->dma_bytes); + return ret; +} +EXPORT_SYMBOL_GPL(snd_imx_pcm_mmap); + +static int imx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) +{ + struct snd_pcm_substream *substream = pcm->streams[stream].substream; + struct snd_dma_buffer *buf = &substream->dma_buffer; + size_t size = IMX_SSI_DMABUF_SIZE; + + buf->dev.type = SNDRV_DMA_TYPE_DEV; + buf->dev.dev = pcm->card->dev; + buf->private_data = NULL; + buf->area = dma_alloc_writecombine(pcm->card->dev, size, + &buf->addr, GFP_KERNEL); + if (!buf->area) + return -ENOMEM; + buf->bytes = size; + + return 0; +} + +static u64 imx_pcm_dmamask = DMA_BIT_MASK(32); + +int imx_pcm_new(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_card *card = rtd->card->snd_card; + struct snd_pcm *pcm = rtd->pcm; + int ret = 0; + + if (!card->dev->dma_mask) + card->dev->dma_mask = &imx_pcm_dmamask; + if (!card->dev->coherent_dma_mask) + card->dev->coherent_dma_mask = DMA_BIT_MASK(32); + if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) { + ret = imx_pcm_preallocate_dma_buffer(pcm, + SNDRV_PCM_STREAM_PLAYBACK); + if (ret) + goto out; + } + + if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) { + ret = imx_pcm_preallocate_dma_buffer(pcm, + SNDRV_PCM_STREAM_CAPTURE); + if (ret) + goto out; + } + +out: + return ret; +} +EXPORT_SYMBOL_GPL(imx_pcm_new); + +void imx_pcm_free(struct snd_pcm *pcm) +{ + struct snd_pcm_substream *substream; + struct snd_dma_buffer *buf; + int stream; + + for (stream = 0; stream < 2; stream++) { + substream = pcm->streams[stream].substream; + if (!substream) + continue; + + buf = &substream->dma_buffer; + if (!buf->area) + continue; + + dma_free_writecombine(pcm->card->dev, buf->bytes, + buf->area, buf->addr); + buf->area = NULL; + } +} +EXPORT_SYMBOL_GPL(imx_pcm_free); diff --git a/sound/soc/imx/imx-pcm.h b/sound/soc/imx/imx-pcm.h new file mode 100644 index 000000000000..b5f5c3acf34d --- /dev/null +++ b/sound/soc/imx/imx-pcm.h @@ -0,0 +1,32 @@ +/* + * Copyright 2009 Sascha Hauer <s.hauer@pengutronix.de> + * + * This code is based on code copyrighted by Freescale, + * Liam Girdwood, Javier Martin and probably others. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#ifndef _IMX_PCM_H +#define _IMX_PCM_H + +/* + * Do not change this as the FIQ handler depends on this size + */ +#define IMX_SSI_DMABUF_SIZE (64 * 1024) + +struct imx_pcm_dma_params { + int dma; + unsigned long dma_addr; + int burstsize; +}; + +int snd_imx_pcm_mmap(struct snd_pcm_substream *substream, + struct vm_area_struct *vma); +int imx_pcm_new(struct snd_soc_pcm_runtime *rtd); +void imx_pcm_free(struct snd_pcm *pcm); + +#endif /* _IMX_PCM_H */ diff --git a/sound/soc/imx/imx-ssi.c b/sound/soc/imx/imx-ssi.c index b6adbed6e506..4f81ed456325 100644 --- a/sound/soc/imx/imx-ssi.c +++ b/sound/soc/imx/imx-ssi.c @@ -233,6 +233,23 @@ static int imx_ssi_set_dai_clkdiv(struct snd_soc_dai *cpu_dai, return 0; } +static int imx_ssi_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) +{ + struct imx_ssi *ssi = snd_soc_dai_get_drvdata(cpu_dai); + struct imx_pcm_dma_params *dma_data; + + /* Tx/Rx config */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + dma_data = &ssi->dma_params_tx; + else + dma_data = &ssi->dma_params_rx; + + snd_soc_dai_set_dma_data(cpu_dai, substream, dma_data); + + return 0; +} + /* * Should only be called when port is inactive (i.e. SSIEN = 0), * although can be called multiple times by upper layers. @@ -242,23 +259,17 @@ static int imx_ssi_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai) { struct imx_ssi *ssi = snd_soc_dai_get_drvdata(cpu_dai); - struct imx_pcm_dma_params *dma_data; u32 reg, sccr; /* Tx/Rx config */ - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) reg = SSI_STCCR; - dma_data = &ssi->dma_params_tx; - } else { + else reg = SSI_SRCCR; - dma_data = &ssi->dma_params_rx; - } if (ssi->flags & IMX_SSI_SYN) reg = SSI_STCCR; - snd_soc_dai_set_dma_data(cpu_dai, substream, dma_data); - sccr = readl(ssi->base + reg) & ~SSI_STCCR_WL_MASK; /* DAI data (word) size */ @@ -343,6 +354,7 @@ static int imx_ssi_trigger(struct snd_pcm_substream *substream, int cmd, } static const struct snd_soc_dai_ops imx_ssi_pcm_dai_ops = { + .startup = imx_ssi_startup, .hw_params = imx_ssi_hw_params, .set_fmt = imx_ssi_set_dai_fmt, .set_clkdiv = imx_ssi_set_dai_clkdiv, @@ -351,94 +363,6 @@ static const struct snd_soc_dai_ops imx_ssi_pcm_dai_ops = { .trigger = imx_ssi_trigger, }; -int snd_imx_pcm_mmap(struct snd_pcm_substream *substream, - struct vm_area_struct *vma) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - int ret; - - ret = dma_mmap_writecombine(substream->pcm->card->dev, vma, - runtime->dma_area, runtime->dma_addr, runtime->dma_bytes); - - pr_debug("%s: ret: %d %p 0x%08x 0x%08x\n", __func__, ret, - runtime->dma_area, - runtime->dma_addr, - runtime->dma_bytes); - return ret; -} -EXPORT_SYMBOL_GPL(snd_imx_pcm_mmap); - -static int imx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) -{ - struct snd_pcm_substream *substream = pcm->streams[stream].substream; - struct snd_dma_buffer *buf = &substream->dma_buffer; - size_t size = IMX_SSI_DMABUF_SIZE; - - buf->dev.type = SNDRV_DMA_TYPE_DEV; - buf->dev.dev = pcm->card->dev; - buf->private_data = NULL; - buf->area = dma_alloc_writecombine(pcm->card->dev, size, - &buf->addr, GFP_KERNEL); - if (!buf->area) - return -ENOMEM; - buf->bytes = size; - - return 0; -} - -static u64 imx_pcm_dmamask = DMA_BIT_MASK(32); - -int imx_pcm_new(struct snd_soc_pcm_runtime *rtd) -{ - struct snd_card *card = rtd->card->snd_card; - struct snd_pcm *pcm = rtd->pcm; - int ret = 0; - - if (!card->dev->dma_mask) - card->dev->dma_mask = &imx_pcm_dmamask; - if (!card->dev->coherent_dma_mask) - card->dev->coherent_dma_mask = DMA_BIT_MASK(32); - if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) { - ret = imx_pcm_preallocate_dma_buffer(pcm, - SNDRV_PCM_STREAM_PLAYBACK); - if (ret) - goto out; - } - - if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) { - ret = imx_pcm_preallocate_dma_buffer(pcm, - SNDRV_PCM_STREAM_CAPTURE); - if (ret) - goto out; - } - -out: - return ret; -} -EXPORT_SYMBOL_GPL(imx_pcm_new); - -void imx_pcm_free(struct snd_pcm *pcm) -{ - struct snd_pcm_substream *substream; - struct snd_dma_buffer *buf; - int stream; - - for (stream = 0; stream < 2; stream++) { - substream = pcm->streams[stream].substream; - if (!substream) - continue; - - buf = &substream->dma_buffer; - if (!buf->area) - continue; - - dma_free_writecombine(pcm->card->dev, buf->bytes, - buf->area, buf->addr); - buf->area = NULL; - } -} -EXPORT_SYMBOL_GPL(imx_pcm_free); - static int imx_ssi_dai_probe(struct snd_soc_dai *dai) { struct imx_ssi *ssi = dev_get_drvdata(dai->dev); @@ -656,7 +580,7 @@ static int imx_ssi_probe(struct platform_device *pdev) ssi->dma_params_rx.dma_addr = res->start + SSI_SRX0; ssi->dma_params_tx.dma_addr = res->start + SSI_STX0; - ssi->dma_params_tx.burstsize = 4; + ssi->dma_params_tx.burstsize = 6; ssi->dma_params_rx.burstsize = 4; res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "tx0"); diff --git a/sound/soc/imx/imx-ssi.h b/sound/soc/imx/imx-ssi.h index 1072dfb53e47..5744e86ca878 100644 --- a/sound/soc/imx/imx-ssi.h +++ b/sound/soc/imx/imx-ssi.h @@ -187,12 +187,7 @@ #include <linux/dmaengine.h> #include <mach/dma.h> - -struct imx_pcm_dma_params { - int dma; - unsigned long dma_addr; - int burstsize; -}; +#include "imx-pcm.h" struct imx_ssi { struct platform_device *ac97_dev; @@ -218,13 +213,4 @@ struct imx_ssi { struct platform_device *soc_platform_pdev_fiq; }; -int snd_imx_pcm_mmap(struct snd_pcm_substream *substream, struct vm_area_struct *vma); -int imx_pcm_new(struct snd_soc_pcm_runtime *rtd); -void imx_pcm_free(struct snd_pcm *pcm); - -/* - * Do not change this as the FIQ handler depends on this size - */ -#define IMX_SSI_DMABUF_SIZE (64 * 1024) - #endif /* _IMX_SSI_H */ diff --git a/sound/soc/imx/mx27vis-aic32x4.c b/sound/soc/imx/mx27vis-aic32x4.c index 3c2eed9094d5..f6d04ad4bb39 100644 --- a/sound/soc/imx/mx27vis-aic32x4.c +++ b/sound/soc/imx/mx27vis-aic32x4.c @@ -25,15 +25,36 @@ #include <linux/moduleparam.h> #include <linux/device.h> #include <linux/i2c.h> +#include <linux/gpio.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/soc.h> #include <sound/soc-dapm.h> +#include <sound/tlv.h> #include <asm/mach-types.h> -#include <mach/audmux.h> +#include <mach/iomux-mx27.h> #include "../codecs/tlv320aic32x4.h" #include "imx-ssi.h" +#include "imx-audmux.h" + +#define MX27VIS_AMP_GAIN 0 +#define MX27VIS_AMP_MUTE 1 + +#define MX27VIS_PIN_G0 (GPIO_PORTF + 9) +#define MX27VIS_PIN_G1 (GPIO_PORTF + 8) +#define MX27VIS_PIN_SDL (GPIO_PORTE + 5) +#define MX27VIS_PIN_SDR (GPIO_PORTF + 7) + +static int mx27vis_amp_gain; +static int mx27vis_amp_mute; + +static const int mx27vis_amp_pins[] = { + MX27VIS_PIN_G0 | GPIO_GPIO | GPIO_OUT, + MX27VIS_PIN_G1 | GPIO_GPIO | GPIO_OUT, + MX27VIS_PIN_SDL | GPIO_GPIO | GPIO_OUT, + MX27VIS_PIN_SDR | GPIO_GPIO | GPIO_OUT, +}; static int mx27vis_aic32x4_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) @@ -74,6 +95,76 @@ static struct snd_soc_ops mx27vis_aic32x4_snd_ops = { .hw_params = mx27vis_aic32x4_hw_params, }; +static int mx27vis_amp_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + int value = ucontrol->value.integer.value[0]; + unsigned int reg = mc->reg; + int max = mc->max; + + if (value > max) + return -EINVAL; + + switch (reg) { + case MX27VIS_AMP_GAIN: + gpio_set_value(MX27VIS_PIN_G0, value & 1); + gpio_set_value(MX27VIS_PIN_G1, value >> 1); + mx27vis_amp_gain = value; + break; + case MX27VIS_AMP_MUTE: + gpio_set_value(MX27VIS_PIN_SDL, value & 1); + gpio_set_value(MX27VIS_PIN_SDR, value >> 1); + mx27vis_amp_mute = value; + break; + } + return 0; +} + +static int mx27vis_amp_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + unsigned int reg = mc->reg; + + switch (reg) { + case MX27VIS_AMP_GAIN: + ucontrol->value.integer.value[0] = mx27vis_amp_gain; + break; + case MX27VIS_AMP_MUTE: + ucontrol->value.integer.value[0] = mx27vis_amp_mute; + break; + } + return 0; +} + +/* From 6dB to 24dB in steps of 6dB */ +static const DECLARE_TLV_DB_SCALE(mx27vis_amp_tlv, 600, 600, 0); + +static const struct snd_kcontrol_new mx27vis_aic32x4_controls[] = { + SOC_DAPM_PIN_SWITCH("External Mic"), + SOC_SINGLE_EXT_TLV("LO Ext Boost", MX27VIS_AMP_GAIN, 0, 3, 0, + mx27vis_amp_get, mx27vis_amp_set, mx27vis_amp_tlv), + SOC_DOUBLE_EXT("LO Ext Mute Switch", MX27VIS_AMP_MUTE, 0, 1, 1, 0, + mx27vis_amp_get, mx27vis_amp_set), +}; + +static const struct snd_soc_dapm_widget aic32x4_dapm_widgets[] = { + SND_SOC_DAPM_MIC("External Mic", NULL), +}; + +static const struct snd_soc_dapm_route aic32x4_dapm_routes[] = { + {"Mic Bias", NULL, "External Mic"}, + {"IN1_R", NULL, "Mic Bias"}, + {"IN2_R", NULL, "Mic Bias"}, + {"IN3_R", NULL, "Mic Bias"}, + {"IN1_L", NULL, "Mic Bias"}, + {"IN2_L", NULL, "Mic Bias"}, + {"IN3_L", NULL, "Mic Bias"}, +}; + static struct snd_soc_dai_link mx27vis_aic32x4_dai = { .name = "tlv320aic32x4", .stream_name = "TLV320AIC32X4", @@ -89,50 +180,66 @@ static struct snd_soc_card mx27vis_aic32x4 = { .owner = THIS_MODULE, .dai_link = &mx27vis_aic32x4_dai, .num_links = 1, + .controls = mx27vis_aic32x4_controls, + .num_controls = ARRAY_SIZE(mx27vis_aic32x4_controls), + .dapm_widgets = aic32x4_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(aic32x4_dapm_widgets), + .dapm_routes = aic32x4_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(aic32x4_dapm_routes), }; -static struct platform_device *mx27vis_aic32x4_snd_device; - -static int __init mx27vis_aic32x4_init(void) +static int __devinit mx27vis_aic32x4_probe(struct platform_device *pdev) { int ret; - mx27vis_aic32x4_snd_device = platform_device_alloc("soc-audio", -1); - if (!mx27vis_aic32x4_snd_device) - return -ENOMEM; - - platform_set_drvdata(mx27vis_aic32x4_snd_device, &mx27vis_aic32x4); - ret = platform_device_add(mx27vis_aic32x4_snd_device); - + mx27vis_aic32x4.dev = &pdev->dev; + ret = snd_soc_register_card(&mx27vis_aic32x4); if (ret) { - printk(KERN_ERR "ASoC: Platform device allocation failed\n"); - platform_device_put(mx27vis_aic32x4_snd_device); + dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", + ret); + return ret; } /* Connect SSI0 as clock slave to SSI1 external pins */ - mxc_audmux_v1_configure_port(MX27_AUDMUX_HPCR1_SSI0, - MXC_AUDMUX_V1_PCR_SYN | - MXC_AUDMUX_V1_PCR_TFSDIR | - MXC_AUDMUX_V1_PCR_TCLKDIR | - MXC_AUDMUX_V1_PCR_TFCSEL(MX27_AUDMUX_PPCR1_SSI_PINS_1) | - MXC_AUDMUX_V1_PCR_RXDSEL(MX27_AUDMUX_PPCR1_SSI_PINS_1) + imx_audmux_v1_configure_port(MX27_AUDMUX_HPCR1_SSI0, + IMX_AUDMUX_V1_PCR_SYN | + IMX_AUDMUX_V1_PCR_TFSDIR | + IMX_AUDMUX_V1_PCR_TCLKDIR | + IMX_AUDMUX_V1_PCR_TFCSEL(MX27_AUDMUX_PPCR1_SSI_PINS_1) | + IMX_AUDMUX_V1_PCR_RXDSEL(MX27_AUDMUX_PPCR1_SSI_PINS_1) ); - mxc_audmux_v1_configure_port(MX27_AUDMUX_PPCR1_SSI_PINS_1, - MXC_AUDMUX_V1_PCR_SYN | - MXC_AUDMUX_V1_PCR_RXDSEL(MX27_AUDMUX_HPCR1_SSI0) + imx_audmux_v1_configure_port(MX27_AUDMUX_PPCR1_SSI_PINS_1, + IMX_AUDMUX_V1_PCR_SYN | + IMX_AUDMUX_V1_PCR_RXDSEL(MX27_AUDMUX_HPCR1_SSI0) ); + ret = mxc_gpio_setup_multiple_pins(mx27vis_amp_pins, + ARRAY_SIZE(mx27vis_amp_pins), "MX27VIS_AMP"); + if (ret) + printk(KERN_ERR "ASoC: unable to setup gpios\n"); + return ret; } -static void __exit mx27vis_aic32x4_exit(void) +static int __devexit mx27vis_aic32x4_remove(struct platform_device *pdev) { - platform_device_unregister(mx27vis_aic32x4_snd_device); + snd_soc_unregister_card(&mx27vis_aic32x4); + + return 0; } -module_init(mx27vis_aic32x4_init); -module_exit(mx27vis_aic32x4_exit); +static struct platform_driver mx27vis_aic32x4_audio_driver = { + .driver = { + .name = "mx27vis", + .owner = THIS_MODULE, + }, + .probe = mx27vis_aic32x4_probe, + .remove = __devexit_p(mx27vis_aic32x4_remove), +}; + +module_platform_driver(mx27vis_aic32x4_audio_driver); MODULE_AUTHOR("Javier Martin <javier.martin@vista-silicon.com>"); MODULE_DESCRIPTION("ALSA SoC AIC32X4 mx27 visstrim"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:mx27vis"); diff --git a/sound/soc/imx/phycore-ac97.c b/sound/soc/imx/phycore-ac97.c index 6ac12111de6a..f8da6dd115ed 100644 --- a/sound/soc/imx/phycore-ac97.c +++ b/sound/soc/imx/phycore-ac97.c @@ -19,6 +19,8 @@ #include <sound/soc.h> #include <asm/mach-types.h> +#include "imx-audmux.h" + static struct snd_soc_card imx_phycore; static struct snd_soc_ops imx_phycore_hifi_ops = { @@ -50,9 +52,32 @@ static int __init imx_phycore_init(void) { int ret; - if (!machine_is_pcm043() && !machine_is_pca100()) + if (machine_is_pca100()) { + imx_audmux_v1_configure_port(MX27_AUDMUX_HPCR1_SSI0, + IMX_AUDMUX_V1_PCR_SYN | /* 4wire mode */ + IMX_AUDMUX_V1_PCR_TFCSEL(3) | + IMX_AUDMUX_V1_PCR_TCLKDIR | /* clock is output */ + IMX_AUDMUX_V1_PCR_RXDSEL(3)); + imx_audmux_v1_configure_port(3, + IMX_AUDMUX_V1_PCR_SYN | /* 4wire mode */ + IMX_AUDMUX_V1_PCR_TFCSEL(0) | + IMX_AUDMUX_V1_PCR_TFSDIR | + IMX_AUDMUX_V1_PCR_RXDSEL(0)); + } else if (machine_is_pcm043()) { + imx_audmux_v2_configure_port(3, + IMX_AUDMUX_V2_PTCR_SYN | /* 4wire mode */ + IMX_AUDMUX_V2_PTCR_TFSEL(0) | + IMX_AUDMUX_V2_PTCR_TFSDIR, + IMX_AUDMUX_V2_PDCR_RXDSEL(0)); + imx_audmux_v2_configure_port(0, + IMX_AUDMUX_V2_PTCR_SYN | /* 4wire mode */ + IMX_AUDMUX_V2_PTCR_TCSEL(3) | + IMX_AUDMUX_V2_PTCR_TCLKDIR, /* clock is output */ + IMX_AUDMUX_V2_PDCR_RXDSEL(3)); + } else { /* return happy. We might run on a totally different machine */ return 0; + } imx_phycore_snd_ac97_device = platform_device_alloc("soc-audio", -1); if (!imx_phycore_snd_ac97_device) diff --git a/sound/soc/imx/wm1133-ev1.c b/sound/soc/imx/wm1133-ev1.c index 37480c90e997..fe54a69073e5 100644 --- a/sound/soc/imx/wm1133-ev1.c +++ b/sound/soc/imx/wm1133-ev1.c @@ -21,10 +21,9 @@ #include <sound/pcm_params.h> #include <sound/soc.h> -#include <mach/audmux.h> - #include "imx-ssi.h" #include "../codecs/wm8350.h" +#include "imx-audmux.h" /* There is a silicon mic on the board optionally connected via a solder pad * SP1. Define this to enable it. @@ -268,17 +267,17 @@ static int __init wm1133_ev1_audio_init(void) unsigned int ptcr, pdcr; /* SSI0 mastered by port 5 */ - ptcr = MXC_AUDMUX_V2_PTCR_SYN | - MXC_AUDMUX_V2_PTCR_TFSDIR | - MXC_AUDMUX_V2_PTCR_TFSEL(MX31_AUDMUX_PORT5_SSI_PINS_5) | - MXC_AUDMUX_V2_PTCR_TCLKDIR | - MXC_AUDMUX_V2_PTCR_TCSEL(MX31_AUDMUX_PORT5_SSI_PINS_5); - pdcr = MXC_AUDMUX_V2_PDCR_RXDSEL(MX31_AUDMUX_PORT5_SSI_PINS_5); - mxc_audmux_v2_configure_port(MX31_AUDMUX_PORT1_SSI0, ptcr, pdcr); - - ptcr = MXC_AUDMUX_V2_PTCR_SYN; - pdcr = MXC_AUDMUX_V2_PDCR_RXDSEL(MX31_AUDMUX_PORT1_SSI0); - mxc_audmux_v2_configure_port(MX31_AUDMUX_PORT5_SSI_PINS_5, ptcr, pdcr); + ptcr = IMX_AUDMUX_V2_PTCR_SYN | + IMX_AUDMUX_V2_PTCR_TFSDIR | + IMX_AUDMUX_V2_PTCR_TFSEL(MX31_AUDMUX_PORT5_SSI_PINS_5) | + IMX_AUDMUX_V2_PTCR_TCLKDIR | + IMX_AUDMUX_V2_PTCR_TCSEL(MX31_AUDMUX_PORT5_SSI_PINS_5); + pdcr = IMX_AUDMUX_V2_PDCR_RXDSEL(MX31_AUDMUX_PORT5_SSI_PINS_5); + imx_audmux_v2_configure_port(MX31_AUDMUX_PORT1_SSI0, ptcr, pdcr); + + ptcr = IMX_AUDMUX_V2_PTCR_SYN; + pdcr = IMX_AUDMUX_V2_PDCR_RXDSEL(MX31_AUDMUX_PORT1_SSI0); + imx_audmux_v2_configure_port(MX31_AUDMUX_PORT5_SSI_PINS_5, ptcr, pdcr); wm1133_ev1_snd_device = platform_device_alloc("soc-audio", -1); if (!wm1133_ev1_snd_device) |