ASoC: Last minute updates These are all new code, they've been in -next already so should be OK for merge this time round. I'd been planning to send a pull request today after they'd had a bit of exposure there to make sure breakage didn't propagate into your tree.
Unified · Split
| @@ -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 | + | |
| @@ -21,10 +21,11 @@ · 20 unmodified lines | ||
| 21 | 21 | /* |
| 22 | 22 | * flags format |
| 23 | 23 | * |
| 24 | − * 0x000000BA | |
| 24 | + * 0x00000CBA | |
| 25 | 25 | * |
| 26 | 26 | * A: inversion |
| 27 | 27 | * B: format mode |
| 28 | + * C: chip specific | |
| 28 | 29 | */ |
| 29 | 30 | |
| 30 | 31 | /* A: clock inversion */ |
| @@ -39,6 +40,9 @@ · 8 unmodified lines | ||
| 39 | 40 | #define SH_FSI_FMT_DAI (0 << 4) |
| 40 | 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 | 48 | * set_rate return value |
| @@ -46,6 +46,7 @@ config SND_SOC_ALL_CODECS · 45 unmodified lines | ||
| 46 | 46 | select SND_SOC_MAX9877 if I2C |
| 47 | 47 | select SND_SOC_MC13783 if MFD_MC13XXX |
| 48 | 48 | select SND_SOC_ML26124 if I2C |
| 49 | + select SND_SOC_OMAP_HDMI_CODEC if OMAP4_DSS_HDMI | |
| 49 | 50 | select SND_SOC_PCM3008 |
| 50 | 51 | select SND_SOC_RT5631 if I2C |
| 51 | 52 | select SND_SOC_SGTL5000 if I2C |
| @@ -236,6 +237,9 @@ config SND_SOC_MAX98095 · 184 unmodified lines | ||
| 236 | 237 | config SND_SOC_MAX9850 |
| 237 | 238 | tristate |
| 238 | 239 | |
| 240 | +config SND_SOC_OMAP_HDMI_CODEC | |
| 241 | + tristate | |
| 242 | + | |
| 239 | 243 | config SND_SOC_PCM3008 |
| 240 | 244 | tristate |
| 241 | 245 | |
| @@ -33,6 +33,7 @@ snd-soc-max98095-objs := max98095.o · 32 unmodified lines | ||
| 33 | 33 | snd-soc-max9850-objs := max9850.o |
| 34 | 34 | snd-soc-mc13783-objs := mc13783.o |
| 35 | 35 | snd-soc-ml26124-objs := ml26124.o |
| 36 | +snd-soc-omap-hdmi-codec-objs := omap-hdmi.o | |
| 36 | 37 | snd-soc-pcm3008-objs := pcm3008.o |
| 37 | 38 | snd-soc-rt5631-objs := rt5631.o |
| 38 | 39 | snd-soc-sgtl5000-objs := sgtl5000.o |
| @@ -143,6 +144,7 @@ obj-$(CONFIG_SND_SOC_MAX98095) += snd-soc-max98095.o · 104 unmodified lines | ||
| 143 | 144 | obj-$(CONFIG_SND_SOC_MAX9850) += snd-soc-max9850.o |
| 144 | 145 | obj-$(CONFIG_SND_SOC_MC13783) += snd-soc-mc13783.o |
| 145 | 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 | 148 | obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o |
| 147 | 149 | obj-$(CONFIG_SND_SOC_RT5631) += snd-soc-rt5631.o |
| 148 | 150 | obj-$(CONFIG_SND_SOC_SGTL5000) += snd-soc-sgtl5000.o |
| @@ -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); | |
| @@ -113,6 +113,7 @@ config SND_OMAP_SOC_OMAP4_HDMI · 112 unmodified lines | ||
| 113 | 113 | tristate "SoC Audio support for Texas Instruments OMAP4 HDMI" |
| 114 | 114 | depends on SND_OMAP_SOC && OMAP4_DSS_HDMI && OMAP2_DSS && ARCH_OMAP4 |
| 115 | 115 | select SND_OMAP_SOC_HDMI |
| 116 | + select SND_SOC_OMAP_HDMI_CODEC | |
| 116 | 117 | help |
| 117 | 118 | Say Y if you want to add support for SoC HDMI audio on Texas Instruments |
| 118 | 119 | OMAP4 chips |
| @@ -131,6 +131,25 @@ · 130 unmodified lines | ||
| 131 | 131 | |
| 132 | 132 | typedef int (*set_rate_func)(struct device *dev, int rate, int enable); |
| 133 | 133 | |
| 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 | + | |
| 134 | 153 | /* |
| 135 | 154 | * FSI driver use below type name for variable |
| 136 | 155 | * |
| @@ -188,6 +207,11 @@ struct fsi_stream { · 51 unmodified lines | ||
| 188 | 207 | int uerr_num; |
| 189 | 208 | int oerr_num; |
| 190 | 209 | |
| 210 | + /* | |
| 211 | + * bus options | |
| 212 | + */ | |
| 213 | + u32 bus_option; | |
| 214 | + | |
| 191 | 215 | /* |
| 192 | 216 | * thse are initialized by fsi_handler_init() |
| 193 | 217 | */ |
| @@ -211,8 +235,7 @@ struct fsi_priv { · 17 unmodified lines | ||
| 211 | 235 | struct fsi_stream playback; |
| 212 | 236 | struct fsi_stream capture; |
| 213 | 237 | |
| 214 | − u32 do_fmt; | |
| 215 | − u32 di_fmt; | |
| 238 | + u32 fmt; | |
| 216 | 239 | |
| 217 | 240 | int chan_num:16; |
| 218 | 241 | int clk_master:1; |
| @@ -321,6 +344,10 @@ static void _fsi_master_mask_set(struct fsi_master *master, · 102 unmodified lines | ||
| 321 | 344 | /* |
| 322 | 345 | * basic function |
| 323 | 346 | */ |
| 347 | +static int fsi_version(struct fsi_master *master) | |
| 348 | +{ | |
| 349 | + return master->core->ver; | |
| 350 | +} | |
| 324 | 351 | |
| 325 | 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, · 168 unmodified lines | ||
| 495 | 522 | io->period_samples = fsi_frame2sample(fsi, runtime->period_size); |
| 496 | 523 | io->period_pos = 0; |
| 497 | 524 | io->sample_width = samples_to_bytes(runtime, 1); |
| 525 | + io->bus_option = 0; | |
| 498 | 526 | io->oerr_num = -1; /* ignore 1st err */ |
| 499 | 527 | io->uerr_num = -1; /* ignore 1st err */ |
| 500 | 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) · 21 unmodified lines | ||
| 522 | 550 | io->period_samples = 0; |
| 523 | 551 | io->period_pos = 0; |
| 524 | 552 | io->sample_width = 0; |
| 553 | + io->bus_option = 0; | |
| 525 | 554 | io->oerr_num = 0; |
| 526 | 555 | io->uerr_num = 0; |
| 527 | 556 | spin_unlock_irqrestore(&master->lock, flags); |
| @@ -580,6 +609,53 @@ static int fsi_stream_remove(struct fsi_priv *fsi) · 52 unmodified lines | ||
| 580 | 609 | return 0; |
| 581 | 610 | } |
| 582 | 611 | |
| 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 | + | |
| 583 | 659 | /* |
| 584 | 660 | * irq function |
| 585 | 661 | */ |
| @@ -629,11 +705,6 @@ static void fsi_spdif_clk_ctrl(struct fsi_priv *fsi, int enable) · 43 unmodified lines | ||
| 629 | 705 | struct fsi_master *master = fsi_get_master(fsi); |
| 630 | 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 | 708 | mask = BP | SE; |
| 638 | 709 | val = enable ? mask : 0; |
| 639 | 710 | |
| @@ -648,9 +719,7 @@ static void fsi_spdif_clk_ctrl(struct fsi_priv *fsi, int enable) · 8 unmodified lines | ||
| 648 | 719 | static int fsi_set_master_clk(struct device *dev, struct fsi_priv *fsi, |
| 649 | 720 | long rate, int enable) |
| 650 | 721 | { |
| 651 | − struct fsi_master *master = fsi_get_master(fsi); | |
| 652 | 722 | set_rate_func set_rate = fsi_get_info_set_rate(fsi); |
| 653 | − int fsi_ver = master->core->ver; | |
| 654 | 723 | int ret; |
| 655 | 724 | |
| 656 | 725 | if (!set_rate) |
| @@ -682,10 +751,7 @@ static int fsi_set_master_clk(struct device *dev, struct fsi_priv *fsi, · 25 unmodified lines | ||
| 682 | 751 | data |= (0x3 << 12); |
| 683 | 752 | break; |
| 684 | 753 | case SH_FSI_ACKMD_32: |
| 685 | − if (fsi_ver < 2) | |
| 686 | − dev_err(dev, "unsupported ACKMD\n"); | |
| 687 | − else | |
| 688 | − data |= (0x4 << 12); | |
| 754 | + data |= (0x4 << 12); | |
| 689 | 755 | break; |
| 690 | 756 | } |
| 691 | 757 | |
| @@ -708,10 +774,7 @@ static int fsi_set_master_clk(struct device *dev, struct fsi_priv *fsi, · 16 unmodified lines | ||
| 708 | 774 | data |= (0x4 << 8); |
| 709 | 775 | break; |
| 710 | 776 | case SH_FSI_BPFMD_16: |
| 711 | − if (fsi_ver < 2) | |
| 712 | − dev_err(dev, "unsupported ACKMD\n"); | |
| 713 | − else | |
| 714 | − data |= (0x7 << 8); | |
| 777 | + data |= (0x7 << 8); | |
| 715 | 778 | break; |
| 716 | 779 | } |
| 717 | 780 | |
| @@ -728,11 +791,26 @@ static int fsi_set_master_clk(struct device *dev, struct fsi_priv *fsi, · 10 unmodified lines | ||
| 728 | 791 | */ |
| 729 | 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 | 795 | int i; |
| 733 | 796 | |
| 734 | − for (i = 0; i < samples; i++) | |
| 735 | − fsi_reg_write(fsi, DODT, ((u32)*(buf + i) << 8)); | |
| 797 | + if (enable_stream) { | |
| 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 | 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, · 133 unmodified lines | ||
| 872 | 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 | 983 | static struct fsi_stream_handler fsi_pio_push_handler = { |
| 984 | + .init = fsi_pio_push_init, | |
| 876 | 985 | .transfer = fsi_pio_push, |
| 877 | 986 | .start_stop = fsi_pio_start_stop, |
| 878 | 987 | }; |
| 879 | 988 | |
| 880 | 989 | static struct fsi_stream_handler fsi_pio_pop_handler = { |
| 990 | + .init = fsi_pio_pop_init, | |
| 881 | 991 | .transfer = fsi_pio_pop, |
| 882 | 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) · 35 unmodified lines | ||
| 919 | 1029 | enum dma_data_direction dir = fsi_stream_is_play(fsi, io) ? |
| 920 | 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 | 1039 | io->dma = dma_map_single(dai->dev, runtime->dma_area, |
| 923 | 1040 | snd_pcm_lib_buffer_bytes(io->substream), dir); |
| 924 | 1041 | return 0; |
| @@ -1055,25 +1172,9 @@ static int fsi_dma_transfer(struct fsi_priv *fsi, struct fsi_stream *io) · 130 unmodified lines | ||
| 1055 | 1172 | static void fsi_dma_push_start_stop(struct fsi_priv *fsi, struct fsi_stream *io, |
| 1056 | 1173 | int start) |
| 1057 | 1174 | { |
| 1058 | − u32 bws; | |
| 1059 | − u32 dma; | |
| 1175 | + u32 enable = start ? DMA_ON : 0; | |
| 1060 | 1176 | |
| 1061 | − switch (io->sample_width * start) { | |
| 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); | |
| 1177 | + fsi_reg_mask_set(fsi, OUT_DMAC, DMA_ON, enable); | |
| 1077 | 1178 | } |
| 1078 | 1179 | |
| 1079 | 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, · 96 unmodified lines | ||
| 1176 | 1277 | struct fsi_stream *io, |
| 1177 | 1278 | struct device *dev) |
| 1178 | 1279 | { |
| 1179 | − struct fsi_master *master = fsi_get_master(fsi); | |
| 1180 | − int fsi_ver = master->core->ver; | |
| 1181 | 1280 | u32 flags = fsi_get_info_flags(fsi); |
| 1182 | 1281 | u32 data = 0; |
| 1183 | 1282 | |
| @@ -1200,10 +1299,6 @@ static int fsi_hw_startup(struct fsi_priv *fsi, · 16 unmodified lines | ||
| 1200 | 1299 | |
| 1201 | 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 | 1302 | /* spdif ? */ |
| 1208 | 1303 | if (fsi_is_spdif(fsi)) { |
| 1209 | 1304 | fsi_spdif_clk_ctrl(fsi, 1); |
| @@ -1211,15 +1306,18 @@ static int fsi_hw_startup(struct fsi_priv *fsi, · 1 unmodified line | ||
| 1211 | 1306 | } |
| 1212 | 1307 | |
| 1213 | 1308 | /* |
| 1214 | − * FIXME | |
| 1215 | − * | |
| 1216 | − * FSI driver assumed that data package is in-back. | |
| 1217 | − * FSI2 chip can select it. | |
| 1309 | + * get bus settings | |
| 1218 | 1310 | */ |
| 1219 | − if (fsi_ver >= 2) { | |
| 1220 | − fsi_reg_write(fsi, OUT_DMAC, (1 << 4)); | |
| 1221 | − fsi_reg_write(fsi, IN_DMAC, (1 << 4)); | |
| 1311 | + data = 0; | |
| 1312 | + switch (io->sample_width) { | |
| 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 | 1322 | /* irq clear */ |
| 1225 | 1323 | fsi_irq_disable(fsi, io); |
| @@ -1243,7 +1341,9 @@ static int fsi_dai_startup(struct snd_pcm_substream *substream, · 17 unmodified lines | ||
| 1243 | 1341 | { |
| 1244 | 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 | 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, · 1 unmodified line | ||
| 1251 | 1351 | { |
| 1252 | 1352 | struct fsi_priv *fsi = fsi_get_priv(substream); |
| 1253 | 1353 | |
| 1254 | − fsi_hw_shutdown(fsi, dai->dev); | |
| 1255 | 1354 | fsi->rate = 0; |
| 1256 | 1355 | } |
| 1257 | 1356 | |
| @@ -1265,11 +1364,13 @@ static int fsi_dai_trigger(struct snd_pcm_substream *substream, int cmd, · 7 unmodified lines | ||
| 1265 | 1364 | switch (cmd) { |
| 1266 | 1365 | case SNDRV_PCM_TRIGGER_START: |
| 1267 | 1366 | fsi_stream_init(fsi, io, substream); |
| 1367 | + fsi_hw_startup(fsi, io, dai->dev); | |
| 1268 | 1368 | ret = fsi_stream_transfer(io); |
| 1269 | 1369 | if (0 == ret) |
| 1270 | 1370 | fsi_stream_start(fsi, io); |
| 1271 | 1371 | break; |
| 1272 | 1372 | case SNDRV_PCM_TRIGGER_STOP: |
| 1373 | + fsi_hw_shutdown(fsi, dai->dev); | |
| 1273 | 1374 | fsi_stream_stop(fsi, io); |
| 1274 | 1375 | fsi_stream_quit(fsi, io); |
| 1275 | 1376 | break; |
| @@ -1280,42 +1381,33 @@ static int fsi_dai_trigger(struct snd_pcm_substream *substream, int cmd, · 4 unmodified lines | ||
| 1280 | 1381 | |
| 1281 | 1382 | static int fsi_set_fmt_dai(struct fsi_priv *fsi, unsigned int fmt) |
| 1282 | 1383 | { |
| 1283 | − u32 data = 0; | |
| 1284 | − | |
| 1285 | 1384 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { |
| 1286 | 1385 | case SND_SOC_DAIFMT_I2S: |
| 1287 | − data = CR_I2S; | |
| 1386 | + fsi->fmt = CR_I2S; | |
| 1288 | 1387 | fsi->chan_num = 2; |
| 1289 | 1388 | break; |
| 1290 | 1389 | case SND_SOC_DAIFMT_LEFT_J: |
| 1291 | − data = CR_PCM; | |
| 1390 | + fsi->fmt = CR_PCM; | |
| 1292 | 1391 | fsi->chan_num = 2; |
| 1293 | 1392 | break; |
| 1294 | 1393 | default: |
| 1295 | 1394 | return -EINVAL; |
| 1296 | 1395 | } |
| 1297 | 1396 | |
| 1298 | − fsi->do_fmt = data; | |
| 1299 | − fsi->di_fmt = data; | |
| 1300 | − | |
| 1301 | 1397 | return 0; |
| 1302 | 1398 | } |
| 1303 | 1399 | |
| 1304 | 1400 | static int fsi_set_fmt_spdif(struct fsi_priv *fsi) |
| 1305 | 1401 | { |
| 1306 | 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 | 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 | 1408 | fsi->chan_num = 2; |
| 1314 | 1409 | fsi->spdif = 1; |
| 1315 | 1410 | |
| 1316 | − fsi->do_fmt = data; | |
| 1317 | − fsi->di_fmt = data; | |
| 1318 | − | |
| 1319 | 1411 | return 0; |
| 1320 | 1412 | } |
| 1321 | 1413 | |
| @@ -48,6 +48,16 @@ config SND_SOC_TEGRA30_I2S · 47 unmodified lines | ||
| 48 | 48 | Tegra30 I2S interface. You will also need to select the individual |
| 49 | 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 | 61 | config MACH_HAS_SND_SOC_TEGRA_WM8903 |
| 52 | 62 | bool |
| 53 | 63 | help |
| @@ -16,10 +16,12 @@ obj-$(CONFIG_SND_SOC_TEGRA30_AHUB) += snd-soc-tegra30-ahub.o · 15 unmodified lines | ||
| 16 | 16 | obj-$(CONFIG_SND_SOC_TEGRA30_I2S) += snd-soc-tegra30-i2s.o |
| 17 | 17 | |
| 18 | 18 | # Tegra machine Support |
| 19 | +snd-soc-tegra-wm8753-objs := tegra_wm8753.o | |
| 19 | 20 | snd-soc-tegra-wm8903-objs := tegra_wm8903.o |
| 20 | 21 | snd-soc-tegra-trimslice-objs := trimslice.o |
| 21 | 22 | snd-soc-tegra-alc5632-objs := tegra_alc5632.o |
| 22 | 23 | |
| 24 | +obj-$(CONFIG_SND_SOC_TEGRA_WM8753) += snd-soc-tegra-wm8753.o | |
| 23 | 25 | obj-$(CONFIG_SND_SOC_TEGRA_WM8903) += snd-soc-tegra-wm8903.o |
| 24 | 26 | obj-$(CONFIG_SND_SOC_TEGRA_TRIMSLICE) += snd-soc-tegra-trimslice.o |
| 25 | 27 | obj-$(CONFIG_SND_SOC_TEGRA_ALC5632) += snd-soc-tegra-alc5632.o |
| @@ -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); | |