Merge tag 'asoc-v4.3-rc2' into asoc-next
authorMark Brown <broonie@kernel.org>
Mon, 26 Oct 2015 02:15:54 +0000 (11:15 +0900)
committerMark Brown <broonie@kernel.org>
Mon, 26 Oct 2015 02:15:54 +0000 (11:15 +0900)
ASoC: Updates for v4.4

A first batch of updates targetted at v4.4.  There are no substantial
core fixes here, the biggest block of changes is updates to the rcar
drivers and the addition of a CODEC driver for the AK4613.

# gpg: Signature made Fri 25 Sep 2015 05:37:06 KST using RSA key ID 5D5487D0
# gpg: key CD7BEEBC: no public key for trusted key - skipped
# gpg: key CD7BEEBC marked as ultimately trusted
# gpg: key AF88CD16: no public key for trusted key - skipped
# gpg: key AF88CD16 marked as ultimately trusted
# gpg: key 16005C11: no public key for trusted key - skipped
# gpg: key 16005C11 marked as ultimately trusted
# gpg: key 5621E907: no public key for trusted key - skipped
# gpg: key 5621E907 marked as ultimately trusted
# gpg: key 5C6153AD: no public key for trusted key - skipped
# gpg: key 5C6153AD marked as ultimately trusted
# gpg: Good signature from "Mark Brown <broonie@sirena.org.uk>"
# gpg:                 aka "Mark Brown <broonie@debian.org>"
# gpg:                 aka "Mark Brown <broonie@kernel.org>"
# gpg:                 aka "Mark Brown <broonie@tardis.ed.ac.uk>"
# gpg:                 aka "Mark Brown <broonie@linaro.org>"
# gpg:                 aka "Mark Brown <Mark.Brown@linaro.org>"

76 files changed:
Documentation/devicetree/bindings/sound/ak4613.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/ak4642.txt
Documentation/devicetree/bindings/sound/renesas,rsnd.txt
Documentation/devicetree/bindings/sound/sun4i-codec.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/tdm-slot.txt
drivers/spi/spi-atmel.c
include/sound/rcar_snd.h
include/sound/rt5645.h
include/sound/simple_card.h
include/sound/soc.h
sound/soc/Kconfig
sound/soc/Makefile
sound/soc/atmel/atmel_wm8904.c
sound/soc/au1x/db1000.c
sound/soc/au1x/db1200.c
sound/soc/blackfin/bf5xx-ad1836.c
sound/soc/blackfin/bfin-eval-adau1373.c
sound/soc/blackfin/bfin-eval-adau1701.c
sound/soc/blackfin/bfin-eval-adav80x.c
sound/soc/codecs/Kconfig
sound/soc/codecs/Makefile
sound/soc/codecs/ak4613.c [new file with mode: 0644]
sound/soc/codecs/ak4642.c
sound/soc/codecs/arizona.c
sound/soc/codecs/arizona.h
sound/soc/codecs/hdmi.c [deleted file]
sound/soc/codecs/rt5645.c
sound/soc/codecs/rt5645.h
sound/soc/codecs/tlv320aic3x.c
sound/soc/codecs/wm5110.c
sound/soc/davinci/davinci-mcasp.c
sound/soc/fsl/fsl-asoc-card.c
sound/soc/fsl/fsl_sai.c
sound/soc/generic/simple-card.c
sound/soc/intel/atom/sst-mfld-platform-pcm.c
sound/soc/intel/boards/broadwell.c
sound/soc/intel/skylake/skl-pcm.c
sound/soc/jz4740/jz4740-i2s.c
sound/soc/kirkwood/armada-370-db.c
sound/soc/mediatek/mt8173-max98090.c
sound/soc/mediatek/mt8173-rt5650-rt5676.c
sound/soc/mxs/mxs-sgtl5000.c
sound/soc/pxa/brownstone.c
sound/soc/pxa/corgi.c
sound/soc/pxa/e740_wm9705.c
sound/soc/pxa/e750_wm9705.c
sound/soc/pxa/e800_wm9712.c
sound/soc/pxa/hx4700.c
sound/soc/pxa/imote2.c
sound/soc/pxa/mioa701_wm9713.c
sound/soc/pxa/palm27x.c
sound/soc/pxa/poodle.c
sound/soc/pxa/pxa-ssp.c
sound/soc/pxa/pxa2xx-pcm.c
sound/soc/pxa/spitz.c
sound/soc/pxa/tosa.c
sound/soc/pxa/ttc-dkb.c
sound/soc/qcom/lpass-cpu.c
sound/soc/rockchip/Kconfig
sound/soc/sh/Kconfig
sound/soc/sh/rcar/adg.c
sound/soc/sh/rcar/core.c
sound/soc/sh/rcar/ctu.c
sound/soc/sh/rcar/dvc.c
sound/soc/sh/rcar/mix.c
sound/soc/sh/rcar/rsnd.h
sound/soc/sh/rcar/src.c
sound/soc/sh/rcar/ssi.c
sound/soc/sh/siu_dai.c
sound/soc/soc-core.c
sound/soc/soc-pcm.c
sound/soc/sunxi/Kconfig [new file with mode: 0644]
sound/soc/sunxi/Makefile [new file with mode: 0644]
sound/soc/sunxi/sun4i-codec.c [new file with mode: 0644]
sound/soc/ux500/mop500.c
sound/soc/ux500/ux500_msp_dai.c

diff --git a/Documentation/devicetree/bindings/sound/ak4613.txt b/Documentation/devicetree/bindings/sound/ak4613.txt
new file mode 100644 (file)
index 0000000..15a9195
--- /dev/null
@@ -0,0 +1,17 @@
+AK4613 I2C transmitter
+
+This device supports I2C mode only.
+
+Required properties:
+
+- compatible : "asahi-kasei,ak4613"
+- reg : The chip select number on the I2C bus
+
+Example:
+
+&i2c {
+       ak4613: ak4613@0x10 {
+               compatible = "asahi-kasei,ak4613";
+               reg = <0x10>;
+       };
+};
index 623d4e70ae11d9ef5ee6231c09f0e8293fb32680..340784db6808809eecf4ae0e310023550713e2c6 100644 (file)
@@ -7,7 +7,14 @@ Required properties:
   - compatible : "asahi-kasei,ak4642" or "asahi-kasei,ak4643" or "asahi-kasei,ak4648"
   - reg : The chip select number on the I2C bus
 
-Example:
+Optional properties:
+
+  - #clock-cells :             common clock binding; shall be set to 0
+  - clocks :                   common clock binding; MCKI clock
+  - clock-frequency :          common clock binding; frequency of MCKO
+  - clock-output-names :       common clock binding; MCKO clock name
+
+Example 1:
 
 &i2c {
        ak4648: ak4648@0x12 {
@@ -15,3 +22,16 @@ Example:
                reg = <0x12>;
        };
 };
+
+Example 2:
+
+&i2c {
+       ak4643: codec@12 {
+               compatible = "asahi-kasei,ak4643";
+               reg = <0x12>;
+               #clock-cells = <0>;
+               clocks = <&audio_clock>;
+               clock-frequency = <12288000>;
+               clock-output-names = "ak4643_mcko";
+       };
+};
index 1173395b5e5c16c070885b06ae06100eb0aa7ff2..c57cbd65736cfaf4bd67c0801fcd86dca19ef106 100644 (file)
@@ -4,10 +4,12 @@ Required properties:
 - compatible                   : "renesas,rcar_sound-<soctype>", fallbacks
                                  "renesas,rcar_sound-gen1" if generation1, and
                                  "renesas,rcar_sound-gen2" if generation2
+                                 "renesas,rcar_sound-gen3" if generation3
                                  Examples with soctypes are:
                                    - "renesas,rcar_sound-r8a7778" (R-Car M1A)
                                    - "renesas,rcar_sound-r8a7790" (R-Car H2)
                                    - "renesas,rcar_sound-r8a7791" (R-Car M2-W)
+                                   - "renesas,rcar_sound-r8a7795" (R-Car H3)
 - reg                          : Should contain the register physical address.
                                  required register is
                                   SRU/ADG/SSI      if generation1
@@ -30,6 +32,11 @@ Required properties:
 - rcar_sound,dai               : DAI contents.
                                  The number of DAI subnode should be same as HW.
                                  see below for detail.
+- #sound-dai-cells             : it must be 0 if your system is using single DAI
+                                 it must be 1 if your system is using multi  DAI
+- #clock-cells                 : it must be 0 if your system has audio_clkout
+                                 it must be 1 if your system has audio_clkout0/1/2/3
+- clock-frequency              : for all audio_clkout0/1/2/3
 
 SSI subnode properties:
 - interrupts                   : Should contain SSI interrupt for PIO transfer
diff --git a/Documentation/devicetree/bindings/sound/sun4i-codec.txt b/Documentation/devicetree/bindings/sound/sun4i-codec.txt
new file mode 100644 (file)
index 0000000..680144b
--- /dev/null
@@ -0,0 +1,33 @@
+* Allwinner A10 Codec
+
+Required properties:
+- compatible: must be either "allwinner,sun4i-a10-codec" or
+  "allwinner,sun7i-a20-codec"
+- reg: must contain the registers location and length
+- interrupts: must contain the codec interrupt
+- dmas: DMA channels for tx and rx dma. See the DMA client binding,
+       Documentation/devicetree/bindings/dma/dma.txt
+- dma-names: should include "tx" and "rx".
+- clocks: a list of phandle + clock-specifer pairs, one for each entry
+  in clock-names.
+- clock-names: should contain followings:
+   - "apb": the parent APB clock for this controller
+   - "codec": the parent module clock
+- routing : A list of the connections between audio components.  Each
+  entry is a pair of strings, the first being the connection's sink,
+  the second being the connection's source.
+
+
+Example:
+codec: codec@01c22c00 {
+       #sound-dai-cells = <0>;
+       compatible = "allwinner,sun7i-a20-codec";
+       reg = <0x01c22c00 0x40>;
+       interrupts = <0 30 4>;
+       clocks = <&apb0_gates 0>, <&codec_clk>;
+       clock-names = "apb", "codec";
+       dmas = <&dma 0 19>, <&dma 0 19>;
+       dma-names = "rx", "tx";
+       routing = "Headphone Jack", "HP Right",
+                 "Headphone Jack", "HP Left";
+};
index 6a2c84247f91520a4e7bd2fc90e232e8a9e6cb05..34cf70e2cbc4720510cf03e05eba6f3bcb6b2cae 100644 (file)
@@ -4,11 +4,15 @@ This specifies audio DAI's TDM slot.
 
 TDM slot properties:
 dai-tdm-slot-num : Number of slots in use.
-dai-tdm-slot-width :  Width in bits for each slot.
+dai-tdm-slot-width : Width in bits for each slot.
+dai-tdm-slot-tx-mask : Transmit direction slot mask, optional
+dai-tdm-slot-rx-mask : Receive direction slot mask, optional
 
 For instance:
        dai-tdm-slot-num = <2>;
        dai-tdm-slot-width = <8>;
+       dai-tdm-slot-tx-mask = <0 1>;
+       dai-tdm-slot-rx-mask = <1 0>;
 
 And for each spcified driver, there could be one .of_xlate_tdm_slot_mask()
 to specify a explicit mapping of the channels and the slots. If it's absent
@@ -18,3 +22,8 @@ tx and rx masks.
 For snd_soc_of_xlate_tdm_slot_mask(), the tx and rx masks will use a 1 bit
 for an active slot as default, and the default active bits are at the LSB of
 the masks.
+
+The explicit masks are given as array of integers, where the first
+number presents bit-0 (LSB), second presents bit-1, etc. Any non zero
+number is considered 1 and 0 is 0. snd_soc_of_xlate_tdm_slot_mask()
+does not do anything, if either mask is set non zero value.
index 63318e2afba1900bc3923d3b4023143699a4842e..41e37a6a1368b273e19fd8eec798f2458e6ae1e4 100644 (file)
@@ -871,14 +871,7 @@ static int atmel_spi_set_xfer_speed(struct atmel_spi *as,
         * Calculate the lowest divider that satisfies the
         * constraint, assuming div32/fdiv/mbz == 0.
         */
-       if (xfer->speed_hz)
-               scbr = DIV_ROUND_UP(bus_hz, xfer->speed_hz);
-       else
-               /*
-                * This can happend if max_speed is null.
-                * In this case, we set the lowest possible speed
-                */
-               scbr = 0xff;
+       scbr = DIV_ROUND_UP(bus_hz, xfer->speed_hz);
 
        /*
         * If the resulting divider doesn't fit into the
@@ -1300,14 +1293,12 @@ static int atmel_spi_one_transfer(struct spi_master *master,
                return -EINVAL;
        }
 
-       if (xfer->bits_per_word) {
-               asd = spi->controller_state;
-               bits = (asd->csr >> 4) & 0xf;
-               if (bits != xfer->bits_per_word - 8) {
-                       dev_dbg(&spi->dev,
+       asd = spi->controller_state;
+       bits = (asd->csr >> 4) & 0xf;
+       if (bits != xfer->bits_per_word - 8) {
+               dev_dbg(&spi->dev,
                        "you can't yet change bits_per_word in transfers\n");
-                       return -ENOPROTOOPT;
-               }
+               return -ENOPROTOOPT;
        }
 
        /*
index bb7b2ebfee7b9380e66f793cf5c6526e1aaf937f..d8e33d38da4334470a2468993f209afe0e78eb38 100644 (file)
@@ -12,7 +12,6 @@
 #ifndef RCAR_SND_H
 #define RCAR_SND_H
 
-#include <linux/sh_clk.h>
 
 #define RSND_GEN1_SRU  0
 #define RSND_GEN1_ADG  1
index 22734bc3ffd4a2e72ec1a1979ec1adf7df8014c6..a5cf6152e778929d572250462b2252a4369b08ca 100644 (file)
@@ -21,6 +21,8 @@ struct rt5645_platform_data {
        /* 0 = IN2P; 1 = GPIO6; 2 = GPIO10; 3 = GPIO12 */
 
        unsigned int jd_mode;
+       /* Invert JD when jack insert */
+       bool jd_invert;
 };
 
 #endif
index b9b4f289fe6b32130848cf4062ac94e4864f5bfb..0399352f3a6243569c87d3691fd664480749802c 100644 (file)
@@ -19,6 +19,8 @@ struct asoc_simple_dai {
        unsigned int sysclk;
        int slots;
        int slot_width;
+       unsigned int tx_slot_mask;
+       unsigned int rx_slot_mask;
        struct clk *clk;
 };
 
index 26ede14597daba32f8bf2cfcc84a821d094cc3d3..824ee7b91734fe7353e1628f635aa30dd3a5cf8f 100644 (file)
        .info = snd_soc_info_volsw, \
        .get = xhandler_get, .put = xhandler_put, \
        .private_value = SOC_SINGLE_VALUE(xreg, xshift, xmax, xinvert, 0) }
+#define SOC_SINGLE_RANGE_EXT_TLV(xname, xreg, xshift, xmin, xmax, xinvert, \
+                                xhandler_get, xhandler_put, tlv_array) \
+{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\
+       .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\
+                SNDRV_CTL_ELEM_ACCESS_READWRITE,\
+       .tlv.p = (tlv_array), \
+       .info = snd_soc_info_volsw_range, \
+       .get = xhandler_get, .put = xhandler_put, \
+       .private_value = (unsigned long)&(struct soc_mixer_control) \
+               {.reg = xreg, .rreg = xreg, .shift = xshift, \
+                .rshift = xshift, .min = xmin, .max = xmax, \
+                .platform_max = xmax, .invert = xinvert} }
 #define SOC_DOUBLE_EXT_TLV(xname, xreg, shift_left, shift_right, xmax, xinvert,\
         xhandler_get, xhandler_put, tlv_array) \
 {      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
@@ -1603,6 +1615,8 @@ int snd_soc_of_parse_card_name(struct snd_soc_card *card,
 int snd_soc_of_parse_audio_simple_widgets(struct snd_soc_card *card,
                                          const char *propname);
 int snd_soc_of_parse_tdm_slot(struct device_node *np,
+                             unsigned int *tx_mask,
+                             unsigned int *rx_mask,
                              unsigned int *slots,
                              unsigned int *slot_width);
 void snd_soc_of_parse_audio_prefix(struct snd_soc_card *card,
index 225bfda414e98d91da49140451d8bb09e16c9f12..7de792b06007eaea856f132660250d32aa5a6270 100644 (file)
@@ -58,6 +58,7 @@ source "sound/soc/sh/Kconfig"
 source "sound/soc/sirf/Kconfig"
 source "sound/soc/spear/Kconfig"
 source "sound/soc/sti/Kconfig"
+source "sound/soc/sunxi/Kconfig"
 source "sound/soc/tegra/Kconfig"
 source "sound/soc/txx9/Kconfig"
 source "sound/soc/ux500/Kconfig"
index 134aca150a50bb62ef6d9d13ddc5636b7adde463..af0a5714e1073e7f524ac96ed783244ff996437d 100644 (file)
@@ -40,6 +40,7 @@ obj-$(CONFIG_SND_SOC) += sh/
 obj-$(CONFIG_SND_SOC)  += sirf/
 obj-$(CONFIG_SND_SOC)  += spear/
 obj-$(CONFIG_SND_SOC)  += sti/
+obj-$(CONFIG_SND_SOC)  += sunxi/
 obj-$(CONFIG_SND_SOC)  += tegra/
 obj-$(CONFIG_SND_SOC)  += txx9/
 obj-$(CONFIG_SND_SOC)  += ux500/
index aa354e1c6ff7ce3df7ffd898682c3040817a6a8c..1933bcd46cca043d68db0ecc771124da37416908 100644 (file)
@@ -176,6 +176,7 @@ static const struct of_device_id atmel_asoc_wm8904_dt_ids[] = {
        { .compatible = "atmel,asoc-wm8904", },
        { }
 };
+MODULE_DEVICE_TABLE(of, atmel_asoc_wm8904_dt_ids);
 #endif
 
 static struct platform_driver atmel_asoc_wm8904_driver = {
index 452f404abfd283abe56caaae06de856a47d2b1d9..e97c32798e98a066dba2eaf47883584f7eb04dad 100644 (file)
@@ -38,14 +38,7 @@ static int db1000_audio_probe(struct platform_device *pdev)
 {
        struct snd_soc_card *card = &db1000_ac97;
        card->dev = &pdev->dev;
-       return snd_soc_register_card(card);
-}
-
-static int db1000_audio_remove(struct platform_device *pdev)
-{
-       struct snd_soc_card *card = platform_get_drvdata(pdev);
-       snd_soc_unregister_card(card);
-       return 0;
+       return devm_snd_soc_register_card(&pdev->dev, card);
 }
 
 static struct platform_driver db1000_audio_driver = {
@@ -54,7 +47,6 @@ static struct platform_driver db1000_audio_driver = {
                .pm     = &snd_soc_pm_ops,
        },
        .probe          = db1000_audio_probe,
-       .remove         = db1000_audio_remove,
 };
 
 module_platform_driver(db1000_audio_driver);
index 8c907ebea18960ec8e48942f5746fd2121705736..5c73061d912ad55a2cf51e75f9779bad900f9f33 100644 (file)
@@ -178,14 +178,7 @@ static int db1200_audio_probe(struct platform_device *pdev)
 
        card = db1200_cards[pid->driver_data];
        card->dev = &pdev->dev;
-       return snd_soc_register_card(card);
-}
-
-static int db1200_audio_remove(struct platform_device *pdev)
-{
-       struct snd_soc_card *card = platform_get_drvdata(pdev);
-       snd_soc_unregister_card(card);
-       return 0;
+       return devm_snd_soc_register_card(&pdev->dev, card);
 }
 
 static struct platform_driver db1200_audio_driver = {
@@ -195,7 +188,6 @@ static struct platform_driver db1200_audio_driver = {
        },
        .id_table       = db1200_pids,
        .probe          = db1200_audio_probe,
-       .remove         = db1200_audio_remove,
 };
 
 module_platform_driver(db1200_audio_driver);
index 5bf1501e5e3c4833cbebf2f5afe4b8fde1cc216f..864df2616e109ae4e2bb70793e6231abe58fd1bc 100644 (file)
@@ -87,27 +87,18 @@ static int bf5xx_ad1836_driver_probe(struct platform_device *pdev)
        card->dev = &pdev->dev;
        platform_set_drvdata(pdev, card);
 
-       ret = snd_soc_register_card(card);
+       ret = devm_snd_soc_register_card(&pdev->dev, card);
        if (ret)
                dev_err(&pdev->dev, "Failed to register card\n");
        return ret;
 }
 
-static int bf5xx_ad1836_driver_remove(struct platform_device *pdev)
-{
-       struct snd_soc_card *card = platform_get_drvdata(pdev);
-
-       snd_soc_unregister_card(card);
-       return 0;
-}
-
 static struct platform_driver bf5xx_ad1836_driver = {
        .driver = {
                .name = "bfin-snd-ad1836",
                .pm = &snd_soc_pm_ops,
        },
        .probe = bf5xx_ad1836_driver_probe,
-       .remove = bf5xx_ad1836_driver_remove,
 };
 module_platform_driver(bf5xx_ad1836_driver);
 
index 523baf5820d737113e3ba17536a788026cf62137..72ac789884262bbdc41e63adfcabe1dee3911814 100644 (file)
@@ -154,16 +154,7 @@ static int bfin_eval_adau1373_probe(struct platform_device *pdev)
 
        card->dev = &pdev->dev;
 
-       return snd_soc_register_card(&bfin_eval_adau1373);
-}
-
-static int bfin_eval_adau1373_remove(struct platform_device *pdev)
-{
-       struct snd_soc_card *card = platform_get_drvdata(pdev);
-
-       snd_soc_unregister_card(card);
-
-       return 0;
+       return devm_snd_soc_register_card(&pdev->dev, &bfin_eval_adau1373);
 }
 
 static struct platform_driver bfin_eval_adau1373_driver = {
@@ -172,7 +163,6 @@ static struct platform_driver bfin_eval_adau1373_driver = {
                .pm = &snd_soc_pm_ops,
        },
        .probe = bfin_eval_adau1373_probe,
-       .remove = bfin_eval_adau1373_remove,
 };
 
 module_platform_driver(bfin_eval_adau1373_driver);
index f9e926dfd4ef735af9e84f9e35363a1670e61421..5c67f72cf9a95e521eb070e5e9b1c9a0835873c8 100644 (file)
@@ -94,16 +94,7 @@ static int bfin_eval_adau1701_probe(struct platform_device *pdev)
 
        card->dev = &pdev->dev;
 
-       return snd_soc_register_card(&bfin_eval_adau1701);
-}
-
-static int bfin_eval_adau1701_remove(struct platform_device *pdev)
-{
-       struct snd_soc_card *card = platform_get_drvdata(pdev);
-
-       snd_soc_unregister_card(card);
-
-       return 0;
+       return devm_snd_soc_register_card(&pdev->dev, &bfin_eval_adau1701);
 }
 
 static struct platform_driver bfin_eval_adau1701_driver = {
@@ -112,7 +103,6 @@ static struct platform_driver bfin_eval_adau1701_driver = {
                .pm = &snd_soc_pm_ops,
        },
        .probe = bfin_eval_adau1701_probe,
-       .remove = bfin_eval_adau1701_remove,
 };
 
 module_platform_driver(bfin_eval_adau1701_driver);
index 27eee66afdb26536aeb41d50e1b29e542bf6b0c5..1037477d10b2f962cf6d7ea13844d01539bdeafb 100644 (file)
@@ -119,16 +119,7 @@ static int bfin_eval_adav80x_probe(struct platform_device *pdev)
 
        card->dev = &pdev->dev;
 
-       return snd_soc_register_card(&bfin_eval_adav80x);
-}
-
-static int bfin_eval_adav80x_remove(struct platform_device *pdev)
-{
-       struct snd_soc_card *card = platform_get_drvdata(pdev);
-
-       snd_soc_unregister_card(card);
-
-       return 0;
+       return devm_snd_soc_register_card(&pdev->dev, &bfin_eval_adav80x);
 }
 
 static const struct platform_device_id bfin_eval_adav80x_ids[] = {
@@ -144,7 +135,6 @@ static struct platform_driver bfin_eval_adav80x_driver = {
                .pm = &snd_soc_pm_ops,
        },
        .probe = bfin_eval_adav80x_probe,
-       .remove = bfin_eval_adav80x_remove,
        .id_table = bfin_eval_adav80x_ids,
 };
 
index 0c9733ecd17f29a040ec3f2a4c7dfab8c59007e7..70e5a75901aa20fbde390478e60de4c01ab3df2f 100644 (file)
@@ -36,6 +36,7 @@ config SND_SOC_ALL_CODECS
        select SND_SOC_AK4104 if SPI_MASTER
        select SND_SOC_AK4535 if I2C
        select SND_SOC_AK4554
+       select SND_SOC_AK4613 if I2C
        select SND_SOC_AK4641 if I2C
        select SND_SOC_AK4642 if I2C
        select SND_SOC_AK4671 if I2C
@@ -79,7 +80,6 @@ config SND_SOC_ALL_CODECS
        select SND_SOC_MAX9877 if I2C
        select SND_SOC_MC13783 if MFD_MC13XXX
        select SND_SOC_ML26124 if I2C
