diff options
author | Takashi Iwai <tiwai@suse.de> | 2015-09-25 08:33:11 +0200 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2015-09-25 08:33:11 +0200 |
commit | 0b2c8c12f92996debdd7a2c2aebb33242e30922a (patch) | |
tree | 1bd21fb439e7a7493ffd8fb05d548d68e9f64980 | |
parent | db25f440f42eca380fcdb339f51cf73633e32485 (diff) | |
parent | 4922e7a1b767fcf6f29ce121ad74e424f4045eff (diff) | |
download | op-kernel-dev-0b2c8c12f92996debdd7a2c2aebb33242e30922a.zip op-kernel-dev-0b2c8c12f92996debdd7a2c2aebb33242e30922a.tar.gz |
Merge tag 'asoc-v4.3-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound into for-next
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.
92 files changed, 2484 insertions, 834 deletions
diff --git a/Documentation/devicetree/bindings/sound/ak4613.txt b/Documentation/devicetree/bindings/sound/ak4613.txt new file mode 100644 index 0000000..15a9195 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/ak4613.txt @@ -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>; + }; +}; diff --git a/Documentation/devicetree/bindings/sound/ak4642.txt b/Documentation/devicetree/bindings/sound/ak4642.txt index 623d4e7..340784d 100644 --- a/Documentation/devicetree/bindings/sound/ak4642.txt +++ b/Documentation/devicetree/bindings/sound/ak4642.txt @@ -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"; + }; +}; diff --git a/Documentation/devicetree/bindings/sound/renesas,rsnd.txt b/Documentation/devicetree/bindings/sound/renesas,rsnd.txt index 1173395..c57cbd6 100644 --- a/Documentation/devicetree/bindings/sound/renesas,rsnd.txt +++ b/Documentation/devicetree/bindings/sound/renesas,rsnd.txt @@ -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 index 0000000..680144b --- /dev/null +++ b/Documentation/devicetree/bindings/sound/sun4i-codec.txt @@ -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"; +}; diff --git a/Documentation/devicetree/bindings/sound/tdm-slot.txt b/Documentation/devicetree/bindings/sound/tdm-slot.txt index 6a2c842..34cf70e 100644 --- a/Documentation/devicetree/bindings/sound/tdm-slot.txt +++ b/Documentation/devicetree/bindings/sound/tdm-slot.txt @@ -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. diff --git a/MAINTAINERS b/MAINTAINERS index 274f854..0581f47 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -11239,7 +11239,6 @@ VOLTAGE AND CURRENT REGULATOR FRAMEWORK M: Liam Girdwood <lgirdwood@gmail.com> M: Mark Brown <broonie@kernel.org> L: linux-kernel@vger.kernel.org -W: http://opensource.wolfsonmicro.com/node/15 W: http://www.slimlogic.co.uk/?p=48 T: git git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regulator.git S: Supported @@ -11368,17 +11367,15 @@ WM97XX TOUCHSCREEN DRIVERS M: Mark Brown <broonie@kernel.org> M: Liam Girdwood <lrg@slimlogic.co.uk> L: linux-input@vger.kernel.org -T: git git://opensource.wolfsonmicro.com/linux-2.6-touch -W: http://opensource.wolfsonmicro.com/node/7 +W: https://github.com/CirrusLogic/linux-drivers/wiki S: Supported F: drivers/input/touchscreen/*wm97* F: include/linux/wm97xx.h WOLFSON MICROELECTRONICS DRIVERS L: patches@opensource.wolfsonmicro.com -T: git git://opensource.wolfsonmicro.com/linux-2.6-asoc -T: git git://opensource.wolfsonmicro.com/linux-2.6-audioplus -W: http://opensource.wolfsonmicro.com/content/linux-drivers-wolfson-devices +T: git https://github.com/CirrusLogic/linux-drivers.git +W: https://github.com/CirrusLogic/linux-drivers/wiki S: Supported F: Documentation/hwmon/wm83?? F: arch/arm/mach-s3c64xx/mach-crag6410* diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c index bf9ed38..1cc1f1e 100644 --- a/drivers/spi/spi-atmel.c +++ b/drivers/spi/spi-atmel.c @@ -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; } /* diff --git a/include/sound/rcar_snd.h b/include/sound/rcar_snd.h index bb7b2eb..d8e33d3 100644 --- a/include/sound/rcar_snd.h +++ b/include/sound/rcar_snd.h @@ -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 diff --git a/include/sound/rt5645.h b/include/sound/rt5645.h index 22734bc..a5cf615 100644 --- a/include/sound/rt5645.h +++ b/include/sound/rt5645.h @@ -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 diff --git a/include/sound/simple_card.h b/include/sound/simple_card.h index b9b4f28..0399352 100644 --- a/include/sound/simple_card.h +++ b/include/sound/simple_card.h @@ -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; }; diff --git a/include/sound/soc.h b/include/sound/soc.h index 884e728..470f208 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -226,6 +226,18 @@ .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), \ @@ -1601,6 +1613,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, diff --git a/sound/arm/Kconfig b/sound/arm/Kconfig index 885683a..e040621 100644 --- a/sound/arm/Kconfig +++ b/sound/arm/Kconfig @@ -9,6 +9,14 @@ menuconfig SND_ARM Drivers that are implemented on ASoC can be found in "ALSA for SoC audio support" section. +config SND_PXA2XX_LIB + tristate + select SND_AC97_CODEC if SND_PXA2XX_LIB_AC97 + select SND_DMAENGINE_PCM + +config SND_PXA2XX_LIB_AC97 + bool + if SND_ARM config SND_ARMAACI @@ -21,13 +29,6 @@ config SND_PXA2XX_PCM tristate select SND_PCM -config SND_PXA2XX_LIB - tristate - select SND_AC97_CODEC if SND_PXA2XX_LIB_AC97 - -config SND_PXA2XX_LIB_AC97 - bool - config SND_PXA2XX_AC97 tristate "AC97 driver for the Intel PXA2xx chip" depends on ARCH_PXA diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index 225bfda..7de792b 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig @@ -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" diff --git a/sound/soc/Makefile b/sound/soc/Makefile index 134aca1..af0a571 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile @@ -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/ diff --git a/sound/soc/atmel/atmel_wm8904.c b/sound/soc/atmel/atmel_wm8904.c index aa354e1..1933bcd 100644 --- a/sound/soc/atmel/atmel_wm8904.c +++ b/sound/soc/atmel/atmel_wm8904.c @@ -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 = { diff --git a/sound/soc/au1x/db1000.c b/sound/soc/au1x/db1000.c index 452f404..e97c327 100644 --- a/sound/soc/au1x/db1000.c +++ b/sound/soc/au1x/db1000.c @@ -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); diff --git a/sound/soc/au1x/db1200.c b/sound/soc/au1x/db1200.c index 58c3164..638ca0b 100644 --- a/sound/soc/au1x/db1200.c +++ b/sound/soc/au1x/db1200.c @@ -174,14 +174,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 = { @@ -191,7 +184,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); diff --git a/sound/soc/au1x/psc-i2s.c b/sound/soc/au1x/psc-i2s.c index 38e853a..0bf9d62 100644 --- a/sound/soc/au1x/psc-i2s.c +++ b/sound/soc/au1x/psc-i2s.c @@ -296,7 +296,6 @@ static int au1xpsc_i2s_drvprobe(struct platform_device *pdev) { struct resource *iores, *dmares; unsigned long sel; - int ret; struct au1xpsc_audio_data *wd; wd = devm_kzalloc(&pdev->dev, sizeof(struct au1xpsc_audio_data), diff --git a/sound/soc/blackfin/bf5xx-ad1836.c b/sound/soc/blackfin/bf5xx-ad1836.c index 5bf1501..864df26 100644 --- a/sound/soc/blackfin/bf5xx-ad1836.c +++ b/sound/soc/blackfin/bf5xx-ad1836.c @@ -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); diff --git a/sound/soc/blackfin/bfin-eval-adau1373.c b/sound/soc/blackfin/bfin-eval-adau1373.c index 523baf58..72ac789 100644 --- a/sound/soc/blackfin/bfin-eval-adau1373.c +++ b/sound/soc/blackfin/bfin-eval-adau1373.c @@ -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); diff --git a/sound/soc/blackfin/bfin-eval-adau1701.c b/sound/soc/blackfin/bfin-eval-adau1701.c index f9e926d..5c67f72 100644 --- a/sound/soc/blackfin/bfin-eval-adau1701.c +++ b/sound/soc/blackfin/bfin-eval-adau1701.c @@ -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); diff --git a/sound/soc/blackfin/bfin-eval-adav80x.c b/sound/soc/blackfin/bfin-eval-adav80x.c index 27eee66..1037477 100644 --- a/sound/soc/blackfin/bfin-eval-adav80x.c +++ b/sound/soc/blackfin/bfin-eval-adav80x.c @@ -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, }; diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 0c9733e..70e5a75 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -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" diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 4a32077..be1491a 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -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 index 0000000..07a2664 --- /dev/null +++ b/sound/soc/codecs/ak4613.c @@ -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"); diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c index 4a90143..cda27c2 100644 --- a/sound/soc/codecs/ak4642.c +++ b/sound/soc/codecs/ak4642.c @@ -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> @@ -128,11 +130,8 @@ #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); } diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index 8a2221a..ac21b85 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -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; diff --git a/sound/soc/codecs/arizona.h b/sound/soc/codecs/arizona.h index ada0a41..7b68d05 100644 --- a/sound/soc/codecs/arizona.h +++ b/sound/soc/codecs/arizona.h @@ -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 index bd42ad3..0000000 --- a/sound/soc/codecs/hdmi.c +++ /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); diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index 4972bf3..3eb184c 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -732,14 +732,14 @@ static const struct snd_kcontrol_new rt5645_mono_adc_r_mix[] = { static const struct snd_kcontrol_new rt5645_dac_l_mix[] = { SOC_DAPM_SINGLE("Stereo ADC Switch", RT5645_AD_DA_MIXER, RT5645_M_ADCMIX_L_SFT, 1, 1), - SOC_DAPM_SINGLE("DAC1 Switch", RT5645_AD_DA_MIXER, + SOC_DAPM_SINGLE_AUTODISABLE("DAC1 Switch", RT5645_AD_DA_MIXER, RT5645_M_DAC1_L_SFT, 1, 1), }; static const struct snd_kcontrol_new rt5645_dac_r_mix[] = { SOC_DAPM_SINGLE("Stereo ADC Switch", RT5645_AD_DA_MIXER, RT5645_M_ADCMIX_R_SFT, 1, 1), - SOC_DAPM_SINGLE("DAC1 Switch", RT5645_AD_DA_MIXER, + SOC_DAPM_SINGLE_AUTODISABLE("DAC1 Switch", RT5645_AD_DA_MIXER, RT5645_M_DAC1_R_SFT, 1, 1), }; @@ -1381,7 +1381,7 @@ static void hp_amp_power(struct snd_soc_codec *codec, int on) regmap_write(rt5645->regmap, RT5645_PR_BASE + RT5645_MAMP_INT_REG2, 0xfc00); snd_soc_write(codec, RT5645_DEPOP_M2, 0x1140); - mdelay(5); + msleep(40); rt5645->hp_on = true; } else { /* depop parameters */ @@ -2829,13 +2829,15 @@ static int rt5645_jack_detect(struct snd_soc_codec *codec, int jack_insert) snd_soc_dapm_sync(dapm); rt5645->jack_type = SND_JACK_HEADPHONE; } - - snd_soc_update_bits(codec, RT5645_CHARGE_PUMP, 0x0300, 0x0200); - snd_soc_write(codec, RT5645_DEPOP_M1, 0x001d); - snd_soc_write(codec, RT5645_DEPOP_M1, 0x0001); + 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; + regmap_update_bits(rt5645->regmap, RT5645_HP_VOL, + RT5645_L_MUTE | RT5645_R_MUTE, + RT5645_L_MUTE | RT5645_R_MUTE); regmap_update_bits(rt5645->regmap, RT5645_IN1_CTRL2, RT5645_CBJ_MN_JD, RT5645_CBJ_MN_JD); regmap_update_bits(rt5645->regmap, RT5645_IN1_CTRL1, @@ -2848,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; @@ -2880,8 +2885,6 @@ int rt5645_set_jack_detect(struct snd_soc_codec *codec, rt5645->en_button_func = true; regmap_update_bits(rt5645->regmap, RT5645_GPIO_CTRL1, RT5645_GP1_PIN_IRQ, RT5645_GP1_PIN_IRQ); - regmap_update_bits(rt5645->regmap, RT5645_DEPOP_M1, - RT5645_HP_CB_MASK, RT5645_HP_CB_PU); regmap_update_bits(rt5645->regmap, RT5645_GEN_CTRL1, RT5645_DIG_GATE_CTRL, RT5645_DIG_GATE_CTRL); } @@ -3205,9 +3208,42 @@ static const struct dmi_system_id dmi_platform_intel_braswell[] = { DMI_MATCH(DMI_PRODUCT_NAME, "Celes"), }, }, + { + .ident = "Google Ultima", + .callback = strago_quirk_cb, + .matches = { + DMI_MATCH(DMI_PRODUCT_NAME, "Ultima"), + }, + }, + { } +}; + +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, @@ -3240,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); diff --git a/sound/soc/codecs/rt5645.h b/sound/soc/codecs/rt5645.h index 0e4cfc6..f45861c 100644 --- a/sound/soc/codecs/rt5645.h +++ b/sound/soc/codecs/rt5645.h @@ -777,8 +777,6 @@ #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) @@ -1626,6 +1624,10 @@ #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) diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c index 1a82b19..f1c9fff 100644 --- a/sound/soc/codecs/tlv320aic3x.c +++ b/sound/soc/codecs/tlv320aic3x.c @@ -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, diff --git a/sound/soc/codecs/wm0010.c b/sound/soc/codecs/wm0010.c index f2c6ad4..581ec15 100644 --- a/sound/soc/codecs/wm0010.c +++ b/sound/soc/codecs/wm0010.c @@ -577,7 +577,6 @@ static int wm0010_boot(struct snd_soc_codec *codec) struct wm0010_priv *wm0010 = snd_soc_codec_get_drvdata(codec); unsigned long flags; int ret; - const struct firmware *fw; struct spi_message m; struct spi_transfer t; struct dfw_pllrec pll_rec; @@ -623,14 +622,6 @@ static int wm0010_boot(struct snd_soc_codec *codec) wm0010->state = WM0010_OUT_OF_RESET; spin_unlock_irqrestore(&wm0010->irq_lock, flags); - /* First the bootloader */ - ret = request_firmware(&fw, "wm0010_stage2.bin", codec->dev); - if (ret != 0) { - dev_err(codec->dev, "Failed to request stage2 loader: %d\n", - ret); - goto abort; - } - if (!wait_for_completion_timeout(&wm0010->boot_completion, msecs_to_jiffies(20))) dev_err(codec->dev, "Failed to get interrupt from DSP\n"); @@ -673,7 +664,7 @@ static int wm0010_boot(struct snd_soc_codec *codec) img_swap = kzalloc(len, GFP_KERNEL | GFP_DMA); if (!img_swap) - goto abort; + goto abort_out; /* We need to re-order for 0010 */ byte_swap_64((u64 *)&pll_rec, img_swap, len); @@ -688,16 +679,16 @@ static int wm0010_boot(struct snd_soc_codec *codec) spi_message_add_tail(&t, &m); ret = spi_sync(spi, &m); - if (ret != 0) { + if (ret) { dev_err(codec->dev, "First PLL write failed: %d\n", ret); - goto abort; + goto abort_swap; } /* Use a second send of the message to get the return status */ ret = spi_sync(spi, &m); - if (ret != 0) { + if (ret) { dev_err(codec->dev, "Second PLL write failed: %d\n", ret); - goto abort; + goto abort_swap; } p = (u32 *)out; @@ -730,6 +721,10 @@ static int wm0010_boot(struct snd_soc_codec *codec) return 0; +abort_swap: + kfree(img_swap); +abort_out: + kfree(out); abort: /* Put the chip back into reset */ wm0010_halt(codec); diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c index 9756578..c04c0bc 100644 --- a/sound/soc/codecs/wm5110.c +++ b/sound/soc/codecs/wm5110.c @@ -38,6 +38,12 @@ 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 | diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c index e3b7d0c..dbd8840 100644 --- a/sound/soc/codecs/wm8960.c +++ b/sound/soc/codecs/wm8960.c @@ -211,28 +211,38 @@ static int wm8960_put_deemph(struct snd_kcontrol *kcontrol, return wm8960_set_deemph(codec); } -static const DECLARE_TLV_DB_SCALE(adc_tlv, -9700, 50, 0); -static const DECLARE_TLV_DB_SCALE(dac_tlv, -12700, 50, 1); +static const DECLARE_TLV_DB_SCALE(adc_tlv, -9750, 50, 1); +static const DECLARE_TLV_DB_SCALE(inpga_tlv, -1725, 75, 0); +static const DECLARE_TLV_DB_SCALE(dac_tlv, -12750, 50, 1); static const DECLARE_TLV_DB_SCALE(bypass_tlv, -2100, 300, 0); static const DECLARE_TLV_DB_SCALE(out_tlv, -12100, 100, 1); -static const DECLARE_TLV_DB_SCALE(boost_tlv, -1200, 300, 1); +static const DECLARE_TLV_DB_SCALE(lineinboost_tlv, -1500, 300, 1); +static const unsigned int micboost_tlv[] = { + TLV_DB_RANGE_HEAD(2), + 0, 1, TLV_DB_SCALE_ITEM(0, 1300, 0), + 2, 3, TLV_DB_SCALE_ITEM(2000, 900, 0), +}; static const struct snd_kcontrol_new wm8960_snd_controls[] = { SOC_DOUBLE_R_TLV("Capture Volume", WM8960_LINVOL, WM8960_RINVOL, - 0, 63, 0, adc_tlv), + 0, 63, 0, inpga_tlv), SOC_DOUBLE_R("Capture Volume ZC Switch", WM8960_LINVOL, WM8960_RINVOL, 6, 1, 0), SOC_DOUBLE_R("Capture Switch", WM8960_LINVOL, WM8960_RINVOL, 7, 1, 0), SOC_SINGLE_TLV("Right Input Boost Mixer RINPUT3 Volume", - WM8960_INBMIX1, 4, 7, 0, boost_tlv), + WM8960_INBMIX1, 4, 7, 0, lineinboost_tlv), SOC_SINGLE_TLV("Right Input Boost Mixer RINPUT2 Volume", - WM8960_INBMIX1, 1, 7, 0, boost_tlv), + WM8960_INBMIX1, 1, 7, 0, lineinboost_tlv), SOC_SINGLE_TLV("Left Input Boost Mixer LINPUT3 Volume", - WM8960_INBMIX2, 4, 7, 0, boost_tlv), + WM8960_INBMIX2, 4, 7, 0, lineinboost_tlv), SOC_SINGLE_TLV("Left Input Boost Mixer LINPUT2 Volume", - WM8960_INBMIX2, 1, 7, 0, boost_tlv), + WM8960_INBMIX2, 1, 7, 0, lineinboost_tlv), +SOC_SINGLE_TLV("Right Input Boost Mixer RINPUT1 Volume", + WM8960_RINPATH, 4, 3, 0, micboost_tlv), +SOC_SINGLE_TLV("Left Input Boost Mixer LINPUT1 Volume", + WM8960_LINPATH, 4, 3, 0, micboost_tlv), SOC_DOUBLE_R_TLV("Playback Volume", WM8960_LDAC, WM8960_RDAC, 0, 255, 0, dac_tlv), diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index b4eb975..293e47a 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -2944,7 +2944,8 @@ static int wm8962_mute(struct snd_soc_dai *dai, int mute) WM8962_DAC_MUTE, val); } -#define WM8962_RATES SNDRV_PCM_RATE_8000_96000 +#define WM8962_RATES (SNDRV_PCM_RATE_8000_48000 |\ + SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000) #define WM8962_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index add6bb9..4495a40 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -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), @@ -663,7 +752,7 @@ static int mcasp_common_hw_param(struct davinci_mcasp *mcasp, int stream, u8 rx_ser = 0; u8 slots = mcasp->tdm_slots; u8 max_active_serializers = (channels + slots - 1) / slots; - int active_serializers, numevt, n; + int active_serializers, numevt; u32 reg; /* Default configuration */ if (mcasp->version < MCASP_VERSION_3) @@ -745,9 +834,8 @@ static int mcasp_common_hw_param(struct davinci_mcasp *mcasp, int stream, * The number of words for numevt need to be in steps of active * serializers. */ - n = numevt % active_serializers; - if (n) - numevt += (active_serializers - n); + numevt = (numevt / active_serializers) * active_serializers; + while (period_words % numevt && numevt > 0) numevt -= active_serializers; if (numevt <= 0) @@ -777,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; } @@ -923,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) @@ -1028,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; @@ -1070,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++; @@ -1095,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; @@ -1115,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 @@ -1125,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 @@ -1185,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) @@ -1299,6 +1429,7 @@ static struct snd_soc_dai_driver davinci_mcasp_dai[] = { .ops = &davinci_mcasp_dai_ops, .symmetric_samplebits = 1, + .symmetric_rates = 1, }, { .name = "davinci-mcasp.1", @@ -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, @@ -1685,7 +1763,7 @@ static int davinci_mcasp_probe(struct platform_device *pdev) irq = platform_get_irq_byname(pdev, "common"); if (irq >= 0) { - irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s_common\n", + irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s_common", dev_name(&pdev->dev)); ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, davinci_mcasp_common_irq_handler, @@ -1702,7 +1780,7 @@ static int davinci_mcasp_probe(struct platform_device *pdev) irq = platform_get_irq_byname(pdev, "rx"); if (irq >= 0) { - irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s_rx\n", + irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s_rx", dev_name(&pdev->dev)); ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, davinci_mcasp_rx_irq_handler, @@ -1717,7 +1795,7 @@ static int davinci_mcasp_probe(struct platform_device *pdev) irq = platform_get_irq_byname(pdev, "tx"); if (irq >= 0) { - irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s_tx\n", + irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s_tx", dev_name(&pdev->dev)); ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, davinci_mcasp_tx_irq_handler, @@ -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; diff --git a/sound/soc/fsl/fsl-asoc-card.c b/sound/soc/fsl/fsl-asoc-card.c index 5aeb6ed..0901d5e 100644 --- a/sound/soc/fsl/fsl-asoc-card.c +++ b/sound/soc/fsl/fsl-asoc-card.c @@ -488,7 +488,8 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) priv->dai_fmt |= SND_SOC_DAIFMT_CBM_CFM; } else { dev_err(&pdev->dev, "unknown Device Tree compatible\n"); - return -EINVAL; + ret = -EINVAL; + goto asrc_fail; } /* Common settings for corresponding Freescale CPU DAI driver */ @@ -592,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, diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index a18fd92..9366b5a 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -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, diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 8ec6fb2..37c5cd4 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -249,7 +249,8 @@ MODULE_DEVICE_TABLE(of, fsl_ssi_ids); static bool fsl_ssi_is_ac97(struct fsl_ssi_private *ssi_private) { - return !!(ssi_private->dai_fmt & SND_SOC_DAIFMT_AC97); + return (ssi_private->dai_fmt & SND_SOC_DAIFMT_FORMAT_MASK) == + SND_SOC_DAIFMT_AC97; } static bool fsl_ssi_is_i2s_master(struct fsl_ssi_private *ssi_private) @@ -947,7 +948,7 @@ static int _fsl_ssi_set_dai_fmt(struct device *dev, CCSR_SSI_SCR_TCH_EN); } - if (fmt & SND_SOC_DAIFMT_AC97) + if ((fmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_AC97) fsl_ssi_setup_ac97(ssi_private); return 0; diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index 3ff76d4..54c3320 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -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; diff --git a/sound/soc/intel/atom/sst-mfld-platform-pcm.c b/sound/soc/intel/atom/sst-mfld-platform-pcm.c index 683e501..5e9c316 100644 --- a/sound/soc/intel/atom/sst-mfld-platform-pcm.c +++ b/sound/soc/intel/atom/sst-mfld-platform-pcm.c @@ -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) { diff --git a/sound/soc/intel/boards/broadwell.c b/sound/soc/intel/boards/broadwell.c index 8bafaf6..3f8a1e1 100644 --- a/sound/soc/intel/boards/broadwell.c +++ b/sound/soc/intel/boards/broadwell.c @@ -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", }, diff --git a/sound/soc/intel/haswell/sst-haswell-ipc.c b/sound/soc/intel/haswell/sst-haswell-ipc.c index f6efa9d..b27f25f 100644 --- a/sound/soc/intel/haswell/sst-haswell-ipc.c +++ b/sound/soc/intel/haswell/sst-haswell-ipc.c @@ -302,6 +302,10 @@ struct sst_hsw { struct sst_hsw_ipc_dx_reply dx; void *dx_context; dma_addr_t dx_context_paddr; + enum sst_hsw_device_id dx_dev; + enum sst_hsw_device_mclk dx_mclk; + enum sst_hsw_device_mode dx_mode; + u32 dx_clock_divider; /* boot */ wait_queue_head_t boot_wait; @@ -1400,10 +1404,10 @@ int sst_hsw_device_set_config(struct sst_hsw *hsw, trace_ipc_request("set device config", dev); - config.ssp_interface = dev; - config.clock_frequency = mclk; - config.mode = mode; - config.clock_divider = clock_divider; + hsw->dx_dev = config.ssp_interface = dev; + hsw->dx_mclk = config.clock_frequency = mclk; + hsw->dx_mode = config.mode = mode; + hsw->dx_clock_divider = config.clock_divider = clock_divider; if (mode == SST_HSW_DEVICE_TDM_CLOCK_MASTER) config.channels = 4; else @@ -1704,10 +1708,10 @@ int sst_hsw_dsp_runtime_resume(struct sst_hsw *hsw) return -EIO; } - /* Set ADSP SSP port settings */ - ret = sst_hsw_device_set_config(hsw, SST_HSW_DEVICE_SSP_0, - SST_HSW_DEVICE_MCLK_FREQ_24_MHZ, - SST_HSW_DEVICE_CLOCK_MASTER, 9); + /* Set ADSP SSP port settings - sadly the FW does not store SSP port + settings as part of the PM context. */ + ret = sst_hsw_device_set_config(hsw, hsw->dx_dev, hsw->dx_mclk, + hsw->dx_mode, hsw->dx_clock_divider); if (ret < 0) dev_err(dev, "error: SSP re-initialization failed\n"); diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index 7d617bf..bea2673 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -510,17 +510,6 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { }, }, { - .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, .playback = { @@ -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) diff --git a/sound/soc/jz4740/jz4740-i2s.c b/sound/soc/jz4740/jz4740-i2s.c index b05fb1c..794a349 100644 --- a/sound/soc/jz4740/jz4740-i2s.c +++ b/sound/soc/jz4740/jz4740-i2s.c @@ -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) diff --git a/sound/soc/kirkwood/armada-370-db.c b/sound/soc/kirkwood/armada-370-db.c index de7563b..e0304d5 100644 --- a/sound/soc/kirkwood/armada-370-db.c +++ b/sound/soc/kirkwood/armada-370-db.c @@ -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 = { diff --git a/sound/soc/mediatek/mt8173-max98090.c b/sound/soc/mediatek/mt8173-max98090.c index 684e8a7..71a1a35 100644 --- a/sound/soc/mediatek/mt8173-max98090.c +++ b/sound/soc/mediatek/mt8173-max98090.c @@ -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); diff --git a/sound/soc/mediatek/mt8173-rt5650-rt5676.c b/sound/soc/mediatek/mt8173-rt5650-rt5676.c index 86cf975..50ba538 100644 --- a/sound/soc/mediatek/mt8173-rt5650-rt5676.c +++ b/sound/soc/mediatek/mt8173-rt5650-rt5676.c @@ -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); diff --git a/sound/soc/mediatek/mtk-afe-pcm.c b/sound/soc/mediatek/mtk-afe-pcm.c index d190fe0..f5baf3c 100644 --- a/sound/soc/mediatek/mtk-afe-pcm.c +++ b/sound/soc/mediatek/mtk-afe-pcm.c @@ -549,6 +549,23 @@ static int mtk_afe_dais_startup(struct snd_pcm_substream *substream, memif->substream = substream; snd_soc_set_runtime_hwparams(substream, &mtk_afe_hardware); + + /* + * Capture cannot use ping-pong buffer since hw_ptr at IRQ may be + * smaller than period_size due to AFE's internal buffer. + * This easily leads to overrun when avail_min is period_size. + * One more period can hold the possible unread buffer. + */ + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + ret = snd_pcm_hw_constraint_minmax(runtime, + SNDRV_PCM_HW_PARAM_PERIODS, + 3, + mtk_afe_hardware.periods_max); + if (ret < 0) { + dev_err(afe->dev, "hw_constraint_minmax failed\n"); + return ret; + } + } ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); if (ret < 0) diff --git a/sound/soc/mxs/mxs-sgtl5000.c b/sound/soc/mxs/mxs-sgtl5000.c index 6e6fce6..2b23ffb 100644 --- a/sound/soc/mxs/mxs-sgtl5000.c +++ b/sound/soc/mxs/mxs-sgtl5000.c @@ -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; } diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig index 39cea80..f2bf866 100644 --- a/sound/soc/pxa/Kconfig +++ b/sound/soc/pxa/Kconfig @@ -1,7 +1,6 @@ config SND_PXA2XX_SOC tristate "SoC Audio for the Intel PXA2xx chip" depends on ARCH_PXA - select SND_ARM select SND_PXA2XX_LIB help Say Y or M if you want to add support for codecs attached to @@ -25,7 +24,6 @@ config SND_PXA2XX_AC97 config SND_PXA2XX_SOC_AC97 tristate select AC97_BUS - select SND_ARM select SND_PXA2XX_LIB_AC97 select SND_SOC_AC97_BUS diff --git a/sound/soc/pxa/brownstone.c b/sound/soc/pxa/brownstone.c index 2b26318..6147e86 100644 --- a/sound/soc/pxa/brownstone.c +++ b/sound/soc/pxa/brownstone.c @@ -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); diff --git a/sound/soc/pxa/corgi.c b/sound/soc/pxa/corgi.c index 3580d10..c97dc13 100644 --- a/sound/soc/pxa/corgi.c +++ b/sound/soc/pxa/corgi.c @@ -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); diff --git a/sound/soc/pxa/e740_wm9705.c b/sound/soc/pxa/e740_wm9705.c index d72e124..1de8765 100644 --- a/sound/soc/pxa/e740_wm9705.c +++ b/sound/soc/pxa/e740_wm9705.c @@ -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; } diff --git a/sound/soc/pxa/e750_wm9705.c b/sound/soc/pxa/e750_wm9705.c index 48f2d7c..b7eb7cd 100644 --- a/sound/soc/pxa/e750_wm9705.c +++ b/sound/soc/pxa/e750_wm9705.c @@ -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; } diff --git a/sound/soc/pxa/e800_wm9712.c b/sound/soc/pxa/e800_wm9712.c index 45d4bd4..41bf714 100644 --- a/sound/soc/pxa/e800_wm9712.c +++ b/sound/soc/pxa/e800_wm9712.c @@ -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; } diff --git a/sound/soc/pxa/hx4700.c b/sound/soc/pxa/hx4700.c index 9f8be7c..ecbf287 100644 --- a/sound/soc/pxa/hx4700.c +++ b/sound/soc/pxa/hx4700.c @@ -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); diff --git a/sound/soc/pxa/imote2.c b/sound/soc/pxa/imote2.c index 29fabbf..9d0e407 100644 --- a/sound/soc/pxa/imote2.c +++ b/sound/soc/pxa/imote2.c @@ -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); diff --git a/sound/soc/pxa/mioa701_wm9713.c b/sound/soc/pxa/mioa701_wm9713.c index a9615a57..29bc60e8 100644 --- a/sound/soc/pxa/mioa701_wm9713.c +++ b/sound/soc/pxa/mioa701_wm9713.c @@ -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, diff --git a/sound/soc/pxa/palm27x.c b/sound/soc/pxa/palm27x.c index c20bbc0..4e74d95 100644 --- a/sound/soc/pxa/palm27x.c +++ b/sound/soc/pxa/palm27x.c @@ -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, diff --git a/sound/soc/pxa/poodle.c b/sound/soc/pxa/poodle.c index 80b457a..84d0e2e 100644 --- a/sound/soc/pxa/poodle.c +++ b/sound/soc/pxa/poodle.c @@ -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); diff --git a/sound/soc/pxa/pxa-ssp.c b/sound/soc/pxa/pxa-ssp.c index 3da485e..da03fad 100644 --- a/sound/soc/pxa/pxa-ssp.c +++ b/sound/soc/pxa/pxa-ssp.c @@ -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) diff --git a/sound/soc/pxa/pxa2xx-ac97.c b/sound/soc/pxa/pxa2xx-ac97.c index 1f60546..9e4b04e 100644 --- a/sound/soc/pxa/pxa2xx-ac97.c +++ b/sound/soc/pxa/pxa2xx-ac97.c @@ -49,7 +49,7 @@ static struct snd_ac97_bus_ops pxa2xx_ac97_ops = { .reset = pxa2xx_ac97_cold_reset, }; -static unsigned long pxa2xx_ac97_pcm_stereo_in_req = 12; +static unsigned long pxa2xx_ac97_pcm_stereo_in_req = 11; static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_stereo_in = { .addr = __PREG(PCDR), .addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, @@ -57,7 +57,7 @@ static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_stereo_in = { .filter_data = &pxa2xx_ac97_pcm_stereo_in_req, }; -static unsigned long pxa2xx_ac97_pcm_stereo_out_req = 11; +static unsigned long pxa2xx_ac97_pcm_stereo_out_req = 12; static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_stereo_out = { .addr = __PREG(PCDR), .addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, diff --git a/sound/soc/pxa/pxa2xx-pcm.c b/sound/soc/pxa/pxa2xx-pcm.c index 831ee37..29a3fdb 100644 --- a/sound/soc/pxa/pxa2xx-pcm.c +++ b/sound/soc/pxa/pxa2xx-pcm.c @@ -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 = { diff --git a/sound/soc/pxa/spitz.c b/sound/soc/pxa/spitz.c index 461123a..b002226 100644 --- a/sound/soc/pxa/spitz.c +++ b/sound/soc/pxa/spitz.c @@ -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; } diff --git a/sound/soc/pxa/tosa.c b/sound/soc/pxa/tosa.c index f59f566..49518dd 100644 --- a/sound/soc/pxa/tosa.c +++ b/sound/soc/pxa/tosa.c @@ -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; } diff --git a/sound/soc/pxa/ttc-dkb.c b/sound/soc/pxa/ttc-dkb.c index 1753c7d..65c20f7 100644 --- a/sound/soc/pxa/ttc-dkb.c +++ b/sound/soc/pxa/ttc-dkb.c @@ -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); diff --git a/sound/soc/qcom/lpass-cpu.c b/sound/soc/qcom/lpass-cpu.c index 97bc202..e5101e0 100644 --- a/sound/soc/qcom/lpass-cpu.c +++ b/sound/soc/qcom/lpass-cpu.c @@ -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]); } } diff --git a/sound/soc/rockchip/Kconfig b/sound/soc/rockchip/Kconfig index 58bae8e..5709057 100644 --- a/sound/soc/rockchip/Kconfig +++ b/sound/soc/rockchip/Kconfig @@ -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 diff --git a/sound/soc/sh/Kconfig b/sound/soc/sh/Kconfig index 07114b0..6ca90aa 100644 --- a/sound/soc/sh/Kconfig +++ b/sound/soc/sh/Kconfig @@ -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 diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c index fefc881..c4ebbb7 100644 --- a/sound/soc/sh/rcar/adg.c +++ b/sound/soc/sh/rcar/adg.c @@ -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 @@ -16,12 +16,26 @@ #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; diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index f3feed5..eec294d 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -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 */ diff --git a/sound/soc/sh/rcar/ctu.c b/sound/soc/sh/rcar/ctu.c index 05498bb..a3e7c71 100644 --- a/sound/soc/sh/rcar/ctu.c +++ b/sound/soc/sh/rcar/ctu.c @@ -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)); } } diff --git a/sound/soc/sh/rcar/dvc.c b/sound/soc/sh/rcar/dvc.c index 5779638..8d8eee6 100644 --- a/sound/soc/sh/rcar/dvc.c +++ b/sound/soc/sh/rcar/dvc.c @@ -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)); } } diff --git a/sound/soc/sh/rcar/mix.c b/sound/soc/sh/rcar/mix.c index 0d5c102..8544403 100644 --- a/sound/soc/sh/rcar/mix.c +++ b/sound/soc/sh/rcar/mix.c @@ -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)); } } diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 7a0e52b..e4068d7 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -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 diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c index 89a18e1..ca7a20f 100644 --- a/sound/soc/sh/rcar/src.c +++ b/sound/soc/sh/rcar/src.c @@ -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; @@ -932,12 +931,6 @@ static int rsnd_src_pcm_new(struct rsnd_mod *mod, */ /* - * Gen1 is not supported - */ - if (rsnd_is_gen1(priv)) - return 0; - - /* * SRC sync convert needs clock master */ if (!rsnd_rdai_is_clk_master(rdai)) @@ -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)); } } diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index d45b9a7..5e05f942 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -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)); } } diff --git a/sound/soc/sh/siu_dai.c b/sound/soc/sh/siu_dai.c index abb0d95..76b2ab8 100644 --- a/sound/soc/sh/siu_dai.c +++ b/sound/soc/sh/siu_dai.c @@ -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; } diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 6173d15..3b471f9 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -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) diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index f4bf21a..ff8bda4 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -3501,7 +3501,7 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w, default: WARN(1, "Unknown event %d\n", event); - return -EINVAL; + ret = -EINVAL; } out: diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 70e4b9d..3173958 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -34,6 +34,24 @@ #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/soc-utils.c b/sound/soc/soc-utils.c index 362c69a..53dd085 100644 --- a/sound/soc/soc-utils.c +++ b/sound/soc/soc-utils.c @@ -101,6 +101,15 @@ static struct snd_soc_codec_driver dummy_codec; SNDRV_PCM_FMTBIT_S32_LE | \ SNDRV_PCM_FMTBIT_U32_LE | \ SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE) +/* + * The dummy CODEC is only meant to be used in situations where there is no + * actual hardware. + * + * If there is actual hardware even if it does not have a control bus + * the hardware will still have constraints like supported samplerates, etc. + * which should be modelled. And the data flow graph also should be modelled + * using DAPM. + */ static struct snd_soc_dai_driver dummy_dai = { .name = "snd-soc-dummy-dai", .playback = { diff --git a/sound/soc/spear/Kconfig b/sound/soc/spear/Kconfig index 0a53053..4fb9141 100644 --- a/sound/soc/spear/Kconfig +++ b/sound/soc/spear/Kconfig @@ -1,6 +1,6 @@ config SND_SPEAR_SOC tristate - select SND_DMAENGINE_PCM + select SND_SOC_GENERIC_DMAENGINE_PCM config SND_SPEAR_SPDIF_OUT tristate diff --git a/sound/soc/sti/uniperif_player.c b/sound/soc/sti/uniperif_player.c index f6eefe1..843f037 100644 --- a/sound/soc/sti/uniperif_player.c +++ b/sound/soc/sti/uniperif_player.c @@ -989,8 +989,8 @@ static int uni_player_parse_dt(struct platform_device *pdev, if (!info) return -ENOMEM; - of_property_read_u32(pnode, "version", &player->ver); - if (player->ver == SND_ST_UNIPERIF_VERSION_UNKNOWN) { + if (of_property_read_u32(pnode, "version", &player->ver) || + player->ver == SND_ST_UNIPERIF_VERSION_UNKNOWN) { dev_err(dev, "Unknown uniperipheral version "); return -EINVAL; } @@ -998,10 +998,16 @@ static int uni_player_parse_dt(struct platform_device *pdev, if (player->ver >= SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0) info->underflow_enabled = 1; - of_property_read_u32(pnode, "uniperiph-id", &info->id); + if (of_property_read_u32(pnode, "uniperiph-id", &info->id)) { + dev_err(dev, "uniperipheral id not defined"); + return -EINVAL; + } /* Read the device mode property */ - of_property_read_string(pnode, "mode", &mode); + if (of_property_read_string(pnode, "mode", &mode)) { + dev_err(dev, "uniperipheral mode not defined"); + return -EINVAL; + } if (strcasecmp(mode, "hdmi") == 0) info->player_type = SND_ST_UNIPERIF_PLAYER_TYPE_HDMI; diff --git a/sound/soc/sti/uniperif_reader.c b/sound/soc/sti/uniperif_reader.c index c502626..f791239 100644 --- a/sound/soc/sti/uniperif_reader.c +++ b/sound/soc/sti/uniperif_reader.c @@ -316,7 +316,11 @@ static int uni_reader_parse_dt(struct platform_device *pdev, if (!info) return -ENOMEM; - of_property_read_u32(node, "version", &reader->ver); + if (of_property_read_u32(node, "version", &reader->ver) || + reader->ver == SND_ST_UNIPERIF_VERSION_UNKNOWN) { + dev_err(&pdev->dev, "Unknown uniperipheral version "); + return -EINVAL; + } /* Save the info structure */ reader->info = info; diff --git a/sound/soc/sunxi/Kconfig b/sound/soc/sunxi/Kconfig new file mode 100644 index 0000000..84c72ec --- /dev/null +++ b/sound/soc/sunxi/Kconfig @@ -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 index 0000000..ea8a08c --- /dev/null +++ b/sound/soc/sunxi/Makefile @@ -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 index 0000000..8d59d83 --- /dev/null +++ b/sound/soc/sunxi/sun4i-codec.c @@ -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"); diff --git a/sound/soc/ux500/mop500.c b/sound/soc/ux500/mop500.c index 4e0c0e5..ba9fc09 100644 --- a/sound/soc/ux500/mop500.c +++ b/sound/soc/ux500/mop500.c @@ -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 = { diff --git a/sound/soc/ux500/ux500_msp_dai.c b/sound/soc/ux500/ux500_msp_dai.c index f5df08d..6ba8ae9 100644 --- a/sound/soc/ux500/ux500_msp_dai.c +++ b/sound/soc/ux500/ux500_msp_dai.c @@ -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 = { |