diff options
| -rw-r--r-- | Documentation/devicetree/bindings/sound/tegra-audio-wm8753.txt | 54 | ||||
| -rw-r--r-- | include/sound/sh_fsi.h | 6 | ||||
| -rw-r--r-- | sound/soc/codecs/Kconfig | 4 | ||||
| -rw-r--r-- | sound/soc/codecs/Makefile | 2 | ||||
| -rw-r--r-- | sound/soc/codecs/omap-hdmi.c | 69 | ||||
| -rw-r--r-- | sound/soc/omap/Kconfig | 1 | ||||
| -rw-r--r-- | sound/soc/sh/fsi.c | 224 | ||||
| -rw-r--r-- | sound/soc/tegra/Kconfig | 10 | ||||
| -rw-r--r-- | sound/soc/tegra/Makefile | 2 | ||||
| -rw-r--r-- | sound/soc/tegra/tegra_wm8753.c | 224 |
10 files changed, 529 insertions, 67 deletions
diff --git a/Documentation/devicetree/bindings/sound/tegra-audio-wm8753.txt b/Documentation/devicetree/bindings/sound/tegra-audio-wm8753.txt new file mode 100644 index 000000000000..c4dd39ce6165 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/tegra-audio-wm8753.txt | |||
| @@ -0,0 +1,54 @@ | |||
| 1 | NVIDIA Tegra audio complex | ||
| 2 | |||
| 3 | Required properties: | ||
| 4 | - compatible : "nvidia,tegra-audio-wm8753" | ||
| 5 | - nvidia,model : The user-visible name of this sound complex. | ||
| 6 | - nvidia,audio-routing : A list of the connections between audio components. | ||
| 7 | Each entry is a pair of strings, the first being the connection's sink, | ||
| 8 | the second being the connection's source. Valid names for sources and | ||
| 9 | sinks are the WM8753's pins, and the jacks on the board: | ||
| 10 | |||
| 11 | WM8753 pins: | ||
| 12 | |||
| 13 | * LOUT1 | ||
| 14 | * LOUT2 | ||
| 15 | * ROUT1 | ||
| 16 | * ROUT2 | ||
| 17 | * MONO1 | ||
| 18 | * MONO2 | ||
| 19 | * OUT3 | ||
| 20 | * OUT4 | ||
| 21 | * LINE1 | ||
| 22 | * LINE2 | ||
| 23 | * RXP | ||
| 24 | * RXN | ||
| 25 | * ACIN | ||
| 26 | * ACOP | ||
| 27 | * MIC1N | ||
| 28 | * MIC1 | ||
| 29 | * MIC2N | ||
| 30 | * MIC2 | ||
| 31 | * Mic Bias | ||
| 32 | |||
| 33 | Board connectors: | ||
| 34 | |||
| 35 | * Headphone Jack | ||
| 36 | * Mic Jack | ||
| 37 | |||
| 38 | - nvidia,i2s-controller : The phandle of the Tegra I2S1 controller | ||
| 39 | - nvidia,audio-codec : The phandle of the WM8753 audio codec | ||
| 40 | Example: | ||
| 41 | |||
| 42 | sound { | ||
| 43 | compatible = "nvidia,tegra-audio-wm8753-whistler", | ||
| 44 | "nvidia,tegra-audio-wm8753" | ||
| 45 | nvidia,model = "tegra-wm8753-harmony"; | ||
| 46 | |||
| 47 | nvidia,audio-routing = | ||
| 48 | "Headphone Jack", "LOUT1", | ||
| 49 | "Headphone Jack", "ROUT1"; | ||
| 50 | |||
| 51 | nvidia,i2s-controller = <&i2s1>; | ||
| 52 | nvidia,audio-codec = <&wm8753>; | ||
| 53 | }; | ||
| 54 | |||
diff --git a/include/sound/sh_fsi.h b/include/sound/sh_fsi.h index 956e30e89ea8..906010344dd7 100644 --- a/include/sound/sh_fsi.h +++ b/include/sound/sh_fsi.h | |||
| @@ -21,10 +21,11 @@ | |||
| 21 | /* | 21 | /* |
| 22 | * flags format | 22 | * flags format |
| 23 | * | 23 | * |
| 24 | * 0x000000BA | 24 | * 0x00000CBA |
| 25 | * | 25 | * |
| 26 | * A: inversion | 26 | * A: inversion |
| 27 | * B: format mode | 27 | * B: format mode |
| 28 | * C: chip specific | ||
| 28 | */ | 29 | */ |
| 29 | 30 | ||
| 30 | /* A: clock inversion */ | 31 | /* A: clock inversion */ |
| @@ -39,6 +40,9 @@ | |||
| 39 | #define SH_FSI_FMT_DAI (0 << 4) | 40 | #define SH_FSI_FMT_DAI (0 << 4) |
| 40 | #define SH_FSI_FMT_SPDIF (1 << 4) | 41 | #define SH_FSI_FMT_SPDIF (1 << 4) |
| 41 | 42 | ||
| 43 | /* C: chip specific */ | ||
| 44 | #define SH_FSI_OPTION_MASK 0x00000F00 | ||
| 45 | #define SH_FSI_ENABLE_STREAM_MODE (1 << 8) /* for 16bit data */ | ||
| 42 | 46 | ||
| 43 | /* | 47 | /* |
| 44 | * set_rate return value | 48 | * set_rate return value |
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 22c686444633..1e1613a438dd 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig | |||
| @@ -46,6 +46,7 @@ config SND_SOC_ALL_CODECS | |||
| 46 | select SND_SOC_MAX9877 if I2C | 46 | select SND_SOC_MAX9877 if I2C |
| 47 | select SND_SOC_MC13783 if MFD_MC13XXX | 47 | select SND_SOC_MC13783 if MFD_MC13XXX |
| 48 | select SND_SOC_ML26124 if I2C | 48 | select SND_SOC_ML26124 if I2C |
| 49 | select SND_SOC_OMAP_HDMI_CODEC if OMAP4_DSS_HDMI | ||
| 49 | select SND_SOC_PCM3008 | 50 | select SND_SOC_PCM3008 |
| 50 | select SND_SOC_RT5631 if I2C | 51 | select SND_SOC_RT5631 if I2C |
| 51 | select SND_SOC_SGTL5000 if I2C | 52 | select SND_SOC_SGTL5000 if I2C |
| @@ -236,6 +237,9 @@ config SND_SOC_MAX98095 | |||
| 236 | config SND_SOC_MAX9850 | 237 | config SND_SOC_MAX9850 |
| 237 | tristate | 238 | tristate |
| 238 | 239 | ||
| 240 | config SND_SOC_OMAP_HDMI_CODEC | ||
| 241 | tristate | ||
| 242 | |||
| 239 | config SND_SOC_PCM3008 | 243 | config SND_SOC_PCM3008 |
| 240 | tristate | 244 | tristate |
| 241 | 245 | ||
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index a9663e9c375b..fc27fec39487 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile | |||
| @@ -33,6 +33,7 @@ snd-soc-max98095-objs := max98095.o | |||
| 33 | snd-soc-max9850-objs := max9850.o | 33 | snd-soc-max9850-objs := max9850.o |
| 34 | snd-soc-mc13783-objs := mc13783.o | 34 | snd-soc-mc13783-objs := mc13783.o |
| 35 | snd-soc-ml26124-objs := ml26124.o | 35 | snd-soc-ml26124-objs := ml26124.o |
| 36 | snd-soc-omap-hdmi-codec-objs := omap-hdmi.o | ||
| 36 | snd-soc-pcm3008-objs := pcm3008.o | 37 | snd-soc-pcm3008-objs := pcm3008.o |
| 37 | snd-soc-rt5631-objs := rt5631.o | 38 | snd-soc-rt5631-objs := rt5631.o |
| 38 | snd-soc-sgtl5000-objs := sgtl5000.o | 39 | snd-soc-sgtl5000-objs := sgtl5000.o |
| @@ -143,6 +144,7 @@ obj-$(CONFIG_SND_SOC_MAX98095) += snd-soc-max98095.o | |||
| 143 | obj-$(CONFIG_SND_SOC_MAX9850) += snd-soc-max9850.o | 144 | obj-$(CONFIG_SND_SOC_MAX9850) += snd-soc-max9850.o |
| 144 | obj-$(CONFIG_SND_SOC_MC13783) += snd-soc-mc13783.o | 145 | obj-$(CONFIG_SND_SOC_MC13783) += snd-soc-mc13783.o |
| 145 | obj-$(CONFIG_SND_SOC_ML26124) += snd-soc-ml26124.o | 146 | obj-$(CONFIG_SND_SOC_ML26124) += snd-soc-ml26124.o |
| 147 | obj-$(CONFIG_SND_SOC_OMAP_HDMI_CODEC) += snd-soc-omap-hdmi-codec.o | ||
| 146 | obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o | 148 | obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o |
| 147 | obj-$(CONFIG_SND_SOC_RT5631) += snd-soc-rt5631.o | 149 | obj-$(CONFIG_SND_SOC_RT5631) += snd-soc-rt5631.o |
| 148 | obj-$(CONFIG_SND_SOC_SGTL5000) += snd-soc-sgtl5000.o | 150 | obj-$(CONFIG_SND_SOC_SGTL5000) += snd-soc-sgtl5000.o |
diff --git a/sound/soc/codecs/omap-hdmi.c b/sound/soc/codecs/omap-hdmi.c new file mode 100644 index 000000000000..1bf5c74f5f96 --- /dev/null +++ b/sound/soc/codecs/omap-hdmi.c | |||
| @@ -0,0 +1,69 @@ | |||
| 1 | /* | ||
| 2 | * ALSA SoC codec driver for HDMI audio on OMAP processors. | ||
| 3 | * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ | ||
| 4 | * Author: Ricardo Neri <ricardo.neri@ti.com> | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or | ||
| 7 | * modify it under the terms of the GNU General Public License | ||
| 8 | * version 2 as published by the Free Software Foundation. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope that it will be useful, but | ||
| 11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU General Public License | ||
| 16 | * along with this program; if not, write to the Free Software | ||
| 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
| 18 | * 02110-1301 USA | ||
| 19 | * | ||
| 20 | */ | ||
| 21 | #include <linux/module.h> | ||
| 22 | #include <sound/soc.h> | ||
| 23 | |||
| 24 | #define DRV_NAME "hdmi-audio-codec" | ||
| 25 | |||
| 26 | static struct snd_soc_codec_driver omap_hdmi_codec; | ||
| 27 | |||
| 28 | static struct snd_soc_dai_driver omap_hdmi_codec_dai = { | ||
| 29 | .name = "omap-hdmi-hifi", | ||
| 30 | .playback = { | ||
| 31 | .channels_min = 2, | ||
| 32 | .channels_max = 8, | ||
| 33 | .rates = SNDRV_PCM_RATE_32000 | | ||
| 34 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | | ||
| 35 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | | ||
| 36 | SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000, | ||
| 37 | .formats = SNDRV_PCM_FMTBIT_S16_LE | | ||
| 38 | SNDRV_PCM_FMTBIT_S24_LE, | ||
| 39 | }, | ||
| 40 | }; | ||
| 41 | |||
| 42 | static __devinit int omap_hdmi_codec_probe(struct platform_device *pdev) | ||
| 43 | { | ||
| 44 | return snd_soc_register_codec(&pdev->dev, &omap_hdmi_codec, | ||
| 45 | &omap_hdmi_codec_dai, 1); | ||
| 46 | } | ||
| 47 | |||
| 48 | static __devexit int omap_hdmi_codec_remove(struct platform_device *pdev) | ||
| 49 | { | ||
| 50 | snd_soc_unregister_codec(&pdev->dev); | ||
| 51 | return 0; | ||
| 52 | } | ||
| 53 | |||
| 54 | static struct platform_driver omap_hdmi_codec_driver = { | ||
| 55 | .driver = { | ||
| 56 | .name = DRV_NAME, | ||
| 57 | .owner = THIS_MODULE, | ||
| 58 | }, | ||
| 59 | |||
| 60 | .probe = omap_hdmi_codec_probe, | ||
| 61 | .remove = __devexit_p(omap_hdmi_codec_remove), | ||
| 62 | }; | ||
| 63 | |||
| 64 | module_platform_driver(omap_hdmi_codec_driver); | ||
| 65 | |||
| 66 | MODULE_AUTHOR("Ricardo Neri <ricardo.neri@ti.com>"); | ||
| 67 | MODULE_DESCRIPTION("ASoC OMAP HDMI codec driver"); | ||
| 68 | MODULE_LICENSE("GPL"); | ||
| 69 | MODULE_ALIAS("platform:" DRV_NAME); | ||
diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig index deafbfaacdbf..9ccfa5e1c11b 100644 --- a/sound/soc/omap/Kconfig +++ b/sound/soc/omap/Kconfig | |||
| @@ -113,6 +113,7 @@ config SND_OMAP_SOC_OMAP4_HDMI | |||
| 113 | tristate "SoC Audio support for Texas Instruments OMAP4 HDMI" | 113 | tristate "SoC Audio support for Texas Instruments OMAP4 HDMI" |
| 114 | depends on SND_OMAP_SOC && OMAP4_DSS_HDMI && OMAP2_DSS && ARCH_OMAP4 | 114 | depends on SND_OMAP_SOC && OMAP4_DSS_HDMI && OMAP2_DSS && ARCH_OMAP4 |
| 115 | select SND_OMAP_SOC_HDMI | 115 | select SND_OMAP_SOC_HDMI |
| 116 | select SND_SOC_OMAP_HDMI_CODEC | ||
| 116 | help | 117 | help |
| 117 | Say Y if you want to add support for SoC HDMI audio on Texas Instruments | 118 | Say Y if you want to add support for SoC HDMI audio on Texas Instruments |
| 118 | OMAP4 chips | 119 | OMAP4 chips |
diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c index 74ed2dffbffd..7cee22515d9d 100644 --- a/sound/soc/sh/fsi.c +++ b/sound/soc/sh/fsi.c | |||
| @@ -132,6 +132,25 @@ | |||
| 132 | typedef int (*set_rate_func)(struct device *dev, int rate, int enable); | 132 | typedef int (*set_rate_func)(struct device *dev, int rate, int enable); |
| 133 | 133 | ||
| 134 | /* | 134 | /* |
| 135 | * bus options | ||
| 136 | * | ||
| 137 | * 0x000000BA | ||
| 138 | * | ||
| 139 | * A : sample widtht 16bit setting | ||
| 140 | * B : sample widtht 24bit setting | ||
| 141 | */ | ||
| 142 | |||
| 143 | #define SHIFT_16DATA 0 | ||
| 144 | #define SHIFT_24DATA 4 | ||
| 145 | |||
| 146 | #define PACKAGE_24BITBUS_BACK 0 | ||
| 147 | #define PACKAGE_24BITBUS_FRONT 1 | ||
| 148 | #define PACKAGE_16BITBUS_STREAM 2 | ||
| 149 | |||
| 150 | #define BUSOP_SET(s, a) ((a) << SHIFT_ ## s ## DATA) | ||
| 151 | #define BUSOP_GET(s, a) (((a) >> SHIFT_ ## s ## DATA) & 0xF) | ||
| 152 | |||
| 153 | /* | ||
| 135 | * FSI driver use below type name for variable | 154 | * FSI driver use below type name for variable |
| 136 | * | 155 | * |
| 137 | * xxx_num : number of data | 156 | * xxx_num : number of data |
| @@ -189,6 +208,11 @@ struct fsi_stream { | |||
| 189 | int oerr_num; | 208 | int oerr_num; |
| 190 | 209 | ||
| 191 | /* | 210 | /* |
| 211 | * bus options | ||
| 212 | */ | ||
| 213 | u32 bus_option; | ||
| 214 | |||
| 215 | /* | ||
| 192 | * thse are initialized by fsi_handler_init() | 216 | * thse are initialized by fsi_handler_init() |
| 193 | */ | 217 | */ |
| 194 | struct fsi_stream_handler *handler; | 218 | struct fsi_stream_handler *handler; |
| @@ -211,8 +235,7 @@ struct fsi_priv { | |||
| 211 | struct fsi_stream playback; | 235 | struct fsi_stream playback; |
| 212 | struct fsi_stream capture; | 236 | struct fsi_stream capture; |
| 213 | 237 | ||
| 214 | u32 do_fmt; | 238 | u32 fmt; |
| 215 | u32 di_fmt; | ||
| 216 | 239 | ||
| 217 | int chan_num:16; | 240 | int chan_num:16; |
| 218 | int clk_master:1; | 241 | int clk_master:1; |
| @@ -321,6 +344,10 @@ static void _fsi_master_mask_set(struct fsi_master *master, | |||
| 321 | /* | 344 | /* |
| 322 | * basic function | 345 | * basic function |
| 323 | */ | 346 | */ |
| 347 | static int fsi_version(struct fsi_master *master) | ||
| 348 | { | ||
| 349 | return master->core->ver; | ||
| 350 | } | ||
| 324 | 351 | ||
| 325 | static struct fsi_master *fsi_get_master(struct fsi_priv *fsi) | 352 | static struct fsi_master *fsi_get_master(struct fsi_priv *fsi) |
| 326 | { | 353 | { |
| @@ -495,6 +522,7 @@ static void fsi_stream_init(struct fsi_priv *fsi, | |||
| 495 | io->period_samples = fsi_frame2sample(fsi, runtime->period_size); | 522 | io->period_samples = fsi_frame2sample(fsi, runtime->period_size); |
| 496 | io->period_pos = 0; | 523 | io->period_pos = 0; |
| 497 | io->sample_width = samples_to_bytes(runtime, 1); | 524 | io->sample_width = samples_to_bytes(runtime, 1); |
| 525 | io->bus_option = 0; | ||
| 498 | io->oerr_num = -1; /* ignore 1st err */ | 526 | io->oerr_num = -1; /* ignore 1st err */ |
| 499 | io->uerr_num = -1; /* ignore 1st err */ | 527 | io->uerr_num = -1; /* ignore 1st err */ |
| 500 | fsi_stream_handler_call(io, init, fsi, io); | 528 | fsi_stream_handler_call(io, init, fsi, io); |
| @@ -522,6 +550,7 @@ static void fsi_stream_quit(struct fsi_priv *fsi, struct fsi_stream *io) | |||
| 522 | io->period_samples = 0; | 550 | io->period_samples = 0; |
| 523 | io->period_pos = 0; | 551 | io->period_pos = 0; |
| 524 | io->sample_width = 0; | 552 | io->sample_width = 0; |
| 553 | io->bus_option = 0; | ||
| 525 | io->oerr_num = 0; | 554 | io->oerr_num = 0; |
| 526 | io->uerr_num = 0; | 555 | io->uerr_num = 0; |
| 527 | spin_unlock_irqrestore(&master->lock, flags); | 556 | spin_unlock_irqrestore(&master->lock, flags); |
| @@ -581,6 +610,53 @@ static int fsi_stream_remove(struct fsi_priv *fsi) | |||
| 581 | } | 610 | } |
| 582 | 611 | ||
| 583 | /* | 612 | /* |
| 613 | * format/bus/dma setting | ||
| 614 | */ | ||
| 615 | static void fsi_format_bus_setup(struct fsi_priv *fsi, struct fsi_stream *io, | ||
| 616 | u32 bus, struct device *dev) | ||
| 617 | { | ||
| 618 | struct fsi_master *master = fsi_get_master(fsi); | ||
| 619 | int is_play = fsi_stream_is_play(fsi, io); | ||
| 620 | u32 fmt = fsi->fmt; | ||
| 621 | |||
| 622 | if (fsi_version(master) >= 2) { | ||
| 623 | u32 dma = 0; | ||
| 624 | |||
| 625 | /* | ||
| 626 | * FSI2 needs DMA/Bus setting | ||
| 627 | */ | ||
| 628 | switch (bus) { | ||
| 629 | case PACKAGE_24BITBUS_FRONT: | ||
| 630 | fmt |= CR_BWS_24; | ||
| 631 | dma |= VDMD_FRONT; | ||
| 632 | dev_dbg(dev, "24bit bus / package in front\n"); | ||
| 633 | break; | ||
| 634 | case PACKAGE_16BITBUS_STREAM: | ||
| 635 | fmt |= CR_BWS_16; | ||
| 636 | dma |= VDMD_STREAM; | ||
| 637 | dev_dbg(dev, "16bit bus / stream mode\n"); | ||
| 638 | break; | ||
| 639 | case PACKAGE_24BITBUS_BACK: | ||
| 640 | default: | ||
| 641 | fmt |= CR_BWS_24; | ||
| 642 | dma |= VDMD_BACK; | ||
| 643 | dev_dbg(dev, "24bit bus / package in back\n"); | ||
| 644 | break; | ||
| 645 | } | ||
| 646 | |||
| 647 | if (is_play) | ||
| 648 | fsi_reg_write(fsi, OUT_DMAC, dma); | ||
| 649 | else | ||
| 650 | fsi_reg_write(fsi, IN_DMAC, dma); | ||
| 651 | } | ||
| 652 | |||
| 653 | if (is_play) | ||
| 654 | fsi_reg_write(fsi, DO_FMT, fmt); | ||
| 655 | else | ||
| 656 | fsi_reg_write(fsi, DI_FMT, fmt); | ||
| 657 | } | ||
| 658 | |||
| 659 | /* | ||
| 584 | * irq function | 660 | * irq function |
| 585 | */ | 661 | */ |
| 586 | 662 | ||
| @@ -629,11 +705,6 @@ static void fsi_spdif_clk_ctrl(struct fsi_priv *fsi, int enable) | |||
| 629 | struct fsi_master *master = fsi_get_master(fsi); | 705 | struct fsi_master *master = fsi_get_master(fsi); |
| 630 | u32 mask, val; | 706 | u32 mask, val; |
| 631 | 707 | ||
| 632 | if (master->core->ver < 2) { | ||
| 633 | pr_err("fsi: register access err (%s)\n", __func__); | ||
| 634 | return; | ||
| 635 | } | ||
| 636 | |||
| 637 | mask = BP | SE; | 708 | mask = BP | SE; |
| 638 | val = enable ? mask : 0; | 709 | val = enable ? mask : 0; |
| 639 | 710 | ||
| @@ -648,9 +719,7 @@ static void fsi_spdif_clk_ctrl(struct fsi_priv *fsi, int enable) | |||
| 648 | static int fsi_set_master_clk(struct device *dev, struct fsi_priv *fsi, | 719 | static int fsi_set_master_clk(struct device *dev, struct fsi_priv *fsi, |
| 649 | long rate, int enable) | 720 | long rate, int enable) |
| 650 | { | 721 | { |
| 651 | struct fsi_master *master = fsi_get_master(fsi); | ||
| 652 | set_rate_func set_rate = fsi_get_info_set_rate(fsi); | 722 | set_rate_func set_rate = fsi_get_info_set_rate(fsi); |
| 653 | int fsi_ver = master->core->ver; | ||
| 654 | int ret; | 723 | int ret; |
| 655 | 724 | ||
| 656 | if (!set_rate) | 725 | if (!set_rate) |
| @@ -682,10 +751,7 @@ static int fsi_set_master_clk(struct device *dev, struct fsi_priv *fsi, | |||
| 682 | data |= (0x3 << 12); | 751 | data |= (0x3 << 12); |
| 683 | break; | 752 | break; |
| 684 | case SH_FSI_ACKMD_32: | 753 | case SH_FSI_ACKMD_32: |
| 685 | if (fsi_ver < 2) | 754 | data |= (0x4 << 12); |
| 686 | dev_err(dev, "unsupported ACKMD\n"); | ||
| 687 | else | ||
| 688 | data |= (0x4 << 12); | ||
| 689 | break; | 755 | break; |
| 690 | } | 756 | } |
| 691 | 757 | ||
| @@ -708,10 +774,7 @@ static int fsi_set_master_clk(struct device *dev, struct fsi_priv *fsi, | |||
| 708 | data |= (0x4 << 8); | 774 | data |= (0x4 << 8); |
| 709 | break; | 775 | break; |
| 710 | case SH_FSI_BPFMD_16: | 776 | case SH_FSI_BPFMD_16: |
| 711 | if (fsi_ver < 2) | 777 | data |= (0x7 << 8); |
| 712 | dev_err(dev, "unsupported ACKMD\n"); | ||
| 713 | else | ||
| 714 | data |= (0x7 << 8); | ||
| 715 | break; | 778 | break; |
| 716 | } | 779 | } |
| 717 | 780 | ||
| @@ -728,11 +791,26 @@ static int fsi_set_master_clk(struct device *dev, struct fsi_priv *fsi, | |||
| 728 | */ | 791 | */ |
| 729 | static void fsi_pio_push16(struct fsi_priv *fsi, u8 *_buf, int samples) | 792 | static void fsi_pio_push16(struct fsi_priv *fsi, u8 *_buf, int samples) |
| 730 | { | 793 | { |
| 731 | u16 *buf = (u16 *)_buf; | 794 | u32 enable_stream = fsi_get_info_flags(fsi) & SH_FSI_ENABLE_STREAM_MODE; |
| 732 | int i; | 795 | int i; |
| 733 | 796 | ||
| 734 | for (i = 0; i < samples; i++) | 797 | if (enable_stream) { |
| 735 | fsi_reg_write(fsi, DODT, ((u32)*(buf + i) << 8)); | 798 | /* |
| 799 | * stream mode | ||
| 800 | * see | ||
| 801 | * fsi_pio_push_init() | ||
| 802 | */ | ||
| 803 | u32 *buf = (u32 *)_buf; | ||
| 804 | |||
| 805 | for (i = 0; i < samples / 2; i++) | ||
| 806 | fsi_reg_write(fsi, DODT, buf[i]); | ||
| 807 | } else { | ||
| 808 | /* normal mode */ | ||
| 809 | u16 *buf = (u16 *)_buf; | ||
| 810 | |||
| 811 | for (i = 0; i < samples; i++) | ||
| 812 | fsi_reg_write(fsi, DODT, ((u32)*(buf + i) << 8)); | ||
| 813 | } | ||
| 736 | } | 814 | } |
| 737 | 815 | ||
| 738 | static void fsi_pio_pop16(struct fsi_priv *fsi, u8 *_buf, int samples) | 816 | static void fsi_pio_pop16(struct fsi_priv *fsi, u8 *_buf, int samples) |
| @@ -872,12 +950,44 @@ static void fsi_pio_start_stop(struct fsi_priv *fsi, struct fsi_stream *io, | |||
| 872 | fsi_master_mask_set(master, CLK_RST, clk, (enable) ? clk : 0); | 950 | fsi_master_mask_set(master, CLK_RST, clk, (enable) ? clk : 0); |
| 873 | } | 951 | } |
| 874 | 952 | ||
| 953 | static int fsi_pio_push_init(struct fsi_priv *fsi, struct fsi_stream *io) | ||
| 954 | { | ||
| 955 | u32 enable_stream = fsi_get_info_flags(fsi) & SH_FSI_ENABLE_STREAM_MODE; | ||
| 956 | |||
| 957 | /* | ||
| 958 | * we can use 16bit stream mode | ||
| 959 | * when "playback" and "16bit data" | ||
| 960 | * and platform allows "stream mode" | ||
| 961 | * see | ||
| 962 | * fsi_pio_push16() | ||
| 963 | */ | ||
| 964 | if (enable_stream) | ||
| 965 | io->bus_option = BUSOP_SET(24, PACKAGE_24BITBUS_BACK) | | ||
| 966 | BUSOP_SET(16, PACKAGE_16BITBUS_STREAM); | ||
| 967 | else | ||
| 968 | io->bus_option = BUSOP_SET(24, PACKAGE_24BITBUS_BACK) | | ||
| 969 | BUSOP_SET(16, PACKAGE_24BITBUS_BACK); | ||
| 970 | return 0; | ||
| 971 | } | ||
| 972 | |||
| 973 | static int fsi_pio_pop_init(struct fsi_priv *fsi, struct fsi_stream *io) | ||
| 974 | { | ||
| 975 | /* | ||
| 976 | * always 24bit bus, package back when "capture" | ||
| 977 | */ | ||
| 978 | io->bus_option = BUSOP_SET(24, PACKAGE_24BITBUS_BACK) | | ||
| 979 | BUSOP_SET(16, PACKAGE_24BITBUS_BACK); | ||
| 980 | return 0; | ||
| 981 | } | ||
| 982 | |||
| 875 | static struct fsi_stream_handler fsi_pio_push_handler = { | 983 | static struct fsi_stream_handler fsi_pio_push_handler = { |
| 984 | .init = fsi_pio_push_init, | ||
| 876 | .transfer = fsi_pio_push, | 985 | .transfer = fsi_pio_push, |
| 877 | .start_stop = fsi_pio_start_stop, | 986 | .start_stop = fsi_pio_start_stop, |
| 878 | }; | 987 | }; |
| 879 | 988 | ||
| 880 | static struct fsi_stream_handler fsi_pio_pop_handler = { | 989 | static struct fsi_stream_handler fsi_pio_pop_handler = { |
| 990 | .init = fsi_pio_pop_init, | ||
| 881 | .transfer = fsi_pio_pop, | 991 | .transfer = fsi_pio_pop, |
| 882 | .start_stop = fsi_pio_start_stop, | 992 | .start_stop = fsi_pio_start_stop, |
| 883 | }; | 993 | }; |
| @@ -919,6 +1029,13 @@ static int fsi_dma_init(struct fsi_priv *fsi, struct fsi_stream *io) | |||
| 919 | enum dma_data_direction dir = fsi_stream_is_play(fsi, io) ? | 1029 | enum dma_data_direction dir = fsi_stream_is_play(fsi, io) ? |
| 920 | DMA_TO_DEVICE : DMA_FROM_DEVICE; | 1030 | DMA_TO_DEVICE : DMA_FROM_DEVICE; |
| 921 | 1031 | ||
| 1032 | /* | ||
| 1033 | * 24bit data : 24bit bus / package in back | ||
| 1034 | * 16bit data : 16bit bus / stream mode | ||
| 1035 | */ | ||
| 1036 | io->bus_option = BUSOP_SET(24, PACKAGE_24BITBUS_BACK) | | ||
| 1037 | BUSOP_SET(16, PACKAGE_16BITBUS_STREAM); | ||
| 1038 | |||
| 922 | io->dma = dma_map_single(dai->dev, runtime->dma_area, | 1039 | io->dma = dma_map_single(dai->dev, runtime->dma_area, |
| 923 | snd_pcm_lib_buffer_bytes(io->substream), dir); | 1040 | snd_pcm_lib_buffer_bytes(io->substream), dir); |
| 924 | return 0; | 1041 | return 0; |
| @@ -1055,25 +1172,9 @@ static int fsi_dma_transfer(struct fsi_priv *fsi, struct fsi_stream *io) | |||
| 1055 | static void fsi_dma_push_start_stop(struct fsi_priv *fsi, struct fsi_stream *io, | 1172 | static void fsi_dma_push_start_stop(struct fsi_priv *fsi, struct fsi_stream *io, |
| 1056 | int start) | 1173 | int start) |
| 1057 | { | 1174 | { |
| 1058 | u32 bws; | 1175 | u32 enable = start ? DMA_ON : 0; |
| 1059 | u32 dma; | ||
| 1060 | 1176 | ||
| 1061 | switch (io->sample_width * start) { | 1177 | fsi_reg_mask_set(fsi, OUT_DMAC, DMA_ON, enable); |
| 1062 | case 2: | ||
| 1063 | bws = CR_BWS_16; | ||
| 1064 | dma = VDMD_STREAM | DMA_ON; | ||
| 1065 | break; | ||
| 1066 | case 4: | ||
| 1067 | bws = CR_BWS_24; | ||
| 1068 | dma = VDMD_BACK | DMA_ON; | ||
| 1069 | break; | ||
| 1070 | default: | ||
| 1071 | bws = 0; | ||
| 1072 | dma = 0; | ||
| 1073 | } | ||
| 1074 | |||
| 1075 | fsi_reg_mask_set(fsi, DO_FMT, CR_BWS_MASK, bws); | ||
| 1076 | fsi_reg_write(fsi, OUT_DMAC, dma); | ||
| 1077 | } | 1178 | } |
| 1078 | 1179 | ||
| 1079 | static int fsi_dma_probe(struct fsi_priv *fsi, struct fsi_stream *io) | 1180 | static int fsi_dma_probe(struct fsi_priv *fsi, struct fsi_stream *io) |
| @@ -1176,8 +1277,6 @@ static int fsi_hw_startup(struct fsi_priv *fsi, | |||
| 1176 | struct fsi_stream *io, | 1277 | struct fsi_stream *io, |
| 1177 | struct device *dev) | 1278 | struct device *dev) |
| 1178 | { | 1279 | { |
| 1179 | struct fsi_master *master = fsi_get_master(fsi); | ||
| 1180 | int fsi_ver = master->core->ver; | ||
| 1181 | u32 flags = fsi_get_info_flags(fsi); | 1280 | u32 flags = fsi_get_info_flags(fsi); |
| 1182 | u32 data = 0; | 1281 | u32 data = 0; |
| 1183 | 1282 | ||
| @@ -1200,10 +1299,6 @@ static int fsi_hw_startup(struct fsi_priv *fsi, | |||
| 1200 | 1299 | ||
| 1201 | fsi_reg_write(fsi, CKG2, data); | 1300 | fsi_reg_write(fsi, CKG2, data); |
| 1202 | 1301 | ||
| 1203 | /* set format */ | ||
| 1204 | fsi_reg_write(fsi, DO_FMT, fsi->do_fmt); | ||
| 1205 | fsi_reg_write(fsi, DI_FMT, fsi->di_fmt); | ||
| 1206 | |||
| 1207 | /* spdif ? */ | 1302 | /* spdif ? */ |
| 1208 | if (fsi_is_spdif(fsi)) { | 1303 | if (fsi_is_spdif(fsi)) { |
| 1209 | fsi_spdif_clk_ctrl(fsi, 1); | 1304 | fsi_spdif_clk_ctrl(fsi, 1); |
| @@ -1211,15 +1306,18 @@ static int fsi_hw_startup(struct fsi_priv *fsi, | |||
| 1211 | } | 1306 | } |
| 1212 | 1307 | ||
| 1213 | /* | 1308 | /* |
| 1214 | * FIXME | 1309 | * get bus settings |
| 1215 | * | ||
| 1216 | * FSI driver assumed that data package is in-back. | ||
| 1217 | * FSI2 chip can select it. | ||
| 1218 | */ | 1310 | */ |
| 1219 | if (fsi_ver >= 2) { | 1311 | data = 0; |
| 1220 | fsi_reg_write(fsi, OUT_DMAC, (1 << 4)); | 1312 | switch (io->sample_width) { |
| 1221 | fsi_reg_write(fsi, IN_DMAC, (1 << 4)); | 1313 | case 2: |
| 1314 | data = BUSOP_GET(16, io->bus_option); | ||
| 1315 | break; | ||
| 1316 | case 4: | ||
| 1317 | data = BUSOP_GET(24, io->bus_option); | ||
| 1318 | break; | ||
| 1222 | } | 1319 | } |
| 1320 | fsi_format_bus_setup(fsi, io, data, dev); | ||
| 1223 | 1321 | ||
| 1224 | /* irq clear */ | 1322 | /* irq clear */ |
| 1225 | fsi_irq_disable(fsi, io); | 1323 | fsi_irq_disable(fsi, io); |
| @@ -1243,7 +1341,9 @@ static int fsi_dai_startup(struct snd_pcm_substream *substream, | |||
| 1243 | { | 1341 | { |
| 1244 | struct fsi_priv *fsi = fsi_get_priv(substream); | 1342 | struct fsi_priv *fsi = fsi_get_priv(substream); |
| 1245 | 1343 | ||
| 1246 | return fsi_hw_startup(fsi, fsi_stream_get(fsi, substream), dai->dev); | 1344 | fsi->rate = 0; |
| 1345 | |||
| 1346 | return 0; | ||
| 1247 | } | 1347 | } |
| 1248 | 1348 | ||
| 1249 | static void fsi_dai_shutdown(struct snd_pcm_substream *substream, | 1349 | static void fsi_dai_shutdown(struct snd_pcm_substream *substream, |
| @@ -1251,7 +1351,6 @@ static void fsi_dai_shutdown(struct snd_pcm_substream *substream, | |||
| 1251 | { | 1351 | { |
| 1252 | struct fsi_priv *fsi = fsi_get_priv(substream); | 1352 | struct fsi_priv *fsi = fsi_get_priv(substream); |
| 1253 | 1353 | ||
| 1254 | fsi_hw_shutdown(fsi, dai->dev); | ||
| 1255 | fsi->rate = 0; | 1354 | fsi->rate = 0; |
| 1256 | } | 1355 | } |
| 1257 | 1356 | ||
| @@ -1265,11 +1364,13 @@ static int fsi_dai_trigger(struct snd_pcm_substream *substream, int cmd, | |||
| 1265 | switch (cmd) { | 1364 | switch (cmd) { |
| 1266 | case SNDRV_PCM_TRIGGER_START: | 1365 | case SNDRV_PCM_TRIGGER_START: |
| 1267 | fsi_stream_init(fsi, io, substream); | 1366 | fsi_stream_init(fsi, io, substream); |
| 1367 | fsi_hw_startup(fsi, io, dai->dev); | ||
| 1268 | ret = fsi_stream_transfer(io); | 1368 | ret = fsi_stream_transfer(io); |
| 1269 | if (0 == ret) | 1369 | if (0 == ret) |
| 1270 | fsi_stream_start(fsi, io); | 1370 | fsi_stream_start(fsi, io); |
| 1271 | break; | 1371 | break; |
| 1272 | case SNDRV_PCM_TRIGGER_STOP: | 1372 | case SNDRV_PCM_TRIGGER_STOP: |
| 1373 | fsi_hw_shutdown(fsi, dai->dev); | ||
| 1273 | fsi_stream_stop(fsi, io); | 1374 | fsi_stream_stop(fsi, io); |
| 1274 | fsi_stream_quit(fsi, io); | 1375 | fsi_stream_quit(fsi, io); |
| 1275 | break; | 1376 | break; |
| @@ -1280,42 +1381,33 @@ static int fsi_dai_trigger(struct snd_pcm_substream *substream, int cmd, | |||
| 1280 | 1381 | ||
| 1281 | static int fsi_set_fmt_dai(struct fsi_priv *fsi, unsigned int fmt) | 1382 | static int fsi_set_fmt_dai(struct fsi_priv *fsi, unsigned int fmt) |
| 1282 | { | 1383 | { |
| 1283 | u32 data = 0; | ||
| 1284 | |||
| 1285 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { | 1384 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { |
| 1286 | case SND_SOC_DAIFMT_I2S: | 1385 | case SND_SOC_DAIFMT_I2S: |
| 1287 | data = CR_I2S; | 1386 | fsi->fmt = CR_I2S; |
| 1288 | fsi->chan_num = 2; | 1387 | fsi->chan_num = 2; |
| 1289 | break; | 1388 | break; |
| 1290 | case SND_SOC_DAIFMT_LEFT_J: | 1389 | case SND_SOC_DAIFMT_LEFT_J: |
| 1291 | data = CR_PCM; | 1390 | fsi->fmt = CR_PCM; |
| 1292 | fsi->chan_num = 2; | 1391 | fsi->chan_num = 2; |
| 1293 | break; | 1392 | break; |
| 1294 | default: | 1393 | default: |
| 1295 | return -EINVAL; | 1394 | return -EINVAL; |
| 1296 | } | 1395 | } |
| 1297 | 1396 | ||
| 1298 | fsi->do_fmt = data; | ||
| 1299 | fsi->di_fmt = data; | ||
| 1300 | |||
| 1301 | return 0; | 1397 | return 0; |
| 1302 | } | 1398 | } |
| 1303 | 1399 | ||
| 1304 | static int fsi_set_fmt_spdif(struct fsi_priv *fsi) | 1400 | static int fsi_set_fmt_spdif(struct fsi_priv *fsi) |
| 1305 | { | 1401 | { |
| 1306 | struct fsi_master *master = fsi_get_master(fsi); | 1402 | struct fsi_master *master = fsi_get_master(fsi); |
| 1307 | u32 data = 0; | ||
| 1308 | 1403 | ||
| 1309 | if (master->core->ver < 2) | 1404 | if (fsi_version(master) < 2) |
| 1310 | return -EINVAL; | 1405 | return -EINVAL; |
| 1311 | 1406 | ||
| 1312 | data = CR_BWS_16 | CR_DTMD_SPDIF_PCM | CR_PCM; | 1407 | fsi->fmt = CR_DTMD_SPDIF_PCM | CR_PCM; |
| 1313 | fsi->chan_num = 2; | 1408 | fsi->chan_num = 2; |
| 1314 | fsi->spdif = 1; | 1409 | fsi->spdif = 1; |
| 1315 | 1410 | ||
| 1316 | fsi->do_fmt = data; | ||
| 1317 | fsi->di_fmt = data; | ||
| 1318 | |||
| 1319 | return 0; | 1411 | return 0; |
| 1320 | } | 1412 | } |
| 1321 | 1413 | ||
diff --git a/sound/soc/tegra/Kconfig b/sound/soc/tegra/Kconfig index 5331c61faf01..c1c8e955f4d3 100644 --- a/sound/soc/tegra/Kconfig +++ b/sound/soc/tegra/Kconfig | |||
| @@ -48,6 +48,16 @@ config SND_SOC_TEGRA30_I2S | |||
| 48 | Tegra30 I2S interface. You will also need to select the individual | 48 | Tegra30 I2S interface. You will also need to select the individual |
| 49 | machine drivers to support below. | 49 | machine drivers to support below. |
| 50 | 50 | ||
| 51 | config SND_SOC_TEGRA_WM8753 | ||
| 52 | tristate "SoC Audio support for Tegra boards using a WM8753 codec" | ||
| 53 | depends on SND_SOC_TEGRA && I2C | ||
| 54 | select SND_SOC_TEGRA20_I2S if ARCH_TEGRA_2x_SOC | ||
| 55 | select SND_SOC_TEGRA30_I2S if ARCH_TEGRA_3x_SOC | ||
| 56 | select SND_SOC_WM8753 | ||
| 57 | help | ||
| 58 | Say Y or M here if you want to add support for SoC audio on Tegra | ||
| 59 | boards using the WM8753 codec, such as Whistler. | ||
| 60 | |||
| 51 | config MACH_HAS_SND_SOC_TEGRA_WM8903 | 61 | config MACH_HAS_SND_SOC_TEGRA_WM8903 |
| 52 | bool | 62 | bool |
| 53 | help | 63 | help |
diff --git a/sound/soc/tegra/Makefile b/sound/soc/tegra/Makefile index 98704b48b62a..391e78a34c06 100644 --- a/sound/soc/tegra/Makefile +++ b/sound/soc/tegra/Makefile | |||
| @@ -16,10 +16,12 @@ obj-$(CONFIG_SND_SOC_TEGRA30_AHUB) += snd-soc-tegra30-ahub.o | |||
| 16 | obj-$(CONFIG_SND_SOC_TEGRA30_I2S) += snd-soc-tegra30-i2s.o | 16 | obj-$(CONFIG_SND_SOC_TEGRA30_I2S) += snd-soc-tegra30-i2s.o |
| 17 | 17 | ||
| 18 | # Tegra machine Support | 18 | # Tegra machine Support |
| 19 | snd-soc-tegra-wm8753-objs := tegra_wm8753.o | ||
| 19 | snd-soc-tegra-wm8903-objs := tegra_wm8903.o | 20 | snd-soc-tegra-wm8903-objs := tegra_wm8903.o |
| 20 | snd-soc-tegra-trimslice-objs := trimslice.o | 21 | snd-soc-tegra-trimslice-objs := trimslice.o |
| 21 | snd-soc-tegra-alc5632-objs := tegra_alc5632.o | 22 | snd-soc-tegra-alc5632-objs := tegra_alc5632.o |
| 22 | 23 | ||
| 24 | obj-$(CONFIG_SND_SOC_TEGRA_WM8753) += snd-soc-tegra-wm8753.o | ||
| 23 | obj-$(CONFIG_SND_SOC_TEGRA_WM8903) += snd-soc-tegra-wm8903.o | 25 | obj-$(CONFIG_SND_SOC_TEGRA_WM8903) += snd-soc-tegra-wm8903.o |
| 24 | obj-$(CONFIG_SND_SOC_TEGRA_TRIMSLICE) += snd-soc-tegra-trimslice.o | 26 | obj-$(CONFIG_SND_SOC_TEGRA_TRIMSLICE) += snd-soc-tegra-trimslice.o |
| 25 | obj-$(CONFIG_SND_SOC_TEGRA_ALC5632) += snd-soc-tegra-alc5632.o | 27 | obj-$(CONFIG_SND_SOC_TEGRA_ALC5632) += snd-soc-tegra-alc5632.o |
diff --git a/sound/soc/tegra/tegra_wm8753.c b/sound/soc/tegra/tegra_wm8753.c new file mode 100644 index 000000000000..4e77026807a2 --- /dev/null +++ b/sound/soc/tegra/tegra_wm8753.c | |||
| @@ -0,0 +1,224 @@ | |||
| 1 | /* | ||
| 2 | * tegra_wm8753.c - Tegra machine ASoC driver for boards using WM8753 codec. | ||
| 3 | * | ||
| 4 | * Author: Stephen Warren <swarren@nvidia.com> | ||
| 5 | * Copyright (C) 2010-2012 - NVIDIA, Inc. | ||
| 6 | * | ||
| 7 | * Based on code copyright/by: | ||
| 8 | * | ||
| 9 | * (c) 2009, 2010 Nvidia Graphics Pvt. Ltd. | ||
| 10 | * | ||
| 11 | * Copyright 2007 Wolfson Microelectronics PLC. | ||
| 12 | * Author: Graeme Gregory | ||
| 13 | * graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com | ||
| 14 | * | ||
| 15 | * This program is free software; you can redistribute it and/or | ||
| 16 | * modify it under the terms of the GNU General Public License | ||
| 17 | * version 2 as published by the Free Software Foundation. | ||
| 18 | * | ||
| 19 | * This program is distributed in the hope that it will be useful, but | ||
| 20 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 21 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 22 | * General Public License for more details. | ||
| 23 | * | ||
| 24 | * You should have received a copy of the GNU General Public License | ||
| 25 | * along with this program; if not, write to the Free Software | ||
| 26 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
| 27 | * 02110-1301 USA | ||
| 28 | * | ||
| 29 | */ | ||
| 30 | |||
| 31 | #include <asm/mach-types.h> | ||
| 32 | |||
| 33 | #include <linux/module.h> | ||
| 34 | #include <linux/platform_device.h> | ||
| 35 | #include <linux/slab.h> | ||
| 36 | #include <linux/gpio.h> | ||
| 37 | #include <linux/of_gpio.h> | ||
| 38 | |||
| 39 | #include <sound/core.h> | ||
| 40 | #include <sound/jack.h> | ||
| 41 | #include <sound/pcm.h> | ||
| 42 | #include <sound/pcm_params.h> | ||
| 43 | #include <sound/soc.h> | ||
| 44 | |||
| 45 | #include "../codecs/wm8753.h" | ||
| 46 | |||
| 47 | #include "tegra_asoc_utils.h" | ||
| 48 | |||
| 49 | #define DRV_NAME "tegra-snd-wm8753" | ||
| 50 | |||
| 51 | struct tegra_wm8753 { | ||
| 52 | struct tegra_asoc_utils_data util_data; | ||
| 53 | }; | ||
| 54 | |||
| 55 | static int tegra_wm8753_hw_params(struct snd_pcm_substream *substream, | ||
| 56 | struct snd_pcm_hw_params *params) | ||
| 57 | { | ||
| 58 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
| 59 | struct snd_soc_dai *codec_dai = rtd->codec_dai; | ||
| 60 | struct snd_soc_codec *codec = rtd->codec; | ||
| 61 | struct snd_soc_card *card = codec->card; | ||
| 62 | struct tegra_wm8753 *machine = snd_soc_card_get_drvdata(card); | ||
| 63 | int srate, mclk; | ||
| 64 | int err; | ||
| 65 | |||
| 66 | srate = params_rate(params); | ||
| 67 | switch (srate) { | ||
| 68 | case 11025: | ||
| 69 | case 22050: | ||
| 70 | case 44100: | ||
| 71 | case 88200: | ||
| 72 | mclk = 11289600; | ||
| 73 | break; | ||
| 74 | default: | ||
| 75 | mclk = 12288000; | ||
| 76 | break; | ||
| 77 | } | ||
| 78 | |||
| 79 | err = tegra_asoc_utils_set_rate(&machine->util_data, srate, mclk); | ||
| 80 | if (err < 0) { | ||
| 81 | dev_err(card->dev, "Can't configure clocks\n"); | ||
| 82 | return err; | ||
| 83 | } | ||
| 84 | |||
| 85 | err = snd_soc_dai_set_sysclk(codec_dai, WM8753_MCLK, mclk, | ||
| 86 | SND_SOC_CLOCK_IN); | ||
| 87 | if (err < 0) { | ||
| 88 | dev_err(card->dev, "codec_dai clock not set\n"); | ||
| 89 | return err; | ||
| 90 | } | ||
| 91 | |||
| 92 | return 0; | ||
| 93 | } | ||
| 94 | |||
| 95 | static struct snd_soc_ops tegra_wm8753_ops = { | ||
| 96 | .hw_params = tegra_wm8753_hw_params, | ||
| 97 | }; | ||
| 98 | |||
| 99 | static const struct snd_soc_dapm_widget tegra_wm8753_dapm_widgets[] = { | ||
| 100 | SND_SOC_DAPM_HP("Headphone Jack", NULL), | ||
| 101 | SND_SOC_DAPM_MIC("Mic Jack", NULL), | ||
| 102 | }; | ||
| 103 | |||
| 104 | static struct snd_soc_dai_link tegra_wm8753_dai = { | ||
| 105 | .name = "WM8753", | ||
| 106 | .stream_name = "WM8753 PCM", | ||
| 107 | .codec_dai_name = "wm8753-hifi", | ||
| 108 | .ops = &tegra_wm8753_ops, | ||
| 109 | .dai_fmt = SND_SOC_DAIFMT_I2S | | ||
| 110 | SND_SOC_DAIFMT_NB_NF | | ||
| 111 | SND_SOC_DAIFMT_CBS_CFS, | ||
| 112 | }; | ||
| 113 | |||
| 114 | static struct snd_soc_card snd_soc_tegra_wm8753 = { | ||
| 115 | .name = "tegra-wm8753", | ||
| 116 | .owner = THIS_MODULE, | ||
| 117 | .dai_link = &tegra_wm8753_dai, | ||
| 118 | .num_links = 1, | ||
| 119 | |||
| 120 | .dapm_widgets = tegra_wm8753_dapm_widgets, | ||
| 121 | .num_dapm_widgets = ARRAY_SIZE(tegra_wm8753_dapm_widgets), | ||
| 122 | .fully_routed = true, | ||
| 123 | }; | ||
| 124 | |||
| 125 | static __devinit int tegra_wm8753_driver_probe(struct platform_device *pdev) | ||
| 126 | { | ||
| 127 | struct snd_soc_card *card = &snd_soc_tegra_wm8753; | ||
| 128 | struct tegra_wm8753 *machine; | ||
| 129 | int ret; | ||
| 130 | |||
| 131 | machine = devm_kzalloc(&pdev->dev, sizeof(struct tegra_wm8753), | ||
| 132 | GFP_KERNEL); | ||
| 133 | if (!machine) { | ||
| 134 | dev_err(&pdev->dev, "Can't allocate tegra_wm8753 struct\n"); | ||
| 135 | ret = -ENOMEM; | ||
| 136 | goto err; | ||
| 137 | } | ||
| 138 | |||
| 139 | card->dev = &pdev->dev; | ||
| 140 | platform_set_drvdata(pdev, card); | ||
| 141 | snd_soc_card_set_drvdata(card, machine); | ||
| 142 | |||
| 143 | ret = snd_soc_of_parse_card_name(card, "nvidia,model"); | ||
| 144 | if (ret) | ||
| 145 | goto err; | ||
| 146 | |||
| 147 | ret = snd_soc_of_parse_audio_routing(card, "nvidia,audio-routing"); | ||
| 148 | if (ret) | ||
| 149 | goto err; | ||
| 150 | |||
| 151 | tegra_wm8753_dai.codec_of_node = of_parse_phandle( | ||
| 152 | pdev->dev.of_node, "nvidia,audio-codec", 0); | ||
| 153 | if (!tegra_wm8753_dai.codec_of_node) { | ||
| 154 | dev_err(&pdev->dev, | ||
| 155 | "Property 'nvidia,audio-codec' missing or invalid\n"); | ||
| 156 | ret = -EINVAL; | ||
| 157 | goto err; | ||
| 158 | } | ||
| 159 | |||
| 160 | tegra_wm8753_dai.cpu_dai_of_node = of_parse_phandle( | ||
| 161 | pdev->dev.of_node, "nvidia,i2s-controller", 0); | ||
| 162 | if (!tegra_wm8753_dai.cpu_dai_of_node) { | ||
| 163 | dev_err(&pdev->dev, | ||
| 164 | "Property 'nvidia,i2s-controller' missing or invalid\n"); | ||
| 165 | ret = -EINVAL; | ||
| 166 | goto err; | ||
| 167 | } | ||
| 168 | |||
| 169 | tegra_wm8753_dai.platform_of_node = | ||
| 170 | tegra_wm8753_dai.cpu_dai_of_node; | ||
| 171 | |||
| 172 | ret = tegra_asoc_utils_init(&machine->util_data, &pdev->dev); | ||
| 173 | if (ret) | ||
| 174 | goto err; | ||
| 175 | |||
| 176 | ret = snd_soc_register_card(card); | ||
| 177 | if (ret) { | ||
| 178 | dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", | ||
| 179 | ret); | ||
| 180 | goto err_fini_utils; | ||
| 181 | } | ||
| 182 | |||
| 183 | return 0; | ||
| 184 | |||
| 185 | err_fini_utils: | ||
| 186 | tegra_asoc_utils_fini(&machine->util_data); | ||
| 187 | err: | ||
| 188 | return ret; | ||
| 189 | } | ||
| 190 | |||
| 191 | static int __devexit tegra_wm8753_driver_remove(struct platform_device *pdev) | ||
| 192 | { | ||
| 193 | struct snd_soc_card *card = platform_get_drvdata(pdev); | ||
| 194 | struct tegra_wm8753 *machine = snd_soc_card_get_drvdata(card); | ||
| 195 | |||
| 196 | snd_soc_unregister_card(card); | ||
| 197 | |||
| 198 | tegra_asoc_utils_fini(&machine->util_data); | ||
| 199 | |||
| 200 | return 0; | ||
| 201 | } | ||
| 202 | |||
| 203 | static const struct of_device_id tegra_wm8753_of_match[] __devinitconst = { | ||
| 204 | { .compatible = "nvidia,tegra-audio-wm8753", }, | ||
| 205 | {}, | ||
| 206 | }; | ||
| 207 | |||
| 208 | static struct platform_driver tegra_wm8753_driver = { | ||
| 209 | .driver = { | ||
| 210 | .name = DRV_NAME, | ||
| 211 | .owner = THIS_MODULE, | ||
| 212 | .pm = &snd_soc_pm_ops, | ||
| 213 | .of_match_table = tegra_wm8753_of_match, | ||
| 214 | }, | ||
| 215 | .probe = tegra_wm8753_driver_probe, | ||
| 216 | .remove = __devexit_p(tegra_wm8753_driver_remove), | ||
| 217 | }; | ||
| 218 | module_platform_driver(tegra_wm8753_driver); | ||
| 219 | |||
| 220 | MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>"); | ||
| 221 | MODULE_DESCRIPTION("Tegra+WM8753 machine ASoC driver"); | ||
| 222 | MODULE_LICENSE("GPL"); | ||
| 223 | MODULE_ALIAS("platform:" DRV_NAME); | ||
| 224 | MODULE_DEVICE_TABLE(of, tegra_wm8753_of_match); | ||