-       select SND_SOC_HDMI_CODEC
        select SND_SOC_PCM1681 if I2C
        select SND_SOC_PCM1792A if SPI_MASTER
        select SND_SOC_PCM3008
@@ -319,6 +319,10 @@ config SND_SOC_AK4535
 config SND_SOC_AK4554
        tristate "AKM AK4554 CODEC"
 
+config SND_SOC_AK4613
+       tristate "AKM AK4613 CODEC"
+       depends on I2C
+
 config SND_SOC_AK4641
        tristate
 
@@ -442,9 +446,6 @@ config SND_SOC_BT_SCO
 config SND_SOC_DMIC
        tristate
 
-config SND_SOC_HDMI_CODEC
-       tristate "HDMI stub CODEC"
-
 config SND_SOC_ES8328
        tristate "Everest Semi ES8328 CODEC"
 
index 4a32077954aee6aee2966ced2573991d1673b02a..be1491acb6aea0aeff3df80e7f039d36eef295c4 100644 (file)
@@ -26,6 +26,7 @@ snd-soc-ads117x-objs := ads117x.o
 snd-soc-ak4104-objs := ak4104.o
 snd-soc-ak4535-objs := ak4535.o
 snd-soc-ak4554-objs := ak4554.o
+snd-soc-ak4613-objs := ak4613.o
 snd-soc-ak4641-objs := ak4641.o
 snd-soc-ak4642-objs := ak4642.o
 snd-soc-ak4671-objs := ak4671.o
@@ -72,7 +73,6 @@ snd-soc-max98925-objs := max98925.o
 snd-soc-max9850-objs := max9850.o
 snd-soc-mc13783-objs := mc13783.o
 snd-soc-ml26124-objs := ml26124.o
-snd-soc-hdmi-codec-objs := hdmi.o
 snd-soc-pcm1681-objs := pcm1681.o
 snd-soc-pcm1792a-codec-objs := pcm1792a.o
 snd-soc-pcm3008-objs := pcm3008.o
@@ -216,6 +216,7 @@ obj-$(CONFIG_SND_SOC_ADS117X)       += snd-soc-ads117x.o
 obj-$(CONFIG_SND_SOC_AK4104)   += snd-soc-ak4104.o
 obj-$(CONFIG_SND_SOC_AK4535)   += snd-soc-ak4535.o
 obj-$(CONFIG_SND_SOC_AK4554)   += snd-soc-ak4554.o
+obj-$(CONFIG_SND_SOC_AK4613)   += snd-soc-ak4613.o
 obj-$(CONFIG_SND_SOC_AK4641)   += snd-soc-ak4641.o
 obj-$(CONFIG_SND_SOC_AK4642)   += snd-soc-ak4642.o
 obj-$(CONFIG_SND_SOC_AK4671)   += snd-soc-ak4671.o
@@ -264,7 +265,6 @@ obj-$(CONFIG_SND_SOC_MAX98925)      += snd-soc-max98925.o
 obj-$(CONFIG_SND_SOC_MAX9850)  += snd-soc-max9850.o
 obj-$(CONFIG_SND_SOC_MC13783)  += snd-soc-mc13783.o
 obj-$(CONFIG_SND_SOC_ML26124)  += snd-soc-ml26124.o
-obj-$(CONFIG_SND_SOC_HDMI_CODEC) += snd-soc-hdmi-codec.o
 obj-$(CONFIG_SND_SOC_PCM1681)  += snd-soc-pcm1681.o
 obj-$(CONFIG_SND_SOC_PCM1792A) += snd-soc-pcm1792a-codec.o
 obj-$(CONFIG_SND_SOC_PCM3008)  += snd-soc-pcm3008.o
diff --git a/sound/soc/codecs/ak4613.c b/sound/soc/codecs/ak4613.c
new file mode 100644 (file)
index 0000000..07a2664
--- /dev/null
@@ -0,0 +1,497 @@
+/*
+ * ak4613.c  --  Asahi Kasei ALSA Soc Audio driver
+ *
+ * Copyright (C) 2015 Renesas Electronics Corporation
+ * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ *
+ * Based on ak4642.c by Kuninori Morimoto
+ * Based on wm8731.c by Richard Purdie
+ * Based on ak4535.c by Richard Purdie
+ * Based on wm8753.c by Liam Girdwood
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/clk.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/of_device.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+
+#define PW_MGMT1       0x00 /* Power Management 1 */
+#define PW_MGMT2       0x01 /* Power Management 2 */
+#define PW_MGMT3       0x02 /* Power Management 3 */
+#define CTRL1          0x03 /* Control 1 */
+#define CTRL2          0x04 /* Control 2 */
+#define DEMP1          0x05 /* De-emphasis1 */
+#define DEMP2          0x06 /* De-emphasis2 */
+#define OFD            0x07 /* Overflow Detect */
+#define ZRD            0x08 /* Zero Detect */
+#define ICTRL          0x09 /* Input Control */
+#define OCTRL          0x0a /* Output Control */
+#define LOUT1          0x0b /* LOUT1 Volume Control */
+#define ROUT1          0x0c /* ROUT1 Volume Control */
+#define LOUT2          0x0d /* LOUT2 Volume Control */
+#define ROUT2          0x0e /* ROUT2 Volume Control */
+#define LOUT3          0x0f /* LOUT3 Volume Control */
+#define ROUT3          0x10 /* ROUT3 Volume Control */
+#define LOUT4          0x11 /* LOUT4 Volume Control */
+#define ROUT4          0x12 /* ROUT4 Volume Control */
+#define LOUT5          0x13 /* LOUT5 Volume Control */
+#define ROUT5          0x14 /* ROUT5 Volume Control */
+#define LOUT6          0x15 /* LOUT6 Volume Control */
+#define ROUT6          0x16 /* ROUT6 Volume Control */
+
+/* PW_MGMT1 */
+#define RSTN           BIT(0)
+#define PMDAC          BIT(1)
+#define PMADC          BIT(2)
+#define PMVR           BIT(3)
+
+/* PW_MGMT2 */
+#define PMAD_ALL       0x7
+
+/* PW_MGMT3 */
+#define PMDA_ALL       0x3f
+
+/* CTRL1 */
+#define DIF0           BIT(3)
+#define DIF1           BIT(4)
+#define DIF2           BIT(5)
+#define TDM0           BIT(6)
+#define TDM1           BIT(7)
+#define NO_FMT         (0xff)
+#define FMT_MASK       (0xf8)
+
+/* CTRL2 */
+#define DFS_NORMAL_SPEED       (0 << 2)
+#define DFS_DOUBLE_SPEED       (1 << 2)
+#define DFS_QUAD_SPEED         (2 << 2)
+
+struct ak4613_priv {
+       struct mutex lock;
+
+       unsigned int fmt;
+       u8 fmt_ctrl;
+       int cnt;
+};
+
+struct ak4613_formats {
+       unsigned int width;
+       unsigned int fmt;
+};
+
+struct ak4613_interface {
+       struct ak4613_formats capture;
+       struct ak4613_formats playback;
+};
+
+/*
+ * Playback Volume
+ *
+ * max : 0x00 : 0 dB
+ *       ( 0.5 dB step )
+ * min : 0xFE : -127.0 dB
+ * mute: 0xFF
+ */
+static const DECLARE_TLV_DB_SCALE(out_tlv, -12750, 50, 1);
+
+static const struct snd_kcontrol_new ak4613_snd_controls[] = {
+       SOC_DOUBLE_R_TLV("Digital Playback Volume1", LOUT1, ROUT1,
+                        0, 0xFF, 1, out_tlv),
+       SOC_DOUBLE_R_TLV("Digital Playback Volume2", LOUT2, ROUT2,
+                        0, 0xFF, 1, out_tlv),
+       SOC_DOUBLE_R_TLV("Digital Playback Volume3", LOUT3, ROUT3,
+                        0, 0xFF, 1, out_tlv),
+       SOC_DOUBLE_R_TLV("Digital Playback Volume4", LOUT4, ROUT4,
+                        0, 0xFF, 1, out_tlv),
+       SOC_DOUBLE_R_TLV("Digital Playback Volume5", LOUT5, ROUT5,
+                        0, 0xFF, 1, out_tlv),
+       SOC_DOUBLE_R_TLV("Digital Playback Volume6", LOUT6, ROUT6,
+                        0, 0xFF, 1, out_tlv),
+};
+
+static const struct reg_default ak4613_reg[] = {
+       { 0x0,  0x0f }, { 0x1,  0x07 }, { 0x2,  0x3f }, { 0x3,  0x20 },
+       { 0x4,  0x20 }, { 0x5,  0x55 }, { 0x6,  0x05 }, { 0x7,  0x07 },
+       { 0x8,  0x0f }, { 0x9,  0x07 }, { 0xa,  0x3f }, { 0xb,  0x00 },
+       { 0xc,  0x00 }, { 0xd,  0x00 }, { 0xe,  0x00 }, { 0xf,  0x00 },
+       { 0x10, 0x00 }, { 0x11, 0x00 }, { 0x12, 0x00 }, { 0x13, 0x00 },
+       { 0x14, 0x00 }, { 0x15, 0x00 }, { 0x16, 0x00 },
+};
+
+#define AUDIO_IFACE_IDX_TO_VAL(i) (i << 3)
+#define AUDIO_IFACE(b, fmt) { b, SND_SOC_DAIFMT_##fmt }
+static const struct ak4613_interface ak4613_iface[] = {
+       /* capture */                           /* playback */
+       [0] = { AUDIO_IFACE(24, LEFT_J),        AUDIO_IFACE(16, RIGHT_J) },
+       [1] = { AUDIO_IFACE(24, LEFT_J),        AUDIO_IFACE(20, RIGHT_J) },
+       [2] = { AUDIO_IFACE(24, LEFT_J),        AUDIO_IFACE(24, RIGHT_J) },
+       [3] = { AUDIO_IFACE(24, LEFT_J),        AUDIO_IFACE(24, LEFT_J) },
+       [4] = { AUDIO_IFACE(24, I2S),           AUDIO_IFACE(24, I2S) },
+};
+
+static const struct regmap_config ak4613_regmap_cfg = {
+       .reg_bits               = 8,
+       .val_bits               = 8,
+       .max_register           = 0x16,
+       .reg_defaults           = ak4613_reg,
+       .num_reg_defaults       = ARRAY_SIZE(ak4613_reg),
+};
+
+static const struct of_device_id ak4613_of_match[] = {
+       { .compatible = "asahi-kasei,ak4613",   .data = &ak4613_regmap_cfg },
+       {},
+};
+MODULE_DEVICE_TABLE(of, ak4613_of_match);
+
+static const struct i2c_device_id ak4613_i2c_id[] = {
+       { "ak4613", (kernel_ulong_t)&ak4613_regmap_cfg },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, ak4613_i2c_id);
+
+static const struct snd_soc_dapm_widget ak4613_dapm_widgets[] = {
+
+       /* Outputs */
+       SND_SOC_DAPM_OUTPUT("LOUT1"),
+       SND_SOC_DAPM_OUTPUT("LOUT2"),
+       SND_SOC_DAPM_OUTPUT("LOUT3"),
+       SND_SOC_DAPM_OUTPUT("LOUT4"),
+       SND_SOC_DAPM_OUTPUT("LOUT5"),
+       SND_SOC_DAPM_OUTPUT("LOUT6"),
+
+       SND_SOC_DAPM_OUTPUT("ROUT1"),
+       SND_SOC_DAPM_OUTPUT("ROUT2"),
+       SND_SOC_DAPM_OUTPUT("ROUT3"),
+       SND_SOC_DAPM_OUTPUT("ROUT4"),
+       SND_SOC_DAPM_OUTPUT("ROUT5"),
+       SND_SOC_DAPM_OUTPUT("ROUT6"),
+
+       /* Inputs */
+       SND_SOC_DAPM_INPUT("LIN1"),
+       SND_SOC_DAPM_INPUT("LIN2"),
+
+       SND_SOC_DAPM_INPUT("RIN1"),
+       SND_SOC_DAPM_INPUT("RIN2"),
+
+       /* DAC */
+       SND_SOC_DAPM_DAC("DAC1", NULL, PW_MGMT3, 0, 0),
+       SND_SOC_DAPM_DAC("DAC2", NULL, PW_MGMT3, 1, 0),
+       SND_SOC_DAPM_DAC("DAC3", NULL, PW_MGMT3, 2, 0),
+       SND_SOC_DAPM_DAC("DAC4", NULL, PW_MGMT3, 3, 0),
+       SND_SOC_DAPM_DAC("DAC5", NULL, PW_MGMT3, 4, 0),
+       SND_SOC_DAPM_DAC("DAC6", NULL, PW_MGMT3, 5, 0),
+
+       /* ADC */
+       SND_SOC_DAPM_ADC("ADC1", NULL, PW_MGMT2, 0, 0),
+       SND_SOC_DAPM_ADC("ADC2", NULL, PW_MGMT2, 1, 0),
+};
+
+static const struct snd_soc_dapm_route ak4613_intercon[] = {
+       {"LOUT1", NULL, "DAC1"},
+       {"LOUT2", NULL, "DAC2"},
+       {"LOUT3", NULL, "DAC3"},
+       {"LOUT4", NULL, "DAC4"},
+       {"LOUT5", NULL, "DAC5"},
+       {"LOUT6", NULL, "DAC6"},
+
+       {"ROUT1", NULL, "DAC1"},
+       {"ROUT2", NULL, "DAC2"},
+       {"ROUT3", NULL, "DAC3"},
+       {"ROUT4", NULL, "DAC4"},
+       {"ROUT5", NULL, "DAC5"},
+       {"ROUT6", NULL, "DAC6"},
+
+       {"DAC1", NULL, "Playback"},
+       {"DAC2", NULL, "Playback"},
+       {"DAC3", NULL, "Playback"},
+       {"DAC4", NULL, "Playback"},
+       {"DAC5", NULL, "Playback"},
+       {"DAC6", NULL, "Playback"},
+
+       {"Capture", NULL, "ADC1"},
+       {"Capture", NULL, "ADC2"},
+
+       {"ADC1", NULL, "LIN1"},
+       {"ADC2", NULL, "LIN2"},
+
+       {"ADC1", NULL, "RIN1"},
+       {"ADC2", NULL, "RIN2"},
+};
+
+static void ak4613_dai_shutdown(struct snd_pcm_substream *substream,
+                              struct snd_soc_dai *dai)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct ak4613_priv *priv = snd_soc_codec_get_drvdata(codec);
+       struct device *dev = codec->dev;
+
+       mutex_lock(&priv->lock);
+       priv->cnt--;
+       if (priv->cnt < 0) {
+               dev_err(dev, "unexpected counter error\n");
+               priv->cnt = 0;
+       }
+       if (!priv->cnt)
+               priv->fmt_ctrl = NO_FMT;
+       mutex_unlock(&priv->lock);
+}
+
+static int ak4613_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct ak4613_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+       fmt &= SND_SOC_DAIFMT_FORMAT_MASK;
+
+       switch (fmt) {
+       case SND_SOC_DAIFMT_RIGHT_J:
+       case SND_SOC_DAIFMT_LEFT_J:
+       case SND_SOC_DAIFMT_I2S:
+               priv->fmt = fmt;
+
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int ak4613_dai_hw_params(struct snd_pcm_substream *substream,
+                               struct snd_pcm_hw_params *params,
+                               struct snd_soc_dai *dai)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct ak4613_priv *priv = snd_soc_codec_get_drvdata(codec);
+       const struct ak4613_formats *fmts;
+       struct device *dev = codec->dev;
+       unsigned int width = params_width(params);
+       unsigned int fmt = priv->fmt;
+       unsigned int rate;
+       int is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+       int i, ret;
+       u8 fmt_ctrl, ctrl2;
+
+       rate = params_rate(params);
+       switch (rate) {
+       case 32000:
+       case 44100:
+       case 48000:
+               ctrl2 = DFS_NORMAL_SPEED;
+               break;
+       case 88200:
+       case 96000:
+               ctrl2 = DFS_DOUBLE_SPEED;
+               break;
+       case 176400:
+       case 192000:
+               ctrl2 = DFS_QUAD_SPEED;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       /*
+        * FIXME
+        *
+        * It doesn't support TDM at this point
+        */
+       fmt_ctrl = NO_FMT;
+       for (i = 0; i < ARRAY_SIZE(ak4613_iface); i++) {
+               fmts = (is_play) ?      &ak4613_iface[i].playback :
+                                       &ak4613_iface[i].capture;
+
+               if (fmts->fmt != fmt)
+                       continue;
+
+               if (fmt == SND_SOC_DAIFMT_RIGHT_J) {
+                       if (fmts->width != width)
+                               continue;
+               } else {
+                       if (fmts->width < width)
+                               continue;
+               }
+
+               fmt_ctrl = AUDIO_IFACE_IDX_TO_VAL(i);
+               break;
+       }
+
+       ret = -EINVAL;
+       if (fmt_ctrl == NO_FMT)
+               goto hw_params_end;
+
+       mutex_lock(&priv->lock);
+       if ((priv->fmt_ctrl == NO_FMT) ||
+           (priv->fmt_ctrl == fmt_ctrl)) {
+               priv->fmt_ctrl = fmt_ctrl;
+               priv->cnt++;
+               ret = 0;
+       }
+       mutex_unlock(&priv->lock);
+
+       if (ret < 0)
+               goto hw_params_end;
+
+       snd_soc_update_bits(codec, CTRL1, FMT_MASK, fmt_ctrl);
+       snd_soc_write(codec, CTRL2, ctrl2);
+
+hw_params_end:
+       if (ret < 0)
+               dev_warn(dev, "unsupported data width/format combination\n");
+
+       return ret;
+}
+
+static int ak4613_set_bias_level(struct snd_soc_codec *codec,
+                                enum snd_soc_bias_level level)
+{
+       u8 mgmt1 = 0;
+
+       switch (level) {
+       case SND_SOC_BIAS_ON:
+               mgmt1 |= RSTN;
+               /* fall through */
+       case SND_SOC_BIAS_PREPARE:
+               mgmt1 |= PMADC | PMDAC;
+               /* fall through */
+       case SND_SOC_BIAS_STANDBY:
+               mgmt1 |= PMVR;
+               /* fall through */
+       case SND_SOC_BIAS_OFF:
+       default:
+               break;
+       }
+
+       snd_soc_write(codec, PW_MGMT1, mgmt1);
+
+       return 0;
+}
+
+static const struct snd_soc_dai_ops ak4613_dai_ops = {
+       .shutdown       = ak4613_dai_shutdown,
+       .set_fmt        = ak4613_dai_set_fmt,
+       .hw_params      = ak4613_dai_hw_params,
+};
+
+#define AK4613_PCM_RATE                (SNDRV_PCM_RATE_32000  |\
+                                SNDRV_PCM_RATE_44100  |\
+                                SNDRV_PCM_RATE_48000  |\
+                                SNDRV_PCM_RATE_64000  |\
+                                SNDRV_PCM_RATE_88200  |\
+                                SNDRV_PCM_RATE_96000  |\
+                                SNDRV_PCM_RATE_176400 |\
+                                SNDRV_PCM_RATE_192000)
+#define AK4613_PCM_FMTBIT      (SNDRV_PCM_FMTBIT_S16_LE |\
+                                SNDRV_PCM_FMTBIT_S24_LE)
+
+static struct snd_soc_dai_driver ak4613_dai = {
+       .name = "ak4613-hifi",
+       .playback = {
+               .stream_name    = "Playback",
+               .channels_min   = 2,
+               .channels_max   = 2,
+               .rates          = AK4613_PCM_RATE,
+               .formats        = AK4613_PCM_FMTBIT,
+       },
+       .capture = {
+               .stream_name    = "Capture",
+               .channels_min   = 2,
+               .channels_max   = 2,
+               .rates          = AK4613_PCM_RATE,
+               .formats        = AK4613_PCM_FMTBIT,
+       },
+       .ops = &ak4613_dai_ops,
+       .symmetric_rates = 1,
+};
+
+static int ak4613_resume(struct snd_soc_codec *codec)
+{
+       struct regmap *regmap = dev_get_regmap(codec->dev, NULL);
+
+       regcache_mark_dirty(regmap);
+       return regcache_sync(regmap);
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_ak4613 = {
+       .resume                 = ak4613_resume,
+       .set_bias_level         = ak4613_set_bias_level,
+       .controls               = ak4613_snd_controls,
+       .num_controls           = ARRAY_SIZE(ak4613_snd_controls),
+       .dapm_widgets           = ak4613_dapm_widgets,
+       .num_dapm_widgets       = ARRAY_SIZE(ak4613_dapm_widgets),
+       .dapm_routes            = ak4613_intercon,
+       .num_dapm_routes        = ARRAY_SIZE(ak4613_intercon),
+};
+
+static int ak4613_i2c_probe(struct i2c_client *i2c,
+                           const struct i2c_device_id *id)
+{
+       struct device *dev = &i2c->dev;
+       struct device_node *np = dev->of_node;
+       const struct regmap_config *regmap_cfg;
+       struct regmap *regmap;
+       struct ak4613_priv *priv;
+
+       regmap_cfg = NULL;
+       if (np) {
+               const struct of_device_id *of_id;
+
+               of_id = of_match_device(ak4613_of_match, dev);
+               if (of_id)
+                       regmap_cfg = of_id->data;
+       } else {
+               regmap_cfg = (const struct regmap_config *)id->driver_data;
+       }
+
+       if (!regmap_cfg)
+               return -EINVAL;
+
+       priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       priv->fmt_ctrl          = NO_FMT;
+       priv->cnt               = 0;
+
+       mutex_init(&priv->lock);
+
+       i2c_set_clientdata(i2c, priv);
+
+       regmap = devm_regmap_init_i2c(i2c, regmap_cfg);
+       if (IS_ERR(regmap))
+               return PTR_ERR(regmap);
+
+       return snd_soc_register_codec(dev, &soc_codec_dev_ak4613,
+                                     &ak4613_dai, 1);
+}
+
+static int ak4613_i2c_remove(struct i2c_client *client)
+{
+       snd_soc_unregister_codec(&client->dev);
+       return 0;
+}
+
+static struct i2c_driver ak4613_i2c_driver = {
+       .driver = {
+               .name = "ak4613-codec",
+               .owner = THIS_MODULE,
+               .of_match_table = ak4613_of_match,
+       },
+       .probe          = ak4613_i2c_probe,
+       .remove         = ak4613_i2c_remove,
+       .id_table       = ak4613_i2c_id,
+};
+
+module_i2c_driver(ak4613_i2c_driver);
+
+MODULE_DESCRIPTION("Soc AK4613 driver");
+MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
+MODULE_LICENSE("GPL v2");
index 4a90143d0e907b5034d0f4caa506c97000500f71..cda27c22812a71442dd3d927088c2118aa16dc17 100644 (file)
@@ -23,6 +23,8 @@
  * AK4648 is tested.
  */
 
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
 #include <linux/delay.h>
 #include <linux/i2c.h>
 #include <linux/slab.h>
 #define I2S            (3 << 0)
 
 /* MD_CTL2 */
-#define FS0            (1 << 0)
-#define FS1            (1 << 1)
-#define FS2            (1 << 2)
-#define FS3            (1 << 5)
-#define FS_MASK                (FS0 | FS1 | FS2 | FS3)
+#define FSs(val)       (((val & 0x7) << 0) | ((val & 0x8) << 2))
+#define PSs(val)       ((val & 0x3) << 6)
 
 /* MD_CTL3 */
 #define BST1           (1 << 3)
@@ -147,6 +146,7 @@ struct ak4642_drvdata {
 
 struct ak4642_priv {
        const struct ak4642_drvdata *drvdata;
+       struct clk *mcko;
 };
 
 /*
@@ -430,56 +430,56 @@ static int ak4642_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
        return 0;
 }
 
+static int ak4642_set_mcko(struct snd_soc_codec *codec,
+                          u32 frequency)
+{
+       u32 fs_list[] = {
+               [0] = 8000,
+               [1] = 12000,
+               [2] = 16000,
+               [3] = 24000,
+               [4] = 7350,
+               [5] = 11025,
+               [6] = 14700,
+               [7] = 22050,
+               [10] = 32000,
+               [11] = 48000,
+               [14] = 29400,
+               [15] = 44100,
+       };
+       u32 ps_list[] = {
+               [0] = 256,
+               [1] = 128,
+               [2] = 64,
+               [3] = 32
+       };
+       int ps, fs;
+
+       for (ps = 0; ps < ARRAY_SIZE(ps_list); ps++) {
+               for (fs = 0; fs < ARRAY_SIZE(fs_list); fs++) {
+                       if (frequency == ps_list[ps] * fs_list[fs]) {
+                               snd_soc_write(codec, MD_CTL2,
+                                             PSs(ps) | FSs(fs));
+                               return 0;
+                       }
+               }
+       }
+
+       return 0;
+}
+
 static int ak4642_dai_hw_params(struct snd_pcm_substream *substream,
                                struct snd_pcm_hw_params *params,
                                struct snd_soc_dai *dai)
 {
        struct snd_soc_codec *codec = dai->codec;
-       u8 rate;
+       struct ak4642_priv *priv = snd_soc_codec_get_drvdata(codec);
+       u32 rate = clk_get_rate(priv->mcko);
 
-       switch (params_rate(params)) {
-       case 7350:
-               rate = FS2;
-               break;
-       case 8000:
-               rate = 0;
-               break;
-       case 11025:
-               rate = FS2 | FS0;
-               break;
-       case 12000:
-               rate = FS0;
-               break;
-       case 14700:
-               rate = FS2 | FS1;
-               break;
-       case 16000:
-               rate = FS1;
-               break;
-       case 22050:
-               rate = FS2 | FS1 | FS0;
-               break;
-       case 24000:
-               rate = FS1 | FS0;
-               break;
-       case 29400:
-               rate = FS3 | FS2 | FS1;
-               break;
-       case 32000:
-               rate = FS3 | FS1;
-               break;
-       case 44100:
-               rate = FS3 | FS2 | FS1 | FS0;
-               break;
-       case 48000:
-               rate = FS3 | FS1 | FS0;
-               break;
-       default:
-               return -EINVAL;
-       }
-       snd_soc_update_bits(codec, MD_CTL2, FS_MASK, rate);
+       if (!rate)
+               rate = params_rate(params) * 256;
 
-       return 0;
+       return ak4642_set_mcko(codec, rate);
 }
 
 static int ak4642_set_bias_level(struct snd_soc_codec *codec,
@@ -532,7 +532,18 @@ static int ak4642_resume(struct snd_soc_codec *codec)
        return 0;
 }
 
+static int ak4642_probe(struct snd_soc_codec *codec)
+{
+       struct ak4642_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+       if (priv->mcko)
+               ak4642_set_mcko(codec, clk_get_rate(priv->mcko));
+
+       return 0;
+}
+
 static struct snd_soc_codec_driver soc_codec_dev_ak4642 = {
+       .probe                  = ak4642_probe,
        .resume                 = ak4642_resume,
        .set_bias_level         = ak4642_set_bias_level,
        .controls               = ak4642_snd_controls,
@@ -580,19 +591,54 @@ static const struct ak4642_drvdata ak4648_drvdata = {
        .extended_frequencies = 1,
 };
 
+#ifdef CONFIG_COMMON_CLK
+static struct clk *ak4642_of_parse_mcko(struct device *dev)
+{
+       struct device_node *np = dev->of_node;
+       struct clk *clk;
+       const char *clk_name = np->name;
+       const char *parent_clk_name = NULL;
+       u32 rate;
+
+       if (of_property_read_u32(np, "clock-frequency", &rate))
+               return NULL;
+
+       if (of_property_read_bool(np, "clocks"))
+               parent_clk_name = of_clk_get_parent_name(np, 0);
+
+       of_property_read_string(np, "clock-output-names", &clk_name);
+
+       clk = clk_register_fixed_rate(dev, clk_name, parent_clk_name,
+                                     (parent_clk_name) ? 0 : CLK_IS_ROOT,
+                                     rate);
+       if (!IS_ERR(clk))
+               of_clk_add_provider(np, of_clk_src_simple_get, clk);
+
+       return clk;
+}
+#else
+#define ak4642_of_parse_mcko(d) 0
+#endif
+
 static const struct of_device_id ak4642_of_match[];
 static int ak4642_i2c_probe(struct i2c_client *i2c,
                            const struct i2c_device_id *id)
 {
-       struct device_node *np = i2c->dev.of_node;
+       struct device *dev = &i2c->dev;
+       struct device_node *np = dev->of_node;
        const struct ak4642_drvdata *drvdata = NULL;
        struct regmap *regmap;
        struct ak4642_priv *priv;
+       struct clk *mcko = NULL;
 
        if (np) {
                const struct of_device_id *of_id;
 
-               of_id = of_match_device(ak4642_of_match, &i2c->dev);
+               mcko = ak4642_of_parse_mcko(dev);
+               if (IS_ERR(mcko))
+                       mcko = NULL;
+
+               of_id = of_match_device(ak4642_of_match, dev);
                if (of_id)
                        drvdata = of_id->data;
        } else {
@@ -600,15 +646,16 @@ static int ak4642_i2c_probe(struct i2c_client *i2c,
        }
 
        if (!drvdata) {
-               dev_err(&i2c->dev, "Unknown device type\n");
+               dev_err(dev, "Unknown device type\n");
                return -EINVAL;
        }
 
-       priv = devm_kzalloc(&i2c->dev, sizeof(*priv), GFP_KERNEL);
+       priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
        if (!priv)
                return -ENOMEM;
 
        priv->drvdata = drvdata;
+       priv->mcko = mcko;
 
        i2c_set_clientdata(i2c, priv);
 
@@ -616,7 +663,7 @@ static int ak4642_i2c_probe(struct i2c_client *i2c,
        if (IS_ERR(regmap))
                return PTR_ERR(regmap);
 
-       return snd_soc_register_codec(&i2c->dev,
+       return snd_soc_register_codec(dev,
                                      &soc_codec_dev_ak4642, &ak4642_dai, 1);
 }
 
index 8a2221ab3d10b97f5f7a97c21251264aa93eaa5b..ac21b85ff75ffb7b3305d8200c8c6a38ed3896bf 100644 (file)
@@ -147,6 +147,8 @@ static int arizona_spk_ev(struct snd_soc_dapm_widget *w,
                                                   0x4f5, 0x0da);
                }
                break;
+       default:
+               break;
        }
 
        return 0;
@@ -689,6 +691,15 @@ static void arizona_in_set_vu(struct snd_soc_codec *codec, int ena)
                                    ARIZONA_IN_VU, val);
 }
 
+bool arizona_input_analog(struct snd_soc_codec *codec, int shift)
+{
+       unsigned int reg = ARIZONA_IN1L_CONTROL + ((shift / 2) * 8);
+       unsigned int val = snd_soc_read(codec, reg);
+
+       return !(val & ARIZONA_IN1_MODE_MASK);
+}
+EXPORT_SYMBOL_GPL(arizona_input_analog);
+
 int arizona_in_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol,
                  int event)
 {
@@ -725,6 +736,9 @@ int arizona_in_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol,
                reg = snd_soc_read(codec, ARIZONA_INPUT_ENABLES);
                if (reg == 0)
                        arizona_in_set_vu(codec, 0);
+               break;
+       default:
+               break;
        }
 
        return 0;
@@ -806,6 +820,8 @@ int arizona_out_ev(struct snd_soc_dapm_widget *w,
                        break;
                }
                break;
+       default:
+               break;
        }
 
        return 0;
index ada0a418ff4b648034841e9b36f4df598d03fac2..7b68d05a0939049efec30f58aafb5aa673df20f3 100644 (file)
@@ -294,4 +294,6 @@ extern int arizona_init_dai(struct arizona_priv *priv, int dai);
 int arizona_set_output_mode(struct snd_soc_codec *codec, int output,
                            bool diff);
 
+extern bool arizona_input_analog(struct snd_soc_codec *codec, int shift);
+
 #endif
diff --git a/sound/soc/codecs/hdmi.c b/sound/soc/codecs/hdmi.c
deleted file mode 100644 (file)
index bd42ad3..0000000
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * ALSA SoC codec driver for HDMI audio codecs.
- * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
- * Author: Ricardo Neri <ricardo.neri@ti.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * version 2 as published by the Free Software Foundation.
- *
- * 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- *
- */
-#include <linux/module.h>
-#include <sound/soc.h>
-#include <linux/of.h>
-#include <linux/of_device.h>
-
-#define DRV_NAME "hdmi-audio-codec"
-
-static const struct snd_soc_dapm_widget hdmi_widgets[] = {
-       SND_SOC_DAPM_INPUT("RX"),
-       SND_SOC_DAPM_OUTPUT("TX"),
-};
-
-static const struct snd_soc_dapm_route hdmi_routes[] = {
-       { "Capture", NULL, "RX" },
-       { "TX", NULL, "Playback" },
-};
-
-static struct snd_soc_dai_driver hdmi_codec_dai = {
-       .name = "hdmi-hifi",
-       .playback = {
-               .stream_name = "Playback",
-               .channels_min = 2,
-               .channels_max = 8,
-               .rates = SNDRV_PCM_RATE_32000 |
-                       SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
-                       SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |
-                       SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000,
-               .formats = SNDRV_PCM_FMTBIT_S16_LE |
-                       SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE,
-               .sig_bits = 24,
-       },
-       .capture = {
-               .stream_name = "Capture",
-               .channels_min = 2,
-               .channels_max = 2,
-               .rates = SNDRV_PCM_RATE_32000 |
-                       SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
-                       SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |
-                       SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000,
-               .formats = SNDRV_PCM_FMTBIT_S16_LE |
-                       SNDRV_PCM_FMTBIT_S24_LE,
-       },
-
-};
-
-#ifdef CONFIG_OF
-static const struct of_device_id hdmi_audio_codec_ids[] = {
-       { .compatible = "linux,hdmi-audio", },
-       { }
-};
-MODULE_DEVICE_TABLE(of, hdmi_audio_codec_ids);
-#endif
-
-static struct snd_soc_codec_driver hdmi_codec = {
-       .dapm_widgets = hdmi_widgets,
-       .num_dapm_widgets = ARRAY_SIZE(hdmi_widgets),
-       .dapm_routes = hdmi_routes,
-       .num_dapm_routes = ARRAY_SIZE(hdmi_routes),
-       .ignore_pmdown_time = true,
-};
-
-static int hdmi_codec_probe(struct platform_device *pdev)
-{
-       return snd_soc_register_codec(&pdev->dev, &hdmi_codec,
-                       &hdmi_codec_dai, 1);
-}
-
-static int hdmi_codec_remove(struct platform_device *pdev)
-{
-       snd_soc_unregister_codec(&pdev->dev);
-       return 0;
-}
-
-static struct platform_driver hdmi_codec_driver = {
-       .driver         = {
-               .name   = DRV_NAME,
-               .of_match_table = of_match_ptr(hdmi_audio_codec_ids),
-       },
-
-       .probe          = hdmi_codec_probe,
-       .remove         = hdmi_codec_remove,
-};
-
-module_platform_driver(hdmi_codec_driver);
-
-MODULE_AUTHOR("Ricardo Neri <ricardo.neri@ti.com>");
-MODULE_DESCRIPTION("ASoC generic HDMI codec driver");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:" DRV_NAME);
index 5c101af0ac630dddf1cf12085846d5cfd9c08911..080cc1ce3963c834a82e3ba5f73e2ffd9b8af6dc 100644 (file)
@@ -2829,6 +2829,9 @@ static int rt5645_jack_detect(struct snd_soc_codec *codec, int jack_insert)
                        snd_soc_dapm_sync(dapm);
                        rt5645->jack_type = SND_JACK_HEADPHONE;
                }
+               if (rt5645->pdata.jd_invert)
+                       regmap_update_bits(rt5645->regmap, RT5645_IRQ_CTRL2,
+                               RT5645_JD_1_1_MASK, RT5645_JD_1_1_INV);
        } else { /* jack out */
                rt5645->jack_type = 0;
 
@@ -2847,6 +2850,9 @@ static int rt5645_jack_detect(struct snd_soc_codec *codec, int jack_insert)
                        snd_soc_dapm_disable_pin(dapm, "LDO2");
                snd_soc_dapm_disable_pin(dapm, "Mic Det Power");
                snd_soc_dapm_sync(dapm);
+               if (rt5645->pdata.jd_invert)
+                       regmap_update_bits(rt5645->regmap, RT5645_IRQ_CTRL2,
+                               RT5645_JD_1_1_MASK, RT5645_JD_1_1_NOR);
        }
 
        return rt5645->jack_type;
