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. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQEcBAABAgAGBQJWBF7yAAoJECTWi3JdVIfQ1MEH/jnzSyEVIuG+l8UkMaz6gf4w zGsM1KCn//mfPl7yAoOdsnElOLR+Fmf+0jx4pCPQKrjvBGwjwH/IwBR1rwuEeUPY 7d66efpWOKlTf3qpsF1S7ZIlAZOs0NFvo0jwA1ZY/pc3YEBekyWxbABk/uWAVrM5 HJJKafI7WeiYrF0l0z2sG7BpsFtr8JKqrOVM+SGaPTNn2k+/lQ1bwTk1liOEUbsv oq8NFNrUWPBCwbUNJQxBOvmoXC6Oa6+JBVO3+SsoS0q2FweNpqtZopjmoqHM8CiN SkBeFT+wYlSGSnnFgAXXA2+kq74TeP2CvToo6tw+gf4LZXydKIaAdeuT6M9weZA= =8h3u -----END PGP SIGNATURE----- Merge tag 'asoc-v4.3-rc2' into asoc-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. # gpg: Signature made Fri 25 Sep 2015 05:37:06 KST using RSA key ID 5D5487D0 # gpg: key CD7BEEBC: no public key for trusted key - skipped # gpg: key CD7BEEBC marked as ultimately trusted # gpg: key AF88CD16: no public key for trusted key - skipped # gpg: key AF88CD16 marked as ultimately trusted # gpg: key 16005C11: no public key for trusted key - skipped # gpg: key 16005C11 marked as ultimately trusted # gpg: key 5621E907: no public key for trusted key - skipped # gpg: key 5621E907 marked as ultimately trusted # gpg: key 5C6153AD: no public key for trusted key - skipped # gpg: key 5C6153AD marked as ultimately trusted # gpg: Good signature from "Mark Brown <broonie@sirena.org.uk>" # gpg: aka "Mark Brown <broonie@debian.org>" # gpg: aka "Mark Brown <broonie@kernel.org>" # gpg: aka "Mark Brown <broonie@tardis.ed.ac.uk>" # gpg: aka "Mark Brown <broonie@linaro.org>" # gpg: aka "Mark Brown <Mark.Brown@linaro.org>"
This commit is contained in:
commit
7c9c29eefa
17
Documentation/devicetree/bindings/sound/ak4613.txt
Normal file
17
Documentation/devicetree/bindings/sound/ak4613.txt
Normal file
|
@ -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>;
|
||||
};
|
||||
};
|
|
@ -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";
|
||||
};
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
|
33
Documentation/devicetree/bindings/sound/sun4i-codec.txt
Normal file
33
Documentation/devicetree/bindings/sound/sun4i-codec.txt
Normal file
|
@ -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";
|
||||
};
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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), \
|
||||
|
@ -1603,6 +1615,8 @@ int snd_soc_of_parse_card_name(struct snd_soc_card *card,
|
|||
int snd_soc_of_parse_audio_simple_widgets(struct snd_soc_card *card,
|
||||
const char *propname);
|
||||
int snd_soc_of_parse_tdm_slot(struct device_node *np,
|
||||
unsigned int *tx_mask,
|
||||
unsigned int *rx_mask,
|
||||
unsigned int *slots,
|
||||
unsigned int *slot_width);
|
||||
void snd_soc_of_parse_audio_prefix(struct snd_soc_card *card,
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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/
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -178,14 +178,7 @@ static int db1200_audio_probe(struct platform_device *pdev)
|
|||
|
||||
card = db1200_cards[pid->driver_data];
|
||||
card->dev = &pdev->dev;
|
||||
return snd_soc_register_card(card);
|
||||
}
|
||||
|
||||
static int db1200_audio_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct snd_soc_card *card = platform_get_drvdata(pdev);
|
||||
snd_soc_unregister_card(card);
|
||||
return 0;
|
||||
return devm_snd_soc_register_card(&pdev->dev, card);
|
||||
}
|
||||
|
||||
static struct platform_driver db1200_audio_driver = {
|
||||
|
@ -195,7 +188,6 @@ static struct platform_driver db1200_audio_driver = {
|
|||
},
|
||||
.id_table = db1200_pids,
|
||||
.probe = db1200_audio_probe,
|
||||
.remove = db1200_audio_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(db1200_audio_driver);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
497
sound/soc/codecs/ak4613.c
Normal file
497
sound/soc/codecs/ak4613.c
Normal file
|
@ -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");
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
|
@ -2829,6 +2829,9 @@ static int rt5645_jack_detect(struct snd_soc_codec *codec, int jack_insert)
|
|||
snd_soc_dapm_sync(dapm);
|
||||
rt5645->jack_type = SND_JACK_HEADPHONE;
|
||||
}
|
||||
if (rt5645->pdata.jd_invert)
|
||||
regmap_update_bits(rt5645->regmap, RT5645_IRQ_CTRL2,
|
||||
RT5645_JD_1_1_MASK, RT5645_JD_1_1_INV);
|
||||
} else { /* jack out */
|
||||
rt5645->jack_type = 0;
|
||||
|
||||
|
@ -2847,6 +2850,9 @@ static int rt5645_jack_detect(struct snd_soc_codec *codec, int jack_insert)
|
|||
snd_soc_dapm_disable_pin(dapm, "LDO2");
|
||||
snd_soc_dapm_disable_pin(dapm, "Mic Det Power");
|
||||
snd_soc_dapm_sync(dapm);
|
||||
if (rt5645->pdata.jd_invert)
|
||||
regmap_update_bits(rt5645->regmap, RT5645_IRQ_CTRL2,
|
||||
RT5645_JD_1_1_MASK, RT5645_JD_1_1_NOR);
|
||||
}
|
||||
|
||||
return rt5645->jack_type;
|
||||
|
@ -3212,6 +3218,32 @@ static const struct dmi_system_id dmi_platform_intel_braswell[] = {
|
|||
{ }
|
||||
};
|
||||
|
||||
static struct rt5645_platform_data buddy_platform_data = {
|
||||
.dmic1_data_pin = RT5645_DMIC_DATA_GPIO5,
|
||||
.dmic2_data_pin = RT5645_DMIC_DATA_IN2P,
|
||||
.jd_mode = 3,
|
||||
.jd_invert = true,
|
||||
};
|
||||
|
||||
static int buddy_quirk_cb(const struct dmi_system_id *id)
|
||||
{
|
||||
rt5645_pdata = &buddy_platform_data;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct dmi_system_id dmi_platform_intel_broadwell[] __initdata = {
|
||||
{
|
||||
.ident = "Chrome Buddy",
|
||||
.callback = buddy_quirk_cb,
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Buddy"),
|
||||
},
|
||||
},
|
||||
{ }
|
||||
};
|
||||
|
||||
|
||||
static int rt5645_parse_dt(struct rt5645_priv *rt5645, struct device *dev)
|
||||
{
|
||||
rt5645->pdata.in2_diff = device_property_read_bool(dev,
|
||||
|
@ -3244,7 +3276,8 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
|
|||
|
||||
if (pdata)
|
||||
rt5645->pdata = *pdata;
|
||||
else if (dmi_check_system(dmi_platform_intel_braswell))
|
||||
else if (dmi_check_system(dmi_platform_intel_braswell) ||
|
||||
dmi_check_system(dmi_platform_intel_broadwell))
|
||||
rt5645->pdata = *rt5645_pdata;
|
||||
else
|
||||
rt5645_parse_dt(rt5645, &i2c->dev);
|
||||
|
|
|
@ -779,8 +779,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)
|
||||
|
@ -1628,6 +1626,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)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 |
|
||||
|
|
|
@ -80,12 +80,13 @@ struct davinci_mcasp {
|
|||
|
||||
/* McASP specific data */
|
||||
int tdm_slots;
|
||||
u32 tdm_mask[2];
|
||||
int slot_width;
|
||||
u8 op_mode;
|
||||
u8 num_serializer;
|
||||
u8 *serial_dir;
|
||||
u8 version;
|
||||
u8 bclk_div;
|
||||
u16 bclk_lrclk_ratio;
|
||||
int streams;
|
||||
u32 irq_request[2];
|
||||
int dma_request[2];
|
||||
|
@ -556,8 +557,21 @@ static int __davinci_mcasp_set_clkdiv(struct snd_soc_dai *dai, int div_id,
|
|||
mcasp->bclk_div = div;
|
||||
break;
|
||||
|
||||
case 2: /* BCLK/LRCLK ratio */
|
||||
mcasp->bclk_lrclk_ratio = div;
|
||||
case 2: /*
|
||||
* BCLK/LRCLK ratio descries how many bit-clock cycles
|
||||
* fit into one frame. The clock ratio is given for a
|
||||
* full period of data (for I2S format both left and
|
||||
* right channels), so it has to be divided by number
|
||||
* of tdm-slots (for I2S - divided by 2).
|
||||
* Instead of storing this ratio, we calculate a new
|
||||
* tdm_slot width by dividing the the ratio by the
|
||||
* number of configured tdm slots.
|
||||
*/
|
||||
mcasp->slot_width = div / mcasp->tdm_slots;
|
||||
if (div % mcasp->tdm_slots)
|
||||
dev_warn(mcasp->dev,
|
||||
"%s(): BCLK/LRCLK %d is not divisible by %d tdm slots",
|
||||
__func__, div, mcasp->tdm_slots);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -596,12 +610,92 @@ static int davinci_mcasp_set_sysclk(struct snd_soc_dai *dai, int clk_id,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* All serializers must have equal number of channels */
|
||||
static int davinci_mcasp_ch_constraint(struct davinci_mcasp *mcasp, int stream,
|
||||
int serializers)
|
||||
{
|
||||
struct snd_pcm_hw_constraint_list *cl = &mcasp->chconstr[stream];
|
||||
unsigned int *list = (unsigned int *) cl->list;
|
||||
int slots = mcasp->tdm_slots;
|
||||
int i, count = 0;
|
||||
|
||||
if (mcasp->tdm_mask[stream])
|
||||
slots = hweight32(mcasp->tdm_mask[stream]);
|
||||
|
||||
for (i = 2; i <= slots; i++)
|
||||
list[count++] = i;
|
||||
|
||||
for (i = 2; i <= serializers; i++)
|
||||
list[count++] = i*slots;
|
||||
|
||||
cl->count = count;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int davinci_mcasp_set_ch_constraints(struct davinci_mcasp *mcasp)
|
||||
{
|
||||
int rx_serializers = 0, tx_serializers = 0, ret, i;
|
||||
|
||||
for (i = 0; i < mcasp->num_serializer; i++)
|
||||
if (mcasp->serial_dir[i] == TX_MODE)
|
||||
tx_serializers++;
|
||||
else if (mcasp->serial_dir[i] == RX_MODE)
|
||||
rx_serializers++;
|
||||
|
||||
ret = davinci_mcasp_ch_constraint(mcasp, SNDRV_PCM_STREAM_PLAYBACK,
|
||||
tx_serializers);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = davinci_mcasp_ch_constraint(mcasp, SNDRV_PCM_STREAM_CAPTURE,
|
||||
rx_serializers);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static int davinci_mcasp_set_tdm_slot(struct snd_soc_dai *dai,
|
||||
unsigned int tx_mask,
|
||||
unsigned int rx_mask,
|
||||
int slots, int slot_width)
|
||||
{
|
||||
struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(dai);
|
||||
|
||||
dev_dbg(mcasp->dev,
|
||||
"%s() tx_mask 0x%08x rx_mask 0x%08x slots %d width %d\n",
|
||||
__func__, tx_mask, rx_mask, slots, slot_width);
|
||||
|
||||
if (tx_mask >= (1<<slots) || rx_mask >= (1<<slots)) {
|
||||
dev_err(mcasp->dev,
|
||||
"Bad tdm mask tx: 0x%08x rx: 0x%08x slots %d\n",
|
||||
tx_mask, rx_mask, slots);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (slot_width &&
|
||||
(slot_width < 8 || slot_width > 32 || slot_width % 4 != 0)) {
|
||||
dev_err(mcasp->dev, "%s: Unsupported slot_width %d\n",
|
||||
__func__, slot_width);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mcasp->tdm_slots = slots;
|
||||
mcasp->tdm_mask[SNDRV_PCM_STREAM_PLAYBACK] = rx_mask;
|
||||
mcasp->tdm_mask[SNDRV_PCM_STREAM_CAPTURE] = tx_mask;
|
||||
mcasp->slot_width = slot_width;
|
||||
|
||||
return davinci_mcasp_set_ch_constraints(mcasp);
|
||||
}
|
||||
|
||||
static int davinci_config_channel_size(struct davinci_mcasp *mcasp,
|
||||
int word_length)
|
||||
int sample_width)
|
||||
{
|
||||
u32 fmt;
|
||||
u32 tx_rotate = (word_length / 4) & 0x7;
|
||||
u32 mask = (1ULL << word_length) - 1;
|
||||
u32 tx_rotate = (sample_width / 4) & 0x7;
|
||||
u32 mask = (1ULL << sample_width) - 1;
|
||||
u32 slot_width = sample_width;
|
||||
|
||||
/*
|
||||
* For captured data we should not rotate, inversion and masking is
|
||||
* enoguh to get the data to the right position:
|
||||
|
@ -614,28 +708,23 @@ static int davinci_config_channel_size(struct davinci_mcasp *mcasp,
|
|||
u32 rx_rotate = 0;
|
||||
|
||||
/*
|
||||
* if s BCLK-to-LRCLK ratio has been configured via the set_clkdiv()
|
||||
* callback, take it into account here. That allows us to for example
|
||||
* send 32 bits per channel to the codec, while only 16 of them carry
|
||||
* audio payload.
|
||||
* The clock ratio is given for a full period of data (for I2S format
|
||||
* both left and right channels), so it has to be divided by number of
|
||||
* tdm-slots (for I2S - divided by 2).
|
||||
* Setting the tdm slot width either with set_clkdiv() or
|
||||
* set_tdm_slot() allows us to for example send 32 bits per
|
||||
* channel to the codec, while only 16 of them carry audio
|
||||
* payload.
|
||||
*/
|
||||
if (mcasp->bclk_lrclk_ratio) {
|
||||
u32 slot_length = mcasp->bclk_lrclk_ratio / mcasp->tdm_slots;
|
||||
|
||||
if (mcasp->slot_width) {
|
||||
/*
|
||||
* When we have more bclk then it is needed for the data, we
|
||||
* need to use the rotation to move the received samples to have
|
||||
* correct alignment.
|
||||
* When we have more bclk then it is needed for the
|
||||
* data, we need to use the rotation to move the
|
||||
* received samples to have correct alignment.
|
||||
*/
|
||||
rx_rotate = (slot_length - word_length) / 4;
|
||||
word_length = slot_length;
|
||||
slot_width = mcasp->slot_width;
|
||||
rx_rotate = (slot_width - sample_width) / 4;
|
||||
}
|
||||
|
||||
/* mapping of the XSSZ bit-field as described in the datasheet */
|
||||
fmt = (word_length >> 1) - 1;
|
||||
fmt = (slot_width >> 1) - 1;
|
||||
|
||||
if (mcasp->op_mode != DAVINCI_MCASP_DIT_MODE) {
|
||||
mcasp_mod_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, RXSSZ(fmt),
|
||||
|
@ -776,33 +865,50 @@ static int mcasp_i2s_hw_param(struct davinci_mcasp *mcasp, int stream,
|
|||
|
||||
/*
|
||||
* If more than one serializer is needed, then use them with
|
||||
* their specified tdm_slots count. Otherwise, one serializer
|
||||
* can cope with the transaction using as many slots as channels
|
||||
* in the stream, requires channels symmetry
|
||||
* all the specified tdm_slots. Otherwise, one serializer can
|
||||
* cope with the transaction using just as many slots as there
|
||||
* are channels in the stream.
|
||||
*/
|
||||
active_serializers = (channels + total_slots - 1) / total_slots;
|
||||
if (active_serializers == 1)
|
||||
active_slots = channels;
|
||||
else
|
||||
active_slots = total_slots;
|
||||
|
||||
for (i = 0; i < active_slots; i++)
|
||||
mask |= (1 << i);
|
||||
if (mcasp->tdm_mask[stream]) {
|
||||
active_slots = hweight32(mcasp->tdm_mask[stream]);
|
||||
active_serializers = (channels + active_slots - 1) /
|
||||
active_slots;
|
||||
if (active_serializers == 1) {
|
||||
active_slots = channels;
|
||||
for (i = 0; i < total_slots; i++) {
|
||||
if ((1 << i) & mcasp->tdm_mask[stream]) {
|
||||
mask |= (1 << i);
|
||||
if (--active_slots <= 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
active_serializers = (channels + total_slots - 1) / total_slots;
|
||||
if (active_serializers == 1)
|
||||
active_slots = channels;
|
||||
else
|
||||
active_slots = total_slots;
|
||||
|
||||
for (i = 0; i < active_slots; i++)
|
||||
mask |= (1 << i);
|
||||
}
|
||||
mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, TX_ASYNC);
|
||||
|
||||
if (!mcasp->dat_port)
|
||||
busel = TXSEL;
|
||||
|
||||
mcasp_set_reg(mcasp, DAVINCI_MCASP_TXTDM_REG, mask);
|
||||
mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, busel | TXORD);
|
||||
mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG,
|
||||
FSXMOD(total_slots), FSXMOD(0x1FF));
|
||||
|
||||
mcasp_set_reg(mcasp, DAVINCI_MCASP_RXTDM_REG, mask);
|
||||
mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, busel | RXORD);
|
||||
mcasp_mod_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG,
|
||||
FSRMOD(total_slots), FSRMOD(0x1FF));
|
||||
if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
mcasp_set_reg(mcasp, DAVINCI_MCASP_TXTDM_REG, mask);
|
||||
mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, busel | TXORD);
|
||||
mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG,
|
||||
FSXMOD(total_slots), FSXMOD(0x1FF));
|
||||
} else if (stream == SNDRV_PCM_STREAM_CAPTURE) {
|
||||
mcasp_set_reg(mcasp, DAVINCI_MCASP_RXTDM_REG, mask);
|
||||
mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, busel | RXORD);
|
||||
mcasp_mod_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG,
|
||||
FSRMOD(total_slots), FSRMOD(0x1FF));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -922,6 +1028,9 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream,
|
|||
int sbits = params_width(params);
|
||||
int ppm, div;
|
||||
|
||||
if (mcasp->slot_width)
|
||||
sbits = mcasp->slot_width;
|
||||
|
||||
div = davinci_mcasp_calc_clk_div(mcasp, rate*sbits*slots,
|
||||
&ppm);
|
||||
if (ppm)
|
||||
|
@ -1027,6 +1136,9 @@ static int davinci_mcasp_hw_rule_rate(struct snd_pcm_hw_params *params,
|
|||
struct snd_interval range;
|
||||
int i;
|
||||
|
||||
if (rd->mcasp->slot_width)
|
||||
sbits = rd->mcasp->slot_width;
|
||||
|
||||
snd_interval_any(&range);
|
||||
range.empty = 1;
|
||||
|
||||
|
@ -1069,10 +1181,14 @@ static int davinci_mcasp_hw_rule_format(struct snd_pcm_hw_params *params,
|
|||
|
||||
for (i = 0; i < SNDRV_PCM_FORMAT_LAST; i++) {
|
||||
if (snd_mask_test(fmt, i)) {
|
||||
uint bclk_freq = snd_pcm_format_width(i)*slots*rate;
|
||||
uint sbits = snd_pcm_format_width(i);
|
||||
int ppm;
|
||||
|
||||
davinci_mcasp_calc_clk_div(rd->mcasp, bclk_freq, &ppm);
|
||||
if (rd->mcasp->slot_width)
|
||||
sbits = rd->mcasp->slot_width;
|
||||
|
||||
davinci_mcasp_calc_clk_div(rd->mcasp, sbits*slots*rate,
|
||||
&ppm);
|
||||
if (abs(ppm) < DAVINCI_MAX_RATE_ERROR_PPM) {
|
||||
snd_mask_set(&nfmt, i);
|
||||
count++;
|
||||
|
@ -1094,6 +1210,10 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream,
|
|||
&mcasp->ruledata[substream->stream];
|
||||
u32 max_channels = 0;
|
||||
int i, dir;
|
||||
int tdm_slots = mcasp->tdm_slots;
|
||||
|
||||
if (mcasp->tdm_mask[substream->stream])
|
||||
tdm_slots = hweight32(mcasp->tdm_mask[substream->stream]);
|
||||
|
||||
mcasp->substreams[substream->stream] = substream;
|
||||
|
||||
|
@ -1114,7 +1234,7 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream,
|
|||
max_channels++;
|
||||
}
|
||||
ruledata->serializers = max_channels;
|
||||
max_channels *= mcasp->tdm_slots;
|
||||
max_channels *= tdm_slots;
|
||||
/*
|
||||
* If the already active stream has less channels than the calculated
|
||||
* limnit based on the seirializers * tdm_slots, we need to use that as
|
||||
|
@ -1124,15 +1244,25 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream,
|
|||
*/
|
||||
if (mcasp->channels && mcasp->channels < max_channels)
|
||||
max_channels = mcasp->channels;
|
||||
/*
|
||||
* But we can always allow channels upto the amount of
|
||||
* the available tdm_slots.
|
||||
*/
|
||||
if (max_channels < tdm_slots)
|
||||
max_channels = tdm_slots;
|
||||
|
||||
snd_pcm_hw_constraint_minmax(substream->runtime,
|
||||
SNDRV_PCM_HW_PARAM_CHANNELS,
|
||||
2, max_channels);
|
||||
|
||||
if (mcasp->chconstr[substream->stream].count)
|
||||
snd_pcm_hw_constraint_list(substream->runtime,
|
||||
0, SNDRV_PCM_HW_PARAM_CHANNELS,
|
||||
&mcasp->chconstr[substream->stream]);
|
||||
snd_pcm_hw_constraint_list(substream->runtime,
|
||||
0, SNDRV_PCM_HW_PARAM_CHANNELS,
|
||||
&mcasp->chconstr[substream->stream]);
|
||||
|
||||
if (mcasp->slot_width)
|
||||
snd_pcm_hw_constraint_minmax(substream->runtime,
|
||||
SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
|
||||
8, mcasp->slot_width);
|
||||
|
||||
/*
|
||||
* If we rely on implicit BCLK divider setting we should
|
||||
|
@ -1184,6 +1314,7 @@ static const struct snd_soc_dai_ops davinci_mcasp_dai_ops = {
|
|||
.set_fmt = davinci_mcasp_set_dai_fmt,
|
||||
.set_clkdiv = davinci_mcasp_set_clkdiv,
|
||||
.set_sysclk = davinci_mcasp_set_sysclk,
|
||||
.set_tdm_slot = davinci_mcasp_set_tdm_slot,
|
||||
};
|
||||
|
||||
static int davinci_mcasp_dai_probe(struct snd_soc_dai *dai)
|
||||
|
@ -1514,59 +1645,6 @@ nodata:
|
|||
return pdata;
|
||||
}
|
||||
|
||||
/* All serializers must have equal number of channels */
|
||||
static int davinci_mcasp_ch_constraint(struct davinci_mcasp *mcasp,
|
||||
struct snd_pcm_hw_constraint_list *cl,
|
||||
int serializers)
|
||||
{
|
||||
unsigned int *list;
|
||||
int i, count = 0;
|
||||
|
||||
if (serializers <= 1)
|
||||
return 0;
|
||||
|
||||
list = devm_kzalloc(mcasp->dev, sizeof(unsigned int) *
|
||||
(mcasp->tdm_slots + serializers - 2),
|
||||
GFP_KERNEL);
|
||||
if (!list)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 2; i <= mcasp->tdm_slots; i++)
|
||||
list[count++] = i;
|
||||
|
||||
for (i = 2; i <= serializers; i++)
|
||||
list[count++] = i*mcasp->tdm_slots;
|
||||
|
||||
cl->count = count;
|
||||
cl->list = list;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int davinci_mcasp_init_ch_constraints(struct davinci_mcasp *mcasp)
|
||||
{
|
||||
int rx_serializers = 0, tx_serializers = 0, ret, i;
|
||||
|
||||
for (i = 0; i < mcasp->num_serializer; i++)
|
||||
if (mcasp->serial_dir[i] == TX_MODE)
|
||||
tx_serializers++;
|
||||
else if (mcasp->serial_dir[i] == RX_MODE)
|
||||
rx_serializers++;
|
||||
|
||||
ret = davinci_mcasp_ch_constraint(mcasp, &mcasp->chconstr[
|
||||
SNDRV_PCM_STREAM_PLAYBACK],
|
||||
tx_serializers);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = davinci_mcasp_ch_constraint(mcasp, &mcasp->chconstr[
|
||||
SNDRV_PCM_STREAM_CAPTURE],
|
||||
rx_serializers);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
enum {
|
||||
PCM_EDMA,
|
||||
PCM_SDMA,
|
||||
|
@ -1783,7 +1861,28 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
|
|||
mcasp->fifo_base = DAVINCI_MCASP_V3_AFIFO_BASE;
|
||||
}
|
||||
|
||||
ret = davinci_mcasp_init_ch_constraints(mcasp);
|
||||
/* Allocate memory for long enough list for all possible
|
||||
* scenarios. Maximum number tdm slots is 32 and there cannot
|
||||
* be more serializers than given in the configuration. The
|
||||
* serializer directions could be taken into account, but it
|
||||
* would make code much more complex and save only couple of
|
||||
* bytes.
|
||||
*/
|
||||
mcasp->chconstr[SNDRV_PCM_STREAM_PLAYBACK].list =
|
||||
devm_kzalloc(mcasp->dev, sizeof(unsigned int) *
|
||||
(32 + mcasp->num_serializer - 2),
|
||||
GFP_KERNEL);
|
||||
|
||||
mcasp->chconstr[SNDRV_PCM_STREAM_CAPTURE].list =
|
||||
devm_kzalloc(mcasp->dev, sizeof(unsigned int) *
|
||||
(32 + mcasp->num_serializer - 2),
|
||||
GFP_KERNEL);
|
||||
|
||||
if (!mcasp->chconstr[SNDRV_PCM_STREAM_PLAYBACK].list ||
|
||||
!mcasp->chconstr[SNDRV_PCM_STREAM_CAPTURE].list)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = davinci_mcasp_set_ch_constraints(mcasp);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
|
|
|
@ -593,6 +593,7 @@ static const struct of_device_id fsl_asoc_card_dt_ids[] = {
|
|||
{ .compatible = "fsl,imx-audio-wm8960", },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, fsl_asoc_card_dt_ids);
|
||||
|
||||
static struct platform_driver fsl_asoc_card_driver = {
|
||||
.probe = fsl_asoc_card_probe,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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",
|
||||
},
|
||||
|
|
|
@ -509,17 +509,6 @@ static struct snd_soc_dai_driver skl_platform_dai[] = {
|
|||
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
|
||||
},
|
||||
},
|
||||
{
|
||||
.name = "DMIC23 Pin",
|
||||
.ops = &skl_dmic_dai_ops,
|
||||
.capture = {
|
||||
.stream_name = "DMIC23 Rx",
|
||||
.channels_min = HDA_STEREO,
|
||||
.channels_max = HDA_STEREO,
|
||||
.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_16000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
|
||||
},
|
||||
},
|
||||
{
|
||||
.name = "HD-Codec Pin",
|
||||
.ops = &skl_link_dai_ops,
|
||||
|
@ -538,28 +527,6 @@ static struct snd_soc_dai_driver skl_platform_dai[] = {
|
|||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
},
|
||||
},
|
||||
{
|
||||
.name = "HD-Codec-SPK Pin",
|
||||
.ops = &skl_link_dai_ops,
|
||||
.playback = {
|
||||
.stream_name = "HD-Codec-SPK Tx",
|
||||
.channels_min = HDA_STEREO,
|
||||
.channels_max = HDA_STEREO,
|
||||
.rates = SNDRV_PCM_RATE_48000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
},
|
||||
},
|
||||
{
|
||||
.name = "HD-Codec-AMIC Pin",
|
||||
.ops = &skl_link_dai_ops,
|
||||
.capture = {
|
||||
.stream_name = "HD-Codec-AMIC Rx",
|
||||
.channels_min = HDA_STEREO,
|
||||
.channels_max = HDA_STEREO,
|
||||
.rates = SNDRV_PCM_RATE_48000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static int skl_platform_open(struct snd_pcm_substream *substream)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
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;
|
||||
unsigned long rate;
|
||||
u32 ckr;
|
||||
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->ckr = ckr;
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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");
|
||||
/*
|
||||
* 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;
|
||||
|
||||
for_each_rsnd_clk(clk, adg, i)
|
||||
dev_dbg(dev, "clk %d : %p : %ld\n", i, clk, clk_get_rate(clk));
|
||||
|
||||
rsnd_adg_ssi_clk_init(priv, adg);
|
||||
rsnd_adg_get_clkin(priv, adg);
|
||||
rsnd_adg_get_clkout(priv, adg);
|
||||
|
||||
priv->adg = adg;
|
||||
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -918,11 +918,10 @@ static void rsnd_src_reconvert_update(struct rsnd_dai_stream *io,
|
|||
rsnd_mod_write(mod, SRC_IFSVR, fsrate);
|
||||
}
|
||||
|
||||
static int rsnd_src_pcm_new(struct rsnd_mod *mod,
|
||||
static int rsnd_src_pcm_new_gen2(struct rsnd_mod *mod,
|
||||
struct rsnd_dai_stream *io,
|
||||
struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
|
||||
struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
|
||||
struct rsnd_src *src = rsnd_mod_to_src(mod);
|
||||
int ret;
|
||||
|
@ -931,12 +930,6 @@ static int rsnd_src_pcm_new(struct rsnd_mod *mod,
|
|||
* enable SRC sync convert if possible
|
||||
*/
|
||||
|
||||
/*
|
||||
* Gen1 is not supported
|
||||
*/
|
||||
if (rsnd_is_gen1(priv))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* SRC sync convert needs clock master
|
||||
*/
|
||||
|
@ -975,7 +968,7 @@ static struct rsnd_mod_ops rsnd_src_gen2_ops = {
|
|||
.start = rsnd_src_start_gen2,
|
||||
.stop = rsnd_src_stop_gen2,
|
||||
.hw_params = rsnd_src_hw_params,
|
||||
.pcm_new = rsnd_src_pcm_new,
|
||||
.pcm_new = rsnd_src_pcm_new_gen2,
|
||||
};
|
||||
|
||||
struct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id)
|
||||
|
@ -983,7 +976,7 @@ struct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id)
|
|||
if (WARN_ON(id < 0 || id >= rsnd_src_nr(priv)))
|
||||
id = 0;
|
||||
|
||||
return &((struct rsnd_src *)(priv->src) + id)->mod;
|
||||
return rsnd_mod_get((struct rsnd_src *)(priv->src) + id);
|
||||
}
|
||||
|
||||
static void rsnd_of_parse_src(struct platform_device *pdev,
|
||||
|
@ -1078,7 +1071,7 @@ int rsnd_src_probe(struct platform_device *pdev,
|
|||
|
||||
src->info = &info->src_info[i];
|
||||
|
||||
ret = rsnd_mod_init(priv, &src->mod, ops, clk, RSND_MOD_SRC, i);
|
||||
ret = rsnd_mod_init(priv, rsnd_mod_get(src), ops, clk, RSND_MOD_SRC, i);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
@ -1093,6 +1086,6 @@ void rsnd_src_remove(struct platform_device *pdev,
|
|||
int i;
|
||||
|
||||
for_each_rsnd_src(src, priv, i) {
|
||||
rsnd_mod_quit(&src->mod);
|
||||
rsnd_mod_quit(rsnd_mod_get(src));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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++) {
|
||||
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];
|
||||
/*
|
||||
* 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(&ssi->mod, main_rate);
|
||||
if (0 == ret) {
|
||||
ssi->cr_clk = FORCE | SWL_32 |
|
||||
SCKD | SWSD | CKDV(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(&ssi->mod),
|
||||
rsnd_mod_id(&ssi->mod), rate);
|
||||
dev_dbg(dev, "%s[%d] outputs %u Hz\n",
|
||||
rsnd_mod_name(mod),
|
||||
rsnd_mod_id(mod), rate);
|
||||
|
||||
return 0;
|
||||
}
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
11
sound/soc/sunxi/Kconfig
Normal file
11
sound/soc/sunxi/Kconfig
Normal file
|
@ -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
|
2
sound/soc/sunxi/Makefile
Normal file
2
sound/soc/sunxi/Makefile
Normal file
|
@ -0,0 +1,2 @@
|
|||
obj-$(CONFIG_SND_SUN4I_CODEC) += sun4i-codec.o
|
||||
|
719
sound/soc/sunxi/sun4i-codec.c
Normal file
719
sound/soc/sunxi/sun4i-codec.c
Normal file
|
@ -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");
|
|
@ -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 = {
|
||||
|
|
|
@ -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 = {
|
||||
|
|
Loading…
Reference in a new issue