@@ -3212,6 +3218,32 @@ static const struct dmi_system_id dmi_platform_intel_braswell[] = {
        { }
 };
 
+static struct rt5645_platform_data buddy_platform_data = {
+       .dmic1_data_pin = RT5645_DMIC_DATA_GPIO5,
+       .dmic2_data_pin = RT5645_DMIC_DATA_IN2P,
+       .jd_mode = 3,
+       .jd_invert = true,
+};
+
+static int buddy_quirk_cb(const struct dmi_system_id *id)
+{
+       rt5645_pdata = &buddy_platform_data;
+
+       return 1;
+}
+
+static struct dmi_system_id dmi_platform_intel_broadwell[] __initdata = {
+       {
+               .ident = "Chrome Buddy",
+               .callback = buddy_quirk_cb,
+               .matches = {
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Buddy"),
+               },
+       },
+       { }
+};
+
+
 static int rt5645_parse_dt(struct rt5645_priv *rt5645, struct device *dev)
 {
        rt5645->pdata.in2_diff = device_property_read_bool(dev,
@@ -3244,7 +3276,8 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
 
        if (pdata)
                rt5645->pdata = *pdata;
-       else if (dmi_check_system(dmi_platform_intel_braswell))
+       else if (dmi_check_system(dmi_platform_intel_braswell) ||
+                       dmi_check_system(dmi_platform_intel_broadwell))
                rt5645->pdata = *rt5645_pdata;
        else
                rt5645_parse_dt(rt5645, &i2c->dev);
index 8c964cfb120ddc4130e5d39e9ab96ae34623eb34..61bc8ab776468e8857664ef7ea32646b30c089a7 100644 (file)
 #define RT5645_PWR_CLS_D_R_BIT                 9
 #define RT5645_PWR_CLS_D_L                     (0x1 << 8)
 #define RT5645_PWR_CLS_D_L_BIT                 8
-#define RT5645_PWR_ADC_R                       (0x1 << 1)
-#define RT5645_PWR_ADC_R_BIT                   1
 #define RT5645_PWR_DAC_L2                      (0x1 << 7)
 #define RT5645_PWR_DAC_L2_BIT                  7
 #define RT5645_PWR_DAC_R2                      (0x1 << 6)
 #define RT5645_OT_P_NOR                                (0x0 << 10)
 #define RT5645_OT_P_INV                                (0x1 << 10)
 #define RT5645_IRQ_JD_1_1_EN                   (0x1 << 9)
+#define RT5645_JD_1_1_MASK                     (0x1 << 7)
+#define RT5645_JD_1_1_SFT                      7
+#define RT5645_JD_1_1_NOR                      (0x0 << 7)
+#define RT5645_JD_1_1_INV                      (0x1 << 7)
 
 /* IRQ Control 2 (0xbe) */
 #define RT5645_IRQ_MB1_OC_MASK                 (0x1 << 15)
index 8739126a1f6f60d4c9e3eac08aaf337c2f7b3a2b..a564759845f980119a29c49633cb537ef685bcd9 100644 (file)
@@ -80,6 +80,7 @@ struct aic3x_priv {
        unsigned int sysclk;
        unsigned int dai_fmt;
        unsigned int tdm_delay;
+       unsigned int slot_width;
        struct list_head list;
        int master;
        int gpio_reset;
@@ -1025,10 +1026,14 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream,
        u8 data, j, r, p, pll_q, pll_p = 1, pll_r = 1, pll_j = 1;
        u16 d, pll_d = 1;
        int clk;
+       int width = aic3x->slot_width;
+
+       if (!width)
+               width = params_width(params);
 
        /* select data word length */
        data = snd_soc_read(codec, AIC3X_ASD_INTF_CTRLB) & (~(0x3 << 4));
-       switch (params_width(params)) {
+       switch (width) {
        case 16:
                break;
        case 20:
@@ -1170,12 +1175,16 @@ static int aic3x_prepare(struct snd_pcm_substream *substream,
        struct snd_soc_codec *codec = dai->codec;
        struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec);
        int delay = 0;
+       int width = aic3x->slot_width;
+
+       if (!width)
+               width = substream->runtime->sample_bits;
 
        /* TDM slot selection only valid in DSP_A/_B mode */
        if (aic3x->dai_fmt == SND_SOC_DAIFMT_DSP_A)
-               delay += (aic3x->tdm_delay + 1);
+               delay += (aic3x->tdm_delay*width + 1);
        else if (aic3x->dai_fmt == SND_SOC_DAIFMT_DSP_B)
-               delay += aic3x->tdm_delay;
+               delay += aic3x->tdm_delay*width;
 
        /* Configure data delay */
        snd_soc_write(codec, AIC3X_ASD_INTF_CTRLC, delay);
@@ -1296,7 +1305,20 @@ static int aic3x_set_dai_tdm_slot(struct snd_soc_dai *codec_dai,
                return -EINVAL;
        }
 
-       aic3x->tdm_delay = lsb * slot_width;
+       switch (slot_width) {
+       case 16:
+       case 20:
+       case 24:
+       case 32:
+               break;
+       default:
+               dev_err(codec->dev, "Unsupported slot width %d\n", slot_width);
+               return -EINVAL;
+       }
+
+
+       aic3x->tdm_delay = lsb;
+       aic3x->slot_width = slot_width;
 
        /* DOUT in high-impedance on inactive bit clocks */
        snd_soc_update_bits(codec, AIC3X_ASD_INTF_CTRLA,
index 9756578fc7526a3182039bad491d3388e297de57..c04c0bc6f58ad434de2bbe5825be1b41182b1c24 100644 (file)
 struct wm5110_priv {
        struct arizona_priv core;
        struct arizona_fll fll[2];
+
+       unsigned int in_value;
+       int in_pre_pending;
+       int in_post_pending;
+
+       unsigned int in_pga_cache[6];
 };
 
 static const struct wm_adsp_region wm5110_dsp1_regions[] = {
@@ -428,6 +434,127 @@ err:
        return ret;
 }
 
+static int wm5110_in_pga_get(struct snd_kcontrol *kcontrol,
+                            struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+       struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+       struct snd_soc_card *card = dapm->card;
+       int ret;
+
+       /*
+        * PGA Volume is also used as part of the enable sequence, so
+        * usage of it should be avoided whilst that is running.
+        */
+       mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
+
+       ret = snd_soc_get_volsw_range(kcontrol, ucontrol);
+
+       mutex_unlock(&card->dapm_mutex);
+
+       return ret;
+}
+
+static int wm5110_in_pga_put(struct snd_kcontrol *kcontrol,
+                            struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+       struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+       struct snd_soc_card *card = dapm->card;
+       int ret;
+
+       /*
+        * PGA Volume is also used as part of the enable sequence, so
+        * usage of it should be avoided whilst that is running.
+        */
+       mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
+
+       ret = snd_soc_put_volsw_range(kcontrol, ucontrol);
+
+       mutex_unlock(&card->dapm_mutex);
+
+       return ret;
+}
+
+static int wm5110_in_analog_ev(struct snd_soc_dapm_widget *w,
+                              struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+       struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+       struct wm5110_priv *wm5110 = snd_soc_codec_get_drvdata(codec);
+       struct arizona *arizona = priv->arizona;
+       unsigned int reg, mask;
+       struct reg_sequence analog_seq[] = {
+               { 0x80, 0x3 },
+               { 0x35d, 0 },
+               { 0x80, 0x0 },
+       };
+
+       reg = ARIZONA_IN1L_CONTROL + ((w->shift ^ 0x1) * 4);
+       mask = ARIZONA_IN1L_PGA_VOL_MASK;
+
+       switch (event) {
+       case SND_SOC_DAPM_WILL_PMU:
+               wm5110->in_value |= 0x3 << ((w->shift ^ 0x1) * 2);
+               wm5110->in_pre_pending++;
+               wm5110->in_post_pending++;
+               return 0;
+       case SND_SOC_DAPM_PRE_PMU:
+               wm5110->in_pga_cache[w->shift] = snd_soc_read(codec, reg);
+
+               snd_soc_update_bits(codec, reg, mask,
+                                   0x40 << ARIZONA_IN1L_PGA_VOL_SHIFT);
+
+               wm5110->in_pre_pending--;
+               if (wm5110->in_pre_pending == 0) {
+                       analog_seq[1].def = wm5110->in_value;
+                       regmap_multi_reg_write_bypassed(arizona->regmap,
+                                                       analog_seq,
+                                                       ARRAY_SIZE(analog_seq));
+
+                       msleep(55);
+
+                       wm5110->in_value = 0;
+               }
+
+               break;
+       case SND_SOC_DAPM_POST_PMU:
+               snd_soc_update_bits(codec, reg, mask,
+                                   wm5110->in_pga_cache[w->shift]);
+
+               wm5110->in_post_pending--;
+               if (wm5110->in_post_pending == 0)
+                       regmap_multi_reg_write_bypassed(arizona->regmap,
+                                                       analog_seq,
+                                                       ARRAY_SIZE(analog_seq));
+               break;
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+static int wm5110_in_ev(struct snd_soc_dapm_widget *w,
+                       struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+       struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+       struct arizona *arizona = priv->arizona;
+
+       switch (arizona->rev) {
+       case 0 ... 4:
+               if (arizona_input_analog(codec, w->shift))
+                       wm5110_in_analog_ev(w, kcontrol, event);
+
+               break;
+       default:
+               break;
+       }
+
+       return arizona_in_ev(w, kcontrol, event);
+}
+
 static DECLARE_TLV_DB_SCALE(ana_tlv, 0, 100, 0);
 static DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
 static DECLARE_TLV_DB_SCALE(digital_tlv, -6400, 50, 0);
@@ -454,18 +581,24 @@ SOC_ENUM("IN2 OSR", arizona_in_dmic_osr[1]),
 SOC_ENUM("IN3 OSR", arizona_in_dmic_osr[2]),
 SOC_ENUM("IN4 OSR", arizona_in_dmic_osr[3]),
 
-SOC_SINGLE_RANGE_TLV("IN1L Volume", ARIZONA_IN1L_CONTROL,
-                    ARIZONA_IN1L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
-SOC_SINGLE_RANGE_TLV("IN1R Volume", ARIZONA_IN1R_CONTROL,
-                    ARIZONA_IN1R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
-SOC_SINGLE_RANGE_TLV("IN2L Volume", ARIZONA_IN2L_CONTROL,
-                    ARIZONA_IN2L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
-SOC_SINGLE_RANGE_TLV("IN2R Volume", ARIZONA_IN2R_CONTROL,
-                    ARIZONA_IN2R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
-SOC_SINGLE_RANGE_TLV("IN3L Volume", ARIZONA_IN3L_CONTROL,
-                    ARIZONA_IN3L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
-SOC_SINGLE_RANGE_TLV("IN3R Volume", ARIZONA_IN3R_CONTROL,
-                    ARIZONA_IN3R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
+SOC_SINGLE_RANGE_EXT_TLV("IN1L Volume", ARIZONA_IN1L_CONTROL,
+                        ARIZONA_IN1L_PGA_VOL_SHIFT, 0x40, 0x5f, 0,
+                        wm5110_in_pga_get, wm5110_in_pga_put, ana_tlv),
+SOC_SINGLE_RANGE_EXT_TLV("IN1R Volume", ARIZONA_IN1R_CONTROL,
+                        ARIZONA_IN1R_PGA_VOL_SHIFT, 0x40, 0x5f, 0,
+                        wm5110_in_pga_get, wm5110_in_pga_put, ana_tlv),
+SOC_SINGLE_RANGE_EXT_TLV("IN2L Volume", ARIZONA_IN2L_CONTROL,
+                        ARIZONA_IN2L_PGA_VOL_SHIFT, 0x40, 0x5f, 0,
+                        wm5110_in_pga_get, wm5110_in_pga_put, ana_tlv),
+SOC_SINGLE_RANGE_EXT_TLV("IN2R Volume", ARIZONA_IN2R_CONTROL,
+                        ARIZONA_IN2R_PGA_VOL_SHIFT, 0x40, 0x5f, 0,
+                        wm5110_in_pga_get, wm5110_in_pga_put, ana_tlv),
+SOC_SINGLE_RANGE_EXT_TLV("IN3L Volume", ARIZONA_IN3L_CONTROL,
+                        ARIZONA_IN3L_PGA_VOL_SHIFT, 0x40, 0x5f, 0,
+                        wm5110_in_pga_get, wm5110_in_pga_put, ana_tlv),
+SOC_SINGLE_RANGE_EXT_TLV("IN3R Volume", ARIZONA_IN3R_CONTROL,
+                        ARIZONA_IN3R_PGA_VOL_SHIFT, 0x40, 0x5f, 0,
+                        wm5110_in_pga_get, wm5110_in_pga_put, ana_tlv),
 
 SOC_ENUM("IN HPF Cutoff Frequency", arizona_in_hpf_cut_enum),
 
@@ -896,29 +1029,35 @@ SND_SOC_DAPM_OUTPUT("DRC1 Signal Activity"),
 SND_SOC_DAPM_OUTPUT("DRC2 Signal Activity"),
 
 SND_SOC_DAPM_PGA_E("IN1L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN1L_ENA_SHIFT,
-                  0, NULL, 0, arizona_in_ev,
+                  0, NULL, 0, wm5110_in_ev,
                   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
-                  SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+                  SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+                  SND_SOC_DAPM_WILL_PMU),
 SND_SOC_DAPM_PGA_E("IN1R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN1R_ENA_SHIFT,
-                  0, NULL, 0, arizona_in_ev,
+                  0, NULL, 0, wm5110_in_ev,
                   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
-                  SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+                  SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+                  SND_SOC_DAPM_WILL_PMU),
 SND_SOC_DAPM_PGA_E("IN2L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN2L_ENA_SHIFT,
-                  0, NULL, 0, arizona_in_ev,
+                  0, NULL, 0, wm5110_in_ev,
                   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
-                  SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+                  SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+                  SND_SOC_DAPM_WILL_PMU),
 SND_SOC_DAPM_PGA_E("IN2R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN2R_ENA_SHIFT,
-                  0, NULL, 0, arizona_in_ev,
+                  0, NULL, 0, wm5110_in_ev,
                   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
-                  SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+                  SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+                  SND_SOC_DAPM_WILL_PMU),
 SND_SOC_DAPM_PGA_E("IN3L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN3L_ENA_SHIFT,
-                  0, NULL, 0, arizona_in_ev,
+                  0, NULL, 0, wm5110_in_ev,
                   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
-                  SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+                  SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+                  SND_SOC_DAPM_WILL_PMU),
 SND_SOC_DAPM_PGA_E("IN3R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN3R_ENA_SHIFT,
-                  0, NULL, 0, arizona_in_ev,
+                  0, NULL, 0, wm5110_in_ev,
                   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
-                  SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+                  SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+                  SND_SOC_DAPM_WILL_PMU),
 SND_SOC_DAPM_PGA_E("IN4L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN4L_ENA_SHIFT,
                   0, NULL, 0, arizona_in_ev,
                   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
index 7d45d98a861fccb65f32987e3760c2b74e487f9a..4495a40a94680600ff324bd035098ff3d73f594b 100644 (file)
@@ -80,12 +80,13 @@ struct davinci_mcasp {
 
        /* McASP specific data */
        int     tdm_slots;
+       u32     tdm_mask[2];
+       int     slot_width;
        u8      op_mode;
        u8      num_serializer;
        u8      *serial_dir;
        u8      version;
        u8      bclk_div;
-       u16     bclk_lrclk_ratio;
        int     streams;
        u32     irq_request[2];
        int     dma_request[2];
@@ -556,8 +557,21 @@ static int __davinci_mcasp_set_clkdiv(struct snd_soc_dai *dai, int div_id,
                        mcasp->bclk_div = div;
                break;
 
-       case 2:         /* BCLK/LRCLK ratio */
-               mcasp->bclk_lrclk_ratio = div;
+       case 2: /*
+                * BCLK/LRCLK ratio descries how many bit-clock cycles
+                * fit into one frame. The clock ratio is given for a
+                * full period of data (for I2S format both left and
+                * right channels), so it has to be divided by number
+                * of tdm-slots (for I2S - divided by 2).
+                * Instead of storing this ratio, we calculate a new
+                * tdm_slot width by dividing the the ratio by the
+                * number of configured tdm slots.
+                */
+               mcasp->slot_width = div / mcasp->tdm_slots;
+               if (div % mcasp->tdm_slots)
+                       dev_warn(mcasp->dev,
+                                "%s(): BCLK/LRCLK %d is not divisible by %d tdm slots",
+                                __func__, div, mcasp->tdm_slots);
                break;
 
        default:
@@ -596,12 +610,92 @@ static int davinci_mcasp_set_sysclk(struct snd_soc_dai *dai, int clk_id,
        return 0;
 }
 
+/* All serializers must have equal number of channels */
+static int davinci_mcasp_ch_constraint(struct davinci_mcasp *mcasp, int stream,
+                                      int serializers)
+{
+       struct snd_pcm_hw_constraint_list *cl = &mcasp->chconstr[stream];
+       unsigned int *list = (unsigned int *) cl->list;
+       int slots = mcasp->tdm_slots;
+       int i, count = 0;
+
+       if (mcasp->tdm_mask[stream])
+               slots = hweight32(mcasp->tdm_mask[stream]);
+
+       for (i = 2; i <= slots; i++)
+               list[count++] = i;
+
+       for (i = 2; i <= serializers; i++)
+               list[count++] = i*slots;
+
+       cl->count = count;
+
+       return 0;
+}
+
+static int davinci_mcasp_set_ch_constraints(struct davinci_mcasp *mcasp)
+{
+       int rx_serializers = 0, tx_serializers = 0, ret, i;
+
+       for (i = 0; i < mcasp->num_serializer; i++)
+               if (mcasp->serial_dir[i] == TX_MODE)
+                       tx_serializers++;
+               else if (mcasp->serial_dir[i] == RX_MODE)
+                       rx_serializers++;
+
+       ret = davinci_mcasp_ch_constraint(mcasp, SNDRV_PCM_STREAM_PLAYBACK,
+                                         tx_serializers);
+       if (ret)
+               return ret;
+
+       ret = davinci_mcasp_ch_constraint(mcasp, SNDRV_PCM_STREAM_CAPTURE,
+                                         rx_serializers);
+
+       return ret;
+}
+
+
+static int davinci_mcasp_set_tdm_slot(struct snd_soc_dai *dai,
+                                     unsigned int tx_mask,
+                                     unsigned int rx_mask,
+                                     int slots, int slot_width)
+{
+       struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(dai);
+
+       dev_dbg(mcasp->dev,
+                "%s() tx_mask 0x%08x rx_mask 0x%08x slots %d width %d\n",
+                __func__, tx_mask, rx_mask, slots, slot_width);
+
+       if (tx_mask >= (1<<slots) || rx_mask >= (1<<slots)) {
+               dev_err(mcasp->dev,
+                       "Bad tdm mask tx: 0x%08x rx: 0x%08x slots %d\n",
+                       tx_mask, rx_mask, slots);
+               return -EINVAL;
+       }
+
+       if (slot_width &&
+           (slot_width < 8 || slot_width > 32 || slot_width % 4 != 0)) {
+               dev_err(mcasp->dev, "%s: Unsupported slot_width %d\n",
+                       __func__, slot_width);
+               return -EINVAL;
+       }
+
+       mcasp->tdm_slots = slots;
+       mcasp->tdm_mask[SNDRV_PCM_STREAM_PLAYBACK] = rx_mask;
+       mcasp->tdm_mask[SNDRV_PCM_STREAM_CAPTURE] = tx_mask;
+       mcasp->slot_width = slot_width;
+
+       return davinci_mcasp_set_ch_constraints(mcasp);
+}
+
 static int davinci_config_channel_size(struct davinci_mcasp *mcasp,
-                                      int word_length)
+                                      int sample_width)
 {
        u32 fmt;
-       u32 tx_rotate = (word_length / 4) & 0x7;
-       u32 mask = (1ULL << word_length) - 1;
+       u32 tx_rotate = (sample_width / 4) & 0x7;
+       u32 mask = (1ULL << sample_width) - 1;
+       u32 slot_width = sample_width;
+
        /*
         * For captured data we should not rotate, inversion and masking is
         * enoguh to get the data to the right position:
@@ -614,28 +708,23 @@ static int davinci_config_channel_size(struct davinci_mcasp *mcasp,
        u32 rx_rotate = 0;
 
        /*
-        * if s BCLK-to-LRCLK ratio has been configured via the set_clkdiv()
-        * callback, take it into account here. That allows us to for example
-        * send 32 bits per channel to the codec, while only 16 of them carry
-        * audio payload.
-        * The clock ratio is given for a full period of data (for I2S format
-        * both left and right channels), so it has to be divided by number of
-        * tdm-slots (for I2S - divided by 2).
+        * Setting the tdm slot width either with set_clkdiv() or
+        * set_tdm_slot() allows us to for example send 32 bits per
+        * channel to the codec, while only 16 of them carry audio
+        * payload.
         */
-       if (mcasp->bclk_lrclk_ratio) {
-               u32 slot_length = mcasp->bclk_lrclk_ratio / mcasp->tdm_slots;
-
+       if (mcasp->slot_width) {
                /*
-                * When we have more bclk then it is needed for the data, we
-                * need to use the rotation to move the received samples to have
-                * correct alignment.
+                * When we have more bclk then it is needed for the
+                * data, we need to use the rotation to move the
+                * received samples to have correct alignment.
                 */
-               rx_rotate = (slot_length - word_length) / 4;
-               word_length = slot_length;
+               slot_width = mcasp->slot_width;
+               rx_rotate = (slot_width - sample_width) / 4;
        }
 
        /* mapping of the XSSZ bit-field as described in the datasheet */
-       fmt = (word_length >> 1) - 1;
+       fmt = (slot_width >> 1) - 1;
 
        if (mcasp->op_mode != DAVINCI_MCASP_DIT_MODE) {
                mcasp_mod_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, RXSSZ(fmt),
@@ -776,33 +865,50 @@ static int mcasp_i2s_hw_param(struct davinci_mcasp *mcasp, int stream,
 
        /*
         * If more than one serializer is needed, then use them with
-        * their specified tdm_slots count. Otherwise, one serializer
-        * can cope with the transaction using as many slots as channels
-        * in the stream, requires channels symmetry
+        * all the specified tdm_slots. Otherwise, one serializer can
+        * cope with the transaction using just as many slots as there
+        * are channels in the stream.
         */
-       active_serializers = (channels + total_slots - 1) / total_slots;
-       if (active_serializers == 1)
-               active_slots = channels;
-       else
-               active_slots = total_slots;
-
-       for (i = 0; i < active_slots; i++)
-               mask |= (1 << i);
+       if (mcasp->tdm_mask[stream]) {
+               active_slots = hweight32(mcasp->tdm_mask[stream]);
+               active_serializers = (channels + active_slots - 1) /
+                       active_slots;
+               if (active_serializers == 1) {
+                       active_slots = channels;
+                       for (i = 0; i < total_slots; i++) {
+                               if ((1 << i) & mcasp->tdm_mask[stream]) {
+                                       mask |= (1 << i);
+                                       if (--active_slots <= 0)
+                                               break;
+                               }
+                       }
+               }
+       } else {
+               active_serializers = (channels + total_slots - 1) / total_slots;
+               if (active_serializers == 1)
+                       active_slots = channels;
+               else
+                       active_slots = total_slots;
 
+               for (i = 0; i < active_slots; i++)
+                       mask |= (1 << i);
+       }
        mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, TX_ASYNC);
 
        if (!mcasp->dat_port)
                busel = TXSEL;
 
-       mcasp_set_reg(mcasp, DAVINCI_MCASP_TXTDM_REG, mask);
-       mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, busel | TXORD);
-       mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG,
-                      FSXMOD(total_slots), FSXMOD(0x1FF));
-
-       mcasp_set_reg(mcasp, DAVINCI_MCASP_RXTDM_REG, mask);
-       mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, busel | RXORD);
-       mcasp_mod_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG,
-                      FSRMOD(total_slots), FSRMOD(0x1FF));
+       if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               mcasp_set_reg(mcasp, DAVINCI_MCASP_TXTDM_REG, mask);
+               mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, busel | TXORD);
+               mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG,
+                              FSXMOD(total_slots), FSXMOD(0x1FF));
+       } else if (stream == SNDRV_PCM_STREAM_CAPTURE) {
+               mcasp_set_reg(mcasp, DAVINCI_MCASP_RXTDM_REG, mask);
+               mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, busel | RXORD);
+               mcasp_mod_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG,
+                              FSRMOD(total_slots), FSRMOD(0x1FF));
+       }
 
        return 0;
 }
@@ -922,6 +1028,9 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream,
                int sbits = params_width(params);
                int ppm, div;
 
+               if (mcasp->slot_width)
+                       sbits = mcasp->slot_width;
+
                div = davinci_mcasp_calc_clk_div(mcasp, rate*sbits*slots,
                                                 &ppm);
                if (ppm)
@@ -1027,6 +1136,9 @@ static int davinci_mcasp_hw_rule_rate(struct snd_pcm_hw_params *params,
        struct snd_interval range;
        int i;
 
+       if (rd->mcasp->slot_width)
+               sbits = rd->mcasp->slot_width;
+
        snd_interval_any(&range);
        range.empty = 1;
 
@@ -1069,10 +1181,14 @@ static int davinci_mcasp_hw_rule_format(struct snd_pcm_hw_params *params,
 
        for (i = 0; i < SNDRV_PCM_FORMAT_LAST; i++) {
                if (snd_mask_test(fmt, i)) {
-                       uint bclk_freq = snd_pcm_format_width(i)*slots*rate;
+                       uint sbits = snd_pcm_format_width(i);
                        int ppm;
 
-                       davinci_mcasp_calc_clk_div(rd->mcasp, bclk_freq, &ppm);
+                       if (rd->mcasp->slot_width)
+                               sbits = rd->mcasp->slot_width;
+
+                       davinci_mcasp_calc_clk_div(rd->mcasp, sbits*slots*rate,
+                                                  &ppm);
                        if (abs(ppm) < DAVINCI_MAX_RATE_ERROR_PPM) {
                                snd_mask_set(&nfmt, i);
                                count++;
@@ -1094,6 +1210,10 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream,
                                        &mcasp->ruledata[substream->stream];
        u32 max_channels = 0;
        int i, dir;
+       int tdm_slots = mcasp->tdm_slots;
+
+       if (mcasp->tdm_mask[substream->stream])
+               tdm_slots = hweight32(mcasp->tdm_mask[substream->stream]);
 
        mcasp->substreams[substream->stream] = substream;
 
@@ -1114,7 +1234,7 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream,
                        max_channels++;
        }
        ruledata->serializers = max_channels;
-       max_channels *= mcasp->tdm_slots;
+       max_channels *= tdm_slots;
        /*
         * If the already active stream has less channels than the calculated
         * limnit based on the seirializers * tdm_slots, we need to use that as
@@ -1124,15 +1244,25 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream,
         */
        if (mcasp->channels && mcasp->channels < max_channels)
                max_channels = mcasp->channels;
+       /*
+        * But we can always allow channels upto the amount of
+        * the available tdm_slots.
+        */
+       if (max_channels < tdm_slots)
+               max_channels = tdm_slots;
 
        snd_pcm_hw_constraint_minmax(substream->runtime,
                                     SNDRV_PCM_HW_PARAM_CHANNELS,
                                     2, max_channels);
 
-       if (mcasp->chconstr[substream->stream].count)
-               snd_pcm_hw_constraint_list(substream->runtime,
-                                          0, SNDRV_PCM_HW_PARAM_CHANNELS,
-                                          &mcasp->chconstr[substream->stream]);
+       snd_pcm_hw_constraint_list(substream->runtime,
+                                  0, SNDRV_PCM_HW_PARAM_CHANNELS,
+                                  &mcasp->chconstr[substream->stream]);
+
+       if (mcasp->slot_width)
+               snd_pcm_hw_constraint_minmax(substream->runtime,
+                                            SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
+                                            8, mcasp->slot_width);
 
        /*
         * If we rely on implicit BCLK divider setting we should
@@ -1184,6 +1314,7 @@ static const struct snd_soc_dai_ops davinci_mcasp_dai_ops = {
        .set_fmt        = davinci_mcasp_set_dai_fmt,
        .set_clkdiv     = davinci_mcasp_set_clkdiv,
        .set_sysclk     = davinci_mcasp_set_sysclk,
+       .set_tdm_slot   = davinci_mcasp_set_tdm_slot,
 };
 
 static int davinci_mcasp_dai_probe(struct snd_soc_dai *dai)
@@ -1514,59 +1645,6 @@ nodata:
        return  pdata;
 }
 
-/* All serializers must have equal number of channels */
-static int davinci_mcasp_ch_constraint(struct davinci_mcasp *mcasp,
-                                      struct snd_pcm_hw_constraint_list *cl,
-                                      int serializers)
-{
-       unsigned int *list;
-       int i, count = 0;
-
-       if (serializers <= 1)
-               return 0;
-
-       list = devm_kzalloc(mcasp->dev, sizeof(unsigned int) *
-                           (mcasp->tdm_slots + serializers - 2),
-                           GFP_KERNEL);
-       if (!list)
-               return -ENOMEM;
-
-       for (i = 2; i <= mcasp->tdm_slots; i++)
-               list[count++] = i;
-
-       for (i = 2; i <= serializers; i++)
-               list[count++] = i*mcasp->tdm_slots;
-
-       cl->count = count;
-       cl->list = list;
-
-       return 0;
-}
-
-
-static int davinci_mcasp_init_ch_constraints(struct davinci_mcasp *mcasp)
-{
-       int rx_serializers = 0, tx_serializers = 0, ret, i;
-
-       for (i = 0; i < mcasp->num_serializer; i++)
-               if (mcasp->serial_dir[i] == TX_MODE)
-                       tx_serializers++;
-               else if (mcasp->serial_dir[i] == RX_MODE)
-                       rx_serializers++;
-
-       ret = davinci_mcasp_ch_constraint(mcasp, &mcasp->chconstr[
-                                                 SNDRV_PCM_STREAM_PLAYBACK],
-                                         tx_serializers);
-       if (ret)
-               return ret;
-
-       ret = davinci_mcasp_ch_constraint(mcasp, &mcasp->chconstr[
-                                                 SNDRV_PCM_STREAM_CAPTURE],
-                                         rx_serializers);
-
-       return ret;
-}
-
 enum {
        PCM_EDMA,
        PCM_SDMA,
@@ -1783,7 +1861,28 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
                mcasp->fifo_base = DAVINCI_MCASP_V3_AFIFO_BASE;
        }
 
-       ret = davinci_mcasp_init_ch_constraints(mcasp);
+       /* Allocate memory for long enough list for all possible
+        * scenarios. Maximum number tdm slots is 32 and there cannot
+        * be more serializers than given in the configuration.  The
+        * serializer directions could be taken into account, but it
+        * would make code much more complex and save only couple of
+        * bytes.
+        */
+       mcasp->chconstr[SNDRV_PCM_STREAM_PLAYBACK].list =
+               devm_kzalloc(mcasp->dev, sizeof(unsigned int) *
+                            (32 + mcasp->num_serializer - 2),
+                            GFP_KERNEL);
+
+       mcasp->chconstr[SNDRV_PCM_STREAM_CAPTURE].list =
+               devm_kzalloc(mcasp->dev, sizeof(unsigned int) *
+                            (32 + mcasp->num_serializer - 2),
+                            GFP_KERNEL);
+
+       if (!mcasp->chconstr[SNDRV_PCM_STREAM_PLAYBACK].list ||
+           !mcasp->chconstr[SNDRV_PCM_STREAM_CAPTURE].list)
+               return -ENOMEM;
+
+       ret = davinci_mcasp_set_ch_constraints(mcasp);
        if (ret)
                goto err;
 
index 96f55ae75c719c87d1dcf9084ccc4e6a679a187f..0901d5e20df2823c8cd19393007ed0ed946caf7f 100644 (file)
@@ -593,6 +593,7 @@ static const struct of_device_id fsl_asoc_card_dt_ids[] = {
        { .compatible = "fsl,imx-audio-wm8960", },
        {}
 };
+MODULE_DEVICE_TABLE(of, fsl_asoc_card_dt_ids);
 
 static struct platform_driver fsl_asoc_card_driver = {
        .probe = fsl_asoc_card_probe,
index a18fd92c4a85c3d67306c544c1af6652789d1467..9366b5a42e1defb9f9d274d142ef9d1fef25104c 100644 (file)
@@ -801,6 +801,7 @@ static const struct of_device_id fsl_sai_ids[] = {
        { .compatible = "fsl,imx6sx-sai", },
        { /* sentinel */ }
 };
+MODULE_DEVICE_TABLE(of, fsl_sai_ids);
 
 static struct platform_driver fsl_sai_driver = {
        .probe = fsl_sai_probe,
index 3ff76d419436ebbac7ac194e42510495a715f183..54c33204541fdb23dc78c8e2e801f3075fc1fd11 100644 (file)
@@ -151,7 +151,9 @@ static int __asoc_simple_card_dai_init(struct snd_soc_dai *dai,
        }
 
        if (set->slots) {
-               ret = snd_soc_dai_set_tdm_slot(dai, 0, 0,
+               ret = snd_soc_dai_set_tdm_slot(dai,
+                                              set->tx_slot_mask,
+                                              set->rx_slot_mask,
                                                set->slots,
                                                set->slot_width);
                if (ret && ret != -ENOTSUPP) {
@@ -243,7 +245,9 @@ asoc_simple_card_sub_parse_of(struct device_node *np,
                return ret;
 
        /* Parse TDM slot */
-       ret = snd_soc_of_parse_tdm_slot(np, &dai->slots, &dai->slot_width);
+       ret = snd_soc_of_parse_tdm_slot(np, &dai->tx_slot_mask,
+                                       &dai->rx_slot_mask,
+                                       &dai->slots, &dai->slot_width);
        if (ret)
                return ret;
 
index 683e5011615241b026d91c4056ce3cb28e97c537..5e9c316c142aaf9f17b093a03d17f812ba9e06ae 100644 (file)
@@ -368,23 +368,6 @@ static void sst_media_close(struct snd_pcm_substream *substream,
        kfree(stream);
 }
 
-static inline unsigned int get_current_pipe_id(struct snd_soc_dai *dai,
-                                              struct snd_pcm_substream *substream)
-{
-       struct sst_data *sst = snd_soc_dai_get_drvdata(dai);
-       struct sst_dev_stream_map *map = sst->pdata->pdev_strm_map;
-       struct sst_runtime_stream *stream =
-                       substream->runtime->private_data;
-       u32 str_id = stream->stream_info.str_id;
-       unsigned int pipe_id;
-
-       pipe_id = map[str_id].device_id;
-
-       dev_dbg(dai->dev, "got pipe_id = %#x for str_id = %d\n",
-                       pipe_id, str_id);
-       return pipe_id;
-}
-
 static int sst_media_prepare(struct snd_pcm_substream *substream,
                struct snd_soc_dai *dai)
 {
index 8bafaf6ceab1266f6b85a0d6ca1e4ee04bc5046f..3f8a1e10bed02841f6b437d3d4a24615879ca35e 100644 (file)
@@ -266,18 +266,11 @@ static int broadwell_audio_probe(struct platform_device *pdev)
 {
        broadwell_rt286.dev = &pdev->dev;
 
-       return snd_soc_register_card(&broadwell_rt286);
-}
-
-static int broadwell_audio_remove(struct platform_device *pdev)
-{
-       snd_soc_unregister_card(&broadwell_rt286);
-       return 0;
+       return devm_snd_soc_register_card(&pdev->dev, &broadwell_rt286);
 }
 
 static struct platform_driver broadwell_audio = {
        .probe = broadwell_audio_probe,
-       .remove = broadwell_audio_remove,
        .driver = {
                .name = "broadwell-audio",
        },
index 7d617bf493bc7a1295560d034d76ba161e2eb431..bea26730873c0fffc63c27d4c913da507e71e9ca 100644 (file)
@@ -509,17 +509,6 @@ static struct snd_soc_dai_driver skl_platform_dai[] = {
                .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
        },
 },
-{
-       .name = "DMIC23 Pin",
-       .ops = &skl_dmic_dai_ops,
-       .capture = {
-               .stream_name = "DMIC23 Rx",
-               .channels_min = HDA_STEREO,
-               .channels_max = HDA_STEREO,
-               .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_16000,
-               .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
-       },
-},
 {
        .name = "HD-Codec Pin",
        .ops = &skl_link_dai_ops,
@@ -538,28 +527,6 @@ static struct snd_soc_dai_driver skl_platform_dai[] = {
                .formats = SNDRV_PCM_FMTBIT_S16_LE,
        },
 },
-{
-       .name = "HD-Codec-SPK Pin",
-       .ops = &skl_link_dai_ops,
-       .playback = {
-               .stream_name = "HD-Codec-SPK Tx",
-               .channels_min = HDA_STEREO,
-               .channels_max = HDA_STEREO,
-               .rates = SNDRV_PCM_RATE_48000,
-               .formats = SNDRV_PCM_FMTBIT_S16_LE,
-       },
-},
-{
-       .name = "HD-Codec-AMIC Pin",
-       .ops = &skl_link_dai_ops,
-       .capture = {
-               .stream_name = "HD-Codec-AMIC Rx",
-               .channels_min = HDA_STEREO,
-               .channels_max = HDA_STEREO,
-               .rates = SNDRV_PCM_RATE_48000,
-               .formats = SNDRV_PCM_FMTBIT_S16_LE,
-       },
-},
 };
 
 static int skl_platform_open(struct snd_pcm_substream *substream)
index b05fb1c1a8483e18ea7e5226325563ea82d044c0..794a3499e567c3a907e70b8c1c57aafce6aab207 100644 (file)
@@ -485,6 +485,7 @@ static const struct of_device_id jz4740_of_matches[] = {
        { .compatible = "ingenic,jz4780-i2s", .data = (void *)JZ_I2S_JZ4780 },
        { /* sentinel */ }
 };
+MODULE_DEVICE_TABLE(of, jz4740_of_matches);
 #endif
 
 static int jz4740_i2s_dev_probe(struct platform_device *pdev)
index de7563bdc5c28e2c7954a00ca9929e508176d718..e0304d544f26ba45660e94fa98942d2416ddb94a 100644 (file)
@@ -130,6 +130,7 @@ static const struct of_device_id a370db_dt_ids[] = {
        { .compatible = "marvell,a370db-audio" },
        { },
 };
+MODULE_DEVICE_TABLE(of, a370db_dt_ids);
 
 static struct platform_driver a370db_driver = {
        .driver         = {
index 684e8a78bed0640310e0b54e292df725b409fe22..71a1a35047bafd085881efd5369f5275bf9a050f 100644 (file)
@@ -179,21 +179,13 @@ static int mt8173_max98090_dev_probe(struct platform_device *pdev)
        }
        card->dev = &pdev->dev;
 
-       ret = snd_soc_register_card(card);
+       ret = devm_snd_soc_register_card(&pdev->dev, card);
        if (ret)
                dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n",
                        __func__, ret);
        return ret;
 }
 
-static int mt8173_max98090_dev_remove(struct platform_device *pdev)
-{
-       struct snd_soc_card *card = platform_get_drvdata(pdev);
-
-       snd_soc_unregister_card(card);
-       return 0;
-}
-
 static const struct of_device_id mt8173_max98090_dt_match[] = {
        { .compatible = "mediatek,mt8173-max98090", },
        { }
@@ -209,7 +201,6 @@ static struct platform_driver mt8173_max98090_driver = {
 #endif
        },
        .probe = mt8173_max98090_dev_probe,
-       .remove = mt8173_max98090_dev_remove,
 };
 
 module_platform_driver(mt8173_max98090_driver);
index 86cf9752f18a343791eaeeff8ca8ab9c2c4a83e8..50ba538eccb3f771da9c08685d4e33aa0cbfa80b 100644 (file)
@@ -246,21 +246,13 @@ static int mt8173_rt5650_rt5676_dev_probe(struct platform_device *pdev)
        card->dev = &pdev->dev;
        platform_set_drvdata(pdev, card);
 
-       ret = snd_soc_register_card(card);
+       ret = devm_snd_soc_register_card(&pdev->dev, card);
        if (ret)
                dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n",
                        __func__, ret);
        return ret;
 }
 
-static int mt8173_rt5650_rt5676_dev_remove(struct platform_device *pdev)
-{
-       struct snd_soc_card *card = platform_get_drvdata(pdev);
-
-       snd_soc_unregister_card(card);
-       return 0;
-}
-
 static const struct of_device_id mt8173_rt5650_rt5676_dt_match[] = {
        { .compatible = "mediatek,mt8173-rt5650-rt5676", },
        { }
@@ -276,7 +268,6 @@ static struct platform_driver mt8173_rt5650_rt5676_driver = {
 #endif
        },
        .probe = mt8173_rt5650_rt5676_dev_probe,
-       .remove = mt8173_rt5650_rt5676_dev_remove,
 };
 
 module_platform_driver(mt8173_rt5650_rt5676_driver);
index 6e6fce6a14ba6dc1c2ab14d054df8f5b07b2caea..2b23ffbac6b126cdff2674f68133dea77c40f546 100644 (file)
@@ -142,7 +142,7 @@ static int mxs_sgtl5000_probe(struct platform_device *pdev)
        card->dev = &pdev->dev;
        platform_set_drvdata(pdev, card);
 
-       ret = snd_soc_register_card(card);
+       ret = devm_snd_soc_register_card(&pdev->dev, card);
        if (ret) {
                dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
                        ret);
@@ -154,12 +154,8 @@ static int mxs_sgtl5000_probe(struct platform_device *pdev)
 
 static int mxs_sgtl5000_remove(struct platform_device *pdev)
 {
-       struct snd_soc_card *card = platform_get_drvdata(pdev);
-
        mxs_saif_put_mclk(0);
 
-       snd_soc_unregister_card(card);
-
        return 0;
 }
 
index 2b26318bc20097b0c5e369b50f151b7bde511e59..6147e86e9b0fc2f0addc82a2374c7aa3f095bbb8 100644 (file)
@@ -116,26 +116,19 @@ static int brownstone_probe(struct platform_device *pdev)
        int ret;
 
        brownstone.dev = &pdev->dev;
-       ret = snd_soc_register_card(&brownstone);
+       ret = devm_snd_soc_register_card(&pdev->dev, &brownstone);
        if (ret)
                dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
                                ret);
        return ret;
 }
 
-static int brownstone_remove(struct platform_device *pdev)
-{
-       snd_soc_unregister_card(&brownstone);
-       return 0;
-}
-
 static struct platform_driver mmp_driver = {
        .driver         = {
                .name   = "brownstone-audio",
                .pm     = &snd_soc_pm_ops,
        },
        .probe          = brownstone_probe,
-       .remove         = brownstone_remove,
 };
 
 module_platform_driver(mmp_driver);
index 3580d10c9f282312056d6ad3952b6adbcf632ffe..c97dc13d36087628433de516a1b2217df02b492b 100644 (file)
@@ -295,28 +295,19 @@ static int corgi_probe(struct platform_device *pdev)
 
        card->dev = &pdev->dev;
 
-       ret = snd_soc_register_card(card);
+       ret = devm_snd_soc_register_card(&pdev->dev, card);
        if (ret)
                dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
                        ret);
        return ret;
 }
 
-static int corgi_remove(struct platform_device *pdev)
-{
-       struct snd_soc_card *card = platform_get_drvdata(pdev);
-
-       snd_soc_unregister_card(card);
-       return 0;
-}
-
 static struct platform_driver corgi_driver = {
        .driver         = {
                .name   = "corgi-audio",
                .pm     = &snd_soc_pm_ops,
        },
        .probe          = corgi_probe,
-       .remove         = corgi_remove,
 };
 
 module_platform_driver(corgi_driver);
index d72e124a3676bf545be9d0afa49e6180973a00b2..1de876529aa128c39f9fc5000dcb371a778849a3 100644 (file)
@@ -138,7 +138,7 @@ static int e740_probe(struct platform_device *pdev)
 
        card->dev = &pdev->dev;
 
-       ret = snd_soc_register_card(card);
+       ret = devm_snd_soc_register_card(&pdev->dev, card);
        if (ret) {
                dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
                        ret);
@@ -149,10 +149,7 @@ static int e740_probe(struct platform_device *pdev)
 
 static int e740_remove(struct platform_device *pdev)
 {
-       struct snd_soc_card *card = platform_get_drvdata(pdev);
-
        gpio_free_array(e740_audio_gpios, ARRAY_SIZE(e740_audio_gpios));
-       snd_soc_unregister_card(card);
        return 0;
 }
 
index 48f2d7c2e68c2621f7cce3c6225a166ae8f0920f..b7eb7cd5df7d0814e4c4abe172215d38d2dd5ab1 100644 (file)
@@ -120,7 +120,7 @@ static int e750_probe(struct platform_device *pdev)
 
        card->dev = &pdev->dev;
 
-       ret = snd_soc_register_card(card);
+       ret = devm_snd_soc_register_card(&pdev->dev, card);
        if (ret) {
                dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
                        ret);
@@ -131,10 +131,7 @@ static int e750_probe(struct platform_device *pdev)
 
 static int e750_remove(struct platform_device *pdev)
 {
-       struct snd_soc_card *card = platform_get_drvdata(pdev);
-
        gpio_free_array(e750_audio_gpios, ARRAY_SIZE(e750_audio_gpios));
-       snd_soc_unregister_card(card);
        return 0;
 }
 
index 45d4bd46fff60d08dbda013b5e7b8f8cefd1851e..41bf71466a7bb619c06de89e81d4e1b6b731c8fb 100644 (file)
@@ -119,7 +119,7 @@ static int e800_probe(struct platform_device *pdev)
 
        card->dev = &pdev->dev;
 
-       ret = snd_soc_register_card(card);
+       ret = devm_snd_soc_register_card(&pdev->dev, card);
        if (ret) {
                dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
                        ret);
@@ -130,10 +130,7 @@ static int e800_probe(struct platform_device *pdev)
 
 static int e800_remove(struct platform_device *pdev)
 {
-       struct snd_soc_card *card = platform_get_drvdata(pdev);
-
        gpio_free_array(e800_audio_gpios, ARRAY_SIZE(e800_audio_gpios));
-       snd_soc_unregister_card(card);
        return 0;
 }
 
index 9f8be7cd567e48c83218624ddfe2cad538ef28d7..ecbf2873b7ffd8e205ce86fdd7dfa743ebe9435c 100644 (file)
@@ -193,7 +193,7 @@ static int hx4700_audio_probe(struct platform_device *pdev)
                return ret;
 
        snd_soc_card_hx4700.dev = &pdev->dev;
-       ret = snd_soc_register_card(&snd_soc_card_hx4700);
+       ret = devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_hx4700);
        if (ret)
                gpio_free_array(hx4700_audio_gpios,
                                ARRAY_SIZE(hx4700_audio_gpios));
@@ -203,8 +203,6 @@ static int hx4700_audio_probe(struct platform_device *pdev)
 
 static int hx4700_audio_remove(struct platform_device *pdev)
 {
-       snd_soc_unregister_card(&snd_soc_card_hx4700);
-
        gpio_set_value(GPIO92_HX4700_HP_DRIVER, 0);
        gpio_set_value(GPIO107_HX4700_SPK_nSD, 0);
 
index 29fabbfd21f1796cd91142c0e99d7addfcc5f924..9d0e40771ef5b01de482aeb9daef67c15f30297f 100644 (file)
@@ -72,28 +72,19 @@ static int imote2_probe(struct platform_device *pdev)
 
        card->dev = &pdev->dev;
 
-       ret = snd_soc_register_card(card);
+       ret = devm_snd_soc_register_card(&pdev->dev, card);
        if (ret)
                dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
                        ret);
        return ret;
 }
 
-static int imote2_remove(struct platform_device *pdev)
-{
-       struct snd_soc_card *card = platform_get_drvdata(pdev);
-
-       snd_soc_unregister_card(card);
-       return 0;
-}
-
 static struct platform_driver imote2_driver = {
        .driver         = {
                .name   = "imote2-audio",
                .pm     = &snd_soc_pm_ops,
        },
        .probe          = imote2_probe,
-       .remove         = imote2_remove,
 };
 
 module_platform_driver(imote2_driver);
index a9615a574546988f6fc8e83fe5a018dc05528cba..29bc60e85e92898e585a57202ad2540f4be61cae 100644 (file)
@@ -181,7 +181,7 @@ static int mioa701_wm9713_probe(struct platform_device *pdev)
                return -ENODEV;
 
        mioa701.dev = &pdev->dev;
-       rc =  snd_soc_register_card(&mioa701);
+       rc = devm_snd_soc_register_card(&pdev->dev, &mioa701);
        if (!rc)
                dev_warn(&pdev->dev, "Be warned that incorrect mixers/muxes setup will"
                         "lead to overheating and possible destruction of your device."
@@ -189,17 +189,8 @@ static int mioa701_wm9713_probe(struct platform_device *pdev)
        return rc;
 }
 
-static int mioa701_wm9713_remove(struct platform_device *pdev)
-{
-       struct snd_soc_card *card = platform_get_drvdata(pdev);
-
-       snd_soc_unregister_card(card);
-       return 0;
-}
-
 static struct platform_driver mioa701_wm9713_driver = {
        .probe          = mioa701_wm9713_probe,
-       .remove         = mioa701_wm9713_remove,
        .driver         = {
                .name           = "mioa701-wm9713",
                .pm     = &snd_soc_pm_ops,
index c20bbc042425dacb2a478d247f9087b87ba8469c..4e74d9573f032eb77cb79a05f16cfd3da95e4f5b 100644 (file)
@@ -140,22 +140,15 @@ static int palm27x_asoc_probe(struct platform_device *pdev)
 
        palm27x_asoc.dev = &pdev->dev;
 
-       ret = snd_soc_register_card(&palm27x_asoc);
+       ret = devm_snd_soc_register_card(&pdev->dev, &palm27x_asoc);
        if (ret)
                dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
                        ret);
        return ret;
 }
 
-static int palm27x_asoc_remove(struct platform_device *pdev)
-{
-       snd_soc_unregister_card(&palm27x_asoc);
-       return 0;
-}
-
 static struct platform_driver palm27x_wm9712_driver = {
        .probe          = palm27x_asoc_probe,
-       .remove         = palm27x_asoc_remove,
        .driver         = {
                .name           = "palm27x-asoc",
                .pm     = &snd_soc_pm_ops,
index 80b457ac522acb2c9bdbf1318dfc59bbd36d3789..84d0e2e508088a260b54911bca03236a50046203 100644 (file)
@@ -267,28 +267,19 @@ static int poodle_probe(struct platform_device *pdev)
 
        card->dev = &pdev->dev;
 
-       ret = snd_soc_register_card(card);
+       ret = devm_snd_soc_register_card(&pdev->dev, card);
        if (ret)
                dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
                        ret);
        return ret;
 }
 
-static int poodle_remove(struct platform_device *pdev)
-{
-       struct snd_soc_card *card = platform_get_drvdata(pdev);
-
-       snd_soc_unregister_card(card);
-       return 0;
-}
-
 static struct platform_driver poodle_driver = {
        .driver         = {
                .name   = "poodle-audio",
                .pm     = &snd_soc_pm_ops,
        },
        .probe          = poodle_probe,
-       .remove         = poodle_remove,
 };
 
 module_platform_driver(poodle_driver);
index 3da485ec1de7fa9d968f1cfd819252258a35cc46..da03fad1b9cda1ad8fb31f4724db0d149948a27e 100644 (file)
@@ -809,6 +809,7 @@ static const struct of_device_id pxa_ssp_of_ids[] = {
        { .compatible = "mrvl,pxa-ssp-dai" },
        {}
 };
+MODULE_DEVICE_TABLE(of, pxa_ssp_of_ids);
 #endif
 
 static int asoc_ssp_probe(struct platform_device *pdev)
index 831ee37d2e3e714941855ebdef31bb3eaf15d26a..29a3fdbb7b599eb19b3a4e5f7d941d323ad7e267 100644 (file)
@@ -132,6 +132,7 @@ static const struct of_device_id snd_soc_pxa_audio_match[] = {
        { .compatible   = "mrvl,pxa-pcm-audio" },
        { }
 };
+MODULE_DEVICE_TABLE(of, snd_soc_pxa_audio_match);
 #endif
 
 static struct platform_driver pxa_pcm_driver = {
index 461123ad5ff2d4c56e21a97a14e27f8ce7d841c4..b00222620fd01be5cf00d4118d44f18832ad3bdc 100644 (file)
@@ -305,7 +305,7 @@ static int spitz_probe(struct platform_device *pdev)
 
        card->dev = &pdev->dev;
 
-       ret = snd_soc_register_card(card);
+       ret = devm_snd_soc_register_card(&pdev->dev, card);
        if (ret) {
                dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
                        ret);
@@ -322,9 +322,6 @@ err1:
 
 static int spitz_remove(struct platform_device *pdev)
 {
-       struct snd_soc_card *card = platform_get_drvdata(pdev);
-
-       snd_soc_unregister_card(card);
        gpio_free(spitz_mic_gpio);
        return 0;
 }
index f59f566551ef72ad4e0b5c4bb369d1c8b8de7ec0..49518dd642aa18ffd3f0004fe2f425210e820b54 100644 (file)
@@ -233,7 +233,7 @@ static int tosa_probe(struct platform_device *pdev)
 
        card->dev = &pdev->dev;
 
-       ret = snd_soc_register_card(card);
+       ret = devm_snd_soc_register_card(&pdev->dev, card);
        if (ret) {
                dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
                        ret);
@@ -244,10 +244,7 @@ static int tosa_probe(struct platform_device *pdev)
 
 static int tosa_remove(struct platform_device *pdev)
 {
-       struct snd_soc_card *card = platform_get_drvdata(pdev);
-
        gpio_free(TOSA_GPIO_L_MUTE);
-       snd_soc_unregister_card(card);
        return 0;
 }
 
index 1753c7d9e760e4bfa14e5b5ed8844b530e7413cf..65c20f779177589bcb7eb0ce8d61a2196806eb69 100644 (file)
@@ -128,7 +128,7 @@ static int ttc_dkb_probe(struct platform_device *pdev)
 
        card->dev = &pdev->dev;
 
-       ret = snd_soc_register_card(card);
+       ret = devm_snd_soc_register_card(&pdev->dev, card);
        if (ret)
                dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
                        ret);
@@ -136,22 +136,12 @@ static int ttc_dkb_probe(struct platform_device *pdev)
        return ret;
 }
 
-static int ttc_dkb_remove(struct platform_device *pdev)
-{
-       struct snd_soc_card *card = platform_get_drvdata(pdev);
-
-       snd_soc_unregister_card(card);
-
-       return 0;
-}
-
 static struct platform_driver ttc_dkb_driver = {
        .driver         = {
                .name   = "ttc-dkb-audio",
                .pm     = &snd_soc_pm_ops,
        },
        .probe          = ttc_dkb_probe,
-       .remove         = ttc_dkb_remove,
 };
 
 module_platform_driver(ttc_dkb_driver);
index 97bc2023f08aa81fff0b1524b6fb0f34e992c23b..e5101e0d2d372262f8ecf7d0498240cba971b86f 100644 (file)
@@ -438,7 +438,8 @@ int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev)
                if (IS_ERR(drvdata->mi2s_bit_clk[dai_id])) {
                        dev_err(&pdev->dev,
                                "%s() error getting mi2s-bit-clk: %ld\n",
-                               __func__, PTR_ERR(drvdata->mi2s_bit_clk[i]));
+                               __func__,
+                               PTR_ERR(drvdata->mi2s_bit_clk[dai_id]));
                        return PTR_ERR(drvdata->mi2s_bit_clk[dai_id]);
                }
        }
index 58bae8e2cf5ff7b9d1922feceaa0ac7347483561..570905709d3a8b2e7f7393d050fc47d4b7b8869f 100644 (file)
@@ -17,7 +17,7 @@ config SND_SOC_ROCKCHIP_I2S
 
 config SND_SOC_ROCKCHIP_MAX98090
        tristate "ASoC support for Rockchip boards using a MAX98090 codec"
-       depends on SND_SOC_ROCKCHIP && I2C && GPIOLIB
+       depends on SND_SOC_ROCKCHIP && I2C && GPIOLIB && CLKDEV_LOOKUP
        select SND_SOC_ROCKCHIP_I2S
        select SND_SOC_MAX98090
        select SND_SOC_TS3A227E
@@ -27,7 +27,7 @@ config SND_SOC_ROCKCHIP_MAX98090
 
 config SND_SOC_ROCKCHIP_RT5645
        tristate "ASoC support for Rockchip boards using a RT5645/RT5650 codec"
-       depends on SND_SOC_ROCKCHIP && I2C && GPIOLIB
+       depends on SND_SOC_ROCKCHIP && I2C && GPIOLIB && CLKDEV_LOOKUP
        select SND_SOC_ROCKCHIP_I2S
        select SND_SOC_RT5645
        help
index 07114b0b0dc12bcf23e0a25fb342ad3df23b2464..6ca90aaf141f62604c3756b88ffc1be4d024f718 100644 (file)
@@ -37,6 +37,7 @@ config SND_SOC_SH4_SIU
 config SND_SOC_RCAR
        tristate "R-Car series SRU/SCU/SSIU/SSI support"
        depends on DMA_OF
+       depends on COMMON_CLK
        select SND_SIMPLE_CARD
        select REGMAP_MMIO
        help
index fefc881dbac24acf8e190703973712d8ccc8f22c..c4ebbb7a7b6ffe90f01da89b3164cf23105117dd 100644 (file)
@@ -7,7 +7,7 @@
  * License.  See the file "COPYING" in the main directory of this archive
  * for more details.
  */
-#include <linux/sh_clk.h>
+#include <linux/clk-provider.h>
 #include "rsnd.h"
 
 #define CLKA   0
 #define CLKI   3
 #define CLKMAX 4
 
+#define CLKOUT 0
+#define CLKOUT1        1
+#define CLKOUT2        2
+#define CLKOUT3        3
+#define CLKOUTMAX 4
+
+#define BRRx_MASK(x) (0x3FF & x)
+
+static struct rsnd_mod_ops adg_ops = {
+       .name = "adg",
+};
+
 struct rsnd_adg {
        struct clk *clk[CLKMAX];
+       struct clk *clkout[CLKOUTMAX];
+       struct clk_onecell_data onecell;
+       struct rsnd_mod mod;
 
-       int rbga_rate_for_441khz_div_6; /* RBGA */
-       int rbgb_rate_for_48khz_div_6;  /* RBGB */
-       u32 ckr;
+       int rbga_rate_for_441khz; /* RBGA */
+       int rbgb_rate_for_48khz;  /* RBGB */
 };
 
 #define for_each_rsnd_clk(pos, adg, i)         \
@@ -29,8 +43,28 @@ struct rsnd_adg {
             (i < CLKMAX) &&                    \
             ((pos) = adg->clk[i]);             \
             i++)
+#define for_each_rsnd_clkout(pos, adg, i)      \
+       for (i = 0;                             \
+            (i < CLKOUTMAX) &&                 \
+            ((pos) = adg->clkout[i]);  \
+            i++)
 #define rsnd_priv_to_adg(priv) ((struct rsnd_adg *)(priv)->adg)
 
+static u32 rsnd_adg_calculate_rbgx(unsigned long div)
+{
+       int i, ratio;
+
+       if (!div)
+               return 0;
+
+       for (i = 3; i >= 0; i--) {
+               ratio = 2 << (i * 2);
+               if (0 == (div % ratio))
+                       return (u32)((i << 8) | ((div / ratio) - 1));
+       }
+
+       return ~0;
+}
 
 static u32 rsnd_adg_ssi_ws_timing_gen2(struct rsnd_dai_stream *io)
 {
@@ -60,6 +94,9 @@ static u32 rsnd_adg_ssi_ws_timing_gen2(struct rsnd_dai_stream *io)
 int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_mod *mod,
                                 struct rsnd_dai_stream *io)
 {
+       struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+       struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
+       struct rsnd_mod *adg_mod = rsnd_mod_get(adg);
        int id = rsnd_mod_id(mod);
        int shift = (id % 2) ? 16 : 0;
        u32 mask, val;
@@ -69,21 +106,26 @@ int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_mod *mod,
        val  = val      << shift;
        mask = 0xffff   << shift;
 
-       rsnd_mod_bset(mod, CMDOUT_TIMSEL, mask, val);
+       rsnd_mod_bset(adg_mod, CMDOUT_TIMSEL, mask, val);
 
        return 0;
 }
 
-static int rsnd_adg_set_src_timsel_gen2(struct rsnd_mod *mod,
+static int rsnd_adg_set_src_timsel_gen2(struct rsnd_mod *src_mod,
                                        struct rsnd_dai_stream *io,
                                        u32 timsel)
 {
+       struct rsnd_priv *priv = rsnd_mod_to_priv(src_mod);
+       struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
+       struct rsnd_mod *adg_mod = rsnd_mod_get(adg);
        int is_play = rsnd_io_is_play(io);
-       int id = rsnd_mod_id(mod);
+       int id = rsnd_mod_id(src_mod);
        int shift = (id % 2) ? 16 : 0;
        u32 mask, ws;
        u32 in, out;
 
+       rsnd_mod_confirm_src(src_mod);
+
        ws = rsnd_adg_ssi_ws_timing_gen2(io);
 
        in  = (is_play) ? timsel : ws;
@@ -95,37 +137,38 @@ static int rsnd_adg_set_src_timsel_gen2(struct rsnd_mod *mod,
 
        switch (id / 2) {
        case 0:
-               rsnd_mod_bset(mod, SRCIN_TIMSEL0,  mask, in);
-               rsnd_mod_bset(mod, SRCOUT_TIMSEL0, mask, out);
+               rsnd_mod_bset(adg_mod, SRCIN_TIMSEL0,  mask, in);
+               rsnd_mod_bset(adg_mod, SRCOUT_TIMSEL0, mask, out);
                break;
        case 1:
-               rsnd_mod_bset(mod, SRCIN_TIMSEL1,  mask, in);
-               rsnd_mod_bset(mod, SRCOUT_TIMSEL1, mask, out);
+               rsnd_mod_bset(adg_mod, SRCIN_TIMSEL1,  mask, in);
+               rsnd_mod_bset(adg_mod, SRCOUT_TIMSEL1, mask, out);
                break;
        case 2:
-               rsnd_mod_bset(mod, SRCIN_TIMSEL2,  mask, in);
-               rsnd_mod_bset(mod, SRCOUT_TIMSEL2, mask, out);
+               rsnd_mod_bset(adg_mod, SRCIN_TIMSEL2,  mask, in);
+               rsnd_mod_bset(adg_mod, SRCOUT_TIMSEL2, mask, out);
                break;
        case 3:
-               rsnd_mod_bset(mod, SRCIN_TIMSEL3,  mask, in);
-               rsnd_mod_bset(mod, SRCOUT_TIMSEL3, mask, out);
+               rsnd_mod_bset(adg_mod, SRCIN_TIMSEL3,  mask, in);
+               rsnd_mod_bset(adg_mod, SRCOUT_TIMSEL3, mask, out);
                break;
        case 4:
-               rsnd_mod_bset(mod, SRCIN_TIMSEL4,  mask, in);
-               rsnd_mod_bset(mod, SRCOUT_TIMSEL4, mask, out);
+               rsnd_mod_bset(adg_mod, SRCIN_TIMSEL4,  mask, in);
+               rsnd_mod_bset(adg_mod, SRCOUT_TIMSEL4, mask, out);
                break;
        }
 
        return 0;
 }
 
-int rsnd_adg_set_convert_clk_gen2(struct rsnd_mod *mod,
+int rsnd_adg_set_convert_clk_gen2(struct rsnd_mod *src_mod,
                                  struct rsnd_dai_stream *io,
                                  unsigned int src_rate,
                                  unsigned int dst_rate)
 {
-       struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+       struct rsnd_priv *priv = rsnd_mod_to_priv(src_mod);
        struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
+       struct rsnd_mod *adg_mod = rsnd_mod_get(adg);
        struct device *dev = rsnd_priv_to_dev(priv);
        int idx, sel, div, step, ret;
        u32 val, en;
@@ -134,10 +177,12 @@ int rsnd_adg_set_convert_clk_gen2(struct rsnd_mod *mod,
                clk_get_rate(adg->clk[CLKA]),   /* 0000: CLKA */
                clk_get_rate(adg->clk[CLKB]),   /* 0001: CLKB */
                clk_get_rate(adg->clk[CLKC]),   /* 0010: CLKC */
-               adg->rbga_rate_for_441khz_div_6,/* 0011: RBGA */
-               adg->rbgb_rate_for_48khz_div_6, /* 0100: RBGB */
+               adg->rbga_rate_for_441khz,      /* 0011: RBGA */
+               adg->rbgb_rate_for_48khz,       /* 0100: RBGB */
        };
 
+       rsnd_mod_confirm_src(src_mod);
+
        min = ~0;
        val = 0;
        en = 0;
@@ -175,25 +220,27 @@ int rsnd_adg_set_convert_clk_gen2(struct rsnd_mod *mod,
                return -EIO;
        }
 
-       ret = rsnd_adg_set_src_timsel_gen2(mod, io, val);
+       ret = rsnd_adg_set_src_timsel_gen2(src_mod, io, val);
        if (ret < 0) {
                dev_err(dev, "timsel error\n");
                return ret;
        }
 
-       rsnd_mod_bset(mod, DIV_EN, en, en);
+       rsnd_mod_bset(adg_mod, DIV_EN, en, en);
 
        dev_dbg(dev, "convert rate %d <-> %d\n", src_rate, dst_rate);
 
        return 0;
 }
 
-int rsnd_adg_set_convert_timing_gen2(struct rsnd_mod *mod,
+int rsnd_adg_set_convert_timing_gen2(struct rsnd_mod *src_mod,
                                     struct rsnd_dai_stream *io)
 {
        u32 val = rsnd_adg_ssi_ws_timing_gen2(io);
 
-       return rsnd_adg_set_src_timsel_gen2(mod, io, val);
+       rsnd_mod_confirm_src(src_mod);
+
+       return rsnd_adg_set_src_timsel_gen2(src_mod, io, val);
 }
 
 int rsnd_adg_set_convert_clk_gen1(struct rsnd_priv *priv,
@@ -202,6 +249,7 @@ int rsnd_adg_set_convert_clk_gen1(struct rsnd_priv *priv,
                                  unsigned int dst_rate)
 {
        struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
+       struct rsnd_mod *adg_mod = rsnd_mod_get(adg);
        struct device *dev = rsnd_priv_to_dev(priv);
        int idx, sel, div, shift;
        u32 mask, val;
@@ -211,8 +259,8 @@ int rsnd_adg_set_convert_clk_gen1(struct rsnd_priv *priv,
                clk_get_rate(adg->clk[CLKB]),   /* 001: CLKB */
                clk_get_rate(adg->clk[CLKC]),   /* 010: CLKC */
                0,                              /* 011: MLBCLK (not used) */
-               adg->rbga_rate_for_441khz_div_6,/* 100: RBGA */
-               adg->rbgb_rate_for_48khz_div_6, /* 101: RBGB */
+               adg->rbga_rate_for_441khz,      /* 100: RBGA */
+               adg->rbgb_rate_for_48khz,       /* 101: RBGB */
        };
 
        /* find div (= 1/128, 1/256, 1/512, 1/1024, 1/2048 */
@@ -238,13 +286,13 @@ find_rate:
 
        switch (id / 4) {
        case 0:
-               rsnd_mod_bset(mod, AUDIO_CLK_SEL3, mask, val);
+               rsnd_mod_bset(adg_mod, AUDIO_CLK_SEL3, mask, val);
                break;
        case 1:
-               rsnd_mod_bset(mod, AUDIO_CLK_SEL4, mask, val);
+               rsnd_mod_bset(adg_mod, AUDIO_CLK_SEL4, mask, val);
                break;
        case 2:
-               rsnd_mod_bset(mod, AUDIO_CLK_SEL5, mask, val);
+               rsnd_mod_bset(adg_mod, AUDIO_CLK_SEL5, mask, val);
                break;
        }
 
@@ -257,12 +305,17 @@ find_rate:
        return 0;
 }
 
-static void rsnd_adg_set_ssi_clk(struct rsnd_mod *mod, u32 val)
+static void rsnd_adg_set_ssi_clk(struct rsnd_mod *ssi_mod, u32 val)
 {
-       int id = rsnd_mod_id(mod);
+       struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod);
+       struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
+       struct rsnd_mod *adg_mod = rsnd_mod_get(adg);
+       int id = rsnd_mod_id(ssi_mod);
        int shift = (id % 4) * 8;
        u32 mask = 0xFF << shift;
 
+       rsnd_mod_confirm_ssi(ssi_mod);
+
        val = val << shift;
 
        /*
@@ -274,13 +327,13 @@ static void rsnd_adg_set_ssi_clk(struct rsnd_mod *mod, u32 val)
 
        switch (id / 4) {
        case 0:
-               rsnd_mod_bset(mod, AUDIO_CLK_SEL0, mask, val);
+               rsnd_mod_bset(adg_mod, AUDIO_CLK_SEL0, mask, val);
                break;
        case 1:
-               rsnd_mod_bset(mod, AUDIO_CLK_SEL1, mask, val);
+               rsnd_mod_bset(adg_mod, AUDIO_CLK_SEL1, mask, val);
                break;
        case 2:
-               rsnd_mod_bset(mod, AUDIO_CLK_SEL2, mask, val);
+               rsnd_mod_bset(adg_mod, AUDIO_CLK_SEL2, mask, val);
                break;
        }
 }
@@ -326,14 +379,14 @@ int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *mod, unsigned int rate)
        }
 
        /*
-        * find 1/6 clock from BRGA/BRGB
+        * find divided clock from BRGA/BRGB
         */
-       if (rate == adg->rbga_rate_for_441khz_div_6) {
+       if (rate  == adg->rbga_rate_for_441khz) {
                data = 0x10;
                goto found_clock;
        }
 
-       if (rate == adg->rbgb_rate_for_48khz_div_6) {
+       if (rate == adg->rbgb_rate_for_48khz) {
                data = 0x20;
                goto found_clock;
        }
@@ -342,29 +395,60 @@ int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *mod, unsigned int rate)
 
 found_clock:
 
-       /* see rsnd_adg_ssi_clk_init() */
-       rsnd_mod_bset(mod, SSICKR, 0x00FF0000, adg->ckr);
-       rsnd_mod_write(mod, BRRA,  0x00000002); /* 1/6 */
-       rsnd_mod_write(mod, BRRB,  0x00000002); /* 1/6 */
-
        /*
         * This "mod" = "ssi" here.
         * we can get "ssi id" from mod
         */
        rsnd_adg_set_ssi_clk(mod, data);
 
-       dev_dbg(dev, "ADG: ssi%d selects clk%d = %d",
-               rsnd_mod_id(mod), i, rate);
+       dev_dbg(dev, "ADG: %s[%d] selects 0x%x for %d\n",
+               rsnd_mod_name(mod), rsnd_mod_id(mod),
+               data, rate);
 
        return 0;
 }
 
-static void rsnd_adg_ssi_clk_init(struct rsnd_priv *priv, struct rsnd_adg *adg)
+static void rsnd_adg_get_clkin(struct rsnd_priv *priv,
+                              struct rsnd_adg *adg)
 {
+       struct device *dev = rsnd_priv_to_dev(priv);
        struct clk *clk;
-       unsigned long rate;
-       u32 ckr;
+       static const char * const clk_name[] = {
+               [CLKA]  = "clk_a",
+               [CLKB]  = "clk_b",
+               [CLKC]  = "clk_c",
+               [CLKI]  = "clk_i",
+       };
        int i;
+
+       for (i = 0; i < CLKMAX; i++) {
+               clk = devm_clk_get(dev, clk_name[i]);
+               adg->clk[i] = IS_ERR(clk) ? NULL : clk;
+       }
+
+       for_each_rsnd_clk(clk, adg, i)
+               dev_dbg(dev, "clk %d : %p : %ld\n", i, clk, clk_get_rate(clk));
+}
+
+static void rsnd_adg_get_clkout(struct rsnd_priv *priv,
+                               struct rsnd_adg *adg)
+{
+       struct clk *clk;
+       struct rsnd_mod *adg_mod = rsnd_mod_get(adg);
+       struct device *dev = rsnd_priv_to_dev(priv);
+       struct device_node *np = dev->of_node;
+       u32 ckr, rbgx, rbga, rbgb;
+       u32 rate, req_rate, div;
+       uint32_t count = 0;
+       unsigned long req_48kHz_rate, req_441kHz_rate;
+       int i;
+       const char *parent_clk_name = NULL;
+       static const char * const clkout_name[] = {
+               [CLKOUT]  = "audio_clkout",
+               [CLKOUT1] = "audio_clkout1",
+               [CLKOUT2] = "audio_clkout2",
+               [CLKOUT3] = "audio_clkout3",
+       };
        int brg_table[] = {
                [CLKA] = 0x0,
                [CLKB] = 0x1,
@@ -372,19 +456,34 @@ static void rsnd_adg_ssi_clk_init(struct rsnd_priv *priv, struct rsnd_adg *adg)
                [CLKI] = 0x2,
        };
 
+       of_property_read_u32(np, "#clock-cells", &count);
+
+       /*
+        * ADG supports BRRA/BRRB output only
+        * this means all clkout0/1/2/3 will be same rate
+        */
+       of_property_read_u32(np, "clock-frequency", &req_rate);
+       req_48kHz_rate = 0;
+       req_441kHz_rate = 0;
+       if (0 == (req_rate % 44100))
+               req_441kHz_rate = req_rate;
+       if (0 == (req_rate % 48000))
+               req_48kHz_rate = req_rate;
+
        /*
         * This driver is assuming that AUDIO_CLKA/AUDIO_CLKB/AUDIO_CLKC
         * have 44.1kHz or 48kHz base clocks for now.
         *
         * SSI itself can divide parent clock by 1/1 - 1/16
-        * So,  BRGA outputs 44.1kHz base parent clock 1/32,
-        * and, BRGB outputs 48.0kHz base parent clock 1/32 here.
         * see
         *      rsnd_adg_ssi_clk_try_start()
+        *      rsnd_ssi_master_clk_start()
         */
        ckr = 0;
-       adg->rbga_rate_for_441khz_div_6 = 0;
-       adg->rbgb_rate_for_48khz_div_6  = 0;
+       rbga = 2; /* default 1/6 */
+       rbgb = 2; /* default 1/6 */
+       adg->rbga_rate_for_441khz       = 0;
+       adg->rbgb_rate_for_48khz        = 0;
        for_each_rsnd_clk(clk, adg, i) {
                rate = clk_get_rate(clk);
 
@@ -392,19 +491,86 @@ static void rsnd_adg_ssi_clk_init(struct rsnd_priv *priv, struct rsnd_adg *adg)
                        continue;
 
                /* RBGA */
-               if (!adg->rbga_rate_for_441khz_div_6 && (0 == rate % 44100)) {
-                       adg->rbga_rate_for_441khz_div_6 = rate / 6;
-                       ckr |= brg_table[i] << 20;
+               if (!adg->rbga_rate_for_441khz && (0 == rate % 44100)) {
+                       div = 6;
+                       if (req_441kHz_rate)
+                               div = rate / req_441kHz_rate;
+                       rbgx = rsnd_adg_calculate_rbgx(div);
+                       if (BRRx_MASK(rbgx) == rbgx) {
+                               rbga = rbgx;
+                               adg->rbga_rate_for_441khz = rate / div;
+                               ckr |= brg_table[i] << 20;
+                               if (req_441kHz_rate)
+                                       parent_clk_name = __clk_get_name(clk);
+                       }
                }
 
                /* RBGB */
-               if (!adg->rbgb_rate_for_48khz_div_6 && (0 == rate % 48000)) {
-                       adg->rbgb_rate_for_48khz_div_6 = rate / 6;
-                       ckr |= brg_table[i] << 16;
+               if (!adg->rbgb_rate_for_48khz && (0 == rate % 48000)) {
+                       div = 6;
+                       if (req_48kHz_rate)
+                               div = rate / req_48kHz_rate;
+                       rbgx = rsnd_adg_calculate_rbgx(div);
+                       if (BRRx_MASK(rbgx) == rbgx) {
+                               rbgb = rbgx;
+                               adg->rbgb_rate_for_48khz = rate / div;
+                               ckr |= brg_table[i] << 16;
+                               if (req_48kHz_rate) {
+                                       parent_clk_name = __clk_get_name(clk);
+                                       ckr |= 0x80000000;
+                               }
+                       }
+               }
+       }
+
+       /*
+        * ADG supports BRRA/BRRB output only.
+        * this means all clkout0/1/2/3 will be * same rate
+        */
+
+       /*
+        * for clkout
+        */
+       if (!count) {
+               clk = clk_register_fixed_rate(dev, clkout_name[CLKOUT],
+                                             parent_clk_name,
+                                             (parent_clk_name) ?
+                                             0 : CLK_IS_ROOT, req_rate);
+               if (!IS_ERR(clk)) {
+                       adg->clkout[CLKOUT] = clk;
+                       of_clk_add_provider(np, of_clk_src_simple_get, clk);
+               }
+       }
+       /*
+        * for clkout0/1/2/3
+        */
+       else {
+               for (i = 0; i < CLKOUTMAX; i++) {
+                       clk = clk_register_fixed_rate(dev, clkout_name[i],
+                                                     parent_clk_name,
+                                                     (parent_clk_name) ?
+                                                     0 : CLK_IS_ROOT,
+                                                     req_rate);
+                       if (!IS_ERR(clk)) {
+                               adg->onecell.clks       = adg->clkout;
+                               adg->onecell.clk_num    = CLKOUTMAX;
+
+                               adg->clkout[i] = clk;
+
+                               of_clk_add_provider(np, of_clk_src_onecell_get,
+                                                   &adg->onecell);
+                       }
                }
        }
 
-       adg->ckr = ckr;
+       rsnd_mod_bset(adg_mod, SSICKR, 0x00FF0000, ckr);
+       rsnd_mod_write(adg_mod, BRRA,  rbga);
+       rsnd_mod_write(adg_mod, BRRB,  rbgb);
+
+       for_each_rsnd_clkout(clk, adg, i)
+               dev_dbg(dev, "clkout %d : %p : %ld\n", i, clk, clk_get_rate(clk));
+       dev_dbg(dev, "SSICKR = 0x%08x, BRRA/BRRB = 0x%x/0x%x\n",
+               ckr, rbga, rbgb);
 }
 
 int rsnd_adg_probe(struct platform_device *pdev,
@@ -413,8 +579,6 @@ int rsnd_adg_probe(struct platform_device *pdev,
 {
        struct rsnd_adg *adg;
        struct device *dev = rsnd_priv_to_dev(priv);
-       struct clk *clk;
-       int i;
 
        adg = devm_kzalloc(dev, sizeof(*adg), GFP_KERNEL);
        if (!adg) {
@@ -422,15 +586,16 @@ int rsnd_adg_probe(struct platform_device *pdev,
                return -ENOMEM;
        }
 
-       adg->clk[CLKA]  = devm_clk_get(dev, "clk_a");
-       adg->clk[CLKB]  = devm_clk_get(dev, "clk_b");
-       adg->clk[CLKC]  = devm_clk_get(dev, "clk_c");
-       adg->clk[CLKI]  = devm_clk_get(dev, "clk_i");
-
-       for_each_rsnd_clk(clk, adg, i)
-               dev_dbg(dev, "clk %d : %p : %ld\n", i, clk, clk_get_rate(clk));
+       /*
+        * ADG is special module.
+        * Use ADG mod without rsnd_mod_init() to make debug easy
+        * for rsnd_write/rsnd_read
+        */
+       adg->mod.ops = &adg_ops;
+       adg->mod.priv = priv;
 
-       rsnd_adg_ssi_clk_init(priv, adg);
+       rsnd_adg_get_clkin(priv, adg);
+       rsnd_adg_get_clkout(priv, adg);
 
        priv->adg = adg;
 
index f3feed5ce9b65ba67d936a399a4a06f91cf77db7..eec294da81e325ca4e9a545c3b750bc6283267d3 100644 (file)
@@ -110,6 +110,7 @@ static const struct rsnd_of_data rsnd_of_data_gen2 = {
 static const struct of_device_id rsnd_of_match[] = {
        { .compatible = "renesas,rcar_sound-gen1", .data = &rsnd_of_data_gen1 },
        { .compatible = "renesas,rcar_sound-gen2", .data = &rsnd_of_data_gen2 },
+       { .compatible = "renesas,rcar_sound-gen3", .data = &rsnd_of_data_gen2 }, /* gen2 compatible */
        {},
 };
 MODULE_DEVICE_TABLE(of, rsnd_of_match);
@@ -126,6 +127,17 @@ MODULE_DEVICE_TABLE(of, rsnd_of_match);
 #define rsnd_info_id(priv, io, name) \
        ((io)->info->name - priv->info->name##_info)
 
+void rsnd_mod_make_sure(struct rsnd_mod *mod, enum rsnd_mod_type type)
+{
+       if (mod->type != type) {
+               struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+               struct device *dev = rsnd_priv_to_dev(priv);
+
+               dev_warn(dev, "%s[%d] is not your expected module\n",
+                        rsnd_mod_name(mod), rsnd_mod_id(mod));
+       }
+}
+
 /*
  *     rsnd_mod functions
  */
index 05498bba5874dbb0fe9fffdb46e26bad0866f045..a3e7c716e1f7283f6ad453ae91c52590b998b516 100644 (file)
@@ -66,7 +66,7 @@ struct rsnd_mod *rsnd_ctu_mod_get(struct rsnd_priv *priv, int id)
        if (WARN_ON(id < 0 || id >= rsnd_ctu_nr(priv)))
                id = 0;
 
-       return &((struct rsnd_ctu *)(priv->ctu) + id)->mod;
+       return rsnd_mod_get((struct rsnd_ctu *)(priv->ctu) + id);
 }
 
 static void rsnd_of_parse_ctu(struct platform_device *pdev,
@@ -150,7 +150,7 @@ int rsnd_ctu_probe(struct platform_device *pdev,
 
                ctu->info = &info->ctu_info[i];
 
-               ret = rsnd_mod_init(priv, &ctu->mod, &rsnd_ctu_ops,
+               ret = rsnd_mod_init(priv, rsnd_mod_get(ctu), &rsnd_ctu_ops,
                                    clk, RSND_MOD_CTU, i);
                if (ret)
                        return ret;
@@ -166,6 +166,6 @@ void rsnd_ctu_remove(struct platform_device *pdev,
        int i;
 
        for_each_rsnd_ctu(ctu, priv, i) {
-               rsnd_mod_quit(&ctu->mod);
+               rsnd_mod_quit(rsnd_mod_get(ctu));
        }
 }
index 57796387d482303bc84c29212dfed167685ae6fe..8d8eee6350c94f43632fcf42a19926613dd38d41 100644 (file)
@@ -282,7 +282,7 @@ struct rsnd_mod *rsnd_dvc_mod_get(struct rsnd_priv *priv, int id)
        if (WARN_ON(id < 0 || id >= rsnd_dvc_nr(priv)))
                id = 0;
 
-       return &((struct rsnd_dvc *)(priv->dvc) + id)->mod;
+       return rsnd_mod_get((struct rsnd_dvc *)(priv->dvc) + id);
 }
 
 static void rsnd_of_parse_dvc(struct platform_device *pdev,
@@ -361,7 +361,7 @@ int rsnd_dvc_probe(struct platform_device *pdev,
 
                dvc->info = &info->dvc_info[i];
 
-               ret = rsnd_mod_init(priv, &dvc->mod, &rsnd_dvc_ops,
+               ret = rsnd_mod_init(priv, rsnd_mod_get(dvc), &rsnd_dvc_ops,
                              clk, RSND_MOD_DVC, i);
                if (ret)
                        return ret;
@@ -377,6 +377,6 @@ void rsnd_dvc_remove(struct platform_device *pdev,
        int i;
 
        for_each_rsnd_dvc(dvc, priv, i) {
-               rsnd_mod_quit(&dvc->mod);
+               rsnd_mod_quit(rsnd_mod_get(dvc));
        }
 }
index 0d5c102db6f5fb6ed54b05e42f97888bdd0c6c20..8544403ffb269fa89ebe5af5b8ee88b2b8dad2da 100644 (file)
@@ -99,7 +99,7 @@ struct rsnd_mod *rsnd_mix_mod_get(struct rsnd_priv *priv, int id)
        if (WARN_ON(id < 0 || id >= rsnd_mix_nr(priv)))
                id = 0;
 
-       return &((struct rsnd_mix *)(priv->mix) + id)->mod;
+       return rsnd_mod_get((struct rsnd_mix *)(priv->mix) + id);
 }
 
 static void rsnd_of_parse_mix(struct platform_device *pdev,
@@ -179,7 +179,7 @@ int rsnd_mix_probe(struct platform_device *pdev,
 
                mix->info = &info->mix_info[i];
 
-               ret = rsnd_mod_init(priv, &mix->mod, &rsnd_mix_ops,
+               ret = rsnd_mod_init(priv, rsnd_mod_get(mix), &rsnd_mix_ops,
                                    clk, RSND_MOD_MIX, i);
                if (ret)
                        return ret;
@@ -195,6 +195,6 @@ void rsnd_mix_remove(struct platform_device *pdev,
        int i;
 
        for_each_rsnd_mix(mix, priv, i) {
-               rsnd_mod_quit(&mix->mod);
+               rsnd_mod_quit(rsnd_mod_get(mix));
        }
 }
index 7a0e52b4640a5a39a0bfb3f1d496d71919e0806c..e4068d78616c0a6542d339aa356402ff0d92fbfc 100644 (file)
@@ -214,6 +214,7 @@ struct rsnd_dma {
 };
 #define rsnd_dma_to_dmaen(dma) (&(dma)->dma.en)
 #define rsnd_dma_to_dmapp(dma) (&(dma)->dma.pp)
+#define rsnd_dma_to_mod(_dma) container_of((_dma), struct rsnd_mod, dma)
 
 void rsnd_dma_start(struct rsnd_dai_stream *io, struct rsnd_dma *dma);
 void rsnd_dma_stop(struct rsnd_dai_stream *io, struct rsnd_dma *dma);
@@ -225,8 +226,6 @@ int rsnd_dma_probe(struct platform_device *pdev,
 struct dma_chan *rsnd_dma_request_channel(struct device_node *of_node,
                                          struct rsnd_mod *mod, char *name);
 
-#define rsnd_dma_to_mod(_dma) container_of((_dma), struct rsnd_mod, dma)
-
 /*
  *     R-Car sound mod
  */
@@ -332,6 +331,7 @@ struct rsnd_mod {
 #define rsnd_mod_id(mod) ((mod) ? (mod)->id : -1)
 #define rsnd_mod_hw_start(mod) clk_enable((mod)->clk)
 #define rsnd_mod_hw_stop(mod)  clk_disable((mod)->clk)
+#define rsnd_mod_get(ip)       (&(ip)->mod)
 
 int rsnd_mod_init(struct rsnd_priv *priv,
                  struct rsnd_mod *mod,
@@ -627,4 +627,15 @@ void rsnd_dvc_remove(struct platform_device *pdev,
                     struct rsnd_priv *priv);
 struct rsnd_mod *rsnd_dvc_mod_get(struct rsnd_priv *priv, int id);
 
+#ifdef DEBUG
+void rsnd_mod_make_sure(struct rsnd_mod *mod, enum rsnd_mod_type type);
+#define rsnd_mod_confirm_ssi(mssi)     rsnd_mod_make_sure(mssi, RSND_MOD_SSI)
+#define rsnd_mod_confirm_src(msrc)     rsnd_mod_make_sure(msrc, RSND_MOD_SRC)
+#define rsnd_mod_confirm_dvc(mdvc)     rsnd_mod_make_sure(mdvc, RSND_MOD_DVC)
+#else
+#define rsnd_mod_confirm_ssi(mssi)
+#define rsnd_mod_confirm_src(msrc)
+#define rsnd_mod_confirm_dvc(mdvc)
+#endif
+
 #endif
index 89a18e102feb100b57350024a3d548261e91243e..ca7a20f03c9bf1f9a987fe8108ec38542af51500 100644 (file)
@@ -918,11 +918,10 @@ static void rsnd_src_reconvert_update(struct rsnd_dai_stream *io,
        rsnd_mod_write(mod, SRC_IFSVR, fsrate);
 }
 
-static int rsnd_src_pcm_new(struct rsnd_mod *mod,
+static int rsnd_src_pcm_new_gen2(struct rsnd_mod *mod,
                            struct rsnd_dai_stream *io,
                            struct snd_soc_pcm_runtime *rtd)
 {
-       struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
        struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
        struct rsnd_src *src = rsnd_mod_to_src(mod);
        int ret;
@@ -931,12 +930,6 @@ static int rsnd_src_pcm_new(struct rsnd_mod *mod,
         * enable SRC sync convert if possible
         */
 
-       /*
-        * Gen1 is not supported
-        */
-       if (rsnd_is_gen1(priv))
-               return 0;
-
        /*
         * SRC sync convert needs clock master
         */
@@ -975,7 +968,7 @@ static struct rsnd_mod_ops rsnd_src_gen2_ops = {
        .start  = rsnd_src_start_gen2,
        .stop   = rsnd_src_stop_gen2,
        .hw_params = rsnd_src_hw_params,
-       .pcm_new = rsnd_src_pcm_new,
+       .pcm_new = rsnd_src_pcm_new_gen2,
 };
 
 struct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id)
@@ -983,7 +976,7 @@ struct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id)
        if (WARN_ON(id < 0 || id >= rsnd_src_nr(priv)))
                id = 0;
 
-       return &((struct rsnd_src *)(priv->src) + id)->mod;
+       return rsnd_mod_get((struct rsnd_src *)(priv->src) + id);
 }
 
 static void rsnd_of_parse_src(struct platform_device *pdev,
@@ -1078,7 +1071,7 @@ int rsnd_src_probe(struct platform_device *pdev,
 
                src->info = &info->src_info[i];
 
-               ret = rsnd_mod_init(priv, &src->mod, ops, clk, RSND_MOD_SRC, i);
+               ret = rsnd_mod_init(priv, rsnd_mod_get(src), ops, clk, RSND_MOD_SRC, i);
                if (ret)
                        return ret;
        }
@@ -1093,6 +1086,6 @@ void rsnd_src_remove(struct platform_device *pdev,
        int i;
 
        for_each_rsnd_src(src, priv, i) {
-               rsnd_mod_quit(&src->mod);
+               rsnd_mod_quit(rsnd_mod_get(src));
        }
 }
index d45b9a7e324efb49a5159417232c9f43815ebfed..5e05f94220730fb63150c28bae8e3144c425622d 100644 (file)
@@ -128,10 +128,8 @@ static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi,
        struct rsnd_priv *priv = rsnd_io_to_priv(io);
        struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
        struct device *dev = rsnd_priv_to_dev(priv);
-       int i, j, ret;
-       int adg_clk_div_table[] = {
-               1, 6, /* see adg.c */
-       };
+       struct rsnd_mod *mod = rsnd_mod_get(ssi);
+       int j, ret;
        int ssi_clk_mul_table[] = {
                1, 2, 4, 8, 16, 6, 12,
        };
@@ -141,28 +139,25 @@ static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi,
        /*
         * Find best clock, and try to start ADG
         */
-       for (i = 0; i < ARRAY_SIZE(adg_clk_div_table); i++) {
-               for (j = 0; j < ARRAY_SIZE(ssi_clk_mul_table); j++) {
-
-                       /*
-                        * this driver is assuming that
-                        * system word is 64fs (= 2 x 32bit)
-                        * see rsnd_ssi_init()
-                        */
-                       main_rate = rate / adg_clk_div_table[i]
-                               * 32 * 2 * ssi_clk_mul_table[j];
-
-                       ret = rsnd_adg_ssi_clk_try_start(&ssi->mod, main_rate);
-                       if (0 == ret) {
-                               ssi->cr_clk     = FORCE | SWL_32 |
-                                                 SCKD | SWSD | CKDV(j);
-
-                               dev_dbg(dev, "%s[%d] outputs %u Hz\n",
-                                       rsnd_mod_name(&ssi->mod),
-                                       rsnd_mod_id(&ssi->mod), rate);
-
-                               return 0;
-                       }
+       for (j = 0; j < ARRAY_SIZE(ssi_clk_mul_table); j++) {
+
+               /*
+                * this driver is assuming that
+                * system word is 64fs (= 2 x 32bit)
+                * see rsnd_ssi_init()
+                */
+               main_rate = rate * 32 * 2 * ssi_clk_mul_table[j];
+
+               ret = rsnd_adg_ssi_clk_try_start(mod, main_rate);
+               if (0 == ret) {
+                       ssi->cr_clk     = FORCE | SWL_32 |
+                               SCKD | SWSD | CKDV(j);
+
+                       dev_dbg(dev, "%s[%d] outputs %u Hz\n",
+                               rsnd_mod_name(mod),
+                               rsnd_mod_id(mod), rate);
+
+                       return 0;
                }
        }
 
@@ -172,8 +167,10 @@ static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi,
 
 static void rsnd_ssi_master_clk_stop(struct rsnd_ssi *ssi)
 {
+       struct rsnd_mod *mod = rsnd_mod_get(ssi);
+
        ssi->cr_clk = 0;
-       rsnd_adg_ssi_clk_stop(&ssi->mod);
+       rsnd_adg_ssi_clk_stop(mod);
 }
 
 static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi,
@@ -182,11 +179,12 @@ static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi,
        struct rsnd_priv *priv = rsnd_io_to_priv(io);
        struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
        struct device *dev = rsnd_priv_to_dev(priv);
+       struct rsnd_mod *mod = rsnd_mod_get(ssi);
        u32 cr_mode;
        u32 cr;
 
        if (0 == ssi->usrcnt) {
-               rsnd_mod_hw_start(&ssi->mod);
+               rsnd_mod_hw_start(mod);
 
                if (rsnd_rdai_is_clk_master(rdai)) {
                        struct rsnd_ssi *ssi_parent = rsnd_ssi_parent(ssi);
@@ -198,7 +196,7 @@ static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi,
                }
        }
 
-       if (rsnd_ssi_is_dma_mode(&ssi->mod)) {
+       if (rsnd_ssi_is_dma_mode(mod)) {
                cr_mode = UIEN | OIEN | /* over/under run */
                          DMEN;         /* DMA : enable DMA */
        } else {
@@ -210,24 +208,25 @@ static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi,
                cr_mode         |
                EN;
 
-       rsnd_mod_write(&ssi->mod, SSICR, cr);
+       rsnd_mod_write(mod, SSICR, cr);
 
        /* enable WS continue */
        if (rsnd_rdai_is_clk_master(rdai))
-               rsnd_mod_write(&ssi->mod, SSIWSR, CONT);
+               rsnd_mod_write(mod, SSIWSR, CONT);
 
        /* clear error status */
-       rsnd_mod_write(&ssi->mod, SSISR, 0);
+       rsnd_mod_write(mod, SSISR, 0);
 
        ssi->usrcnt++;
 
        dev_dbg(dev, "%s[%d] hw started\n",
-               rsnd_mod_name(&ssi->mod), rsnd_mod_id(&ssi->mod));
+               rsnd_mod_name(mod), rsnd_mod_id(mod));
 }
 
 static void rsnd_ssi_hw_stop(struct rsnd_dai_stream *io, struct rsnd_ssi *ssi)
 {
-       struct rsnd_priv *priv = rsnd_mod_to_priv(&ssi->mod);
+       struct rsnd_mod *mod = rsnd_mod_get(ssi);
+       struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
        struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
        struct device *dev = rsnd_priv_to_dev(priv);
        u32 cr;
@@ -247,15 +246,15 @@ static void rsnd_ssi_hw_stop(struct rsnd_dai_stream *io, struct rsnd_ssi *ssi)
                cr  =   ssi->cr_own     |
                        ssi->cr_clk;
 
-               rsnd_mod_write(&ssi->mod, SSICR, cr | EN);
-               rsnd_ssi_status_check(&ssi->mod, DIRQ);
+               rsnd_mod_write(mod, SSICR, cr | EN);
+               rsnd_ssi_status_check(mod, DIRQ);
 
                /*
                 * disable SSI,
                 * and, wait idle state
                 */
-               rsnd_mod_write(&ssi->mod, SSICR, cr);   /* disabled all */
-               rsnd_ssi_status_check(&ssi->mod, IIRQ);
+               rsnd_mod_write(mod, SSICR, cr); /* disabled all */
+               rsnd_ssi_status_check(mod, IIRQ);
 
                if (rsnd_rdai_is_clk_master(rdai)) {
                        struct rsnd_ssi *ssi_parent = rsnd_ssi_parent(ssi);
@@ -266,13 +265,13 @@ static void rsnd_ssi_hw_stop(struct rsnd_dai_stream *io, struct rsnd_ssi *ssi)
                                rsnd_ssi_master_clk_stop(ssi);
                }
 
-               rsnd_mod_hw_stop(&ssi->mod);
+               rsnd_mod_hw_stop(mod);
 
                ssi->chan = 0;
        }
 
        dev_dbg(dev, "%s[%d] hw stopped\n",
-               rsnd_mod_name(&ssi->mod), rsnd_mod_id(&ssi->mod));
+               rsnd_mod_name(mod), rsnd_mod_id(mod));
 }
 
 /*
@@ -371,7 +370,7 @@ static int rsnd_ssi_hw_params(struct rsnd_mod *mod,
        /* It will be removed on rsnd_ssi_hw_stop */
        ssi->chan = chan;
        if (ssi_parent)
-               return rsnd_ssi_hw_params(&ssi_parent->mod, io,
+               return rsnd_ssi_hw_params(rsnd_mod_get(ssi_parent), io,
                                          substream, params);
 
        return 0;
@@ -379,12 +378,14 @@ static int rsnd_ssi_hw_params(struct rsnd_mod *mod,
 
 static void rsnd_ssi_record_error(struct rsnd_ssi *ssi, u32 status)
 {
+       struct rsnd_mod *mod = rsnd_mod_get(ssi);
+
        /* under/over flow error */
        if (status & (UIRQ | OIRQ)) {
                ssi->err++;
 
                /* clear error status */
-               rsnd_mod_write(&ssi->mod, SSISR, 0);
+               rsnd_mod_write(mod, SSISR, 0);
        }
 }
 
@@ -656,7 +657,7 @@ struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id)
        if (WARN_ON(id < 0 || id >= rsnd_ssi_nr(priv)))
                id = 0;
 
-       return &((struct rsnd_ssi *)(priv->ssi) + id)->mod;
+       return rsnd_mod_get((struct rsnd_ssi *)(priv->ssi) + id);
 }
 
 int rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod)
@@ -668,10 +669,12 @@ int rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod)
 
 static void rsnd_ssi_parent_setup(struct rsnd_priv *priv, struct rsnd_ssi *ssi)
 {
-       if (!rsnd_ssi_is_pin_sharing(&ssi->mod))
+       struct rsnd_mod *mod = rsnd_mod_get(ssi);
+
+       if (!rsnd_ssi_is_pin_sharing(mod))
                return;
 
-       switch (rsnd_mod_id(&ssi->mod)) {
+       switch (rsnd_mod_id(mod)) {
        case 1:
        case 2:
                ssi->parent = rsnd_mod_to_ssi(rsnd_ssi_mod_get(priv, 0));
@@ -794,7 +797,8 @@ int rsnd_ssi_probe(struct platform_device *pdev,
                else if (rsnd_ssi_pio_available(ssi))
                        ops = &rsnd_ssi_pio_ops;
 
-               ret = rsnd_mod_init(priv, &ssi->mod, ops, clk, RSND_MOD_SSI, i);
+               ret = rsnd_mod_init(priv, rsnd_mod_get(ssi), ops, clk,
+                                   RSND_MOD_SSI, i);
                if (ret)
                        return ret;
 
@@ -811,6 +815,6 @@ void rsnd_ssi_remove(struct platform_device *pdev,
        int i;
 
        for_each_rsnd_ssi(ssi, priv, i) {
-               rsnd_mod_quit(&ssi->mod);
+               rsnd_mod_quit(rsnd_mod_get(ssi));
        }
 }
index abb0d956231ca1235333ffd516595ca8fc5b083d..76b2ab8c2b4a4f964307b9832677ebb8a4961464 100644 (file)
@@ -738,7 +738,7 @@ static int siu_probe(struct platform_device *pdev)
        struct siu_info *info;
        int ret;
 
-       info = kmalloc(sizeof(*info), GFP_KERNEL);
+       info = devm_kmalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
        if (!info)
                return -ENOMEM;
        siu_i2s_data = info;
@@ -746,7 +746,7 @@ static int siu_probe(struct platform_device *pdev)
 
        ret = request_firmware(&fw_entry, "siu_spb.bin", &pdev->dev);
        if (ret)
-               goto ereqfw;
+               return ret;
 
        /*
         * Loaded firmware is "const" - read only, but we have to modify it in
@@ -757,89 +757,52 @@ static int siu_probe(struct platform_device *pdev)
        release_firmware(fw_entry);
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (!res) {
-               ret = -ENODEV;
-               goto egetres;
-       }
+       if (!res)
+               return -ENODEV;
 
-       region = request_mem_region(res->start, resource_size(res),
-                                   pdev->name);
+       region = devm_request_mem_region(&pdev->dev, res->start,
+                                        resource_size(res), pdev->name);
        if (!region) {
                dev_err(&pdev->dev, "SIU region already claimed\n");
-               ret = -EBUSY;
-               goto ereqmemreg;
+               return -EBUSY;
        }
 
-       ret = -ENOMEM;
-       info->pram = ioremap(res->start, PRAM_SIZE);
+       info->pram = devm_ioremap(&pdev->dev, res->start, PRAM_SIZE);
        if (!info->pram)
-               goto emappram;
-       info->xram = ioremap(res->start + XRAM_OFFSET, XRAM_SIZE);
+               return -ENOMEM;
+       info->xram = devm_ioremap(&pdev->dev, res->start + XRAM_OFFSET,
+                                 XRAM_SIZE);
        if (!info->xram)
-               goto emapxram;
-       info->yram = ioremap(res->start + YRAM_OFFSET, YRAM_SIZE);
+               return -ENOMEM;
+       info->yram = devm_ioremap(&pdev->dev, res->start + YRAM_OFFSET,
+                                 YRAM_SIZE);
        if (!info->yram)
-               goto emapyram;
-       info->reg = ioremap(res->start + REG_OFFSET, resource_size(res) -
-                           REG_OFFSET);
+               return -ENOMEM;
+       info->reg = devm_ioremap(&pdev->dev, res->start + REG_OFFSET,
+                           resource_size(res) - REG_OFFSET);
        if (!info->reg)
-               goto emapreg;
+               return -ENOMEM;
 
        dev_set_drvdata(&pdev->dev, info);
 
        /* register using ARRAY version so we can keep dai name */
-       ret = snd_soc_register_component(&pdev->dev, &siu_i2s_component,
-                                        &siu_i2s_dai, 1);
+       ret = devm_snd_soc_register_component(&pdev->dev, &siu_i2s_component,
+                                             &siu_i2s_dai, 1);
        if (ret < 0)
-               goto edaiinit;
+               return ret;
 
-       ret = snd_soc_register_platform(&pdev->dev, &siu_platform);
+       ret = devm_snd_soc_register_platform(&pdev->dev, &siu_platform);
        if (ret < 0)
-               goto esocregp;
+               return ret;
 
        pm_runtime_enable(&pdev->dev);
 
-       return ret;
-
-esocregp:
-       snd_soc_unregister_component(&pdev->dev);
-edaiinit:
-       iounmap(info->reg);
-emapreg:
-       iounmap(info->yram);
-emapyram:
-       iounmap(info->xram);
-emapxram:
-       iounmap(info->pram);
-emappram:
-       release_mem_region(res->start, resource_size(res));
-ereqmemreg:
-egetres:
-ereqfw:
-       kfree(info);
-
-       return ret;
+       return 0;
 }
 
 static int siu_remove(struct platform_device *pdev)
 {
-       struct siu_info *info = dev_get_drvdata(&pdev->dev);
-       struct resource *res;
-
        pm_runtime_disable(&pdev->dev);
-
-       snd_soc_unregister_platform(&pdev->dev);
-       snd_soc_unregister_component(&pdev->dev);
-
-       iounmap(info->reg);
-       iounmap(info->yram);
-       iounmap(info->xram);
-       iounmap(info->pram);
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (res)
-               release_mem_region(res->start, resource_size(res));
-       kfree(info);
-
        return 0;
 }
 
index 6173d15236c3c0c2ce53c3c31b721310f9576e9e..3b471f9c98c67d978697ca0a9fce033d802e7632 100644 (file)
@@ -3291,13 +3291,38 @@ int snd_soc_of_parse_audio_simple_widgets(struct snd_soc_card *card,
 }
 EXPORT_SYMBOL_GPL(snd_soc_of_parse_audio_simple_widgets);
 
+static int snd_soc_of_get_slot_mask(struct device_node *np,
+                                   const char *prop_name,
+                                   unsigned int *mask)
+{
+       u32 val;
+       const __be32 *of_slot_mask = of_get_property(np, prop_name, &val);
+       int i;
+
+       if (!of_slot_mask)
+               return 0;
+       val /= sizeof(u32);
+       for (i = 0; i < val; i++)
+               if (be32_to_cpup(&of_slot_mask[i]))
+                       *mask |= (1 << i);
+
+       return val;
+}
+
 int snd_soc_of_parse_tdm_slot(struct device_node *np,
+                             unsigned int *tx_mask,
+                             unsigned int *rx_mask,
                              unsigned int *slots,
                              unsigned int *slot_width)
 {
        u32 val;
        int ret;
 
+       if (tx_mask)
+               snd_soc_of_get_slot_mask(np, "dai-tdm-slot-tx-mask", tx_mask);
+       if (rx_mask)
+               snd_soc_of_get_slot_mask(np, "dai-tdm-slot-rx-mask", rx_mask);
+
        if (of_property_read_bool(np, "dai-tdm-slot-num")) {
                ret = of_property_read_u32(np, "dai-tdm-slot-num", &val);
                if (ret)
index 70e4b9d8bdcdfd3fb83f37615a29fae64036d4c0..317395824cd794d093e74b98013a8f2537b89056 100644 (file)
 
 #define DPCM_MAX_BE_USERS      8
 
+/*
+ * snd_soc_dai_stream_valid() - check if a DAI supports the given stream
+ *
+ * Returns true if the DAI supports the indicated stream type.
+ */
+static bool snd_soc_dai_stream_valid(struct snd_soc_dai *dai, int stream)
+{
+       struct snd_soc_pcm_stream *codec_stream;
+
+       if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+               codec_stream = &dai->driver->playback;
+       else
+               codec_stream = &dai->driver->capture;
+
+       /* If the codec specifies any rate at all, it supports the stream. */
+       return codec_stream->rates;
+}
+
 /**
  * snd_soc_runtime_activate() - Increment active count for PCM runtime components
  * @rtd: ASoC PCM runtime that is activated
@@ -371,6 +389,20 @@ static void soc_pcm_init_runtime_hw(struct snd_pcm_substream *substream)
 
        /* first calculate min/max only for CODECs in the DAI link */
        for (i = 0; i < rtd->num_codecs; i++) {
+
+               /*
+                * Skip CODECs which don't support the current stream type.
+                * Otherwise, since the rate, channel, and format values will
+                * zero in that case, we would have no usable settings left,
+                * causing the resulting setup to fail.
+                * At least one CODEC should match, otherwise we should have
+                * bailed out on a higher level, since there would be no
+                * CODEC to support the transfer direction in that case.
+                */
+               if (!snd_soc_dai_stream_valid(rtd->codec_dais[i],
+                                             substream->stream))
+                       continue;
+
                codec_dai_drv = rtd->codec_dais[i]->driver;
                if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
                        codec_stream = &codec_dai_drv->playback;
@@ -827,6 +859,23 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream,
                struct snd_soc_dai *codec_dai = rtd->codec_dais[i];
                struct snd_pcm_hw_params codec_params;
 
+               /*
+                * Skip CODECs which don't support the current stream type,
+                * the idea being that if a CODEC is not used for the currently
+                * set up transfer direction, it should not need to be
+                * configured, especially since the configuration used might
+                * not even be supported by that CODEC. There may be cases
+                * however where a CODEC needs to be set up although it is
+                * actually not being used for the transfer, e.g. if a
+                * capture-only CODEC is acting as an LRCLK and/or BCLK master
+                * for the DAI link including a playback-only CODEC.
+                * If this becomes necessary, we will have to augment the
+                * machine driver setup with information on how to act, so
+                * we can do the right thing here.
+                */
+               if (!snd_soc_dai_stream_valid(codec_dai, substream->stream))
+                       continue;
+
                /* copy params for each codec */
                codec_params = *params;
 
diff --git a/sound/soc/sunxi/Kconfig b/sound/soc/sunxi/Kconfig
new file mode 100644 (file)
index 0000000..84c72ec
--- /dev/null
@@ -0,0 +1,11 @@
+menu "Allwinner SoC Audio support"
+
+config SND_SUN4I_CODEC
+       tristate "Allwinner A10 Codec Support"
+       select SND_SOC_GENERIC_DMAENGINE_PCM
+       select REGMAP_MMIO
+       help
+         Select Y or M to add support for the Codec embedded in the Allwinner
+         A10 and affiliated SoCs.
+
+endmenu
diff --git a/sound/soc/sunxi/Makefile b/sound/soc/sunxi/Makefile
new file mode 100644 (file)
index 0000000..ea8a08c
--- /dev/null
@@ -0,0 +1,2 @@
+obj-$(CONFIG_SND_SUN4I_CODEC) += sun4i-codec.o
+
diff --git a/sound/soc/sunxi/sun4i-codec.c b/sound/soc/sunxi/sun4i-codec.c
new file mode 100644 (file)
index 0000000..8d59d83
--- /dev/null
@@ -0,0 +1,719 @@
+/*
+ * Copyright 2014 Emilio López <emilio@elopez.com.ar>
+ * Copyright 2014 Jon Smirl <jonsmirl@gmail.com>
+ * Copyright 2015 Maxime Ripard <maxime.ripard@free-electrons.com>
+ *
+ * Based on the Allwinner SDK driver, released under the GPL.
+ *
+ * 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/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/of_address.h>
+#include <linux/clk.h>
+#include <linux/regmap.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <sound/initval.h>
+#include <sound/dmaengine_pcm.h>
+
+/* Codec DAC register offsets and bit fields */
+#define SUN4I_CODEC_DAC_DPC                    (0x00)
+#define SUN4I_CODEC_DAC_DPC_EN_DA                      (31)
+#define SUN4I_CODEC_DAC_DPC_DVOL                       (12)
+#define SUN4I_CODEC_DAC_FIFOC                  (0x04)
+#define SUN4I_CODEC_DAC_FIFOC_DAC_FS                   (29)
+#define SUN4I_CODEC_DAC_FIFOC_FIR_VERSION              (28)
+#define SUN4I_CODEC_DAC_FIFOC_SEND_LASAT               (26)
+#define SUN4I_CODEC_DAC_FIFOC_TX_FIFO_MODE             (24)
+#define SUN4I_CODEC_DAC_FIFOC_DRQ_CLR_CNT              (21)
+#define SUN4I_CODEC_DAC_FIFOC_TX_TRIG_LEVEL            (8)
+#define SUN4I_CODEC_DAC_FIFOC_MONO_EN                  (6)
+#define SUN4I_CODEC_DAC_FIFOC_TX_SAMPLE_BITS           (5)
+#define SUN4I_CODEC_DAC_FIFOC_DAC_DRQ_EN               (4)
+#define SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH               (0)
+#define SUN4I_CODEC_DAC_FIFOS                  (0x08)
+#define SUN4I_CODEC_DAC_TXDATA                 (0x0c)
+#define SUN4I_CODEC_DAC_ACTL                   (0x10)
+#define SUN4I_CODEC_DAC_ACTL_DACAENR                   (31)
+#define SUN4I_CODEC_DAC_ACTL_DACAENL                   (30)
+#define SUN4I_CODEC_DAC_ACTL_MIXEN                     (29)
+#define SUN4I_CODEC_DAC_ACTL_LDACLMIXS                 (15)
+#define SUN4I_CODEC_DAC_ACTL_RDACRMIXS                 (14)
+#define SUN4I_CODEC_DAC_ACTL_LDACRMIXS                 (13)
+#define SUN4I_CODEC_DAC_ACTL_DACPAS                    (8)
+#define SUN4I_CODEC_DAC_ACTL_MIXPAS                    (7)
+#define SUN4I_CODEC_DAC_ACTL_PA_MUTE                   (6)
+#define SUN4I_CODEC_DAC_ACTL_PA_VOL                    (0)
+#define SUN4I_CODEC_DAC_TUNE                   (0x14)
+#define SUN4I_CODEC_DAC_DEBUG                  (0x18)
+
+/* Codec ADC register offsets and bit fields */
+#define SUN4I_CODEC_ADC_FIFOC                  (0x1c)
+#define SUN4I_CODEC_ADC_FIFOC_EN_AD                    (28)
+#define SUN4I_CODEC_ADC_FIFOC_RX_FIFO_MODE             (24)
+#define SUN4I_CODEC_ADC_FIFOC_RX_TRIG_LEVEL            (8)
+#define SUN4I_CODEC_ADC_FIFOC_MONO_EN                  (7)
+#define SUN4I_CODEC_ADC_FIFOC_RX_SAMPLE_BITS           (6)
+#define SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN               (4)
+#define SUN4I_CODEC_ADC_FIFOC_FIFO_FLUSH               (0)
+#define SUN4I_CODEC_ADC_FIFOS                  (0x20)
+#define SUN4I_CODEC_ADC_RXDATA                 (0x24)
+#define SUN4I_CODEC_ADC_ACTL                   (0x28)
+#define SUN4I_CODEC_ADC_ACTL_ADC_R_EN                  (31)
+#define SUN4I_CODEC_ADC_ACTL_ADC_L_EN                  (30)
+#define SUN4I_CODEC_ADC_ACTL_PREG1EN                   (29)
+#define SUN4I_CODEC_ADC_ACTL_PREG2EN                   (28)
+#define SUN4I_CODEC_ADC_ACTL_VMICEN                    (27)
+#define SUN4I_CODEC_ADC_ACTL_VADCG                     (20)
+#define SUN4I_CODEC_ADC_ACTL_ADCIS                     (17)
+#define SUN4I_CODEC_ADC_ACTL_PA_EN                     (4)
+#define SUN4I_CODEC_ADC_ACTL_DDE                       (3)
+#define SUN4I_CODEC_ADC_DEBUG                  (0x2c)
+
+/* Other various ADC registers */
+#define SUN4I_CODEC_DAC_TXCNT                  (0x30)
+#define SUN4I_CODEC_ADC_RXCNT                  (0x34)
+#define SUN4I_CODEC_AC_SYS_VERI                        (0x38)
+#define SUN4I_CODEC_AC_MIC_PHONE_CAL           (0x3c)
+
+struct sun4i_codec {
+       struct device   *dev;
+       struct regmap   *regmap;
+       struct clk      *clk_apb;
+       struct clk      *clk_module;
+
+       struct snd_dmaengine_dai_dma_data       playback_dma_data;
+};
+
+static void sun4i_codec_start_playback(struct sun4i_codec *scodec)
+{
+       /*
+        * FIXME: according to the BSP, we might need to drive a PA
+        *        GPIO high here on some boards
+        */
+
+       /* Flush TX FIFO */
+       regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
+                          BIT(SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH),
+                          BIT(SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH));
+
+       /* Enable DAC DRQ */
+       regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
+                          BIT(SUN4I_CODEC_DAC_FIFOC_DAC_DRQ_EN),
+                          BIT(SUN4I_CODEC_DAC_FIFOC_DAC_DRQ_EN));
+}
+
+static void sun4i_codec_stop_playback(struct sun4i_codec *scodec)
+{
+       /*
+        * FIXME: according to the BSP, we might need to drive a PA
+        *        GPIO low here on some boards
+        */
+
+       /* Disable DAC DRQ */
+       regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
+                          BIT(SUN4I_CODEC_DAC_FIFOC_DAC_DRQ_EN),
+                          0);
+}
+
+static int sun4i_codec_trigger(struct snd_pcm_substream *substream, int cmd,
+                              struct snd_soc_dai *dai)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card);
+
+       if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
+               return -ENOTSUPP;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_RESUME:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               sun4i_codec_start_playback(scodec);
+               break;
+
+       case SNDRV_PCM_TRIGGER_STOP:
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               sun4i_codec_stop_playback(scodec);
+               break;
+
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int sun4i_codec_prepare(struct snd_pcm_substream *substream,
+                              struct snd_soc_dai *dai)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card);
+       u32 val;
+
+       if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
+               return -ENOTSUPP;
+
+       /* Flush the TX FIFO */
+       regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
+                          BIT(SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH),
+                          BIT(SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH));
+
+       /* Set TX FIFO Empty Trigger Level */
+       regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
+                          0x3f << SUN4I_CODEC_DAC_FIFOC_TX_TRIG_LEVEL,
+                          0xf << SUN4I_CODEC_DAC_FIFOC_TX_TRIG_LEVEL);
+
+       if (substream->runtime->rate > 32000)
+               /* Use 64 bits FIR filter */
+               val = 0;
+       else
+               /* Use 32 bits FIR filter */
+               val = BIT(SUN4I_CODEC_DAC_FIFOC_FIR_VERSION);
+
+       regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
+                          BIT(SUN4I_CODEC_DAC_FIFOC_FIR_VERSION),
+                          val);
+
+       /* Send zeros when we have an underrun */
+       regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
+                          BIT(SUN4I_CODEC_DAC_FIFOC_SEND_LASAT),
+                          0);
+
+       return 0;
+}
+
+static unsigned long sun4i_codec_get_mod_freq(struct snd_pcm_hw_params *params)
+{
+       unsigned int rate = params_rate(params);
+
+       switch (rate) {
+       case 176400:
+       case 88200:
+       case 44100:
+       case 33075:
+       case 22050:
+       case 14700:
+       case 11025:
+       case 7350:
+               return 22579200;
+
+       case 192000:
+       case 96000:
+       case 48000:
+       case 32000:
+       case 24000:
+       case 16000:
+       case 12000:
+       case 8000:
+               return 24576000;
+
+       default:
+               return 0;
+       }
+}
+
+static int sun4i_codec_get_hw_rate(struct snd_pcm_hw_params *params)
+{
+       unsigned int rate = params_rate(params);
+
+       switch (rate) {
+       case 192000:
+       case 176400:
+               return 6;
+
+       case 96000:
+       case 88200:
+               return 7;
+
+       case 48000:
+       case 44100:
+               return 0;
+
+       case 32000:
+       case 33075:
+               return 1;
+
+       case 24000:
+       case 22050:
+               return 2;
+
+       case 16000:
+       case 14700:
+               return 3;
+
+       case 12000:
+       case 11025:
+               return 4;
+
+       case 8000:
+       case 7350:
+               return 5;
+
+       default:
+               return -EINVAL;
+       }
+}
+
+static int sun4i_codec_hw_params(struct snd_pcm_substream *substream,
+                                struct snd_pcm_hw_params *params,
+                                struct snd_soc_dai *dai)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card);
+       unsigned long clk_freq;
+       int hwrate;
+       u32 val;
+
+       if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
+               return -ENOTSUPP;
+
+       clk_freq = sun4i_codec_get_mod_freq(params);
+       if (!clk_freq)
+               return -EINVAL;
+
+       if (clk_set_rate(scodec->clk_module, clk_freq))
+               return -EINVAL;
+
+       hwrate = sun4i_codec_get_hw_rate(params);
+       if (hwrate < 0)
+               return hwrate;
+
+       /* Set DAC sample rate */
+       regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
+                          7 << SUN4I_CODEC_DAC_FIFOC_DAC_FS,
+                          hwrate << SUN4I_CODEC_DAC_FIFOC_DAC_FS);
+
+       /* Set the number of channels we want to use */
+       if (params_channels(params) == 1)
+               val = BIT(SUN4I_CODEC_DAC_FIFOC_MONO_EN);
+       else
+               val = 0;
+
+       regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
+                          BIT(SUN4I_CODEC_DAC_FIFOC_MONO_EN),
+                          val);
+
+       /* Set the number of sample bits to either 16 or 24 bits */
+       if (hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS)->min == 32) {
+               regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
+                                  BIT(SUN4I_CODEC_DAC_FIFOC_TX_SAMPLE_BITS),
+                                  BIT(SUN4I_CODEC_DAC_FIFOC_TX_SAMPLE_BITS));
+
+               /* Set TX FIFO mode to padding the LSBs with 0 */
+               regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
+                                  BIT(SUN4I_CODEC_DAC_FIFOC_TX_FIFO_MODE),
+                                  0);
+
+               scodec->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+       } else {
+               regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
+                                  BIT(SUN4I_CODEC_DAC_FIFOC_TX_SAMPLE_BITS),
+                                  0);
+
+               /* Set TX FIFO mode to repeat the MSB */
+               regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
+                                  BIT(SUN4I_CODEC_DAC_FIFOC_TX_FIFO_MODE),
+                                  BIT(SUN4I_CODEC_DAC_FIFOC_TX_FIFO_MODE));
+
+               scodec->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+       }
+
+       return 0;
+}
+
+static int sun4i_codec_startup(struct snd_pcm_substream *substream,
+                              struct snd_soc_dai *dai)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card);
+
+       /*
+        * Stop issuing DRQ when we have room for less than 16 samples
+        * in our TX FIFO
+        */
+       regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
+                          3 << SUN4I_CODEC_DAC_FIFOC_DRQ_CLR_CNT,
+                          3 << SUN4I_CODEC_DAC_FIFOC_DRQ_CLR_CNT);
+
+       return clk_prepare_enable(scodec->clk_module);
+}
+
+static void sun4i_codec_shutdown(struct snd_pcm_substream *substream,
+                                struct snd_soc_dai *dai)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card);
+
+       clk_disable_unprepare(scodec->clk_module);
+}
+
+static const struct snd_soc_dai_ops sun4i_codec_dai_ops = {
+       .startup        = sun4i_codec_startup,
+       .shutdown       = sun4i_codec_shutdown,
+       .trigger        = sun4i_codec_trigger,
+       .hw_params      = sun4i_codec_hw_params,
+       .prepare        = sun4i_codec_prepare,
+};
+
+static struct snd_soc_dai_driver sun4i_codec_dai = {
+       .name   = "Codec",
+       .ops    = &sun4i_codec_dai_ops,
+       .playback = {
+               .stream_name    = "Codec Playback",
+               .channels_min   = 1,
+               .channels_max   = 2,
+               .rate_min       = 8000,
+               .rate_max       = 192000,
+               .rates          = SNDRV_PCM_RATE_8000_48000 |
+                                 SNDRV_PCM_RATE_96000 |
+                                 SNDRV_PCM_RATE_192000 |
+                                 SNDRV_PCM_RATE_KNOT,
+               .formats        = SNDRV_PCM_FMTBIT_S16_LE |
+                                 SNDRV_PCM_FMTBIT_S32_LE,
+               .sig_bits       = 24,
+       },
+};
+
+/*** Codec ***/
+static const struct snd_kcontrol_new sun4i_codec_pa_mute =
+       SOC_DAPM_SINGLE("Switch", SUN4I_CODEC_DAC_ACTL,
+                       SUN4I_CODEC_DAC_ACTL_PA_MUTE, 1, 0);
+
+static DECLARE_TLV_DB_SCALE(sun4i_codec_pa_volume_scale, -6300, 100, 1);
+
+static const struct snd_kcontrol_new sun4i_codec_widgets[] = {
+       SOC_SINGLE_TLV("PA Volume", SUN4I_CODEC_DAC_ACTL,
+                      SUN4I_CODEC_DAC_ACTL_PA_VOL, 0x3F, 0,
+                      sun4i_codec_pa_volume_scale),
+};
+
+static const struct snd_kcontrol_new sun4i_codec_left_mixer_controls[] = {
+       SOC_DAPM_SINGLE("Left DAC Playback Switch", SUN4I_CODEC_DAC_ACTL,
+                       SUN4I_CODEC_DAC_ACTL_LDACLMIXS, 1, 0),
+};
+
+static const struct snd_kcontrol_new sun4i_codec_right_mixer_controls[] = {
+       SOC_DAPM_SINGLE("Right DAC Playback Switch", SUN4I_CODEC_DAC_ACTL,
+                       SUN4I_CODEC_DAC_ACTL_RDACRMIXS, 1, 0),
+       SOC_DAPM_SINGLE("Left DAC Playback Switch", SUN4I_CODEC_DAC_ACTL,
+                       SUN4I_CODEC_DAC_ACTL_LDACRMIXS, 1, 0),
+};
+
+static const struct snd_kcontrol_new sun4i_codec_pa_mixer_controls[] = {
+       SOC_DAPM_SINGLE("DAC Playback Switch", SUN4I_CODEC_DAC_ACTL,
+                       SUN4I_CODEC_DAC_ACTL_DACPAS, 1, 0),
+       SOC_DAPM_SINGLE("Mixer Playback Switch", SUN4I_CODEC_DAC_ACTL,
+                       SUN4I_CODEC_DAC_ACTL_MIXPAS, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget sun4i_codec_dapm_widgets[] = {
+       /* Digital parts of the DACs */
+       SND_SOC_DAPM_SUPPLY("DAC", SUN4I_CODEC_DAC_DPC,
+                           SUN4I_CODEC_DAC_DPC_EN_DA, 0,
+                           NULL, 0),
+
+       /* Analog parts of the DACs */
+       SND_SOC_DAPM_DAC("Left DAC", "Codec Playback", SUN4I_CODEC_DAC_ACTL,
+                        SUN4I_CODEC_DAC_ACTL_DACAENL, 0),
+       SND_SOC_DAPM_DAC("Right DAC", "Codec Playback", SUN4I_CODEC_DAC_ACTL,
+                        SUN4I_CODEC_DAC_ACTL_DACAENR, 0),
+
+       /* Mixers */
+       SND_SOC_DAPM_MIXER("Left Mixer", SND_SOC_NOPM, 0, 0,
+                          sun4i_codec_left_mixer_controls,
+                          ARRAY_SIZE(sun4i_codec_left_mixer_controls)),
+       SND_SOC_DAPM_MIXER("Right Mixer", SND_SOC_NOPM, 0, 0,
+                          sun4i_codec_right_mixer_controls,
+                          ARRAY_SIZE(sun4i_codec_right_mixer_controls)),
+
+       /* Global Mixer Enable */
+       SND_SOC_DAPM_SUPPLY("Mixer Enable", SUN4I_CODEC_DAC_ACTL,
+                           SUN4I_CODEC_DAC_ACTL_MIXEN, 0, NULL, 0),
+
+       /* Pre-Amplifier */
+       SND_SOC_DAPM_MIXER("Pre-Amplifier", SUN4I_CODEC_ADC_ACTL,
+                          SUN4I_CODEC_ADC_ACTL_PA_EN, 0,
+                          sun4i_codec_pa_mixer_controls,
+                          ARRAY_SIZE(sun4i_codec_pa_mixer_controls)),
+       SND_SOC_DAPM_SWITCH("Pre-Amplifier Mute", SND_SOC_NOPM, 0, 0,
+                           &sun4i_codec_pa_mute),
+
+       SND_SOC_DAPM_OUTPUT("HP Right"),
+       SND_SOC_DAPM_OUTPUT("HP Left"),
+};
+
+static const struct snd_soc_dapm_route sun4i_codec_dapm_routes[] = {
+       /* Left DAC Routes */
+       { "Left DAC", NULL, "DAC" },
+
+       /* Right DAC Routes */
+       { "Right DAC", NULL, "DAC" },
+
+       /* Right Mixer Routes */
+       { "Right Mixer", NULL, "Mixer Enable" },
+       { "Right Mixer", "Left DAC Playback Switch", "Left DAC" },
+       { "Right Mixer", "Right DAC Playback Switch", "Right DAC" },
+
+       /* Left Mixer Routes */
+       { "Left Mixer", NULL, "Mixer Enable" },
+       { "Left Mixer", "Left DAC Playback Switch", "Left DAC" },
+
+       /* Pre-Amplifier Mixer Routes */
+       { "Pre-Amplifier", "Mixer Playback Switch", "Left Mixer" },
+       { "Pre-Amplifier", "Mixer Playback Switch", "Right Mixer" },
+       { "Pre-Amplifier", "DAC Playback Switch", "Left DAC" },
+       { "Pre-Amplifier", "DAC Playback Switch", "Right DAC" },
+
+       /* PA -> HP path */
+       { "Pre-Amplifier Mute", "Switch", "Pre-Amplifier" },
+       { "HP Right", NULL, "Pre-Amplifier Mute" },
+       { "HP Left", NULL, "Pre-Amplifier Mute" },
+};
+
+static struct snd_soc_codec_driver sun4i_codec_codec = {
+       .controls               = sun4i_codec_widgets,
+       .num_controls           = ARRAY_SIZE(sun4i_codec_widgets),
+       .dapm_widgets           = sun4i_codec_dapm_widgets,
+       .num_dapm_widgets       = ARRAY_SIZE(sun4i_codec_dapm_widgets),
+       .dapm_routes            = sun4i_codec_dapm_routes,
+       .num_dapm_routes        = ARRAY_SIZE(sun4i_codec_dapm_routes),
+};
+
+static const struct snd_soc_component_driver sun4i_codec_component = {
+       .name = "sun4i-codec",
+};
+
+#define SUN4I_CODEC_RATES      SNDRV_PCM_RATE_8000_192000
+#define SUN4I_CODEC_FORMATS    (SNDRV_PCM_FMTBIT_S16_LE | \
+                                SNDRV_PCM_FMTBIT_S32_LE)
+
+static int sun4i_codec_dai_probe(struct snd_soc_dai *dai)
+{
+       struct snd_soc_card *card = snd_soc_dai_get_drvdata(dai);
+       struct sun4i_codec *scodec = snd_soc_card_get_drvdata(card);
+
+       snd_soc_dai_init_dma_data(dai, &scodec->playback_dma_data,
+                                 NULL);
+
+       return 0;
+}
+
+static struct snd_soc_dai_driver dummy_cpu_dai = {
+       .name   = "sun4i-codec-cpu-dai",
+       .probe  = sun4i_codec_dai_probe,
+       .playback = {
+               .stream_name    = "Playback",
+               .channels_min   = 1,
+               .channels_max   = 2,
+               .rates          = SUN4I_CODEC_RATES,
+               .formats        = SUN4I_CODEC_FORMATS,
+               .sig_bits       = 24,
+       },
+};
+
+static const struct regmap_config sun4i_codec_regmap_config = {
+       .reg_bits       = 32,
+       .reg_stride     = 4,
+       .val_bits       = 32,
+       .max_register   = SUN4I_CODEC_AC_MIC_PHONE_CAL,
+};
+
+static const struct of_device_id sun4i_codec_of_match[] = {
+       { .compatible = "allwinner,sun4i-a10-codec" },
+       { .compatible = "allwinner,sun7i-a20-codec" },
+       {}
+};
+MODULE_DEVICE_TABLE(of, sun4i_codec_of_match);
+
+static struct snd_soc_dai_link *sun4i_codec_create_link(struct device *dev,
+                                                       int *num_links)
+{
+       struct snd_soc_dai_link *link = devm_kzalloc(dev, sizeof(*link),
+                                                    GFP_KERNEL);
+       if (!link)
+               return NULL;
+
+       link->name              = "cdc";
+       link->stream_name       = "CDC PCM";
+       link->codec_dai_name    = "Codec";
+       link->cpu_dai_name      = dev_name(dev);
+       link->codec_name        = dev_name(dev);
+       link->platform_name     = dev_name(dev);
+       link->dai_fmt           = SND_SOC_DAIFMT_I2S;
+
+       *num_links = 1;
+
+       return link;
+};
+
+static struct snd_soc_card *sun4i_codec_create_card(struct device *dev)
+{
+       struct snd_soc_card *card;
+       int ret;
+
+       card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
+       if (!card)
+               return NULL;
+
+       card->dai_link = sun4i_codec_create_link(dev, &card->num_links);
+       if (!card->dai_link)
+               return NULL;
+
+       card->dev               = dev;
+       card->name              = "sun4i-codec";
+
+       ret = snd_soc_of_parse_audio_routing(card, "routing");
+       if (ret) {
+               dev_err(dev, "Failed to create our audio routing\n");
+               return NULL;
+       }
+
+       return card;
+};
+
+static int sun4i_codec_probe(struct platform_device *pdev)
+{
+       struct snd_soc_card *card;
+       struct sun4i_codec *scodec;
+       struct resource *res;
+       void __iomem *base;
+       int ret;
+
+       scodec = devm_kzalloc(&pdev->dev, sizeof(*scodec), GFP_KERNEL);
+       if (!scodec)
+               return -ENOMEM;
+
+       scodec->dev = &pdev->dev;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       base = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(base)) {
+               dev_err(&pdev->dev, "Failed to map the registers\n");
+               return PTR_ERR(base);
+       }
+
+       scodec->regmap = devm_regmap_init_mmio(&pdev->dev, base,
+                                            &sun4i_codec_regmap_config);
+       if (IS_ERR(scodec->regmap)) {
+               dev_err(&pdev->dev, "Failed to create our regmap\n");
+               return PTR_ERR(scodec->regmap);
+       }
+
+       /* Get the clocks from the DT */
+       scodec->clk_apb = devm_clk_get(&pdev->dev, "apb");
+       if (IS_ERR(scodec->clk_apb)) {
+               dev_err(&pdev->dev, "Failed to get the APB clock\n");
+               return PTR_ERR(scodec->clk_apb);
+       }
+
+       scodec->clk_module = devm_clk_get(&pdev->dev, "codec");
+       if (IS_ERR(scodec->clk_module)) {
+               dev_err(&pdev->dev, "Failed to get the module clock\n");
+               return PTR_ERR(scodec->clk_module);
+       }
+
+       /* Enable the bus clock */
+       if (clk_prepare_enable(scodec->clk_apb)) {
+               dev_err(&pdev->dev, "Failed to enable the APB clock\n");
+               return -EINVAL;
+       }
+
+       /* DMA configuration for TX FIFO */
+       scodec->playback_dma_data.addr = res->start + SUN4I_CODEC_DAC_TXDATA;
+       scodec->playback_dma_data.maxburst = 4;
+       scodec->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+
+       ret = snd_soc_register_codec(&pdev->dev, &sun4i_codec_codec,
+                                    &sun4i_codec_dai, 1);
+       if (ret) {
+               dev_err(&pdev->dev, "Failed to register our codec\n");
+               goto err_clk_disable;
+       }
+
+       ret = devm_snd_soc_register_component(&pdev->dev,
+                                             &sun4i_codec_component,
+                                             &dummy_cpu_dai, 1);
+       if (ret) {
+               dev_err(&pdev->dev, "Failed to register our DAI\n");
+               goto err_unregister_codec;
+       }
+
+       ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
+       if (ret) {
+               dev_err(&pdev->dev, "Failed to register against DMAEngine\n");
+               goto err_unregister_codec;
+       }
+
+       card = sun4i_codec_create_card(&pdev->dev);
+       if (!card) {
+               dev_err(&pdev->dev, "Failed to create our card\n");
+               goto err_unregister_codec;
+       }
+
+       platform_set_drvdata(pdev, card);
+       snd_soc_card_set_drvdata(card, scodec);
+
+       ret = snd_soc_register_card(card);
+       if (ret) {
+               dev_err(&pdev->dev, "Failed to register our card\n");
+               goto err_unregister_codec;
+       }
+
+       return 0;
+
+err_unregister_codec:
+       snd_soc_unregister_codec(&pdev->dev);
+err_clk_disable:
+       clk_disable_unprepare(scodec->clk_apb);
+       return ret;
+}
+
+static int sun4i_codec_remove(struct platform_device *pdev)
+{
+       struct snd_soc_card *card = platform_get_drvdata(pdev);
+       struct sun4i_codec *scodec = snd_soc_card_get_drvdata(card);
+
+       snd_soc_unregister_card(card);
+       snd_soc_unregister_codec(&pdev->dev);
+       clk_disable_unprepare(scodec->clk_apb);
+
+       return 0;
+}
+
+static struct platform_driver sun4i_codec_driver = {
+       .driver = {
+               .name = "sun4i-codec",
+               .of_match_table = sun4i_codec_of_match,
+       },
+       .probe = sun4i_codec_probe,
+       .remove = sun4i_codec_remove,
+};
+module_platform_driver(sun4i_codec_driver);
+
+MODULE_DESCRIPTION("Allwinner A10 codec driver");
+MODULE_AUTHOR("Emilio López <emilio@elopez.com.ar>");
+MODULE_AUTHOR("Jon Smirl <jonsmirl@gmail.com>");
+MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
+MODULE_LICENSE("GPL");
index 4e0c0e502ade2c228a00db3a44c44ecfa4c9978d..ba9fc099cf67b22997aad328f256c2b1150c86a1 100644 (file)
@@ -152,6 +152,7 @@ static const struct of_device_id snd_soc_mop500_match[] = {
        { .compatible = "stericsson,snd-soc-mop500", },
        {},
 };
+MODULE_DEVICE_TABLE(of, snd_soc_mop500_match);
 
 static struct platform_driver snd_soc_mop500_driver = {
        .driver = {
index f5df08ded770ef9cbafd3ec88903893bc4657344..6ba8ae9ecc7a13a04624b2fe760cdad9d4425735 100644 (file)
@@ -843,6 +843,7 @@ static const struct of_device_id ux500_msp_i2s_match[] = {
        { .compatible = "stericsson,ux500-msp-i2s", },
        {},
 };
+MODULE_DEVICE_TABLE(of, ux500_msp_i2s_match);
 
 static struct platform_driver msp_i2s_driver = {
        .driver = {