sound updates #2 for 4.7-rc1
This is the second update round for 4.7-rc1. Most of changes are about the pending ASoC updates and fixes, including a few new drivers. Below are some highlights: ASoC: - New drivers for MAX98371 and TAS5720 - SPI support for TLV320AIC32x4, along with the module split - TDM support for STI Uniperf IPs - Remaining topology API fixes / updates HDA: - A couple of Dell quirks and new Realtek codec support -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iQIcBAABCAAGBQJXSLGwAAoJEGwxgFQ9KSmkKz4P/2xpAXcwjT/g/WgeNVsZLnxd vs1KlMPSWXLHY7ESZB+oDYtw5pAQWta2gKnG3T0QpkEtyqcyvEAUch55SfPbkDWz bRwboK91NF9Cfrso+QnUG1HdpaeDsNydiAR5u2sdemQG+rh8TmWXNmFsuqPptjbm LP6Spf8Ia2TYAvagZOB+2UTl7Jq8jMXiYP3aGWMHm7P/kREMQkSWcQ9U8F8UK92G 5D0qKGvChsd23ybGUL1nBM7wBvErFoKd4Xa1zMudQt8EkTjistdgm24v3PO+lKDv JYiEugOZctzqtQVUlQMXcIqrlsafXwJN7ttKGst9gj32bM+a7EW0TGG0KyhxXI5w fRgGU7AJwncs9hBzEPBfc6Jms85THN2HpusU61ZYpyFAhLnHAOL7iIZnNKY8Pyyg tOPY2lTwHD9ic9EiC33/IypT50n0lBOi7X+YE7lGWdm2jXNvxtFv1jUw99kx25fj UaFNQaDYXXDKO1POCFrHpIq+jJ71Jmk7mXktI75wfuLyX3PSPyFg8OBbYVbTWkbL xdSqBs6LCESZ1iV9mauxwPSex44BpaMB3E0TA+7iN3+0Uwdfxe0OoLnX6dGiLSZJ QenFu/EDdCsA8rlrDy7AS1e5ulpYsTY1KGSZbfNzMdNsmD2XC3FdZHEF5PAC4wZQ EnNaik6InJgHlMF/6MXo =uNKQ -----END PGP SIGNATURE----- Merge tag 'sound-4.7-rc1-2' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound Pull more sound updates from Takashi Iwai: "This is the second update round for 4.7-rc1. Most of changes are about the pending ASoC updates and fixes, including a few new drivers. Below are some highlights: ASoC: - New drivers for MAX98371 and TAS5720 - SPI support for TLV320AIC32x4, along with the module split - TDM support for STI Uniperf IPs - Remaining topology API fixes / updates HDA: - A couple of Dell quirks and new Realtek codec support" * tag 'sound-4.7-rc1-2' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: (63 commits) ALSA: hda - Fix headset mic detection problem for one Dell machine spi: spi-ep93xx: Fix the PTR_ERR() argument ALSA: hda/realtek - Add support for ALC295/ALC3254 ASoC: kirkwood: fix build failure ALSA: hda - Fix headphone noise on Dell XPS 13 9360 ASoC: ak4642: Enable cache usage to fix crashes on resume ASoC: twl6040: Disconnect AUX output pads on digital mute ASoC: tlv320aic32x4: Properly implement the positive and negative pins into the mixers rcar: src: skip disabled-SRC nodes ASoC: max98371 Remove duplicate entry in max98371_reg ASoC: twl6040: Select LPPLL during standby ASoC: rsnd: don't use prohibited number to PDMACHCRn.SRS ASoC: simple-card: Add pm callbacks to platform driver ASoC: pxa: Fix module autoload for platform drivers ASoC: topology: Fix memory leak in widget creation ASoC: Add max98371 codec driver ASoC: rsnd: count .probe/.remove for rsnd_mod_call() ASoC: topology: Check size mismatch of ABI objects before parsing ASoC: topology: Check failure to create a widget ASoC: add support for TAS5720 digital amplifier ...
This commit is contained in:
commit
0723ab4a97
56 changed files with 2799 additions and 330 deletions
17
Documentation/devicetree/bindings/sound/max98371.txt
Normal file
17
Documentation/devicetree/bindings/sound/max98371.txt
Normal file
|
@ -0,0 +1,17 @@
|
|||
max98371 codec
|
||||
|
||||
This device supports I2C mode only.
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : "maxim,max98371"
|
||||
- reg : The chip select number on the I2C bus
|
||||
|
||||
Example:
|
||||
|
||||
&i2c {
|
||||
max98371: max98371@0x31 {
|
||||
compatible = "maxim,max98371";
|
||||
reg = <0x31>;
|
||||
};
|
||||
};
|
|
@ -1,15 +1,16 @@
|
|||
MT8173 with RT5650 RT5676 CODECS
|
||||
MT8173 with RT5650 RT5676 CODECS and HDMI via I2S
|
||||
|
||||
Required properties:
|
||||
- compatible : "mediatek,mt8173-rt5650-rt5676"
|
||||
- mediatek,audio-codec: the phandles of rt5650 and rt5676 codecs
|
||||
and of the hdmi encoder node
|
||||
- mediatek,platform: the phandle of MT8173 ASoC platform
|
||||
|
||||
Example:
|
||||
|
||||
sound {
|
||||
compatible = "mediatek,mt8173-rt5650-rt5676";
|
||||
mediatek,audio-codec = <&rt5650 &rt5676>;
|
||||
mediatek,audio-codec = <&rt5650 &rt5676 &hdmi0>;
|
||||
mediatek,platform = <&afe>;
|
||||
};
|
||||
|
||||
|
|
|
@ -5,11 +5,21 @@ Required properties:
|
|||
- mediatek,audio-codec: the phandles of rt5650 codecs
|
||||
- mediatek,platform: the phandle of MT8173 ASoC platform
|
||||
|
||||
Optional subnodes:
|
||||
- codec-capture : the subnode of rt5650 codec capture
|
||||
Required codec-capture subnode properties:
|
||||
- sound-dai: audio codec dai name on capture path
|
||||
<&rt5650 0> : Default setting. Connect rt5650 I2S1 for capture. (dai_name = rt5645-aif1)
|
||||
<&rt5650 1> : Connect rt5650 I2S2 for capture. (dai_name = rt5645-aif2)
|
||||
|
||||
Example:
|
||||
|
||||
sound {
|
||||
compatible = "mediatek,mt8173-rt5650";
|
||||
mediatek,audio-codec = <&rt5650>;
|
||||
mediatek,platform = <&afe>;
|
||||
codec-capture {
|
||||
sound-dai = <&rt5650 1>;
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -37,17 +37,18 @@ Required properties:
|
|||
|
||||
- dai-name: DAI name that describes the IP.
|
||||
|
||||
- IP mode: IP working mode depending on associated codec.
|
||||
"HDMI" connected to HDMI codec and support IEC HDMI formats (player only).
|
||||
"SPDIF" connected to SPDIF codec and support SPDIF formats (player only).
|
||||
"PCM" PCM standard mode for I2S or TDM bus.
|
||||
"TDM" TDM mode for TDM bus.
|
||||
|
||||
Required properties ("st,sti-uni-player" compatibility only):
|
||||
- clocks: CPU_DAI IP clock source, listed in the same order than the
|
||||
CPU_DAI properties.
|
||||
|
||||
- uniperiph-id: internal SOC IP instance ID.
|
||||
|
||||
- IP mode: IP working mode depending on associated codec.
|
||||
"HDMI" connected to HDMI codec IP and IEC HDMI formats.
|
||||
"SPDIF"connected to SPDIF codec and support SPDIF formats.
|
||||
"PCM" PCM standard mode for I2S or TDM bus.
|
||||
|
||||
Optional properties:
|
||||
- pinctrl-0: defined for CPU_DAI@1 and CPU_DAI@4 to describe I2S PIOs for
|
||||
external codecs connection.
|
||||
|
@ -56,6 +57,22 @@ Optional properties:
|
|||
|
||||
Example:
|
||||
|
||||
sti_uni_player1: sti-uni-player@1 {
|
||||
compatible = "st,sti-uni-player";
|
||||
status = "okay";
|
||||
#sound-dai-cells = <0>;
|
||||
st,syscfg = <&syscfg_core>;
|
||||
clocks = <&clk_s_d0_flexgen CLK_PCM_1>;
|
||||
reg = <0x8D81000 0x158>;
|
||||
interrupts = <GIC_SPI 85 IRQ_TYPE_NONE>;
|
||||
dmas = <&fdma0 3 0 1>;
|
||||
st,dai-name = "Uni Player #1 (I2S)";
|
||||
dma-names = "tx";
|
||||
st,uniperiph-id = <1>;
|
||||
st,version = <5>;
|
||||
st,mode = "TDM";
|
||||
};
|
||||
|
||||
sti_uni_player2: sti-uni-player@2 {
|
||||
compatible = "st,sti-uni-player";
|
||||
status = "okay";
|
||||
|
@ -65,7 +82,7 @@ Example:
|
|||
reg = <0x8D82000 0x158>;
|
||||
interrupts = <GIC_SPI 86 IRQ_TYPE_NONE>;
|
||||
dmas = <&fdma0 4 0 1>;
|
||||
dai-name = "Uni Player #1 (DAC)";
|
||||
dai-name = "Uni Player #2 (DAC)";
|
||||
dma-names = "tx";
|
||||
uniperiph-id = <2>;
|
||||
version = <5>;
|
||||
|
@ -82,7 +99,7 @@ Example:
|
|||
interrupts = <GIC_SPI 89 IRQ_TYPE_NONE>;
|
||||
dmas = <&fdma0 7 0 1>;
|
||||
dma-names = "tx";
|
||||
dai-name = "Uni Player #1 (PIO)";
|
||||
dai-name = "Uni Player #3 (SPDIF)";
|
||||
uniperiph-id = <3>;
|
||||
version = <5>;
|
||||
mode = "SPDIF";
|
||||
|
@ -99,6 +116,7 @@ Example:
|
|||
dma-names = "rx";
|
||||
dai-name = "Uni Reader #1 (HDMI RX)";
|
||||
version = <3>;
|
||||
st,mode = "PCM";
|
||||
};
|
||||
|
||||
2) sti-sas-codec: internal audio codec IPs driver
|
||||
|
@ -152,4 +170,20 @@ Example of audio card declaration:
|
|||
sound-dai = <&sti_sasg_codec 0>;
|
||||
};
|
||||
};
|
||||
simple-audio-card,dai-link@2 {
|
||||
/* TDM playback */
|
||||
format = "left_j";
|
||||
frame-inversion = <1>;
|
||||
cpu {
|
||||
sound-dai = <&sti_uni_player1>;
|
||||
dai-tdm-slot-num = <16>;
|
||||
dai-tdm-slot-width = <16>;
|
||||
dai-tdm-slot-tx-mask =
|
||||
<1 1 1 1 0 0 0 0 0 0 1 1 0 0 1 1>;
|
||||
};
|
||||
|
||||
codec {
|
||||
sound-dai = <&sti_sasg_codec 3>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
Texas Instruments TAS5711/TAS5717/TAS5719 stereo power amplifiers
|
||||
Texas Instruments TAS5711/TAS5717/TAS5719/TAS5721 stereo power amplifiers
|
||||
|
||||
The codec is controlled through an I2C interface. It also has two other
|
||||
signals that can be wired up to GPIOs: reset (strongly recommended), and
|
||||
|
@ -6,7 +6,11 @@ powerdown (optional).
|
|||
|
||||
Required properties:
|
||||
|
||||
- compatible: "ti,tas5711", "ti,tas5717", or "ti,tas5719"
|
||||
- compatible: should be one of the following:
|
||||
- "ti,tas5711",
|
||||
- "ti,tas5717",
|
||||
- "ti,tas5719",
|
||||
- "ti,tas5721"
|
||||
- reg: The I2C address of the device
|
||||
- #sound-dai-cells: must be equal to 0
|
||||
|
||||
|
@ -25,6 +29,8 @@ Optional properties:
|
|||
- PVDD_B-supply: regulator phandle for the PVDD_B supply (5711)
|
||||
- PVDD_C-supply: regulator phandle for the PVDD_C supply (5711)
|
||||
- PVDD_D-supply: regulator phandle for the PVDD_D supply (5711)
|
||||
- DRVDD-supply: regulator phandle for the DRVDD supply (5721)
|
||||
- PVDD-supply: regulator phandle for the PVDD supply (5721)
|
||||
|
||||
Example:
|
||||
|
||||
|
|
25
Documentation/devicetree/bindings/sound/tas5720.txt
Normal file
25
Documentation/devicetree/bindings/sound/tas5720.txt
Normal file
|
@ -0,0 +1,25 @@
|
|||
Texas Instruments TAS5720 Mono Audio amplifier
|
||||
|
||||
The TAS5720 serial control bus communicates through the I2C protocol only. The
|
||||
serial bus is also used for periodic codec fault checking/reporting during
|
||||
audio playback. For more product information please see the links below:
|
||||
|
||||
http://www.ti.com/product/TAS5720L
|
||||
http://www.ti.com/product/TAS5720M
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : "ti,tas5720"
|
||||
- reg : I2C slave address
|
||||
- dvdd-supply : phandle to a 3.3-V supply for the digital circuitry
|
||||
- pvdd-supply : phandle to a supply used for the Class-D amp and the analog
|
||||
|
||||
Example:
|
||||
|
||||
tas5720: tas5720@6c {
|
||||
status = "okay";
|
||||
compatible = "ti,tas5720";
|
||||
reg = <0x6c>;
|
||||
dvdd-supply = <&vdd_3v3_reg>;
|
||||
pvdd-supply = <&_supply_reg>;
|
||||
};
|
|
@ -567,7 +567,7 @@ static void ep93xx_spi_dma_transfer(struct ep93xx_spi *espi)
|
|||
txd = ep93xx_spi_dma_prepare(espi, DMA_MEM_TO_DEV);
|
||||
if (IS_ERR(txd)) {
|
||||
ep93xx_spi_dma_finish(espi, DMA_DEV_TO_MEM);
|
||||
dev_err(&espi->pdev->dev, "DMA TX failed: %ld\n", PTR_ERR(rxd));
|
||||
dev_err(&espi->pdev->dev, "DMA TX failed: %ld\n", PTR_ERR(txd));
|
||||
msg->status = PTR_ERR(txd);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -134,6 +134,7 @@
|
|||
#define TWL6040_HFDACENA (1 << 0)
|
||||
#define TWL6040_HFPGAENA (1 << 1)
|
||||
#define TWL6040_HFDRVENA (1 << 4)
|
||||
#define TWL6040_HFSWENA (1 << 6)
|
||||
|
||||
/* VIBCTLL/R (0x18/0x1A) fields */
|
||||
|
||||
|
|
|
@ -116,6 +116,14 @@
|
|||
#define SND_SOC_TPLG_STREAM_PLAYBACK 0
|
||||
#define SND_SOC_TPLG_STREAM_CAPTURE 1
|
||||
|
||||
/* vendor tuple types */
|
||||
#define SND_SOC_TPLG_TUPLE_TYPE_UUID 0
|
||||
#define SND_SOC_TPLG_TUPLE_TYPE_STRING 1
|
||||
#define SND_SOC_TPLG_TUPLE_TYPE_BOOL 2
|
||||
#define SND_SOC_TPLG_TUPLE_TYPE_BYTE 3
|
||||
#define SND_SOC_TPLG_TUPLE_TYPE_WORD 4
|
||||
#define SND_SOC_TPLG_TUPLE_TYPE_SHORT 5
|
||||
|
||||
/*
|
||||
* Block Header.
|
||||
* This header precedes all object and object arrays below.
|
||||
|
@ -132,6 +140,35 @@ struct snd_soc_tplg_hdr {
|
|||
__le32 count; /* number of elements in block */
|
||||
} __attribute__((packed));
|
||||
|
||||
/* vendor tuple for uuid */
|
||||
struct snd_soc_tplg_vendor_uuid_elem {
|
||||
__le32 token;
|
||||
char uuid[16];
|
||||
} __attribute__((packed));
|
||||
|
||||
/* vendor tuple for a bool/byte/short/word value */
|
||||
struct snd_soc_tplg_vendor_value_elem {
|
||||
__le32 token;
|
||||
__le32 value;
|
||||
} __attribute__((packed));
|
||||
|
||||
/* vendor tuple for string */
|
||||
struct snd_soc_tplg_vendor_string_elem {
|
||||
__le32 token;
|
||||
char string[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
|
||||
} __attribute__((packed));
|
||||
|
||||
struct snd_soc_tplg_vendor_array {
|
||||
__le32 size; /* size in bytes of the array, including all elements */
|
||||
__le32 type; /* SND_SOC_TPLG_TUPLE_TYPE_ */
|
||||
__le32 num_elems; /* number of elements in array */
|
||||
union {
|
||||
struct snd_soc_tplg_vendor_uuid_elem uuid[0];
|
||||
struct snd_soc_tplg_vendor_value_elem value[0];
|
||||
struct snd_soc_tplg_vendor_string_elem string[0];
|
||||
};
|
||||
} __attribute__((packed));
|
||||
|
||||
/*
|
||||
* Private data.
|
||||
* All topology objects may have private data that can be used by the driver or
|
||||
|
@ -139,7 +176,10 @@ struct snd_soc_tplg_hdr {
|
|||
*/
|
||||
struct snd_soc_tplg_private {
|
||||
__le32 size; /* in bytes of private data */
|
||||
char data[0];
|
||||
union {
|
||||
char data[0];
|
||||
struct snd_soc_tplg_vendor_array array[0];
|
||||
};
|
||||
} __attribute__((packed));
|
||||
|
||||
/*
|
||||
|
@ -383,7 +423,7 @@ struct snd_soc_tplg_pcm {
|
|||
__le32 size; /* in bytes of this structure */
|
||||
char pcm_name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
|
||||
char dai_name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
|
||||
__le32 pcm_id; /* unique ID - used to match */
|
||||
__le32 pcm_id; /* unique ID - used to match with DAI link */
|
||||
__le32 dai_id; /* unique ID - used to match */
|
||||
__le32 playback; /* supports playback mode */
|
||||
__le32 capture; /* supports capture mode */
|
||||
|
|
|
@ -335,6 +335,7 @@ static void alc_fill_eapd_coef(struct hda_codec *codec)
|
|||
case 0x10ec0283:
|
||||
case 0x10ec0286:
|
||||
case 0x10ec0288:
|
||||
case 0x10ec0295:
|
||||
case 0x10ec0298:
|
||||
alc_update_coef_idx(codec, 0x10, 1<<9, 0);
|
||||
break;
|
||||
|
@ -907,6 +908,7 @@ static struct alc_codec_rename_pci_table rename_pci_tbl[] = {
|
|||
{ 0x10ec0298, 0x1028, 0, "ALC3266" },
|
||||
{ 0x10ec0256, 0x1028, 0, "ALC3246" },
|
||||
{ 0x10ec0225, 0x1028, 0, "ALC3253" },
|
||||
{ 0x10ec0295, 0x1028, 0, "ALC3254" },
|
||||
{ 0x10ec0670, 0x1025, 0, "ALC669X" },
|
||||
{ 0x10ec0676, 0x1025, 0, "ALC679X" },
|
||||
{ 0x10ec0282, 0x1043, 0, "ALC3229" },
|
||||
|
@ -3697,6 +3699,7 @@ static void alc_headset_mode_unplugged(struct hda_codec *codec)
|
|||
alc_process_coef_fw(codec, coef0668);
|
||||
break;
|
||||
case 0x10ec0225:
|
||||
case 0x10ec0295:
|
||||
alc_process_coef_fw(codec, coef0225);
|
||||
break;
|
||||
}
|
||||
|
@ -3797,6 +3800,7 @@ static void alc_headset_mode_mic_in(struct hda_codec *codec, hda_nid_t hp_pin,
|
|||
snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50);
|
||||
break;
|
||||
case 0x10ec0225:
|
||||
case 0x10ec0295:
|
||||
alc_update_coef_idx(codec, 0x45, 0x3f<<10, 0x31<<10);
|
||||
snd_hda_set_pin_ctl_cache(codec, hp_pin, 0);
|
||||
alc_process_coef_fw(codec, coef0225);
|
||||
|
@ -3854,6 +3858,7 @@ static void alc_headset_mode_default(struct hda_codec *codec)
|
|||
|
||||
switch (codec->core.vendor_id) {
|
||||
case 0x10ec0225:
|
||||
case 0x10ec0295:
|
||||
alc_process_coef_fw(codec, coef0225);
|
||||
break;
|
||||
case 0x10ec0255:
|
||||
|
@ -3957,6 +3962,7 @@ static void alc_headset_mode_ctia(struct hda_codec *codec)
|
|||
alc_process_coef_fw(codec, coef0688);
|
||||
break;
|
||||
case 0x10ec0225:
|
||||
case 0x10ec0295:
|
||||
alc_process_coef_fw(codec, coef0225);
|
||||
break;
|
||||
}
|
||||
|
@ -4038,6 +4044,7 @@ static void alc_headset_mode_omtp(struct hda_codec *codec)
|
|||
alc_process_coef_fw(codec, coef0688);
|
||||
break;
|
||||
case 0x10ec0225:
|
||||
case 0x10ec0295:
|
||||
alc_process_coef_fw(codec, coef0225);
|
||||
break;
|
||||
}
|
||||
|
@ -4121,6 +4128,7 @@ static void alc_determine_headset_type(struct hda_codec *codec)
|
|||
is_ctia = (val & 0x1c02) == 0x1c02;
|
||||
break;
|
||||
case 0x10ec0225:
|
||||
case 0x10ec0295:
|
||||
alc_process_coef_fw(codec, coef0225);
|
||||
msleep(800);
|
||||
val = alc_read_coef_idx(codec, 0x46);
|
||||
|
@ -5466,8 +5474,9 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
|
|||
SND_PCI_QUIRK(0x1028, 0x06de, "Dell", ALC293_FIXUP_DISABLE_AAMIX_MULTIJACK),
|
||||
SND_PCI_QUIRK(0x1028, 0x06df, "Dell", ALC293_FIXUP_DISABLE_AAMIX_MULTIJACK),
|
||||
SND_PCI_QUIRK(0x1028, 0x06e0, "Dell", ALC293_FIXUP_DISABLE_AAMIX_MULTIJACK),
|
||||
SND_PCI_QUIRK(0x1028, 0x0704, "Dell XPS 13", ALC256_FIXUP_DELL_XPS_13_HEADPHONE_NOISE),
|
||||
SND_PCI_QUIRK(0x1028, 0x0704, "Dell XPS 13 9350", ALC256_FIXUP_DELL_XPS_13_HEADPHONE_NOISE),
|
||||
SND_PCI_QUIRK(0x1028, 0x0725, "Dell Inspiron 3162", ALC255_FIXUP_DELL_SPK_NOISE),
|
||||
SND_PCI_QUIRK(0x1028, 0x075b, "Dell XPS 13 9360", ALC256_FIXUP_DELL_XPS_13_HEADPHONE_NOISE),
|
||||
SND_PCI_QUIRK(0x1028, 0x164a, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
|
||||
SND_PCI_QUIRK(0x1028, 0x164b, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
|
||||
SND_PCI_QUIRK(0x103c, 0x1586, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC2),
|
||||
|
@ -5710,6 +5719,9 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = {
|
|||
SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL2_MIC_NO_PRESENCE,
|
||||
{0x14, 0x90170110},
|
||||
{0x21, 0x02211020}),
|
||||
SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
|
||||
{0x14, 0x90170130},
|
||||
{0x21, 0x02211040}),
|
||||
SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
|
||||
{0x12, 0x90a60140},
|
||||
{0x14, 0x90170110},
|
||||
|
@ -6033,6 +6045,7 @@ static int patch_alc269(struct hda_codec *codec)
|
|||
alc_update_coef_idx(codec, 0x36, 1 << 13, 1 << 5); /* Switch pcbeep path to Line in path*/
|
||||
break;
|
||||
case 0x10ec0225:
|
||||
case 0x10ec0295:
|
||||
spec->codec_variant = ALC269_TYPE_ALC225;
|
||||
break;
|
||||
case 0x10ec0234:
|
||||
|
@ -6979,6 +6992,7 @@ static const struct hda_device_id snd_hda_id_realtek[] = {
|
|||
HDA_CODEC_ENTRY(0x10ec0292, "ALC292", patch_alc269),
|
||||
HDA_CODEC_ENTRY(0x10ec0293, "ALC293", patch_alc269),
|
||||
HDA_CODEC_ENTRY(0x10ec0294, "ALC294", patch_alc269),
|
||||
HDA_CODEC_ENTRY(0x10ec0295, "ALC295", patch_alc269),
|
||||
HDA_CODEC_ENTRY(0x10ec0298, "ALC298", patch_alc269),
|
||||
HDA_CODEC_REV_ENTRY(0x10ec0861, 0x100340, "ALC660", patch_alc861),
|
||||
HDA_CODEC_ENTRY(0x10ec0660, "ALC660-VD", patch_alc861vd),
|
||||
|
|
|
@ -43,6 +43,7 @@ config SND_SOC_ALL_CODECS
|
|||
select SND_SOC_AK5386
|
||||
select SND_SOC_ALC5623 if I2C
|
||||
select SND_SOC_ALC5632 if I2C
|
||||
select SND_SOC_BT_SCO
|
||||
select SND_SOC_CQ0093VC if MFD_DAVINCI_VOICECODEC
|
||||
select SND_SOC_CS35L32 if I2C
|
||||
select SND_SOC_CS42L51_I2C if I2C
|
||||
|
@ -64,7 +65,6 @@ config SND_SOC_ALL_CODECS
|
|||
select SND_SOC_DA732X if I2C
|
||||
select SND_SOC_DA9055 if I2C
|
||||
select SND_SOC_DMIC
|
||||
select SND_SOC_BT_SCO
|
||||
select SND_SOC_ES8328_SPI if SPI_MASTER
|
||||
select SND_SOC_ES8328_I2C if I2C
|
||||
select SND_SOC_GTM601
|
||||
|
@ -79,6 +79,7 @@ config SND_SOC_ALL_CODECS
|
|||
select SND_SOC_MAX98090 if I2C
|
||||
select SND_SOC_MAX98095 if I2C
|
||||
select SND_SOC_MAX98357A if GPIOLIB
|
||||
select SND_SOC_MAX98371 if I2C
|
||||
select SND_SOC_MAX9867 if I2C
|
||||
select SND_SOC_MAX98925 if I2C
|
||||
select SND_SOC_MAX98926 if I2C
|
||||
|
@ -126,12 +127,14 @@ config SND_SOC_ALL_CODECS
|
|||
select SND_SOC_TAS2552 if I2C
|
||||
select SND_SOC_TAS5086 if I2C
|
||||
select SND_SOC_TAS571X if I2C
|
||||
select SND_SOC_TAS5720 if I2C
|
||||
select SND_SOC_TFA9879 if I2C
|
||||
select SND_SOC_TLV320AIC23_I2C if I2C
|
||||
select SND_SOC_TLV320AIC23_SPI if SPI_MASTER
|
||||
select SND_SOC_TLV320AIC26 if SPI_MASTER
|
||||
select SND_SOC_TLV320AIC31XX if I2C
|
||||
select SND_SOC_TLV320AIC32X4 if I2C
|
||||
select SND_SOC_TLV320AIC32X4_I2C if I2C
|
||||
select SND_SOC_TLV320AIC32X4_SPI if SPI_MASTER
|
||||
select SND_SOC_TLV320AIC3X if I2C
|
||||
select SND_SOC_TPA6130A2 if I2C
|
||||
select SND_SOC_TLV320DAC33 if I2C
|
||||
|
@ -367,6 +370,9 @@ config SND_SOC_ALC5623
|
|||
config SND_SOC_ALC5632
|
||||
tristate
|
||||
|
||||
config SND_SOC_BT_SCO
|
||||
tristate
|
||||
|
||||
config SND_SOC_CQ0093VC
|
||||
tristate
|
||||
|
||||
|
@ -473,9 +479,6 @@ config SND_SOC_DA732X
|
|||
config SND_SOC_DA9055
|
||||
tristate
|
||||
|
||||
config SND_SOC_BT_SCO
|
||||
tristate
|
||||
|
||||
config SND_SOC_DMIC
|
||||
tristate
|
||||
|
||||
|
@ -529,6 +532,9 @@ config SND_SOC_MAX98095
|
|||
config SND_SOC_MAX98357A
|
||||
tristate
|
||||
|
||||
config SND_SOC_MAX98371
|
||||
tristate
|
||||
|
||||
config SND_SOC_MAX9867
|
||||
tristate
|
||||
|
||||
|
@ -748,9 +754,16 @@ config SND_SOC_TAS5086
|
|||
depends on I2C
|
||||
|
||||
config SND_SOC_TAS571X
|
||||
tristate "Texas Instruments TAS5711/TAS5717/TAS5719 power amplifiers"
|
||||
tristate "Texas Instruments TAS5711/TAS5717/TAS5719/TAS5721 power amplifiers"
|
||||
depends on I2C
|
||||
|
||||
config SND_SOC_TAS5720
|
||||
tristate "Texas Instruments TAS5720 Mono Audio amplifier"
|
||||
depends on I2C
|
||||
help
|
||||
Enable support for Texas Instruments TAS5720L/M high-efficiency mono
|
||||
Class-D audio power amplifiers.
|
||||
|
||||
config SND_SOC_TFA9879
|
||||
tristate "NXP Semiconductors TFA9879 amplifier"
|
||||
depends on I2C
|
||||
|
@ -780,6 +793,16 @@ config SND_SOC_TLV320AIC31XX
|
|||
config SND_SOC_TLV320AIC32X4
|
||||
tristate
|
||||
|
||||
config SND_SOC_TLV320AIC32X4_I2C
|
||||
tristate
|
||||
depends on I2C
|
||||
select SND_SOC_TLV320AIC32X4
|
||||
|
||||
config SND_SOC_TLV320AIC32X4_SPI
|
||||
tristate
|
||||
depends on SPI_MASTER
|
||||
select SND_SOC_TLV320AIC32X4
|
||||
|
||||
config SND_SOC_TLV320AIC3X
|
||||
tristate "Texas Instruments TLV320AIC3x CODECs"
|
||||
depends on I2C
|
||||
|
@ -920,7 +943,8 @@ config SND_SOC_WM8955
|
|||
tristate
|
||||
|
||||
config SND_SOC_WM8960
|
||||
tristate
|
||||
tristate "Wolfson Microelectronics WM8960 CODEC"
|
||||
depends on I2C
|
||||
|
||||
config SND_SOC_WM8961
|
||||
tristate
|
||||
|
|
|
@ -32,6 +32,7 @@ snd-soc-ak4642-objs := ak4642.o
|
|||
snd-soc-ak4671-objs := ak4671.o
|
||||
snd-soc-ak5386-objs := ak5386.o
|
||||
snd-soc-arizona-objs := arizona.o
|
||||
snd-soc-bt-sco-objs := bt-sco.o
|
||||
snd-soc-cq93vc-objs := cq93vc.o
|
||||
snd-soc-cs35l32-objs := cs35l32.o
|
||||
snd-soc-cs42l51-objs := cs42l51.o
|
||||
|
@ -55,7 +56,6 @@ snd-soc-da7218-objs := da7218.o
|
|||
snd-soc-da7219-objs := da7219.o da7219-aad.o
|
||||
snd-soc-da732x-objs := da732x.o
|
||||
snd-soc-da9055-objs := da9055.o
|
||||
snd-soc-bt-sco-objs := bt-sco.o
|
||||
snd-soc-dmic-objs := dmic.o
|
||||
snd-soc-es8328-objs := es8328.o
|
||||
snd-soc-es8328-i2c-objs := es8328-i2c.o
|
||||
|
@ -74,6 +74,7 @@ snd-soc-max98088-objs := max98088.o
|
|||
snd-soc-max98090-objs := max98090.o
|
||||
snd-soc-max98095-objs := max98095.o
|
||||
snd-soc-max98357a-objs := max98357a.o
|
||||
snd-soc-max98371-objs := max98371.o
|
||||
snd-soc-max9867-objs := max9867.o
|
||||
snd-soc-max98925-objs := max98925.o
|
||||
snd-soc-max98926-objs := max98926.o
|
||||
|
@ -131,6 +132,7 @@ snd-soc-stac9766-objs := stac9766.o
|
|||
snd-soc-sti-sas-objs := sti-sas.o
|
||||
snd-soc-tas5086-objs := tas5086.o
|
||||
snd-soc-tas571x-objs := tas571x.o
|
||||
snd-soc-tas5720-objs := tas5720.o
|
||||
snd-soc-tfa9879-objs := tfa9879.o
|
||||
snd-soc-tlv320aic23-objs := tlv320aic23.o
|
||||
snd-soc-tlv320aic23-i2c-objs := tlv320aic23-i2c.o
|
||||
|
@ -138,6 +140,8 @@ snd-soc-tlv320aic23-spi-objs := tlv320aic23-spi.o
|
|||
snd-soc-tlv320aic26-objs := tlv320aic26.o
|
||||
snd-soc-tlv320aic31xx-objs := tlv320aic31xx.o
|
||||
snd-soc-tlv320aic32x4-objs := tlv320aic32x4.o
|
||||
snd-soc-tlv320aic32x4-i2c-objs := tlv320aic32x4-i2c.o
|
||||
snd-soc-tlv320aic32x4-spi-objs := tlv320aic32x4-spi.o
|
||||
snd-soc-tlv320aic3x-objs := tlv320aic3x.o
|
||||
snd-soc-tlv320dac33-objs := tlv320dac33.o
|
||||
snd-soc-ts3a227e-objs := ts3a227e.o
|
||||
|
@ -243,6 +247,7 @@ obj-$(CONFIG_SND_SOC_AK5386) += snd-soc-ak5386.o
|
|||
obj-$(CONFIG_SND_SOC_ALC5623) += snd-soc-alc5623.o
|
||||
obj-$(CONFIG_SND_SOC_ALC5632) += snd-soc-alc5632.o
|
||||
obj-$(CONFIG_SND_SOC_ARIZONA) += snd-soc-arizona.o
|
||||
obj-$(CONFIG_SND_SOC_BT_SCO) += snd-soc-bt-sco.o
|
||||
obj-$(CONFIG_SND_SOC_CQ0093VC) += snd-soc-cq93vc.o
|
||||
obj-$(CONFIG_SND_SOC_CS35L32) += snd-soc-cs35l32.o
|
||||
obj-$(CONFIG_SND_SOC_CS42L51) += snd-soc-cs42l51.o
|
||||
|
@ -266,7 +271,6 @@ obj-$(CONFIG_SND_SOC_DA7218) += snd-soc-da7218.o
|
|||
obj-$(CONFIG_SND_SOC_DA7219) += snd-soc-da7219.o
|
||||
obj-$(CONFIG_SND_SOC_DA732X) += snd-soc-da732x.o
|
||||
obj-$(CONFIG_SND_SOC_DA9055) += snd-soc-da9055.o
|
||||
obj-$(CONFIG_SND_SOC_BT_SCO) += snd-soc-bt-sco.o
|
||||
obj-$(CONFIG_SND_SOC_DMIC) += snd-soc-dmic.o
|
||||
obj-$(CONFIG_SND_SOC_ES8328) += snd-soc-es8328.o
|
||||
obj-$(CONFIG_SND_SOC_ES8328_I2C)+= snd-soc-es8328-i2c.o
|
||||
|
@ -339,6 +343,7 @@ obj-$(CONFIG_SND_SOC_STI_SAS) += snd-soc-sti-sas.o
|
|||
obj-$(CONFIG_SND_SOC_TAS2552) += snd-soc-tas2552.o
|
||||
obj-$(CONFIG_SND_SOC_TAS5086) += snd-soc-tas5086.o
|
||||
obj-$(CONFIG_SND_SOC_TAS571X) += snd-soc-tas571x.o
|
||||
obj-$(CONFIG_SND_SOC_TAS5720) += snd-soc-tas5720.o
|
||||
obj-$(CONFIG_SND_SOC_TFA9879) += snd-soc-tfa9879.o
|
||||
obj-$(CONFIG_SND_SOC_TLV320AIC23) += snd-soc-tlv320aic23.o
|
||||
obj-$(CONFIG_SND_SOC_TLV320AIC23_I2C) += snd-soc-tlv320aic23-i2c.o
|
||||
|
@ -346,6 +351,8 @@ obj-$(CONFIG_SND_SOC_TLV320AIC23_SPI) += snd-soc-tlv320aic23-spi.o
|
|||
obj-$(CONFIG_SND_SOC_TLV320AIC26) += snd-soc-tlv320aic26.o
|
||||
obj-$(CONFIG_SND_SOC_TLV320AIC31XX) += snd-soc-tlv320aic31xx.o
|
||||
obj-$(CONFIG_SND_SOC_TLV320AIC32X4) += snd-soc-tlv320aic32x4.o
|
||||
obj-$(CONFIG_SND_SOC_TLV320AIC32X4_I2C) += snd-soc-tlv320aic32x4-i2c.o
|
||||
obj-$(CONFIG_SND_SOC_TLV320AIC32X4_SPI) += snd-soc-tlv320aic32x4-spi.o
|
||||
obj-$(CONFIG_SND_SOC_TLV320AIC3X) += snd-soc-tlv320aic3x.o
|
||||
obj-$(CONFIG_SND_SOC_TLV320DAC33) += snd-soc-tlv320dac33.o
|
||||
obj-$(CONFIG_SND_SOC_TS3A227E) += snd-soc-ts3a227e.o
|
||||
|
|
|
@ -560,6 +560,7 @@ static const struct regmap_config ak4642_regmap = {
|
|||
.max_register = FIL1_3,
|
||||
.reg_defaults = ak4642_reg,
|
||||
.num_reg_defaults = NUM_AK4642_REG_DEFAULTS,
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
};
|
||||
|
||||
static const struct regmap_config ak4643_regmap = {
|
||||
|
@ -568,6 +569,7 @@ static const struct regmap_config ak4643_regmap = {
|
|||
.max_register = SPK_MS,
|
||||
.reg_defaults = ak4643_reg,
|
||||
.num_reg_defaults = ARRAY_SIZE(ak4643_reg),
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
};
|
||||
|
||||
static const struct regmap_config ak4648_regmap = {
|
||||
|
@ -576,6 +578,7 @@ static const struct regmap_config ak4648_regmap = {
|
|||
.max_register = EQ_FBEQE,
|
||||
.reg_defaults = ak4648_reg,
|
||||
.num_reg_defaults = ARRAY_SIZE(ak4648_reg),
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
};
|
||||
|
||||
static const struct ak4642_drvdata ak4642_drvdata = {
|
||||
|
|
441
sound/soc/codecs/max98371.c
Normal file
441
sound/soc/codecs/max98371.c
Normal file
|
@ -0,0 +1,441 @@
|
|||
/*
|
||||
* max98371.c -- ALSA SoC Stereo MAX98371 driver
|
||||
*
|
||||
* Copyright 2015-16 Maxim Integrated Products
|
||||
*
|
||||
* 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/i2c.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/tlv.h>
|
||||
#include "max98371.h"
|
||||
|
||||
static const char *const monomix_text[] = {
|
||||
"Left", "Right", "LeftRightDiv2",
|
||||
};
|
||||
|
||||
static const char *const hpf_cutoff_txt[] = {
|
||||
"Disable", "DC Block", "50Hz",
|
||||
"100Hz", "200Hz", "400Hz", "800Hz",
|
||||
};
|
||||
|
||||
static SOC_ENUM_SINGLE_DECL(max98371_monomix, MAX98371_MONOMIX_CFG, 0,
|
||||
monomix_text);
|
||||
|
||||
static SOC_ENUM_SINGLE_DECL(max98371_hpf_cutoff, MAX98371_HPF, 0,
|
||||
hpf_cutoff_txt);
|
||||
|
||||
static const DECLARE_TLV_DB_RANGE(max98371_dht_min_gain,
|
||||
0, 1, TLV_DB_SCALE_ITEM(537, 66, 0),
|
||||
2, 3, TLV_DB_SCALE_ITEM(677, 82, 0),
|
||||
4, 5, TLV_DB_SCALE_ITEM(852, 104, 0),
|
||||
6, 7, TLV_DB_SCALE_ITEM(1072, 131, 0),
|
||||
8, 9, TLV_DB_SCALE_ITEM(1350, 165, 0),
|
||||
10, 11, TLV_DB_SCALE_ITEM(1699, 101, 0),
|
||||
);
|
||||
|
||||
static const DECLARE_TLV_DB_RANGE(max98371_dht_max_gain,
|
||||
0, 1, TLV_DB_SCALE_ITEM(537, 66, 0),
|
||||
2, 3, TLV_DB_SCALE_ITEM(677, 82, 0),
|
||||
4, 5, TLV_DB_SCALE_ITEM(852, 104, 0),
|
||||
6, 7, TLV_DB_SCALE_ITEM(1072, 131, 0),
|
||||
8, 9, TLV_DB_SCALE_ITEM(1350, 165, 0),
|
||||
10, 11, TLV_DB_SCALE_ITEM(1699, 208, 0),
|
||||
);
|
||||
|
||||
static const DECLARE_TLV_DB_RANGE(max98371_dht_rot_gain,
|
||||
0, 1, TLV_DB_SCALE_ITEM(-50, -50, 0),
|
||||
2, 6, TLV_DB_SCALE_ITEM(-100, -100, 0),
|
||||
7, 8, TLV_DB_SCALE_ITEM(-800, -200, 0),
|
||||
9, 11, TLV_DB_SCALE_ITEM(-1200, -300, 0),
|
||||
12, 13, TLV_DB_SCALE_ITEM(-2000, -200, 0),
|
||||
14, 15, TLV_DB_SCALE_ITEM(-2500, -500, 0),
|
||||
);
|
||||
|
||||
static const struct reg_default max98371_reg[] = {
|
||||
{ 0x01, 0x00 },
|
||||
{ 0x02, 0x00 },
|
||||
{ 0x03, 0x00 },
|
||||
{ 0x04, 0x00 },
|
||||
{ 0x05, 0x00 },
|
||||
{ 0x06, 0x00 },
|
||||
{ 0x07, 0x00 },
|
||||
{ 0x08, 0x00 },
|
||||
{ 0x09, 0x00 },
|
||||
{ 0x0A, 0x00 },
|
||||
{ 0x10, 0x06 },
|
||||
{ 0x11, 0x08 },
|
||||
{ 0x14, 0x80 },
|
||||
{ 0x15, 0x00 },
|
||||
{ 0x16, 0x00 },
|
||||
{ 0x18, 0x00 },
|
||||
{ 0x19, 0x00 },
|
||||
{ 0x1C, 0x00 },
|
||||
{ 0x1D, 0x00 },
|
||||
{ 0x1E, 0x00 },
|
||||
{ 0x1F, 0x00 },
|
||||
{ 0x20, 0x00 },
|
||||
{ 0x21, 0x00 },
|
||||
{ 0x22, 0x00 },
|
||||
{ 0x23, 0x00 },
|
||||
{ 0x24, 0x00 },
|
||||
{ 0x25, 0x00 },
|
||||
{ 0x26, 0x00 },
|
||||
{ 0x27, 0x00 },
|
||||
{ 0x28, 0x00 },
|
||||
{ 0x29, 0x00 },
|
||||
{ 0x2A, 0x00 },
|
||||
{ 0x2B, 0x00 },
|
||||
{ 0x2C, 0x00 },
|
||||
{ 0x2D, 0x00 },
|
||||
{ 0x2E, 0x0B },
|
||||
{ 0x31, 0x00 },
|
||||
{ 0x32, 0x18 },
|
||||
{ 0x33, 0x00 },
|
||||
{ 0x34, 0x00 },
|
||||
{ 0x36, 0x00 },
|
||||
{ 0x37, 0x00 },
|
||||
{ 0x38, 0x00 },
|
||||
{ 0x39, 0x00 },
|
||||
{ 0x3A, 0x00 },
|
||||
{ 0x3B, 0x00 },
|
||||
{ 0x3C, 0x00 },
|
||||
{ 0x3D, 0x00 },
|
||||
{ 0x3E, 0x00 },
|
||||
{ 0x3F, 0x00 },
|
||||
{ 0x40, 0x00 },
|
||||
{ 0x41, 0x00 },
|
||||
{ 0x42, 0x00 },
|
||||
{ 0x43, 0x00 },
|
||||
{ 0x4A, 0x00 },
|
||||
{ 0x4B, 0x00 },
|
||||
{ 0x4C, 0x00 },
|
||||
{ 0x4D, 0x00 },
|
||||
{ 0x4E, 0x00 },
|
||||
{ 0x50, 0x00 },
|
||||
{ 0x51, 0x00 },
|
||||
{ 0x55, 0x00 },
|
||||
{ 0x58, 0x00 },
|
||||
{ 0x59, 0x00 },
|
||||
{ 0x5C, 0x00 },
|
||||
{ 0xFF, 0x43 },
|
||||
};
|
||||
|
||||
static bool max98371_volatile_register(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case MAX98371_IRQ_CLEAR1:
|
||||
case MAX98371_IRQ_CLEAR2:
|
||||
case MAX98371_IRQ_CLEAR3:
|
||||
case MAX98371_VERSION:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool max98371_readable_register(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case MAX98371_SOFT_RESET:
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
static const DECLARE_TLV_DB_RANGE(max98371_gain_tlv,
|
||||
0, 7, TLV_DB_SCALE_ITEM(0, 50, 0),
|
||||
8, 10, TLV_DB_SCALE_ITEM(400, 100, 0)
|
||||
);
|
||||
|
||||
static const DECLARE_TLV_DB_RANGE(max98371_noload_gain_tlv,
|
||||
0, 11, TLV_DB_SCALE_ITEM(950, 100, 0),
|
||||
);
|
||||
|
||||
static const DECLARE_TLV_DB_SCALE(digital_tlv, -6300, 50, 1);
|
||||
|
||||
static const struct snd_kcontrol_new max98371_snd_controls[] = {
|
||||
SOC_SINGLE_TLV("Speaker Volume", MAX98371_GAIN,
|
||||
MAX98371_GAIN_SHIFT, (1<<MAX98371_GAIN_WIDTH)-1, 0,
|
||||
max98371_gain_tlv),
|
||||
SOC_SINGLE_TLV("Digital Volume", MAX98371_DIGITAL_GAIN, 0,
|
||||
(1<<MAX98371_DIGITAL_GAIN_WIDTH)-1, 1, digital_tlv),
|
||||
SOC_SINGLE_TLV("Speaker DHT Max Volume", MAX98371_GAIN,
|
||||
0, (1<<MAX98371_DHT_MAX_WIDTH)-1, 0,
|
||||
max98371_dht_max_gain),
|
||||
SOC_SINGLE_TLV("Speaker DHT Min Volume", MAX98371_DHT_GAIN,
|
||||
0, (1<<MAX98371_DHT_GAIN_WIDTH)-1, 0,
|
||||
max98371_dht_min_gain),
|
||||
SOC_SINGLE_TLV("Speaker DHT Rotation Volume", MAX98371_DHT_GAIN,
|
||||
0, (1<<MAX98371_DHT_ROT_WIDTH)-1, 0,
|
||||
max98371_dht_rot_gain),
|
||||
SOC_SINGLE("DHT Attack Step", MAX98371_DHT, MAX98371_DHT_STEP, 3, 0),
|
||||
SOC_SINGLE("DHT Attack Rate", MAX98371_DHT, 0, 7, 0),
|
||||
SOC_ENUM("Monomix Select", max98371_monomix),
|
||||
SOC_ENUM("HPF Cutoff", max98371_hpf_cutoff),
|
||||
};
|
||||
|
||||
static int max98371_dai_set_fmt(struct snd_soc_dai *codec_dai,
|
||||
unsigned int fmt)
|
||||
{
|
||||
struct snd_soc_codec *codec = codec_dai->codec;
|
||||
struct max98371_priv *max98371 = snd_soc_codec_get_drvdata(codec);
|
||||
unsigned int val = 0;
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
|
||||
case SND_SOC_DAIFMT_CBS_CFS:
|
||||
break;
|
||||
default:
|
||||
dev_err(codec->dev, "DAI clock mode unsupported");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
|
||||
case SND_SOC_DAIFMT_I2S:
|
||||
val |= 0;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_RIGHT_J:
|
||||
val |= MAX98371_DAI_RIGHT;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_LEFT_J:
|
||||
val |= MAX98371_DAI_LEFT;
|
||||
break;
|
||||
default:
|
||||
dev_err(codec->dev, "DAI wrong mode unsupported");
|
||||
return -EINVAL;
|
||||
}
|
||||
regmap_update_bits(max98371->regmap, MAX98371_FMT,
|
||||
MAX98371_FMT_MODE_MASK, val);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int max98371_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 max98371_priv *max98371 = snd_soc_codec_get_drvdata(codec);
|
||||
int blr_clk_ratio, ch_size, channels = params_channels(params);
|
||||
int rate = params_rate(params);
|
||||
|
||||
switch (params_format(params)) {
|
||||
case SNDRV_PCM_FORMAT_S8:
|
||||
regmap_update_bits(max98371->regmap, MAX98371_FMT,
|
||||
MAX98371_FMT_MASK, MAX98371_DAI_CHANSZ_16);
|
||||
ch_size = 8;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S16_LE:
|
||||
regmap_update_bits(max98371->regmap, MAX98371_FMT,
|
||||
MAX98371_FMT_MASK, MAX98371_DAI_CHANSZ_16);
|
||||
ch_size = 16;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S24_LE:
|
||||
regmap_update_bits(max98371->regmap, MAX98371_FMT,
|
||||
MAX98371_FMT_MASK, MAX98371_DAI_CHANSZ_32);
|
||||
ch_size = 24;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S32_LE:
|
||||
regmap_update_bits(max98371->regmap, MAX98371_FMT,
|
||||
MAX98371_FMT_MASK, MAX98371_DAI_CHANSZ_32);
|
||||
ch_size = 32;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* BCLK/LRCLK ratio calculation */
|
||||
blr_clk_ratio = channels * ch_size;
|
||||
switch (blr_clk_ratio) {
|
||||
case 32:
|
||||
regmap_update_bits(max98371->regmap,
|
||||
MAX98371_DAI_CLK,
|
||||
MAX98371_DAI_BSEL_MASK, MAX98371_DAI_BSEL_32);
|
||||
break;
|
||||
case 48:
|
||||
regmap_update_bits(max98371->regmap,
|
||||
MAX98371_DAI_CLK,
|
||||
MAX98371_DAI_BSEL_MASK, MAX98371_DAI_BSEL_48);
|
||||
break;
|
||||
case 64:
|
||||
regmap_update_bits(max98371->regmap,
|
||||
MAX98371_DAI_CLK,
|
||||
MAX98371_DAI_BSEL_MASK, MAX98371_DAI_BSEL_64);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (rate) {
|
||||
case 32000:
|
||||
regmap_update_bits(max98371->regmap,
|
||||
MAX98371_SPK_SR,
|
||||
MAX98371_SPK_SR_MASK, MAX98371_SPK_SR_32);
|
||||
break;
|
||||
case 44100:
|
||||
regmap_update_bits(max98371->regmap,
|
||||
MAX98371_SPK_SR,
|
||||
MAX98371_SPK_SR_MASK, MAX98371_SPK_SR_44);
|
||||
break;
|
||||
case 48000:
|
||||
regmap_update_bits(max98371->regmap,
|
||||
MAX98371_SPK_SR,
|
||||
MAX98371_SPK_SR_MASK, MAX98371_SPK_SR_48);
|
||||
break;
|
||||
case 88200:
|
||||
regmap_update_bits(max98371->regmap,
|
||||
MAX98371_SPK_SR,
|
||||
MAX98371_SPK_SR_MASK, MAX98371_SPK_SR_88);
|
||||
break;
|
||||
case 96000:
|
||||
regmap_update_bits(max98371->regmap,
|
||||
MAX98371_SPK_SR,
|
||||
MAX98371_SPK_SR_MASK, MAX98371_SPK_SR_96);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* enabling both the RX channels*/
|
||||
regmap_update_bits(max98371->regmap, MAX98371_MONOMIX_SRC,
|
||||
MAX98371_MONOMIX_SRC_MASK, MONOMIX_RX_0_1);
|
||||
regmap_update_bits(max98371->regmap, MAX98371_DAI_CHANNEL,
|
||||
MAX98371_CHANNEL_MASK, MAX98371_CHANNEL_MASK);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_dapm_widget max98371_dapm_widgets[] = {
|
||||
SND_SOC_DAPM_DAC("DAC", NULL, MAX98371_SPK_ENABLE, 0, 0),
|
||||
SND_SOC_DAPM_SUPPLY("Global Enable", MAX98371_GLOBAL_ENABLE,
|
||||
0, 0, NULL, 0),
|
||||
SND_SOC_DAPM_OUTPUT("SPK_OUT"),
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route max98371_audio_map[] = {
|
||||
{"DAC", NULL, "HiFi Playback"},
|
||||
{"SPK_OUT", NULL, "DAC"},
|
||||
{"SPK_OUT", NULL, "Global Enable"},
|
||||
};
|
||||
|
||||
#define MAX98371_RATES SNDRV_PCM_RATE_8000_48000
|
||||
#define MAX98371_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_BE | \
|
||||
SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S32_BE)
|
||||
|
||||
static const struct snd_soc_dai_ops max98371_dai_ops = {
|
||||
.set_fmt = max98371_dai_set_fmt,
|
||||
.hw_params = max98371_dai_hw_params,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_driver max98371_dai[] = {
|
||||
{
|
||||
.name = "max98371-aif1",
|
||||
.playback = {
|
||||
.stream_name = "HiFi Playback",
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.rates = SNDRV_PCM_RATE_8000_48000,
|
||||
.formats = MAX98371_FORMATS,
|
||||
},
|
||||
.ops = &max98371_dai_ops,
|
||||
}
|
||||
};
|
||||
|
||||
static const struct snd_soc_codec_driver max98371_codec = {
|
||||
.controls = max98371_snd_controls,
|
||||
.num_controls = ARRAY_SIZE(max98371_snd_controls),
|
||||
.dapm_routes = max98371_audio_map,
|
||||
.num_dapm_routes = ARRAY_SIZE(max98371_audio_map),
|
||||
.dapm_widgets = max98371_dapm_widgets,
|
||||
.num_dapm_widgets = ARRAY_SIZE(max98371_dapm_widgets),
|
||||
};
|
||||
|
||||
static const struct regmap_config max98371_regmap = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.max_register = MAX98371_VERSION,
|
||||
.reg_defaults = max98371_reg,
|
||||
.num_reg_defaults = ARRAY_SIZE(max98371_reg),
|
||||
.volatile_reg = max98371_volatile_register,
|
||||
.readable_reg = max98371_readable_register,
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
};
|
||||
|
||||
static int max98371_i2c_probe(struct i2c_client *i2c,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct max98371_priv *max98371;
|
||||
int ret, reg;
|
||||
|
||||
max98371 = devm_kzalloc(&i2c->dev,
|
||||
sizeof(*max98371), GFP_KERNEL);
|
||||
if (!max98371)
|
||||
return -ENOMEM;
|
||||
|
||||
i2c_set_clientdata(i2c, max98371);
|
||||
max98371->regmap = devm_regmap_init_i2c(i2c, &max98371_regmap);
|
||||
if (IS_ERR(max98371->regmap)) {
|
||||
ret = PTR_ERR(max98371->regmap);
|
||||
dev_err(&i2c->dev,
|
||||
"Failed to allocate regmap: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regmap_read(max98371->regmap, MAX98371_VERSION, ®);
|
||||
if (ret < 0) {
|
||||
dev_info(&i2c->dev, "device error %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
dev_info(&i2c->dev, "device version %x\n", reg);
|
||||
|
||||
ret = snd_soc_register_codec(&i2c->dev, &max98371_codec,
|
||||
max98371_dai, ARRAY_SIZE(max98371_dai));
|
||||
if (ret < 0) {
|
||||
dev_err(&i2c->dev, "Failed to register codec: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int max98371_i2c_remove(struct i2c_client *client)
|
||||
{
|
||||
snd_soc_unregister_codec(&client->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id max98371_i2c_id[] = {
|
||||
{ "max98371", 0 },
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(i2c, max98371_i2c_id);
|
||||
|
||||
static const struct of_device_id max98371_of_match[] = {
|
||||
{ .compatible = "maxim,max98371", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, max98371_of_match);
|
||||
|
||||
static struct i2c_driver max98371_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "max98371",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = NULL,
|
||||
.of_match_table = of_match_ptr(max98371_of_match),
|
||||
},
|
||||
.probe = max98371_i2c_probe,
|
||||
.remove = max98371_i2c_remove,
|
||||
.id_table = max98371_i2c_id,
|
||||
};
|
||||
|
||||
module_i2c_driver(max98371_i2c_driver);
|
||||
|
||||
MODULE_AUTHOR("anish kumar <yesanishhere@gmail.com>");
|
||||
MODULE_DESCRIPTION("ALSA SoC MAX98371 driver");
|
||||
MODULE_LICENSE("GPL");
|
67
sound/soc/codecs/max98371.h
Normal file
67
sound/soc/codecs/max98371.h
Normal file
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* max98371.h -- MAX98371 ALSA SoC Audio driver
|
||||
*
|
||||
* Copyright 2011-2012 Maxim Integrated Products
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _MAX98371_H
|
||||
#define _MAX98371_H
|
||||
|
||||
#define MAX98371_IRQ_CLEAR1 0x01
|
||||
#define MAX98371_IRQ_CLEAR2 0x02
|
||||
#define MAX98371_IRQ_CLEAR3 0x03
|
||||
#define MAX98371_DAI_CLK 0x10
|
||||
#define MAX98371_DAI_BSEL_MASK 0xF
|
||||
#define MAX98371_DAI_BSEL_32 2
|
||||
#define MAX98371_DAI_BSEL_48 3
|
||||
#define MAX98371_DAI_BSEL_64 4
|
||||
#define MAX98371_SPK_SR 0x11
|
||||
#define MAX98371_SPK_SR_MASK 0xF
|
||||
#define MAX98371_SPK_SR_32 6
|
||||
#define MAX98371_SPK_SR_44 7
|
||||
#define MAX98371_SPK_SR_48 8
|
||||
#define MAX98371_SPK_SR_88 10
|
||||
#define MAX98371_SPK_SR_96 11
|
||||
#define MAX98371_DAI_CHANNEL 0x15
|
||||
#define MAX98371_CHANNEL_MASK 0x3
|
||||
#define MAX98371_MONOMIX_SRC 0x18
|
||||
#define MAX98371_MONOMIX_CFG 0x19
|
||||
#define MAX98371_HPF 0x1C
|
||||
#define MAX98371_MONOMIX_SRC_MASK 0xFF
|
||||
#define MONOMIX_RX_0_1 ((0x1)<<(4))
|
||||
#define M98371_DAI_CHANNEL_I2S 0x3
|
||||
#define MAX98371_DIGITAL_GAIN 0x2D
|
||||
#define MAX98371_DIGITAL_GAIN_WIDTH 0x7
|
||||
#define MAX98371_GAIN 0x2E
|
||||
#define MAX98371_GAIN_SHIFT 0x4
|
||||
#define MAX98371_GAIN_WIDTH 0x4
|
||||
#define MAX98371_DHT_MAX_WIDTH 4
|
||||
#define MAX98371_FMT 0x14
|
||||
#define MAX98371_CHANSZ_WIDTH 6
|
||||
#define MAX98371_FMT_MASK ((0x3)<<(MAX98371_CHANSZ_WIDTH))
|
||||
#define MAX98371_FMT_MODE_MASK ((0x7)<<(3))
|
||||
#define MAX98371_DAI_LEFT ((0x1)<<(3))
|
||||
#define MAX98371_DAI_RIGHT ((0x2)<<(3))
|
||||
#define MAX98371_DAI_CHANSZ_16 ((1)<<(MAX98371_CHANSZ_WIDTH))
|
||||
#define MAX98371_DAI_CHANSZ_24 ((2)<<(MAX98371_CHANSZ_WIDTH))
|
||||
#define MAX98371_DAI_CHANSZ_32 ((3)<<(MAX98371_CHANSZ_WIDTH))
|
||||
#define MAX98371_DHT 0x32
|
||||
#define MAX98371_DHT_STEP 0x3
|
||||
#define MAX98371_DHT_GAIN 0x31
|
||||
#define MAX98371_DHT_GAIN_WIDTH 0x4
|
||||
#define MAX98371_DHT_ROT_WIDTH 0x4
|
||||
#define MAX98371_SPK_ENABLE 0x4A
|
||||
#define MAX98371_GLOBAL_ENABLE 0x50
|
||||
#define MAX98371_SOFT_RESET 0x51
|
||||
#define MAX98371_VERSION 0xFF
|
||||
|
||||
|
||||
struct max98371_priv {
|
||||
struct regmap *regmap;
|
||||
struct snd_soc_codec *codec;
|
||||
};
|
||||
#endif
|
|
@ -276,6 +276,8 @@ static int rt298_jack_detect(struct rt298_priv *rt298, bool *hp, bool *mic)
|
|||
} else {
|
||||
*mic = false;
|
||||
regmap_write(rt298->regmap, RT298_SET_MIC1, 0x20);
|
||||
regmap_update_bits(rt298->regmap,
|
||||
RT298_CBJ_CTRL1, 0x0400, 0x0000);
|
||||
}
|
||||
} else {
|
||||
regmap_read(rt298->regmap, RT298_GET_HP_SENSE, &buf);
|
||||
|
@ -482,6 +484,26 @@ static int rt298_adc_event(struct snd_soc_dapm_widget *w,
|
|||
snd_soc_update_bits(codec,
|
||||
VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, nid, 0),
|
||||
0x7080, 0x7000);
|
||||
/* If MCLK doesn't exist, reset AD filter */
|
||||
if (!(snd_soc_read(codec, RT298_VAD_CTRL) & 0x200)) {
|
||||
pr_info("NO MCLK\n");
|
||||
switch (nid) {
|
||||
case RT298_ADC_IN1:
|
||||
snd_soc_update_bits(codec,
|
||||
RT298_D_FILTER_CTRL, 0x2, 0x2);
|
||||
mdelay(10);
|
||||
snd_soc_update_bits(codec,
|
||||
RT298_D_FILTER_CTRL, 0x2, 0x0);
|
||||
break;
|
||||
case RT298_ADC_IN2:
|
||||
snd_soc_update_bits(codec,
|
||||
RT298_D_FILTER_CTRL, 0x4, 0x4);
|
||||
mdelay(10);
|
||||
snd_soc_update_bits(codec,
|
||||
RT298_D_FILTER_CTRL, 0x4, 0x0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SND_SOC_DAPM_PRE_PMD:
|
||||
snd_soc_update_bits(codec,
|
||||
|
@ -520,30 +542,12 @@ static int rt298_mic1_event(struct snd_soc_dapm_widget *w,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int rt298_vref_event(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *kcontrol, int event)
|
||||
{
|
||||
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
|
||||
|
||||
switch (event) {
|
||||
case SND_SOC_DAPM_PRE_PMU:
|
||||
snd_soc_update_bits(codec,
|
||||
RT298_CBJ_CTRL1, 0x0400, 0x0000);
|
||||
mdelay(50);
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_dapm_widget rt298_dapm_widgets[] = {
|
||||
|
||||
SND_SOC_DAPM_SUPPLY_S("HV", 1, RT298_POWER_CTRL1,
|
||||
12, 1, NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY("VREF", RT298_POWER_CTRL1,
|
||||
0, 1, rt298_vref_event, SND_SOC_DAPM_PRE_PMU),
|
||||
0, 1, NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY_S("BG_MBIAS", 1, RT298_POWER_CTRL2,
|
||||
1, 0, NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY_S("LDO1", 1, RT298_POWER_CTRL2,
|
||||
|
@ -934,18 +938,9 @@ static int rt298_set_bias_level(struct snd_soc_codec *codec,
|
|||
}
|
||||
break;
|
||||
|
||||
case SND_SOC_BIAS_ON:
|
||||
mdelay(30);
|
||||
snd_soc_update_bits(codec,
|
||||
RT298_CBJ_CTRL1, 0x0400, 0x0400);
|
||||
|
||||
break;
|
||||
|
||||
case SND_SOC_BIAS_STANDBY:
|
||||
snd_soc_write(codec,
|
||||
RT298_SET_AUDIO_POWER, AC_PWRST_D3);
|
||||
snd_soc_update_bits(codec,
|
||||
RT298_CBJ_CTRL1, 0x0400, 0x0000);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
|
@ -137,6 +137,7 @@
|
|||
#define RT298_A_BIAS_CTRL2 0x02
|
||||
#define RT298_POWER_CTRL1 0x03
|
||||
#define RT298_A_BIAS_CTRL3 0x04
|
||||
#define RT298_D_FILTER_CTRL 0x05
|
||||
#define RT298_POWER_CTRL2 0x08
|
||||
#define RT298_I2S_CTRL1 0x09
|
||||
#define RT298_I2S_CTRL2 0x0a
|
||||
|
@ -148,6 +149,7 @@
|
|||
#define RT298_IRQ_CTRL 0x33
|
||||
#define RT298_WIND_FILTER_CTRL 0x46
|
||||
#define RT298_PLL_CTRL1 0x49
|
||||
#define RT298_VAD_CTRL 0x4e
|
||||
#define RT298_CBJ_CTRL1 0x4f
|
||||
#define RT298_CBJ_CTRL2 0x50
|
||||
#define RT298_PLL_CTRL 0x63
|
||||
|
|
|
@ -1241,60 +1241,46 @@ static int rt5677_dmic_use_asrc(struct snd_soc_dapm_widget *source,
|
|||
regmap_read(rt5677->regmap, RT5677_ASRC_5, &asrc_setting);
|
||||
asrc_setting = (asrc_setting & RT5677_AD_STO1_CLK_SEL_MASK) >>
|
||||
RT5677_AD_STO1_CLK_SEL_SFT;
|
||||
if (asrc_setting >= RT5677_CLK_SEL_I2S1_ASRC &&
|
||||
asrc_setting <= RT5677_CLK_SEL_I2S6_ASRC)
|
||||
return 1;
|
||||
break;
|
||||
|
||||
case 10:
|
||||
regmap_read(rt5677->regmap, RT5677_ASRC_5, &asrc_setting);
|
||||
asrc_setting = (asrc_setting & RT5677_AD_STO2_CLK_SEL_MASK) >>
|
||||
RT5677_AD_STO2_CLK_SEL_SFT;
|
||||
if (asrc_setting >= RT5677_CLK_SEL_I2S1_ASRC &&
|
||||
asrc_setting <= RT5677_CLK_SEL_I2S6_ASRC)
|
||||
return 1;
|
||||
break;
|
||||
|
||||
case 9:
|
||||
regmap_read(rt5677->regmap, RT5677_ASRC_5, &asrc_setting);
|
||||
asrc_setting = (asrc_setting & RT5677_AD_STO3_CLK_SEL_MASK) >>
|
||||
RT5677_AD_STO3_CLK_SEL_SFT;
|
||||
if (asrc_setting >= RT5677_CLK_SEL_I2S1_ASRC &&
|
||||
asrc_setting <= RT5677_CLK_SEL_I2S6_ASRC)
|
||||
return 1;
|
||||
break;
|
||||
|
||||
case 8:
|
||||
regmap_read(rt5677->regmap, RT5677_ASRC_5, &asrc_setting);
|
||||
asrc_setting = (asrc_setting & RT5677_AD_STO4_CLK_SEL_MASK) >>
|
||||
RT5677_AD_STO4_CLK_SEL_SFT;
|
||||
if (asrc_setting >= RT5677_CLK_SEL_I2S1_ASRC &&
|
||||
asrc_setting <= RT5677_CLK_SEL_I2S6_ASRC)
|
||||
return 1;
|
||||
break;
|
||||
|
||||
case 7:
|
||||
regmap_read(rt5677->regmap, RT5677_ASRC_6, &asrc_setting);
|
||||
asrc_setting = (asrc_setting & RT5677_AD_MONOL_CLK_SEL_MASK) >>
|
||||
RT5677_AD_MONOL_CLK_SEL_SFT;
|
||||
if (asrc_setting >= RT5677_CLK_SEL_I2S1_ASRC &&
|
||||
asrc_setting <= RT5677_CLK_SEL_I2S6_ASRC)
|
||||
return 1;
|
||||
break;
|
||||
|
||||
case 6:
|
||||
regmap_read(rt5677->regmap, RT5677_ASRC_6, &asrc_setting);
|
||||
asrc_setting = (asrc_setting & RT5677_AD_MONOR_CLK_SEL_MASK) >>
|
||||
RT5677_AD_MONOR_CLK_SEL_SFT;
|
||||
if (asrc_setting >= RT5677_CLK_SEL_I2S1_ASRC &&
|
||||
asrc_setting <= RT5677_CLK_SEL_I2S6_ASRC)
|
||||
return 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (asrc_setting >= RT5677_CLK_SEL_I2S1_ASRC &&
|
||||
asrc_setting <= RT5677_CLK_SEL_I2S6_ASRC)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,9 @@
|
|||
* Copyright (C) 2015 Google, Inc.
|
||||
* Copyright (c) 2013 Daniel Mack <zonque@gmail.com>
|
||||
*
|
||||
* TAS5721 support:
|
||||
* Copyright (C) 2016 Petr Kulhavy, Barix AG <petr@barix.com>
|
||||
*
|
||||
* 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
|
||||
|
@ -57,6 +60,10 @@ static int tas571x_register_size(struct tas571x_private *priv, unsigned int reg)
|
|||
case TAS571X_CH1_VOL_REG:
|
||||
case TAS571X_CH2_VOL_REG:
|
||||
return priv->chip->vol_reg_size;
|
||||
case TAS571X_INPUT_MUX_REG:
|
||||
case TAS571X_CH4_SRC_SELECT_REG:
|
||||
case TAS571X_PWM_MUX_REG:
|
||||
return 4;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
|
@ -167,6 +174,23 @@ static int tas571x_hw_params(struct snd_pcm_substream *substream,
|
|||
TAS571X_SDI_FMT_MASK, val);
|
||||
}
|
||||
|
||||
static int tas571x_mute(struct snd_soc_dai *dai, int mute)
|
||||
{
|
||||
struct snd_soc_codec *codec = dai->codec;
|
||||
u8 sysctl2;
|
||||
int ret;
|
||||
|
||||
sysctl2 = mute ? TAS571X_SYS_CTRL_2_SDN_MASK : 0;
|
||||
|
||||
ret = snd_soc_update_bits(codec,
|
||||
TAS571X_SYS_CTRL_2_REG,
|
||||
TAS571X_SYS_CTRL_2_SDN_MASK,
|
||||
sysctl2);
|
||||
usleep_range(1000, 2000);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tas571x_set_bias_level(struct snd_soc_codec *codec,
|
||||
enum snd_soc_bias_level level)
|
||||
{
|
||||
|
@ -214,6 +238,7 @@ static int tas571x_set_bias_level(struct snd_soc_codec *codec,
|
|||
static const struct snd_soc_dai_ops tas571x_dai_ops = {
|
||||
.set_fmt = tas571x_set_dai_fmt,
|
||||
.hw_params = tas571x_hw_params,
|
||||
.digital_mute = tas571x_mute,
|
||||
};
|
||||
|
||||
static const char *const tas5711_supply_names[] = {
|
||||
|
@ -241,6 +266,26 @@ static const struct snd_kcontrol_new tas5711_controls[] = {
|
|||
1, 1),
|
||||
};
|
||||
|
||||
static const struct regmap_range tas571x_readonly_regs_range[] = {
|
||||
regmap_reg_range(TAS571X_CLK_CTRL_REG, TAS571X_DEV_ID_REG),
|
||||
};
|
||||
|
||||
static const struct regmap_range tas571x_volatile_regs_range[] = {
|
||||
regmap_reg_range(TAS571X_CLK_CTRL_REG, TAS571X_ERR_STATUS_REG),
|
||||
regmap_reg_range(TAS571X_OSC_TRIM_REG, TAS571X_OSC_TRIM_REG),
|
||||
};
|
||||
|
||||
static const struct regmap_access_table tas571x_write_regs = {
|
||||
.no_ranges = tas571x_readonly_regs_range,
|
||||
.n_no_ranges = ARRAY_SIZE(tas571x_readonly_regs_range),
|
||||
};
|
||||
|
||||
static const struct regmap_access_table tas571x_volatile_regs = {
|
||||
.yes_ranges = tas571x_volatile_regs_range,
|
||||
.n_yes_ranges = ARRAY_SIZE(tas571x_volatile_regs_range),
|
||||
|
||||
};
|
||||
|
||||
static const struct reg_default tas5711_reg_defaults[] = {
|
||||
{ 0x04, 0x05 },
|
||||
{ 0x05, 0x40 },
|
||||
|
@ -260,6 +305,8 @@ static const struct regmap_config tas5711_regmap_config = {
|
|||
.reg_defaults = tas5711_reg_defaults,
|
||||
.num_reg_defaults = ARRAY_SIZE(tas5711_reg_defaults),
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
.wr_table = &tas571x_write_regs,
|
||||
.volatile_table = &tas571x_volatile_regs,
|
||||
};
|
||||
|
||||
static const struct tas571x_chip tas5711_chip = {
|
||||
|
@ -314,6 +361,8 @@ static const struct regmap_config tas5717_regmap_config = {
|
|||
.reg_defaults = tas5717_reg_defaults,
|
||||
.num_reg_defaults = ARRAY_SIZE(tas5717_reg_defaults),
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
.wr_table = &tas571x_write_regs,
|
||||
.volatile_table = &tas571x_volatile_regs,
|
||||
};
|
||||
|
||||
/* This entry is reused for tas5719 as the software interface is identical. */
|
||||
|
@ -326,6 +375,77 @@ static const struct tas571x_chip tas5717_chip = {
|
|||
.vol_reg_size = 2,
|
||||
};
|
||||
|
||||
static const char *const tas5721_supply_names[] = {
|
||||
"AVDD",
|
||||
"DVDD",
|
||||
"DRVDD",
|
||||
"PVDD",
|
||||
};
|
||||
|
||||
static const struct snd_kcontrol_new tas5721_controls[] = {
|
||||
SOC_SINGLE_TLV("Master Volume",
|
||||
TAS571X_MVOL_REG,
|
||||
0, 0xff, 1, tas5711_volume_tlv),
|
||||
SOC_DOUBLE_R_TLV("Speaker Volume",
|
||||
TAS571X_CH1_VOL_REG,
|
||||
TAS571X_CH2_VOL_REG,
|
||||
0, 0xff, 1, tas5711_volume_tlv),
|
||||
SOC_DOUBLE("Speaker Switch",
|
||||
TAS571X_SOFT_MUTE_REG,
|
||||
TAS571X_SOFT_MUTE_CH1_SHIFT, TAS571X_SOFT_MUTE_CH2_SHIFT,
|
||||
1, 1),
|
||||
};
|
||||
|
||||
static const struct reg_default tas5721_reg_defaults[] = {
|
||||
{TAS571X_CLK_CTRL_REG, 0x6c},
|
||||
{TAS571X_DEV_ID_REG, 0x00},
|
||||
{TAS571X_ERR_STATUS_REG, 0x00},
|
||||
{TAS571X_SYS_CTRL_1_REG, 0xa0},
|
||||
{TAS571X_SDI_REG, 0x05},
|
||||
{TAS571X_SYS_CTRL_2_REG, 0x40},
|
||||
{TAS571X_SOFT_MUTE_REG, 0x00},
|
||||
{TAS571X_MVOL_REG, 0xff},
|
||||
{TAS571X_CH1_VOL_REG, 0x30},
|
||||
{TAS571X_CH2_VOL_REG, 0x30},
|
||||
{TAS571X_CH3_VOL_REG, 0x30},
|
||||
{TAS571X_VOL_CFG_REG, 0x91},
|
||||
{TAS571X_MODULATION_LIMIT_REG, 0x02},
|
||||
{TAS571X_IC_DELAY_CH1_REG, 0xac},
|
||||
{TAS571X_IC_DELAY_CH2_REG, 0x54},
|
||||
{TAS571X_IC_DELAY_CH3_REG, 0xac},
|
||||
{TAS571X_IC_DELAY_CH4_REG, 0x54},
|
||||
{TAS571X_PWM_CH_SDN_GROUP_REG, 0x30},
|
||||
{TAS571X_START_STOP_PERIOD_REG, 0x0f},
|
||||
{TAS571X_OSC_TRIM_REG, 0x82},
|
||||
{TAS571X_BKND_ERR_REG, 0x02},
|
||||
{TAS571X_INPUT_MUX_REG, 0x17772},
|
||||
{TAS571X_CH4_SRC_SELECT_REG, 0x4303},
|
||||
{TAS571X_PWM_MUX_REG, 0x1021345},
|
||||
};
|
||||
|
||||
static const struct regmap_config tas5721_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 32,
|
||||
.max_register = 0xff,
|
||||
.reg_read = tas571x_reg_read,
|
||||
.reg_write = tas571x_reg_write,
|
||||
.reg_defaults = tas5721_reg_defaults,
|
||||
.num_reg_defaults = ARRAY_SIZE(tas5721_reg_defaults),
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
.wr_table = &tas571x_write_regs,
|
||||
.volatile_table = &tas571x_volatile_regs,
|
||||
};
|
||||
|
||||
|
||||
static const struct tas571x_chip tas5721_chip = {
|
||||
.supply_names = tas5721_supply_names,
|
||||
.num_supply_names = ARRAY_SIZE(tas5721_supply_names),
|
||||
.controls = tas5711_controls,
|
||||
.num_controls = ARRAY_SIZE(tas5711_controls),
|
||||
.regmap_config = &tas5721_regmap_config,
|
||||
.vol_reg_size = 1,
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_widget tas571x_dapm_widgets[] = {
|
||||
SND_SOC_DAPM_DAC("DACL", NULL, SND_SOC_NOPM, 0, 0),
|
||||
SND_SOC_DAPM_DAC("DACR", NULL, SND_SOC_NOPM, 0, 0),
|
||||
|
@ -386,11 +506,10 @@ static int tas571x_i2c_probe(struct i2c_client *client,
|
|||
i2c_set_clientdata(client, priv);
|
||||
|
||||
of_id = of_match_device(tas571x_of_match, dev);
|
||||
if (!of_id) {
|
||||
dev_err(dev, "Unknown device type\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
priv->chip = of_id->data;
|
||||
if (of_id)
|
||||
priv->chip = of_id->data;
|
||||
else
|
||||
priv->chip = (void *) id->driver_data;
|
||||
|
||||
priv->mclk = devm_clk_get(dev, "mclk");
|
||||
if (IS_ERR(priv->mclk) && PTR_ERR(priv->mclk) != -ENOENT) {
|
||||
|
@ -445,10 +564,6 @@ static int tas571x_i2c_probe(struct i2c_client *client,
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_update_bits(priv->regmap, TAS571X_SYS_CTRL_2_REG,
|
||||
TAS571X_SYS_CTRL_2_SDN_MASK, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
memcpy(&priv->codec_driver, &tas571x_codec, sizeof(priv->codec_driver));
|
||||
priv->codec_driver.controls = priv->chip->controls;
|
||||
|
@ -486,14 +601,16 @@ static const struct of_device_id tas571x_of_match[] = {
|
|||
{ .compatible = "ti,tas5711", .data = &tas5711_chip, },
|
||||
{ .compatible = "ti,tas5717", .data = &tas5717_chip, },
|
||||
{ .compatible = "ti,tas5719", .data = &tas5717_chip, },
|
||||
{ .compatible = "ti,tas5721", .data = &tas5721_chip, },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, tas571x_of_match);
|
||||
|
||||
static const struct i2c_device_id tas571x_i2c_id[] = {
|
||||
{ "tas5711", 0 },
|
||||
{ "tas5717", 0 },
|
||||
{ "tas5719", 0 },
|
||||
{ "tas5711", (kernel_ulong_t) &tas5711_chip },
|
||||
{ "tas5717", (kernel_ulong_t) &tas5717_chip },
|
||||
{ "tas5719", (kernel_ulong_t) &tas5717_chip },
|
||||
{ "tas5721", (kernel_ulong_t) &tas5721_chip },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, tas571x_i2c_id);
|
||||
|
|
|
@ -13,6 +13,10 @@
|
|||
#define _TAS571X_H
|
||||
|
||||
/* device registers */
|
||||
#define TAS571X_CLK_CTRL_REG 0x00
|
||||
#define TAS571X_DEV_ID_REG 0x01
|
||||
#define TAS571X_ERR_STATUS_REG 0x02
|
||||
#define TAS571X_SYS_CTRL_1_REG 0x03
|
||||
#define TAS571X_SDI_REG 0x04
|
||||
#define TAS571X_SDI_FMT_MASK 0x0f
|
||||
|
||||
|
@ -27,7 +31,25 @@
|
|||
#define TAS571X_MVOL_REG 0x07
|
||||
#define TAS571X_CH1_VOL_REG 0x08
|
||||
#define TAS571X_CH2_VOL_REG 0x09
|
||||
#define TAS571X_CH3_VOL_REG 0x0a
|
||||
#define TAS571X_VOL_CFG_REG 0x0e
|
||||
#define TAS571X_MODULATION_LIMIT_REG 0x10
|
||||
#define TAS571X_IC_DELAY_CH1_REG 0x11
|
||||
#define TAS571X_IC_DELAY_CH2_REG 0x12
|
||||
#define TAS571X_IC_DELAY_CH3_REG 0x13
|
||||
#define TAS571X_IC_DELAY_CH4_REG 0x14
|
||||
|
||||
#define TAS571X_PWM_CH_SDN_GROUP_REG 0x19 /* N/A on TAS5717, TAS5719 */
|
||||
#define TAS571X_PWM_CH1_SDN_MASK (1<<0)
|
||||
#define TAS571X_PWM_CH2_SDN_SHIFT (1<<1)
|
||||
#define TAS571X_PWM_CH3_SDN_SHIFT (1<<2)
|
||||
#define TAS571X_PWM_CH4_SDN_SHIFT (1<<3)
|
||||
|
||||
#define TAS571X_START_STOP_PERIOD_REG 0x1a
|
||||
#define TAS571X_OSC_TRIM_REG 0x1b
|
||||
#define TAS571X_BKND_ERR_REG 0x1c
|
||||
#define TAS571X_INPUT_MUX_REG 0x20
|
||||
#define TAS571X_CH4_SRC_SELECT_REG 0x21
|
||||
#define TAS571X_PWM_MUX_REG 0x25
|
||||
|
||||
#endif /* _TAS571X_H */
|
||||
|
|
620
sound/soc/codecs/tas5720.c
Normal file
620
sound/soc/codecs/tas5720.c
Normal file
|
@ -0,0 +1,620 @@
|
|||
/*
|
||||
* tas5720.c - ALSA SoC Texas Instruments TAS5720 Mono Audio Amplifier
|
||||
*
|
||||
* Copyright (C)2015-2016 Texas Instruments Incorporated - http://www.ti.com
|
||||
*
|
||||
* Author: Andreas Dannenberg <dannenberg@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.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/soc-dapm.h>
|
||||
#include <sound/tlv.h>
|
||||
|
||||
#include "tas5720.h"
|
||||
|
||||
/* Define how often to check (and clear) the fault status register (in ms) */
|
||||
#define TAS5720_FAULT_CHECK_INTERVAL 200
|
||||
|
||||
static const char * const tas5720_supply_names[] = {
|
||||
"dvdd", /* Digital power supply. Connect to 3.3-V supply. */
|
||||
"pvdd", /* Class-D amp and analog power supply (connected). */
|
||||
};
|
||||
|
||||
#define TAS5720_NUM_SUPPLIES ARRAY_SIZE(tas5720_supply_names)
|
||||
|
||||
struct tas5720_data {
|
||||
struct snd_soc_codec *codec;
|
||||
struct regmap *regmap;
|
||||
struct i2c_client *tas5720_client;
|
||||
struct regulator_bulk_data supplies[TAS5720_NUM_SUPPLIES];
|
||||
struct delayed_work fault_check_work;
|
||||
unsigned int last_fault;
|
||||
};
|
||||
|
||||
static int tas5720_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;
|
||||
unsigned int rate = params_rate(params);
|
||||
bool ssz_ds;
|
||||
int ret;
|
||||
|
||||
switch (rate) {
|
||||
case 44100:
|
||||
case 48000:
|
||||
ssz_ds = false;
|
||||
break;
|
||||
case 88200:
|
||||
case 96000:
|
||||
ssz_ds = true;
|
||||
break;
|
||||
default:
|
||||
dev_err(codec->dev, "unsupported sample rate: %u\n", rate);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = snd_soc_update_bits(codec, TAS5720_DIGITAL_CTRL1_REG,
|
||||
TAS5720_SSZ_DS, ssz_ds);
|
||||
if (ret < 0) {
|
||||
dev_err(codec->dev, "error setting sample rate: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tas5720_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
|
||||
{
|
||||
struct snd_soc_codec *codec = dai->codec;
|
||||
u8 serial_format;
|
||||
int ret;
|
||||
|
||||
if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS) {
|
||||
dev_vdbg(codec->dev, "DAI Format master is not found\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (fmt & (SND_SOC_DAIFMT_FORMAT_MASK |
|
||||
SND_SOC_DAIFMT_INV_MASK)) {
|
||||
case (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF):
|
||||
/* 1st data bit occur one BCLK cycle after the frame sync */
|
||||
serial_format = TAS5720_SAIF_I2S;
|
||||
break;
|
||||
case (SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_NB_NF):
|
||||
/*
|
||||
* Note that although the TAS5720 does not have a dedicated DSP
|
||||
* mode it doesn't care about the LRCLK duty cycle during TDM
|
||||
* operation. Therefore we can use the device's I2S mode with
|
||||
* its delaying of the 1st data bit to receive DSP_A formatted
|
||||
* data. See device datasheet for additional details.
|
||||
*/
|
||||
serial_format = TAS5720_SAIF_I2S;
|
||||
break;
|
||||
case (SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_NB_NF):
|
||||
/*
|
||||
* Similar to DSP_A, we can use the fact that the TAS5720 does
|
||||
* not care about the LRCLK duty cycle during TDM to receive
|
||||
* DSP_B formatted data in LEFTJ mode (no delaying of the 1st
|
||||
* data bit).
|
||||
*/
|
||||
serial_format = TAS5720_SAIF_LEFTJ;
|
||||
break;
|
||||
case (SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_NB_NF):
|
||||
/* No delay after the frame sync */
|
||||
serial_format = TAS5720_SAIF_LEFTJ;
|
||||
break;
|
||||
default:
|
||||
dev_vdbg(codec->dev, "DAI Format is not found\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = snd_soc_update_bits(codec, TAS5720_DIGITAL_CTRL1_REG,
|
||||
TAS5720_SAIF_FORMAT_MASK,
|
||||
serial_format);
|
||||
if (ret < 0) {
|
||||
dev_err(codec->dev, "error setting SAIF format: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tas5720_set_dai_tdm_slot(struct snd_soc_dai *dai,
|
||||
unsigned int tx_mask, unsigned int rx_mask,
|
||||
int slots, int slot_width)
|
||||
{
|
||||
struct snd_soc_codec *codec = dai->codec;
|
||||
unsigned int first_slot;
|
||||
int ret;
|
||||
|
||||
if (!tx_mask) {
|
||||
dev_err(codec->dev, "tx masks must not be 0\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Determine the first slot that is being requested. We will only
|
||||
* use the first slot that is found since the TAS5720 is a mono
|
||||
* amplifier.
|
||||
*/
|
||||
first_slot = __ffs(tx_mask);
|
||||
|
||||
if (first_slot > 7) {
|
||||
dev_err(codec->dev, "slot selection out of bounds (%u)\n",
|
||||
first_slot);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Enable manual TDM slot selection (instead of I2C ID based) */
|
||||
ret = snd_soc_update_bits(codec, TAS5720_DIGITAL_CTRL1_REG,
|
||||
TAS5720_TDM_CFG_SRC, TAS5720_TDM_CFG_SRC);
|
||||
if (ret < 0)
|
||||
goto error_snd_soc_update_bits;
|
||||
|
||||
/* Configure the TDM slot to process audio from */
|
||||
ret = snd_soc_update_bits(codec, TAS5720_DIGITAL_CTRL2_REG,
|
||||
TAS5720_TDM_SLOT_SEL_MASK, first_slot);
|
||||
if (ret < 0)
|
||||
goto error_snd_soc_update_bits;
|
||||
|
||||
return 0;
|
||||
|
||||
error_snd_soc_update_bits:
|
||||
dev_err(codec->dev, "error configuring TDM mode: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tas5720_mute(struct snd_soc_dai *dai, int mute)
|
||||
{
|
||||
struct snd_soc_codec *codec = dai->codec;
|
||||
int ret;
|
||||
|
||||
ret = snd_soc_update_bits(codec, TAS5720_DIGITAL_CTRL2_REG,
|
||||
TAS5720_MUTE, mute ? TAS5720_MUTE : 0);
|
||||
if (ret < 0) {
|
||||
dev_err(codec->dev, "error (un-)muting device: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tas5720_fault_check_work(struct work_struct *work)
|
||||
{
|
||||
struct tas5720_data *tas5720 = container_of(work, struct tas5720_data,
|
||||
fault_check_work.work);
|
||||
struct device *dev = tas5720->codec->dev;
|
||||
unsigned int curr_fault;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(tas5720->regmap, TAS5720_FAULT_REG, &curr_fault);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to read FAULT register: %d\n", ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Check/handle all errors except SAIF clock errors */
|
||||
curr_fault &= TAS5720_OCE | TAS5720_DCE | TAS5720_OTE;
|
||||
|
||||
/*
|
||||
* Only flag errors once for a given occurrence. This is needed as
|
||||
* the TAS5720 will take time clearing the fault condition internally
|
||||
* during which we don't want to bombard the system with the same
|
||||
* error message over and over.
|
||||
*/
|
||||
if ((curr_fault & TAS5720_OCE) && !(tas5720->last_fault & TAS5720_OCE))
|
||||
dev_crit(dev, "experienced an over current hardware fault\n");
|
||||
|
||||
if ((curr_fault & TAS5720_DCE) && !(tas5720->last_fault & TAS5720_DCE))
|
||||
dev_crit(dev, "experienced a DC detection fault\n");
|
||||
|
||||
if ((curr_fault & TAS5720_OTE) && !(tas5720->last_fault & TAS5720_OTE))
|
||||
dev_crit(dev, "experienced an over temperature fault\n");
|
||||
|
||||
/* Store current fault value so we can detect any changes next time */
|
||||
tas5720->last_fault = curr_fault;
|
||||
|
||||
if (!curr_fault)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* Periodically toggle SDZ (shutdown bit) H->L->H to clear any latching
|
||||
* faults as long as a fault condition persists. Always going through
|
||||
* the full sequence no matter the first return value to minimizes
|
||||
* chances for the device to end up in shutdown mode.
|
||||
*/
|
||||
ret = regmap_write_bits(tas5720->regmap, TAS5720_POWER_CTRL_REG,
|
||||
TAS5720_SDZ, 0);
|
||||
if (ret < 0)
|
||||
dev_err(dev, "failed to write POWER_CTRL register: %d\n", ret);
|
||||
|
||||
ret = regmap_write_bits(tas5720->regmap, TAS5720_POWER_CTRL_REG,
|
||||
TAS5720_SDZ, TAS5720_SDZ);
|
||||
if (ret < 0)
|
||||
dev_err(dev, "failed to write POWER_CTRL register: %d\n", ret);
|
||||
|
||||
out:
|
||||
/* Schedule the next fault check at the specified interval */
|
||||
schedule_delayed_work(&tas5720->fault_check_work,
|
||||
msecs_to_jiffies(TAS5720_FAULT_CHECK_INTERVAL));
|
||||
}
|
||||
|
||||
static int tas5720_codec_probe(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct tas5720_data *tas5720 = snd_soc_codec_get_drvdata(codec);
|
||||
unsigned int device_id;
|
||||
int ret;
|
||||
|
||||
tas5720->codec = codec;
|
||||
|
||||
ret = regulator_bulk_enable(ARRAY_SIZE(tas5720->supplies),
|
||||
tas5720->supplies);
|
||||
if (ret != 0) {
|
||||
dev_err(codec->dev, "failed to enable supplies: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regmap_read(tas5720->regmap, TAS5720_DEVICE_ID_REG, &device_id);
|
||||
if (ret < 0) {
|
||||
dev_err(codec->dev, "failed to read device ID register: %d\n",
|
||||
ret);
|
||||
goto probe_fail;
|
||||
}
|
||||
|
||||
if (device_id != TAS5720_DEVICE_ID) {
|
||||
dev_err(codec->dev, "wrong device ID. expected: %u read: %u\n",
|
||||
TAS5720_DEVICE_ID, device_id);
|
||||
ret = -ENODEV;
|
||||
goto probe_fail;
|
||||
}
|
||||
|
||||
/* Set device to mute */
|
||||
ret = snd_soc_update_bits(codec, TAS5720_DIGITAL_CTRL2_REG,
|
||||
TAS5720_MUTE, TAS5720_MUTE);
|
||||
if (ret < 0)
|
||||
goto error_snd_soc_update_bits;
|
||||
|
||||
/*
|
||||
* Enter shutdown mode - our default when not playing audio - to
|
||||
* minimize current consumption. On the TAS5720 there is no real down
|
||||
* side doing so as all device registers are preserved and the wakeup
|
||||
* of the codec is rather quick which we do using a dapm widget.
|
||||
*/
|
||||
ret = snd_soc_update_bits(codec, TAS5720_POWER_CTRL_REG,
|
||||
TAS5720_SDZ, 0);
|
||||
if (ret < 0)
|
||||
goto error_snd_soc_update_bits;
|
||||
|
||||
INIT_DELAYED_WORK(&tas5720->fault_check_work, tas5720_fault_check_work);
|
||||
|
||||
return 0;
|
||||
|
||||
error_snd_soc_update_bits:
|
||||
dev_err(codec->dev, "error configuring device registers: %d\n", ret);
|
||||
|
||||
probe_fail:
|
||||
regulator_bulk_disable(ARRAY_SIZE(tas5720->supplies),
|
||||
tas5720->supplies);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tas5720_codec_remove(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct tas5720_data *tas5720 = snd_soc_codec_get_drvdata(codec);
|
||||
int ret;
|
||||
|
||||
cancel_delayed_work_sync(&tas5720->fault_check_work);
|
||||
|
||||
ret = regulator_bulk_disable(ARRAY_SIZE(tas5720->supplies),
|
||||
tas5720->supplies);
|
||||
if (ret < 0)
|
||||
dev_err(codec->dev, "failed to disable supplies: %d\n", ret);
|
||||
|
||||
return ret;
|
||||
};
|
||||
|
||||
static int tas5720_dac_event(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 tas5720_data *tas5720 = snd_soc_codec_get_drvdata(codec);
|
||||
int ret;
|
||||
|
||||
if (event & SND_SOC_DAPM_POST_PMU) {
|
||||
/* Take TAS5720 out of shutdown mode */
|
||||
ret = snd_soc_update_bits(codec, TAS5720_POWER_CTRL_REG,
|
||||
TAS5720_SDZ, TAS5720_SDZ);
|
||||
if (ret < 0) {
|
||||
dev_err(codec->dev, "error waking codec: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Observe codec shutdown-to-active time. The datasheet only
|
||||
* lists a nominal value however just use-it as-is without
|
||||
* additional padding to minimize the delay introduced in
|
||||
* starting to play audio (actually there is other setup done
|
||||
* by the ASoC framework that will provide additional delays,
|
||||
* so we should always be safe).
|
||||
*/
|
||||
msleep(25);
|
||||
|
||||
/* Turn on TAS5720 periodic fault checking/handling */
|
||||
tas5720->last_fault = 0;
|
||||
schedule_delayed_work(&tas5720->fault_check_work,
|
||||
msecs_to_jiffies(TAS5720_FAULT_CHECK_INTERVAL));
|
||||
} else if (event & SND_SOC_DAPM_PRE_PMD) {
|
||||
/* Disable TAS5720 periodic fault checking/handling */
|
||||
cancel_delayed_work_sync(&tas5720->fault_check_work);
|
||||
|
||||
/* Place TAS5720 in shutdown mode to minimize current draw */
|
||||
ret = snd_soc_update_bits(codec, TAS5720_POWER_CTRL_REG,
|
||||
TAS5720_SDZ, 0);
|
||||
if (ret < 0) {
|
||||
dev_err(codec->dev, "error shutting down codec: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int tas5720_suspend(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct tas5720_data *tas5720 = snd_soc_codec_get_drvdata(codec);
|
||||
int ret;
|
||||
|
||||
regcache_cache_only(tas5720->regmap, true);
|
||||
regcache_mark_dirty(tas5720->regmap);
|
||||
|
||||
ret = regulator_bulk_disable(ARRAY_SIZE(tas5720->supplies),
|
||||
tas5720->supplies);
|
||||
if (ret < 0)
|
||||
dev_err(codec->dev, "failed to disable supplies: %d\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tas5720_resume(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct tas5720_data *tas5720 = snd_soc_codec_get_drvdata(codec);
|
||||
int ret;
|
||||
|
||||
ret = regulator_bulk_enable(ARRAY_SIZE(tas5720->supplies),
|
||||
tas5720->supplies);
|
||||
if (ret < 0) {
|
||||
dev_err(codec->dev, "failed to enable supplies: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
regcache_cache_only(tas5720->regmap, false);
|
||||
|
||||
ret = regcache_sync(tas5720->regmap);
|
||||
if (ret < 0) {
|
||||
dev_err(codec->dev, "failed to sync regcache: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#define tas5720_suspend NULL
|
||||
#define tas5720_resume NULL
|
||||
#endif
|
||||
|
||||
static bool tas5720_is_volatile_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case TAS5720_DEVICE_ID_REG:
|
||||
case TAS5720_FAULT_REG:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct regmap_config tas5720_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
|
||||
.max_register = TAS5720_MAX_REG,
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
.volatile_reg = tas5720_is_volatile_reg,
|
||||
};
|
||||
|
||||
/*
|
||||
* DAC analog gain. There are four discrete values to select from, ranging
|
||||
* from 19.2 dB to 26.3dB.
|
||||
*/
|
||||
static const DECLARE_TLV_DB_RANGE(dac_analog_tlv,
|
||||
0x0, 0x0, TLV_DB_SCALE_ITEM(1920, 0, 0),
|
||||
0x1, 0x1, TLV_DB_SCALE_ITEM(2070, 0, 0),
|
||||
0x2, 0x2, TLV_DB_SCALE_ITEM(2350, 0, 0),
|
||||
0x3, 0x3, TLV_DB_SCALE_ITEM(2630, 0, 0),
|
||||
);
|
||||
|
||||
/*
|
||||
* DAC digital volumes. From -103.5 to 24 dB in 0.5 dB steps. Note that
|
||||
* setting the gain below -100 dB (register value <0x7) is effectively a MUTE
|
||||
* as per device datasheet.
|
||||
*/
|
||||
static DECLARE_TLV_DB_SCALE(dac_tlv, -10350, 50, 0);
|
||||
|
||||
static const struct snd_kcontrol_new tas5720_snd_controls[] = {
|
||||
SOC_SINGLE_TLV("Speaker Driver Playback Volume",
|
||||
TAS5720_VOLUME_CTRL_REG, 0, 0xff, 0, dac_tlv),
|
||||
SOC_SINGLE_TLV("Speaker Driver Analog Gain", TAS5720_ANALOG_CTRL_REG,
|
||||
TAS5720_ANALOG_GAIN_SHIFT, 3, 0, dac_analog_tlv),
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_widget tas5720_dapm_widgets[] = {
|
||||
SND_SOC_DAPM_AIF_IN("DAC IN", "Playback", 0, SND_SOC_NOPM, 0, 0),
|
||||
SND_SOC_DAPM_DAC_E("DAC", NULL, SND_SOC_NOPM, 0, 0, tas5720_dac_event,
|
||||
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
|
||||
SND_SOC_DAPM_OUTPUT("OUT")
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route tas5720_audio_map[] = {
|
||||
{ "DAC", NULL, "DAC IN" },
|
||||
{ "OUT", NULL, "DAC" },
|
||||
};
|
||||
|
||||
static struct snd_soc_codec_driver soc_codec_dev_tas5720 = {
|
||||
.probe = tas5720_codec_probe,
|
||||
.remove = tas5720_codec_remove,
|
||||
.suspend = tas5720_suspend,
|
||||
.resume = tas5720_resume,
|
||||
|
||||
.controls = tas5720_snd_controls,
|
||||
.num_controls = ARRAY_SIZE(tas5720_snd_controls),
|
||||
.dapm_widgets = tas5720_dapm_widgets,
|
||||
.num_dapm_widgets = ARRAY_SIZE(tas5720_dapm_widgets),
|
||||
.dapm_routes = tas5720_audio_map,
|
||||
.num_dapm_routes = ARRAY_SIZE(tas5720_audio_map),
|
||||
};
|
||||
|
||||
/* PCM rates supported by the TAS5720 driver */
|
||||
#define TAS5720_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\
|
||||
SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
|
||||
|
||||
/* Formats supported by TAS5720 driver */
|
||||
#define TAS5720_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S18_3LE |\
|
||||
SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE)
|
||||
|
||||
static struct snd_soc_dai_ops tas5720_speaker_dai_ops = {
|
||||
.hw_params = tas5720_hw_params,
|
||||
.set_fmt = tas5720_set_dai_fmt,
|
||||
.set_tdm_slot = tas5720_set_dai_tdm_slot,
|
||||
.digital_mute = tas5720_mute,
|
||||
};
|
||||
|
||||
/*
|
||||
* TAS5720 DAI structure
|
||||
*
|
||||
* Note that were are advertising .playback.channels_max = 2 despite this being
|
||||
* a mono amplifier. The reason for that is that some serial ports such as TI's
|
||||
* McASP module have a minimum number of channels (2) that they can output.
|
||||
* Advertising more channels than we have will allow us to interface with such
|
||||
* a serial port without really any negative side effects as the TAS5720 will
|
||||
* simply ignore any extra channel(s) asides from the one channel that is
|
||||
* configured to be played back.
|
||||
*/
|
||||
static struct snd_soc_dai_driver tas5720_dai[] = {
|
||||
{
|
||||
.name = "tas5720-amplifier",
|
||||
.playback = {
|
||||
.stream_name = "Playback",
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.rates = TAS5720_RATES,
|
||||
.formats = TAS5720_FORMATS,
|
||||
},
|
||||
.ops = &tas5720_speaker_dai_ops,
|
||||
},
|
||||
};
|
||||
|
||||
static int tas5720_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct device *dev = &client->dev;
|
||||
struct tas5720_data *data;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
data->tas5720_client = client;
|
||||
data->regmap = devm_regmap_init_i2c(client, &tas5720_regmap_config);
|
||||
if (IS_ERR(data->regmap)) {
|
||||
ret = PTR_ERR(data->regmap);
|
||||
dev_err(dev, "failed to allocate register map: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(data->supplies); i++)
|
||||
data->supplies[i].supply = tas5720_supply_names[i];
|
||||
|
||||
ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(data->supplies),
|
||||
data->supplies);
|
||||
if (ret != 0) {
|
||||
dev_err(dev, "failed to request supplies: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
dev_set_drvdata(dev, data);
|
||||
|
||||
ret = snd_soc_register_codec(&client->dev,
|
||||
&soc_codec_dev_tas5720,
|
||||
tas5720_dai, ARRAY_SIZE(tas5720_dai));
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to register codec: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tas5720_remove(struct i2c_client *client)
|
||||
{
|
||||
struct device *dev = &client->dev;
|
||||
|
||||
snd_soc_unregister_codec(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id tas5720_id[] = {
|
||||
{ "tas5720", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, tas5720_id);
|
||||
|
||||
#if IS_ENABLED(CONFIG_OF)
|
||||
static const struct of_device_id tas5720_of_match[] = {
|
||||
{ .compatible = "ti,tas5720", },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, tas5720_of_match);
|
||||
#endif
|
||||
|
||||
static struct i2c_driver tas5720_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "tas5720",
|
||||
.of_match_table = of_match_ptr(tas5720_of_match),
|
||||
},
|
||||
.probe = tas5720_probe,
|
||||
.remove = tas5720_remove,
|
||||
.id_table = tas5720_id,
|
||||
};
|
||||
|
||||
module_i2c_driver(tas5720_i2c_driver);
|
||||
|
||||
MODULE_AUTHOR("Andreas Dannenberg <dannenberg@ti.com>");
|
||||
MODULE_DESCRIPTION("TAS5720 Audio amplifier driver");
|
||||
MODULE_LICENSE("GPL");
|
90
sound/soc/codecs/tas5720.h
Normal file
90
sound/soc/codecs/tas5720.h
Normal file
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* tas5720.h - ALSA SoC Texas Instruments TAS5720 Mono Audio Amplifier
|
||||
*
|
||||
* Copyright (C)2015-2016 Texas Instruments Incorporated - http://www.ti.com
|
||||
*
|
||||
* Author: Andreas Dannenberg <dannenberg@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.
|
||||
*/
|
||||
|
||||
#ifndef __TAS5720_H__
|
||||
#define __TAS5720_H__
|
||||
|
||||
/* Register Address Map */
|
||||
#define TAS5720_DEVICE_ID_REG 0x00
|
||||
#define TAS5720_POWER_CTRL_REG 0x01
|
||||
#define TAS5720_DIGITAL_CTRL1_REG 0x02
|
||||
#define TAS5720_DIGITAL_CTRL2_REG 0x03
|
||||
#define TAS5720_VOLUME_CTRL_REG 0x04
|
||||
#define TAS5720_ANALOG_CTRL_REG 0x06
|
||||
#define TAS5720_FAULT_REG 0x08
|
||||
#define TAS5720_DIGITAL_CLIP2_REG 0x10
|
||||
#define TAS5720_DIGITAL_CLIP1_REG 0x11
|
||||
#define TAS5720_MAX_REG TAS5720_DIGITAL_CLIP1_REG
|
||||
|
||||
/* TAS5720_DEVICE_ID_REG */
|
||||
#define TAS5720_DEVICE_ID 0x01
|
||||
|
||||
/* TAS5720_POWER_CTRL_REG */
|
||||
#define TAS5720_DIG_CLIP_MASK GENMASK(7, 2)
|
||||
#define TAS5720_SLEEP BIT(1)
|
||||
#define TAS5720_SDZ BIT(0)
|
||||
|
||||
/* TAS5720_DIGITAL_CTRL1_REG */
|
||||
#define TAS5720_HPF_BYPASS BIT(7)
|
||||
#define TAS5720_TDM_CFG_SRC BIT(6)
|
||||
#define TAS5720_SSZ_DS BIT(3)
|
||||
#define TAS5720_SAIF_RIGHTJ_24BIT (0x0)
|
||||
#define TAS5720_SAIF_RIGHTJ_20BIT (0x1)
|
||||
#define TAS5720_SAIF_RIGHTJ_18BIT (0x2)
|
||||
#define TAS5720_SAIF_RIGHTJ_16BIT (0x3)
|
||||
#define TAS5720_SAIF_I2S (0x4)
|
||||
#define TAS5720_SAIF_LEFTJ (0x5)
|
||||
#define TAS5720_SAIF_FORMAT_MASK GENMASK(2, 0)
|
||||
|
||||
/* TAS5720_DIGITAL_CTRL2_REG */
|
||||
#define TAS5720_MUTE BIT(4)
|
||||
#define TAS5720_TDM_SLOT_SEL_MASK GENMASK(2, 0)
|
||||
|
||||
/* TAS5720_ANALOG_CTRL_REG */
|
||||
#define TAS5720_PWM_RATE_6_3_FSYNC (0x0 << 4)
|
||||
#define TAS5720_PWM_RATE_8_4_FSYNC (0x1 << 4)
|
||||
#define TAS5720_PWM_RATE_10_5_FSYNC (0x2 << 4)
|
||||
#define TAS5720_PWM_RATE_12_6_FSYNC (0x3 << 4)
|
||||
#define TAS5720_PWM_RATE_14_7_FSYNC (0x4 << 4)
|
||||
#define TAS5720_PWM_RATE_16_8_FSYNC (0x5 << 4)
|
||||
#define TAS5720_PWM_RATE_20_10_FSYNC (0x6 << 4)
|
||||
#define TAS5720_PWM_RATE_24_12_FSYNC (0x7 << 4)
|
||||
#define TAS5720_PWM_RATE_MASK GENMASK(6, 4)
|
||||
#define TAS5720_ANALOG_GAIN_19_2DBV (0x0 << 2)
|
||||
#define TAS5720_ANALOG_GAIN_20_7DBV (0x1 << 2)
|
||||
#define TAS5720_ANALOG_GAIN_23_5DBV (0x2 << 2)
|
||||
#define TAS5720_ANALOG_GAIN_26_3DBV (0x3 << 2)
|
||||
#define TAS5720_ANALOG_GAIN_MASK GENMASK(3, 2)
|
||||
#define TAS5720_ANALOG_GAIN_SHIFT (0x2)
|
||||
|
||||
/* TAS5720_FAULT_REG */
|
||||
#define TAS5720_OC_THRESH_100PCT (0x0 << 4)
|
||||
#define TAS5720_OC_THRESH_75PCT (0x1 << 4)
|
||||
#define TAS5720_OC_THRESH_50PCT (0x2 << 4)
|
||||
#define TAS5720_OC_THRESH_25PCT (0x3 << 4)
|
||||
#define TAS5720_OC_THRESH_MASK GENMASK(5, 4)
|
||||
#define TAS5720_CLKE BIT(3)
|
||||
#define TAS5720_OCE BIT(2)
|
||||
#define TAS5720_DCE BIT(1)
|
||||
#define TAS5720_OTE BIT(0)
|
||||
#define TAS5720_FAULT_MASK GENMASK(3, 0)
|
||||
|
||||
/* TAS5720_DIGITAL_CLIP1_REG */
|
||||
#define TAS5720_CLIP1_MASK GENMASK(7, 2)
|
||||
#define TAS5720_CLIP1_SHIFT (0x2)
|
||||
|
||||
#endif /* __TAS5720_H__ */
|
|
@ -28,6 +28,7 @@
|
|||
#include <linux/i2c.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/slab.h>
|
||||
|
@ -1280,10 +1281,19 @@ static const struct i2c_device_id aic31xx_i2c_id[] = {
|
|||
};
|
||||
MODULE_DEVICE_TABLE(i2c, aic31xx_i2c_id);
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
static const struct acpi_device_id aic31xx_acpi_match[] = {
|
||||
{ "10TI3100", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, aic31xx_acpi_match);
|
||||
#endif
|
||||
|
||||
static struct i2c_driver aic31xx_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "tlv320aic31xx-codec",
|
||||
.of_match_table = of_match_ptr(tlv320aic31xx_of_match),
|
||||
.acpi_match_table = ACPI_PTR(aic31xx_acpi_match),
|
||||
},
|
||||
.probe = aic31xx_i2c_probe,
|
||||
.remove = aic31xx_i2c_remove,
|
||||
|
|
74
sound/soc/codecs/tlv320aic32x4-i2c.c
Normal file
74
sound/soc/codecs/tlv320aic32x4-i2c.c
Normal file
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* linux/sound/soc/codecs/tlv320aic32x4-i2c.c
|
||||
*
|
||||
* Copyright 2011 NW Digital Radio
|
||||
*
|
||||
* Author: Jeremy McDermond <nh6z@nh6z.net>
|
||||
*
|
||||
* Based on sound/soc/codecs/wm8974 and TI driver for kernel 2.6.27.
|
||||
*
|
||||
* 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/i2c.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <sound/soc.h>
|
||||
|
||||
#include "tlv320aic32x4.h"
|
||||
|
||||
static int aic32x4_i2c_probe(struct i2c_client *i2c,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct regmap *regmap;
|
||||
struct regmap_config config;
|
||||
|
||||
config = aic32x4_regmap_config;
|
||||
config.reg_bits = 8;
|
||||
config.val_bits = 8;
|
||||
|
||||
regmap = devm_regmap_init_i2c(i2c, &config);
|
||||
return aic32x4_probe(&i2c->dev, regmap);
|
||||
}
|
||||
|
||||
static int aic32x4_i2c_remove(struct i2c_client *i2c)
|
||||
{
|
||||
return aic32x4_remove(&i2c->dev);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id aic32x4_i2c_id[] = {
|
||||
{ "tlv320aic32x4", 0 },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, aic32x4_i2c_id);
|
||||
|
||||
static const struct of_device_id aic32x4_of_id[] = {
|
||||
{ .compatible = "ti,tlv320aic32x4", },
|
||||
{ /* senitel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, aic32x4_of_id);
|
||||
|
||||
static struct i2c_driver aic32x4_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "tlv320aic32x4",
|
||||
.of_match_table = aic32x4_of_id,
|
||||
},
|
||||
.probe = aic32x4_i2c_probe,
|
||||
.remove = aic32x4_i2c_remove,
|
||||
.id_table = aic32x4_i2c_id,
|
||||
};
|
||||
|
||||
module_i2c_driver(aic32x4_i2c_driver);
|
||||
|
||||
MODULE_DESCRIPTION("ASoC TLV320AIC32x4 codec driver I2C");
|
||||
MODULE_AUTHOR("Jeremy McDermond <nh6z@nh6z.net>");
|
||||
MODULE_LICENSE("GPL");
|
76
sound/soc/codecs/tlv320aic32x4-spi.c
Normal file
76
sound/soc/codecs/tlv320aic32x4-spi.c
Normal file
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* linux/sound/soc/codecs/tlv320aic32x4-spi.c
|
||||
*
|
||||
* Copyright 2011 NW Digital Radio
|
||||
*
|
||||
* Author: Jeremy McDermond <nh6z@nh6z.net>
|
||||
*
|
||||
* Based on sound/soc/codecs/wm8974 and TI driver for kernel 2.6.27.
|
||||
*
|
||||
* 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/spi/spi.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <sound/soc.h>
|
||||
|
||||
#include "tlv320aic32x4.h"
|
||||
|
||||
static int aic32x4_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
struct regmap *regmap;
|
||||
struct regmap_config config;
|
||||
|
||||
config = aic32x4_regmap_config;
|
||||
config.reg_bits = 7;
|
||||
config.pad_bits = 1;
|
||||
config.val_bits = 8;
|
||||
config.read_flag_mask = 0x01;
|
||||
|
||||
regmap = devm_regmap_init_spi(spi, &config);
|
||||
return aic32x4_probe(&spi->dev, regmap);
|
||||
}
|
||||
|
||||
static int aic32x4_spi_remove(struct spi_device *spi)
|
||||
{
|
||||
return aic32x4_remove(&spi->dev);
|
||||
}
|
||||
|
||||
static const struct spi_device_id aic32x4_spi_id[] = {
|
||||
{ "tlv320aic32x4", 0 },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, aic32x4_spi_id);
|
||||
|
||||
static const struct of_device_id aic32x4_of_id[] = {
|
||||
{ .compatible = "ti,tlv320aic32x4", },
|
||||
{ /* senitel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, aic32x4_of_id);
|
||||
|
||||
static struct spi_driver aic32x4_spi_driver = {
|
||||
.driver = {
|
||||
.name = "tlv320aic32x4",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = aic32x4_of_id,
|
||||
},
|
||||
.probe = aic32x4_spi_probe,
|
||||
.remove = aic32x4_spi_remove,
|
||||
.id_table = aic32x4_spi_id,
|
||||
};
|
||||
|
||||
module_spi_driver(aic32x4_spi_driver);
|
||||
|
||||
MODULE_DESCRIPTION("ASoC TLV320AIC32x4 codec driver SPI");
|
||||
MODULE_AUTHOR("Jeremy McDermond <nh6z@nh6z.net>");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -30,7 +30,6 @@
|
|||
#include <linux/pm.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/cdev.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/clk.h>
|
||||
|
@ -160,7 +159,10 @@ static const struct aic32x4_rate_divs aic32x4_divs[] = {
|
|||
/* 48k rate */
|
||||
{AIC32X4_FREQ_12000000, 48000, 1, 8, 1920, 128, 2, 8, 128, 2, 8, 4},
|
||||
{AIC32X4_FREQ_24000000, 48000, 2, 8, 1920, 128, 8, 2, 64, 8, 4, 4},
|
||||
{AIC32X4_FREQ_25000000, 48000, 2, 7, 8643, 128, 8, 2, 64, 8, 4, 4}
|
||||
{AIC32X4_FREQ_25000000, 48000, 2, 7, 8643, 128, 8, 2, 64, 8, 4, 4},
|
||||
|
||||
/* 96k rate */
|
||||
{AIC32X4_FREQ_25000000, 96000, 2, 7, 8643, 64, 4, 4, 64, 4, 4, 1},
|
||||
};
|
||||
|
||||
static const struct snd_kcontrol_new hpl_output_mixer_controls[] = {
|
||||
|
@ -181,16 +183,71 @@ static const struct snd_kcontrol_new lor_output_mixer_controls[] = {
|
|||
SOC_DAPM_SINGLE("R_DAC Switch", AIC32X4_LORROUTE, 3, 1, 0),
|
||||
};
|
||||
|
||||
static const struct snd_kcontrol_new left_input_mixer_controls[] = {
|
||||
SOC_DAPM_SINGLE("IN1_L P Switch", AIC32X4_LMICPGAPIN, 6, 1, 0),
|
||||
SOC_DAPM_SINGLE("IN2_L P Switch", AIC32X4_LMICPGAPIN, 4, 1, 0),
|
||||
SOC_DAPM_SINGLE("IN3_L P Switch", AIC32X4_LMICPGAPIN, 2, 1, 0),
|
||||
static const char * const resistor_text[] = {
|
||||
"Off", "10 kOhm", "20 kOhm", "40 kOhm",
|
||||
};
|
||||
|
||||
static const struct snd_kcontrol_new right_input_mixer_controls[] = {
|
||||
SOC_DAPM_SINGLE("IN1_R P Switch", AIC32X4_RMICPGAPIN, 6, 1, 0),
|
||||
SOC_DAPM_SINGLE("IN2_R P Switch", AIC32X4_RMICPGAPIN, 4, 1, 0),
|
||||
SOC_DAPM_SINGLE("IN3_R P Switch", AIC32X4_RMICPGAPIN, 2, 1, 0),
|
||||
/* Left mixer pins */
|
||||
static SOC_ENUM_SINGLE_DECL(in1l_lpga_p_enum, AIC32X4_LMICPGAPIN, 6, resistor_text);
|
||||
static SOC_ENUM_SINGLE_DECL(in2l_lpga_p_enum, AIC32X4_LMICPGAPIN, 4, resistor_text);
|
||||
static SOC_ENUM_SINGLE_DECL(in3l_lpga_p_enum, AIC32X4_LMICPGAPIN, 2, resistor_text);
|
||||
static SOC_ENUM_SINGLE_DECL(in1r_lpga_p_enum, AIC32X4_LMICPGAPIN, 0, resistor_text);
|
||||
|
||||
static SOC_ENUM_SINGLE_DECL(cml_lpga_n_enum, AIC32X4_LMICPGANIN, 6, resistor_text);
|
||||
static SOC_ENUM_SINGLE_DECL(in2r_lpga_n_enum, AIC32X4_LMICPGANIN, 4, resistor_text);
|
||||
static SOC_ENUM_SINGLE_DECL(in3r_lpga_n_enum, AIC32X4_LMICPGANIN, 2, resistor_text);
|
||||
|
||||
static const struct snd_kcontrol_new in1l_to_lmixer_controls[] = {
|
||||
SOC_DAPM_ENUM("IN1_L L+ Switch", in1l_lpga_p_enum),
|
||||
};
|
||||
static const struct snd_kcontrol_new in2l_to_lmixer_controls[] = {
|
||||
SOC_DAPM_ENUM("IN2_L L+ Switch", in2l_lpga_p_enum),
|
||||
};
|
||||
static const struct snd_kcontrol_new in3l_to_lmixer_controls[] = {
|
||||
SOC_DAPM_ENUM("IN3_L L+ Switch", in3l_lpga_p_enum),
|
||||
};
|
||||
static const struct snd_kcontrol_new in1r_to_lmixer_controls[] = {
|
||||
SOC_DAPM_ENUM("IN1_R L+ Switch", in1r_lpga_p_enum),
|
||||
};
|
||||
static const struct snd_kcontrol_new cml_to_lmixer_controls[] = {
|
||||
SOC_DAPM_ENUM("CM_L L- Switch", cml_lpga_n_enum),
|
||||
};
|
||||
static const struct snd_kcontrol_new in2r_to_lmixer_controls[] = {
|
||||
SOC_DAPM_ENUM("IN2_R L- Switch", in2r_lpga_n_enum),
|
||||
};
|
||||
static const struct snd_kcontrol_new in3r_to_lmixer_controls[] = {
|
||||
SOC_DAPM_ENUM("IN3_R L- Switch", in3r_lpga_n_enum),
|
||||
};
|
||||
|
||||
/* Right mixer pins */
|
||||
static SOC_ENUM_SINGLE_DECL(in1r_rpga_p_enum, AIC32X4_RMICPGAPIN, 6, resistor_text);
|
||||
static SOC_ENUM_SINGLE_DECL(in2r_rpga_p_enum, AIC32X4_RMICPGAPIN, 4, resistor_text);
|
||||
static SOC_ENUM_SINGLE_DECL(in3r_rpga_p_enum, AIC32X4_RMICPGAPIN, 2, resistor_text);
|
||||
static SOC_ENUM_SINGLE_DECL(in2l_rpga_p_enum, AIC32X4_RMICPGAPIN, 0, resistor_text);
|
||||
static SOC_ENUM_SINGLE_DECL(cmr_rpga_n_enum, AIC32X4_RMICPGANIN, 6, resistor_text);
|
||||
static SOC_ENUM_SINGLE_DECL(in1l_rpga_n_enum, AIC32X4_RMICPGANIN, 4, resistor_text);
|
||||
static SOC_ENUM_SINGLE_DECL(in3l_rpga_n_enum, AIC32X4_RMICPGANIN, 2, resistor_text);
|
||||
|
||||
static const struct snd_kcontrol_new in1r_to_rmixer_controls[] = {
|
||||
SOC_DAPM_ENUM("IN1_R R+ Switch", in1r_rpga_p_enum),
|
||||
};
|
||||
static const struct snd_kcontrol_new in2r_to_rmixer_controls[] = {
|
||||
SOC_DAPM_ENUM("IN2_R R+ Switch", in2r_rpga_p_enum),
|
||||
};
|
||||
static const struct snd_kcontrol_new in3r_to_rmixer_controls[] = {
|
||||
SOC_DAPM_ENUM("IN3_R R+ Switch", in3r_rpga_p_enum),
|
||||
};
|
||||
static const struct snd_kcontrol_new in2l_to_rmixer_controls[] = {
|
||||
SOC_DAPM_ENUM("IN2_L R+ Switch", in2l_rpga_p_enum),
|
||||
};
|
||||
static const struct snd_kcontrol_new cmr_to_rmixer_controls[] = {
|
||||
SOC_DAPM_ENUM("CM_R R- Switch", cmr_rpga_n_enum),
|
||||
};
|
||||
static const struct snd_kcontrol_new in1l_to_rmixer_controls[] = {
|
||||
SOC_DAPM_ENUM("IN1_L R- Switch", in1l_rpga_n_enum),
|
||||
};
|
||||
static const struct snd_kcontrol_new in3l_to_rmixer_controls[] = {
|
||||
SOC_DAPM_ENUM("IN3_L R- Switch", in3l_rpga_n_enum),
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_widget aic32x4_dapm_widgets[] = {
|
||||
|
@ -214,14 +271,39 @@ static const struct snd_soc_dapm_widget aic32x4_dapm_widgets[] = {
|
|||
&lor_output_mixer_controls[0],
|
||||
ARRAY_SIZE(lor_output_mixer_controls)),
|
||||
SND_SOC_DAPM_PGA("LOR Power", AIC32X4_OUTPWRCTL, 2, 0, NULL, 0),
|
||||
SND_SOC_DAPM_MIXER("Left Input Mixer", SND_SOC_NOPM, 0, 0,
|
||||
&left_input_mixer_controls[0],
|
||||
ARRAY_SIZE(left_input_mixer_controls)),
|
||||
SND_SOC_DAPM_MIXER("Right Input Mixer", SND_SOC_NOPM, 0, 0,
|
||||
&right_input_mixer_controls[0],
|
||||
ARRAY_SIZE(right_input_mixer_controls)),
|
||||
SND_SOC_DAPM_ADC("Left ADC", "Left Capture", AIC32X4_ADCSETUP, 7, 0),
|
||||
|
||||
SND_SOC_DAPM_ADC("Right ADC", "Right Capture", AIC32X4_ADCSETUP, 6, 0),
|
||||
SND_SOC_DAPM_MUX("IN1_R to Right Mixer Positive Resistor", SND_SOC_NOPM, 0, 0,
|
||||
in1r_to_rmixer_controls),
|
||||
SND_SOC_DAPM_MUX("IN2_R to Right Mixer Positive Resistor", SND_SOC_NOPM, 0, 0,
|
||||
in2r_to_rmixer_controls),
|
||||
SND_SOC_DAPM_MUX("IN3_R to Right Mixer Positive Resistor", SND_SOC_NOPM, 0, 0,
|
||||
in3r_to_rmixer_controls),
|
||||
SND_SOC_DAPM_MUX("IN2_L to Right Mixer Positive Resistor", SND_SOC_NOPM, 0, 0,
|
||||
in2l_to_rmixer_controls),
|
||||
SND_SOC_DAPM_MUX("CM_R to Right Mixer Negative Resistor", SND_SOC_NOPM, 0, 0,
|
||||
cmr_to_rmixer_controls),
|
||||
SND_SOC_DAPM_MUX("IN1_L to Right Mixer Negative Resistor", SND_SOC_NOPM, 0, 0,
|
||||
in1l_to_rmixer_controls),
|
||||
SND_SOC_DAPM_MUX("IN3_L to Right Mixer Negative Resistor", SND_SOC_NOPM, 0, 0,
|
||||
in3l_to_rmixer_controls),
|
||||
|
||||
SND_SOC_DAPM_ADC("Left ADC", "Left Capture", AIC32X4_ADCSETUP, 7, 0),
|
||||
SND_SOC_DAPM_MUX("IN1_L to Left Mixer Positive Resistor", SND_SOC_NOPM, 0, 0,
|
||||
in1l_to_lmixer_controls),
|
||||
SND_SOC_DAPM_MUX("IN2_L to Left Mixer Positive Resistor", SND_SOC_NOPM, 0, 0,
|
||||
in2l_to_lmixer_controls),
|
||||
SND_SOC_DAPM_MUX("IN3_L to Left Mixer Positive Resistor", SND_SOC_NOPM, 0, 0,
|
||||
in3l_to_lmixer_controls),
|
||||
SND_SOC_DAPM_MUX("IN1_R to Left Mixer Positive Resistor", SND_SOC_NOPM, 0, 0,
|
||||
in1r_to_lmixer_controls),
|
||||
SND_SOC_DAPM_MUX("CM_L to Left Mixer Negative Resistor", SND_SOC_NOPM, 0, 0,
|
||||
cml_to_lmixer_controls),
|
||||
SND_SOC_DAPM_MUX("IN2_R to Left Mixer Negative Resistor", SND_SOC_NOPM, 0, 0,
|
||||
in2r_to_lmixer_controls),
|
||||
SND_SOC_DAPM_MUX("IN3_R to Left Mixer Negative Resistor", SND_SOC_NOPM, 0, 0,
|
||||
in3r_to_lmixer_controls),
|
||||
|
||||
SND_SOC_DAPM_MICBIAS("Mic Bias", AIC32X4_MICBIAS, 6, 0),
|
||||
|
||||
SND_SOC_DAPM_OUTPUT("HPL"),
|
||||
|
@ -261,19 +343,77 @@ static const struct snd_soc_dapm_route aic32x4_dapm_routes[] = {
|
|||
{"LOR Power", NULL, "LOR Output Mixer"},
|
||||
{"LOR", NULL, "LOR Power"},
|
||||
|
||||
/* Left input */
|
||||
{"Left Input Mixer", "IN1_L P Switch", "IN1_L"},
|
||||
{"Left Input Mixer", "IN2_L P Switch", "IN2_L"},
|
||||
{"Left Input Mixer", "IN3_L P Switch", "IN3_L"},
|
||||
|
||||
{"Left ADC", NULL, "Left Input Mixer"},
|
||||
|
||||
/* Right Input */
|
||||
{"Right Input Mixer", "IN1_R P Switch", "IN1_R"},
|
||||
{"Right Input Mixer", "IN2_R P Switch", "IN2_R"},
|
||||
{"Right Input Mixer", "IN3_R P Switch", "IN3_R"},
|
||||
{"Right ADC", NULL, "IN1_R to Right Mixer Positive Resistor"},
|
||||
{"IN1_R to Right Mixer Positive Resistor", "10 kOhm", "IN1_R"},
|
||||
{"IN1_R to Right Mixer Positive Resistor", "20 kOhm", "IN1_R"},
|
||||
{"IN1_R to Right Mixer Positive Resistor", "40 kOhm", "IN1_R"},
|
||||
|
||||
{"Right ADC", NULL, "Right Input Mixer"},
|
||||
{"Right ADC", NULL, "IN2_R to Right Mixer Positive Resistor"},
|
||||
{"IN2_R to Right Mixer Positive Resistor", "10 kOhm", "IN2_R"},
|
||||
{"IN2_R to Right Mixer Positive Resistor", "20 kOhm", "IN2_R"},
|
||||
{"IN2_R to Right Mixer Positive Resistor", "40 kOhm", "IN2_R"},
|
||||
|
||||
{"Right ADC", NULL, "IN3_R to Right Mixer Positive Resistor"},
|
||||
{"IN3_R to Right Mixer Positive Resistor", "10 kOhm", "IN3_R"},
|
||||
{"IN3_R to Right Mixer Positive Resistor", "20 kOhm", "IN3_R"},
|
||||
{"IN3_R to Right Mixer Positive Resistor", "40 kOhm", "IN3_R"},
|
||||
|
||||
{"Right ADC", NULL, "IN2_L to Right Mixer Positive Resistor"},
|
||||
{"IN2_L to Right Mixer Positive Resistor", "10 kOhm", "IN2_L"},
|
||||
{"IN2_L to Right Mixer Positive Resistor", "20 kOhm", "IN2_L"},
|
||||
{"IN2_L to Right Mixer Positive Resistor", "40 kOhm", "IN2_L"},
|
||||
|
||||
{"Right ADC", NULL, "CM_R to Right Mixer Negative Resistor"},
|
||||
{"CM_R to Right Mixer Negative Resistor", "10 kOhm", "CM_R"},
|
||||
{"CM_R to Right Mixer Negative Resistor", "20 kOhm", "CM_R"},
|
||||
{"CM_R to Right Mixer Negative Resistor", "40 kOhm", "CM_R"},
|
||||
|
||||
{"Right ADC", NULL, "IN1_L to Right Mixer Negative Resistor"},
|
||||
{"IN1_L to Right Mixer Negative Resistor", "10 kOhm", "IN1_L"},
|
||||
{"IN1_L to Right Mixer Negative Resistor", "20 kOhm", "IN1_L"},
|
||||
{"IN1_L to Right Mixer Negative Resistor", "40 kOhm", "IN1_L"},
|
||||
|
||||
{"Right ADC", NULL, "IN3_L to Right Mixer Negative Resistor"},
|
||||
{"IN3_L to Right Mixer Negative Resistor", "10 kOhm", "IN3_L"},
|
||||
{"IN3_L to Right Mixer Negative Resistor", "20 kOhm", "IN3_L"},
|
||||
{"IN3_L to Right Mixer Negative Resistor", "40 kOhm", "IN3_L"},
|
||||
|
||||
/* Left Input */
|
||||
{"Left ADC", NULL, "IN1_L to Left Mixer Positive Resistor"},
|
||||
{"IN1_L to Left Mixer Positive Resistor", "10 kOhm", "IN1_L"},
|
||||
{"IN1_L to Left Mixer Positive Resistor", "20 kOhm", "IN1_L"},
|
||||
{"IN1_L to Left Mixer Positive Resistor", "40 kOhm", "IN1_L"},
|
||||
|
||||
{"Left ADC", NULL, "IN2_L to Left Mixer Positive Resistor"},
|
||||
{"IN2_L to Left Mixer Positive Resistor", "10 kOhm", "IN2_L"},
|
||||
{"IN2_L to Left Mixer Positive Resistor", "20 kOhm", "IN2_L"},
|
||||
{"IN2_L to Left Mixer Positive Resistor", "40 kOhm", "IN2_L"},
|
||||
|
||||
{"Left ADC", NULL, "IN3_L to Left Mixer Positive Resistor"},
|
||||
{"IN3_L to Left Mixer Positive Resistor", "10 kOhm", "IN3_L"},
|
||||
{"IN3_L to Left Mixer Positive Resistor", "20 kOhm", "IN3_L"},
|
||||
{"IN3_L to Left Mixer Positive Resistor", "40 kOhm", "IN3_L"},
|
||||
|
||||
{"Left ADC", NULL, "IN1_R to Left Mixer Positive Resistor"},
|
||||
{"IN1_R to Left Mixer Positive Resistor", "10 kOhm", "IN1_R"},
|
||||
{"IN1_R to Left Mixer Positive Resistor", "20 kOhm", "IN1_R"},
|
||||
{"IN1_R to Left Mixer Positive Resistor", "40 kOhm", "IN1_R"},
|
||||
|
||||
{"Left ADC", NULL, "CM_L to Left Mixer Negative Resistor"},
|
||||
{"CM_L to Left Mixer Negative Resistor", "10 kOhm", "CM_L"},
|
||||
{"CM_L to Left Mixer Negative Resistor", "20 kOhm", "CM_L"},
|
||||
{"CM_L to Left Mixer Negative Resistor", "40 kOhm", "CM_L"},
|
||||
|
||||
{"Left ADC", NULL, "IN2_R to Left Mixer Negative Resistor"},
|
||||
{"IN2_R to Left Mixer Negative Resistor", "10 kOhm", "IN2_R"},
|
||||
{"IN2_R to Left Mixer Negative Resistor", "20 kOhm", "IN2_R"},
|
||||
{"IN2_R to Left Mixer Negative Resistor", "40 kOhm", "IN2_R"},
|
||||
|
||||
{"Left ADC", NULL, "IN3_R to Left Mixer Negative Resistor"},
|
||||
{"IN3_R to Left Mixer Negative Resistor", "10 kOhm", "IN3_R"},
|
||||
{"IN3_R to Left Mixer Negative Resistor", "20 kOhm", "IN3_R"},
|
||||
{"IN3_R to Left Mixer Negative Resistor", "40 kOhm", "IN3_R"},
|
||||
};
|
||||
|
||||
static const struct regmap_range_cfg aic32x4_regmap_pages[] = {
|
||||
|
@ -287,14 +427,12 @@ static const struct regmap_range_cfg aic32x4_regmap_pages[] = {
|
|||
},
|
||||
};
|
||||
|
||||
static const struct regmap_config aic32x4_regmap = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
|
||||
const struct regmap_config aic32x4_regmap_config = {
|
||||
.max_register = AIC32X4_RMICPGAVOL,
|
||||
.ranges = aic32x4_regmap_pages,
|
||||
.num_ranges = ARRAY_SIZE(aic32x4_regmap_pages),
|
||||
};
|
||||
EXPORT_SYMBOL(aic32x4_regmap_config);
|
||||
|
||||
static inline int aic32x4_get_divs(int mclk, int rate)
|
||||
{
|
||||
|
@ -567,7 +705,7 @@ static int aic32x4_set_bias_level(struct snd_soc_codec *codec,
|
|||
return 0;
|
||||
}
|
||||
|
||||
#define AIC32X4_RATES SNDRV_PCM_RATE_8000_48000
|
||||
#define AIC32X4_RATES SNDRV_PCM_RATE_8000_96000
|
||||
#define AIC32X4_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE \
|
||||
| SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE)
|
||||
|
||||
|
@ -596,7 +734,7 @@ static struct snd_soc_dai_driver aic32x4_dai = {
|
|||
.symmetric_rates = 1,
|
||||
};
|
||||
|
||||
static int aic32x4_probe(struct snd_soc_codec *codec)
|
||||
static int aic32x4_codec_probe(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec);
|
||||
u32 tmp_reg;
|
||||
|
@ -655,7 +793,7 @@ static int aic32x4_probe(struct snd_soc_codec *codec)
|
|||
}
|
||||
|
||||
static struct snd_soc_codec_driver soc_codec_dev_aic32x4 = {
|
||||
.probe = aic32x4_probe,
|
||||
.probe = aic32x4_codec_probe,
|
||||
.set_bias_level = aic32x4_set_bias_level,
|
||||
.suspend_bias_off = true,
|
||||
|
||||
|
@ -777,24 +915,22 @@ static int aic32x4_setup_regulators(struct device *dev,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int aic32x4_i2c_probe(struct i2c_client *i2c,
|
||||
const struct i2c_device_id *id)
|
||||
int aic32x4_probe(struct device *dev, struct regmap *regmap)
|
||||
{
|
||||
struct aic32x4_pdata *pdata = i2c->dev.platform_data;
|
||||
struct aic32x4_priv *aic32x4;
|
||||
struct device_node *np = i2c->dev.of_node;
|
||||
struct aic32x4_pdata *pdata = dev->platform_data;
|
||||
struct device_node *np = dev->of_node;
|
||||
int ret;
|
||||
|
||||
aic32x4 = devm_kzalloc(&i2c->dev, sizeof(struct aic32x4_priv),
|
||||
if (IS_ERR(regmap))
|
||||
return PTR_ERR(regmap);
|
||||
|
||||
aic32x4 = devm_kzalloc(dev, sizeof(struct aic32x4_priv),
|
||||
GFP_KERNEL);
|
||||
if (aic32x4 == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
aic32x4->regmap = devm_regmap_init_i2c(i2c, &aic32x4_regmap);
|
||||
if (IS_ERR(aic32x4->regmap))
|
||||
return PTR_ERR(aic32x4->regmap);
|
||||
|
||||
i2c_set_clientdata(i2c, aic32x4);
|
||||
dev_set_drvdata(dev, aic32x4);
|
||||
|
||||
if (pdata) {
|
||||
aic32x4->power_cfg = pdata->power_cfg;
|
||||
|
@ -804,7 +940,7 @@ static int aic32x4_i2c_probe(struct i2c_client *i2c,
|
|||
} else if (np) {
|
||||
ret = aic32x4_parse_dt(aic32x4, np);
|
||||
if (ret) {
|
||||
dev_err(&i2c->dev, "Failed to parse DT node\n");
|
||||
dev_err(dev, "Failed to parse DT node\n");
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
|
@ -814,71 +950,48 @@ static int aic32x4_i2c_probe(struct i2c_client *i2c,
|
|||
aic32x4->rstn_gpio = -1;
|
||||
}
|
||||
|
||||
aic32x4->mclk = devm_clk_get(&i2c->dev, "mclk");
|
||||
aic32x4->mclk = devm_clk_get(dev, "mclk");
|
||||
if (IS_ERR(aic32x4->mclk)) {
|
||||
dev_err(&i2c->dev, "Failed getting the mclk. The current implementation does not support the usage of this codec without mclk\n");
|
||||
dev_err(dev, "Failed getting the mclk. The current implementation does not support the usage of this codec without mclk\n");
|
||||
return PTR_ERR(aic32x4->mclk);
|
||||
}
|
||||
|
||||
if (gpio_is_valid(aic32x4->rstn_gpio)) {
|
||||
ret = devm_gpio_request_one(&i2c->dev, aic32x4->rstn_gpio,
|
||||
ret = devm_gpio_request_one(dev, aic32x4->rstn_gpio,
|
||||
GPIOF_OUT_INIT_LOW, "tlv320aic32x4 rstn");
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = aic32x4_setup_regulators(&i2c->dev, aic32x4);
|
||||
ret = aic32x4_setup_regulators(dev, aic32x4);
|
||||
if (ret) {
|
||||
dev_err(&i2c->dev, "Failed to setup regulators\n");
|
||||
dev_err(dev, "Failed to setup regulators\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = snd_soc_register_codec(&i2c->dev,
|
||||
ret = snd_soc_register_codec(dev,
|
||||
&soc_codec_dev_aic32x4, &aic32x4_dai, 1);
|
||||
if (ret) {
|
||||
dev_err(&i2c->dev, "Failed to register codec\n");
|
||||
dev_err(dev, "Failed to register codec\n");
|
||||
aic32x4_disable_regulators(aic32x4);
|
||||
return ret;
|
||||
}
|
||||
|
||||
i2c_set_clientdata(i2c, aic32x4);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(aic32x4_probe);
|
||||
|
||||
static int aic32x4_i2c_remove(struct i2c_client *client)
|
||||
int aic32x4_remove(struct device *dev)
|
||||
{
|
||||
struct aic32x4_priv *aic32x4 = i2c_get_clientdata(client);
|
||||
struct aic32x4_priv *aic32x4 = dev_get_drvdata(dev);
|
||||
|
||||
aic32x4_disable_regulators(aic32x4);
|
||||
|
||||
snd_soc_unregister_codec(&client->dev);
|
||||
snd_soc_unregister_codec(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id aic32x4_i2c_id[] = {
|
||||
{ "tlv320aic32x4", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, aic32x4_i2c_id);
|
||||
|
||||
static const struct of_device_id aic32x4_of_id[] = {
|
||||
{ .compatible = "ti,tlv320aic32x4", },
|
||||
{ /* senitel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, aic32x4_of_id);
|
||||
|
||||
static struct i2c_driver aic32x4_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "tlv320aic32x4",
|
||||
.of_match_table = aic32x4_of_id,
|
||||
},
|
||||
.probe = aic32x4_i2c_probe,
|
||||
.remove = aic32x4_i2c_remove,
|
||||
.id_table = aic32x4_i2c_id,
|
||||
};
|
||||
|
||||
module_i2c_driver(aic32x4_i2c_driver);
|
||||
EXPORT_SYMBOL(aic32x4_remove);
|
||||
|
||||
MODULE_DESCRIPTION("ASoC tlv320aic32x4 codec driver");
|
||||
MODULE_AUTHOR("Javier Martin <javier.martin@vista-silicon.com>");
|
||||
|
|
|
@ -10,6 +10,13 @@
|
|||
#ifndef _TLV320AIC32X4_H
|
||||
#define _TLV320AIC32X4_H
|
||||
|
||||
struct device;
|
||||
struct regmap_config;
|
||||
|
||||
extern const struct regmap_config aic32x4_regmap_config;
|
||||
int aic32x4_probe(struct device *dev, struct regmap *regmap);
|
||||
int aic32x4_remove(struct device *dev);
|
||||
|
||||
/* tlv320aic32x4 register space (in decimal to match datasheet) */
|
||||
|
||||
#define AIC32X4_PAGE1 128
|
||||
|
|
|
@ -824,7 +824,7 @@ static int twl6040_set_bias_level(struct snd_soc_codec *codec,
|
|||
{
|
||||
struct twl6040 *twl6040 = codec->control_data;
|
||||
struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
|
||||
int ret;
|
||||
int ret = 0;
|
||||
|
||||
switch (level) {
|
||||
case SND_SOC_BIAS_ON:
|
||||
|
@ -832,12 +832,16 @@ static int twl6040_set_bias_level(struct snd_soc_codec *codec,
|
|||
case SND_SOC_BIAS_PREPARE:
|
||||
break;
|
||||
case SND_SOC_BIAS_STANDBY:
|
||||
if (priv->codec_powered)
|
||||
if (priv->codec_powered) {
|
||||
/* Select low power PLL in standby */
|
||||
ret = twl6040_set_pll(twl6040, TWL6040_SYSCLK_SEL_LPPLL,
|
||||
32768, 19200000);
|
||||
break;
|
||||
}
|
||||
|
||||
ret = twl6040_power(twl6040, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
break;
|
||||
|
||||
priv->codec_powered = 1;
|
||||
|
||||
|
@ -853,7 +857,7 @@ static int twl6040_set_bias_level(struct snd_soc_codec *codec,
|
|||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int twl6040_startup(struct snd_pcm_substream *substream,
|
||||
|
@ -983,9 +987,9 @@ static void twl6040_mute_path(struct snd_soc_codec *codec, enum twl6040_dai_id i
|
|||
if (mute) {
|
||||
/* Power down drivers and DACs */
|
||||
hflctl &= ~(TWL6040_HFDACENA | TWL6040_HFPGAENA |
|
||||
TWL6040_HFDRVENA);
|
||||
TWL6040_HFDRVENA | TWL6040_HFSWENA);
|
||||
hfrctl &= ~(TWL6040_HFDACENA | TWL6040_HFPGAENA |
|
||||
TWL6040_HFDRVENA);
|
||||
TWL6040_HFDRVENA | TWL6040_HFSWENA);
|
||||
}
|
||||
|
||||
twl6040_reg_write(twl6040, TWL6040_REG_HFLCTL, hflctl);
|
||||
|
|
|
@ -3793,9 +3793,8 @@ static int wm8962_runtime_resume(struct device *dev)
|
|||
ret = regulator_bulk_enable(ARRAY_SIZE(wm8962->supplies),
|
||||
wm8962->supplies);
|
||||
if (ret != 0) {
|
||||
dev_err(dev,
|
||||
"Failed to enable supplies: %d\n", ret);
|
||||
return ret;
|
||||
dev_err(dev, "Failed to enable supplies: %d\n", ret);
|
||||
goto disable_clock;
|
||||
}
|
||||
|
||||
regcache_cache_only(wm8962->regmap, false);
|
||||
|
@ -3833,6 +3832,10 @@ static int wm8962_runtime_resume(struct device *dev)
|
|||
msleep(5);
|
||||
|
||||
return 0;
|
||||
|
||||
disable_clock:
|
||||
clk_disable_unprepare(wm8962->pdata.mclk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int wm8962_runtime_suspend(struct device *dev)
|
||||
|
|
|
@ -16,9 +16,9 @@
|
|||
#include <asm/types.h>
|
||||
#include <sound/soc.h>
|
||||
|
||||
#define WM8962_SYSCLK_MCLK 1
|
||||
#define WM8962_SYSCLK_FLL 2
|
||||
#define WM8962_SYSCLK_PLL3 3
|
||||
#define WM8962_SYSCLK_MCLK 0
|
||||
#define WM8962_SYSCLK_FLL 1
|
||||
#define WM8962_SYSCLK_PLL3 2
|
||||
|
||||
#define WM8962_FLL 1
|
||||
|
||||
|
|
|
@ -643,6 +643,7 @@ MODULE_DEVICE_TABLE(of, asoc_simple_of_match);
|
|||
static struct platform_driver asoc_simple_card = {
|
||||
.driver = {
|
||||
.name = "asoc-simple-card",
|
||||
.pm = &snd_soc_pm_ops,
|
||||
.of_match_table = asoc_simple_of_match,
|
||||
},
|
||||
.probe = asoc_simple_card_probe,
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
config SND_KIRKWOOD_SOC
|
||||
tristate "SoC Audio for the Marvell Kirkwood and Dove chips"
|
||||
depends on ARCH_DOVE || ARCH_MVEBU || COMPILE_TEST
|
||||
depends on HAS_DMA
|
||||
help
|
||||
Say Y or M if you want to add support for codecs attached to
|
||||
the Kirkwood I2S interface. You will also need to select the
|
||||
|
|
|
@ -43,6 +43,7 @@ config SND_SOC_MT8173_RT5650_RT5676
|
|||
depends on SND_SOC_MEDIATEK && I2C
|
||||
select SND_SOC_RT5645
|
||||
select SND_SOC_RT5677
|
||||
select SND_SOC_HDMI_CODEC
|
||||
help
|
||||
This adds ASoC driver for Mediatek MT8173 boards
|
||||
with the RT5650 and RT5676 codecs.
|
||||
|
|
|
@ -134,7 +134,9 @@ static struct snd_soc_dai_link_component mt8173_rt5650_rt5676_codecs[] = {
|
|||
enum {
|
||||
DAI_LINK_PLAYBACK,
|
||||
DAI_LINK_CAPTURE,
|
||||
DAI_LINK_HDMI,
|
||||
DAI_LINK_CODEC_I2S,
|
||||
DAI_LINK_HDMI_I2S,
|
||||
DAI_LINK_INTERCODEC
|
||||
};
|
||||
|
||||
|
@ -161,6 +163,16 @@ static struct snd_soc_dai_link mt8173_rt5650_rt5676_dais[] = {
|
|||
.dynamic = 1,
|
||||
.dpcm_capture = 1,
|
||||
},
|
||||
[DAI_LINK_HDMI] = {
|
||||
.name = "HDMI",
|
||||
.stream_name = "HDMI PCM",
|
||||
.cpu_dai_name = "HDMI",
|
||||
.codec_name = "snd-soc-dummy",
|
||||
.codec_dai_name = "snd-soc-dummy-dai",
|
||||
.trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
|
||||
.dynamic = 1,
|
||||
.dpcm_playback = 1,
|
||||
},
|
||||
|
||||
/* Back End DAI links */
|
||||
[DAI_LINK_CODEC_I2S] = {
|
||||
|
@ -177,6 +189,13 @@ static struct snd_soc_dai_link mt8173_rt5650_rt5676_dais[] = {
|
|||
.dpcm_playback = 1,
|
||||
.dpcm_capture = 1,
|
||||
},
|
||||
[DAI_LINK_HDMI_I2S] = {
|
||||
.name = "HDMI BE",
|
||||
.cpu_dai_name = "HDMIO",
|
||||
.no_pcm = 1,
|
||||
.codec_dai_name = "i2s-hifi",
|
||||
.dpcm_playback = 1,
|
||||
},
|
||||
/* rt5676 <-> rt5650 intercodec link: Sets rt5676 I2S2 as master */
|
||||
[DAI_LINK_INTERCODEC] = {
|
||||
.name = "rt5650_rt5676 intercodec",
|
||||
|
@ -251,6 +270,14 @@ static int mt8173_rt5650_rt5676_dev_probe(struct platform_device *pdev)
|
|||
mt8173_rt5650_rt5676_dais[DAI_LINK_INTERCODEC].codec_of_node =
|
||||
mt8173_rt5650_rt5676_codecs[1].of_node;
|
||||
|
||||
mt8173_rt5650_rt5676_dais[DAI_LINK_HDMI_I2S].codec_of_node =
|
||||
of_parse_phandle(pdev->dev.of_node, "mediatek,audio-codec", 2);
|
||||
if (!mt8173_rt5650_rt5676_dais[DAI_LINK_HDMI_I2S].codec_of_node) {
|
||||
dev_err(&pdev->dev,
|
||||
"Property 'audio-codec' missing or invalid\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
card->dev = &pdev->dev;
|
||||
platform_set_drvdata(pdev, card);
|
||||
|
||||
|
|
|
@ -85,12 +85,29 @@ static int mt8173_rt5650_init(struct snd_soc_pcm_runtime *runtime)
|
|||
{
|
||||
struct snd_soc_card *card = runtime->card;
|
||||
struct snd_soc_codec *codec = runtime->codec_dais[0]->codec;
|
||||
const char *codec_capture_dai = runtime->codec_dais[1]->name;
|
||||
int ret;
|
||||
|
||||
rt5645_sel_asrc_clk_src(codec,
|
||||
RT5645_DA_STEREO_FILTER |
|
||||
RT5645_AD_STEREO_FILTER,
|
||||
RT5645_DA_STEREO_FILTER,
|
||||
RT5645_CLK_SEL_I2S1_ASRC);
|
||||
|
||||
if (!strcmp(codec_capture_dai, "rt5645-aif1")) {
|
||||
rt5645_sel_asrc_clk_src(codec,
|
||||
RT5645_AD_STEREO_FILTER,
|
||||
RT5645_CLK_SEL_I2S1_ASRC);
|
||||
} else if (!strcmp(codec_capture_dai, "rt5645-aif2")) {
|
||||
rt5645_sel_asrc_clk_src(codec,
|
||||
RT5645_AD_STEREO_FILTER,
|
||||
RT5645_CLK_SEL_I2S2_ASRC);
|
||||
} else {
|
||||
dev_warn(card->dev,
|
||||
"Only one dai codec found in DTS, enabled rt5645 AD filter\n");
|
||||
rt5645_sel_asrc_clk_src(codec,
|
||||
RT5645_AD_STEREO_FILTER,
|
||||
RT5645_CLK_SEL_I2S1_ASRC);
|
||||
}
|
||||
|
||||
/* enable jack detection */
|
||||
ret = snd_soc_card_jack_new(card, "Headset Jack",
|
||||
SND_JACK_HEADPHONE | SND_JACK_MICROPHONE |
|
||||
|
@ -110,6 +127,11 @@ static int mt8173_rt5650_init(struct snd_soc_pcm_runtime *runtime)
|
|||
|
||||
static struct snd_soc_dai_link_component mt8173_rt5650_codecs[] = {
|
||||
{
|
||||
/* Playback */
|
||||
.dai_name = "rt5645-aif1",
|
||||
},
|
||||
{
|
||||
/* Capture */
|
||||
.dai_name = "rt5645-aif1",
|
||||
},
|
||||
};
|
||||
|
@ -149,7 +171,7 @@ static struct snd_soc_dai_link mt8173_rt5650_dais[] = {
|
|||
.cpu_dai_name = "I2S",
|
||||
.no_pcm = 1,
|
||||
.codecs = mt8173_rt5650_codecs,
|
||||
.num_codecs = 1,
|
||||
.num_codecs = 2,
|
||||
.init = mt8173_rt5650_init,
|
||||
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
|
||||
SND_SOC_DAIFMT_CBS_CFS,
|
||||
|
@ -177,6 +199,8 @@ static int mt8173_rt5650_dev_probe(struct platform_device *pdev)
|
|||
{
|
||||
struct snd_soc_card *card = &mt8173_rt5650_card;
|
||||
struct device_node *platform_node;
|
||||
struct device_node *np;
|
||||
const char *codec_capture_dai;
|
||||
int i, ret;
|
||||
|
||||
platform_node = of_parse_phandle(pdev->dev.of_node,
|
||||
|
@ -199,6 +223,26 @@ static int mt8173_rt5650_dev_probe(struct platform_device *pdev)
|
|||
"Property 'audio-codec' missing or invalid\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
mt8173_rt5650_codecs[1].of_node = mt8173_rt5650_codecs[0].of_node;
|
||||
|
||||
if (of_find_node_by_name(platform_node, "codec-capture")) {
|
||||
np = of_get_child_by_name(pdev->dev.of_node, "codec-capture");
|
||||
if (!np) {
|
||||
dev_err(&pdev->dev,
|
||||
"%s: Can't find codec-capture DT node\n",
|
||||
__func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
ret = snd_soc_of_get_dai_name(np, &codec_capture_dai);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev,
|
||||
"%s codec_capture_dai name fail %d\n",
|
||||
__func__, ret);
|
||||
return ret;
|
||||
}
|
||||
mt8173_rt5650_codecs[1].dai_name = codec_capture_dai;
|
||||
}
|
||||
|
||||
card->dev = &pdev->dev;
|
||||
platform_set_drvdata(pdev, card);
|
||||
|
||||
|
|
|
@ -123,6 +123,7 @@
|
|||
#define AFE_TDM_CON1_WLEN_32BIT (0x2 << 8)
|
||||
#define AFE_TDM_CON1_MSB_ALIGNED (0x1 << 4)
|
||||
#define AFE_TDM_CON1_1_BCK_DELAY (0x1 << 3)
|
||||
#define AFE_TDM_CON1_LRCK_INV (0x1 << 2)
|
||||
#define AFE_TDM_CON1_BCK_INV (0x1 << 1)
|
||||
#define AFE_TDM_CON1_EN (0x1 << 0)
|
||||
|
||||
|
@ -449,6 +450,7 @@ static int mtk_afe_hdmi_prepare(struct snd_pcm_substream *substream,
|
|||
runtime->rate * runtime->channels * 32);
|
||||
|
||||
val = AFE_TDM_CON1_BCK_INV |
|
||||
AFE_TDM_CON1_LRCK_INV |
|
||||
AFE_TDM_CON1_1_BCK_DELAY |
|
||||
AFE_TDM_CON1_MSB_ALIGNED | /* I2S mode */
|
||||
AFE_TDM_CON1_WLEN_32BIT |
|
||||
|
|
|
@ -260,6 +260,10 @@ static void omap_st_on(struct omap_mcbsp *mcbsp)
|
|||
if (mcbsp->pdata->enable_st_clock)
|
||||
mcbsp->pdata->enable_st_clock(mcbsp->id, 1);
|
||||
|
||||
/* Disable Sidetone clock auto-gating for normal operation */
|
||||
w = MCBSP_ST_READ(mcbsp, SYSCONFIG);
|
||||
MCBSP_ST_WRITE(mcbsp, SYSCONFIG, w & ~(ST_AUTOIDLE));
|
||||
|
||||
/* Enable McBSP Sidetone */
|
||||
w = MCBSP_READ(mcbsp, SSELCR);
|
||||
MCBSP_WRITE(mcbsp, SSELCR, w | SIDETONEEN);
|
||||
|
@ -279,6 +283,10 @@ static void omap_st_off(struct omap_mcbsp *mcbsp)
|
|||
w = MCBSP_READ(mcbsp, SSELCR);
|
||||
MCBSP_WRITE(mcbsp, SSELCR, w & ~(SIDETONEEN));
|
||||
|
||||
/* Enable Sidetone clock auto-gating to reduce power consumption */
|
||||
w = MCBSP_ST_READ(mcbsp, SYSCONFIG);
|
||||
MCBSP_ST_WRITE(mcbsp, SYSCONFIG, w | ST_AUTOIDLE);
|
||||
|
||||
if (mcbsp->pdata->enable_st_clock)
|
||||
mcbsp->pdata->enable_st_clock(mcbsp->id, 0);
|
||||
}
|
||||
|
|
|
@ -82,6 +82,8 @@ static int omap_pcm_hw_params(struct snd_pcm_substream *substream,
|
|||
struct dma_chan *chan;
|
||||
int err = 0;
|
||||
|
||||
memset(&config, 0x00, sizeof(config));
|
||||
|
||||
dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
|
||||
|
||||
/* return if this is a bufferless transfer e.g.
|
||||
|
|
|
@ -133,3 +133,4 @@ module_platform_driver(mmp_driver);
|
|||
MODULE_AUTHOR("Leo Yan <leoy@marvell.com>");
|
||||
MODULE_DESCRIPTION("ALSA SoC Brownstone");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:brownstone-audio");
|
||||
|
|
|
@ -207,3 +207,4 @@ module_platform_driver(mioa701_wm9713_driver);
|
|||
MODULE_AUTHOR("Robert Jarzmik (rjarzmik@free.fr)");
|
||||
MODULE_DESCRIPTION("ALSA SoC WM9713 MIO A701");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:mioa701-wm9713");
|
||||
|
|
|
@ -248,3 +248,4 @@ module_platform_driver(mmp_pcm_driver);
|
|||
MODULE_AUTHOR("Leo Yan <leoy@marvell.com>");
|
||||
MODULE_DESCRIPTION("MMP Soc Audio DMA module");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:mmp-pcm-audio");
|
||||
|
|
|
@ -482,3 +482,4 @@ module_platform_driver(asoc_mmp_sspa_driver);
|
|||
MODULE_AUTHOR("Leo Yan <leoy@marvell.com>");
|
||||
MODULE_DESCRIPTION("MMP SSPA SoC Interface");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:mmp-sspa-dai");
|
||||
|
|
|
@ -161,3 +161,4 @@ module_platform_driver(palm27x_wm9712_driver);
|
|||
MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>");
|
||||
MODULE_DESCRIPTION("ALSA SoC Palm T|X, T5 and LifeDrive");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:palm27x-asoc");
|
||||
|
|
|
@ -833,3 +833,4 @@ module_platform_driver(asoc_ssp_driver);
|
|||
MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
|
||||
MODULE_DESCRIPTION("PXA SSP/PCM SoC Interface");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:pxa-ssp-dai");
|
||||
|
|
|
@ -287,3 +287,4 @@ module_platform_driver(pxa2xx_ac97_driver);
|
|||
MODULE_AUTHOR("Nicolas Pitre");
|
||||
MODULE_DESCRIPTION("AC97 driver for the Intel PXA2xx chip");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:pxa2xx-ac97");
|
||||
|
|
|
@ -117,3 +117,4 @@ module_platform_driver(pxa_pcm_driver);
|
|||
MODULE_AUTHOR("Nicolas Pitre");
|
||||
MODULE_DESCRIPTION("Intel PXA2xx PCM DMA module");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:pxa-pcm-audio");
|
||||
|
|
|
@ -474,7 +474,7 @@ static int lpass_platform_pcm_new(struct snd_soc_pcm_runtime *soc_runtime)
|
|||
struct lpass_data *drvdata =
|
||||
snd_soc_platform_get_drvdata(soc_runtime->platform);
|
||||
struct lpass_variant *v = drvdata->variant;
|
||||
int ret;
|
||||
int ret = -EINVAL;
|
||||
struct lpass_pcm_data *data;
|
||||
size_t size = lpass_platform_pcm_hardware.buffer_bytes_max;
|
||||
|
||||
|
@ -518,8 +518,10 @@ static int lpass_platform_pcm_new(struct snd_soc_pcm_runtime *soc_runtime)
|
|||
data->wrdma_ch = v->alloc_dma_channel(drvdata,
|
||||
SNDRV_PCM_STREAM_CAPTURE);
|
||||
|
||||
if (data->wrdma_ch < 0)
|
||||
if (data->wrdma_ch < 0) {
|
||||
ret = data->wrdma_ch;
|
||||
goto capture_alloc_err;
|
||||
}
|
||||
|
||||
drvdata->substream[data->wrdma_ch] = csubstream;
|
||||
|
||||
|
|
|
@ -492,9 +492,7 @@ static void rsnd_adg_get_clkout(struct rsnd_priv *priv,
|
|||
*/
|
||||
if (!count) {
|
||||
clk = clk_register_fixed_rate(dev, clkout_name[CLKOUT],
|
||||
parent_clk_name,
|
||||
(parent_clk_name) ?
|
||||
0 : CLK_IS_ROOT, req_rate);
|
||||
parent_clk_name, 0, req_rate);
|
||||
if (!IS_ERR(clk)) {
|
||||
adg->clkout[CLKOUT] = clk;
|
||||
of_clk_add_provider(np, of_clk_src_simple_get, clk);
|
||||
|
@ -506,9 +504,7 @@ static void rsnd_adg_get_clkout(struct rsnd_priv *priv,
|
|||
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,
|
||||
parent_clk_name, 0,
|
||||
req_rate);
|
||||
if (!IS_ERR(clk)) {
|
||||
adg->onecell.clks = adg->clkout;
|
||||
|
|
|
@ -316,11 +316,15 @@ static u32 rsnd_dmapp_get_id(struct rsnd_dai_stream *io,
|
|||
size = ARRAY_SIZE(gen2_id_table_cmd);
|
||||
}
|
||||
|
||||
if (!entry)
|
||||
return 0xFF;
|
||||
if ((!entry) || (size <= id)) {
|
||||
struct device *dev = rsnd_priv_to_dev(rsnd_io_to_priv(io));
|
||||
|
||||
if (size <= id)
|
||||
return 0xFF;
|
||||
dev_err(dev, "unknown connection (%s[%d])\n",
|
||||
rsnd_mod_name(mod), rsnd_mod_id(mod));
|
||||
|
||||
/* use non-prohibited SRS number as error */
|
||||
return 0x00; /* SSI00 */
|
||||
}
|
||||
|
||||
return entry[id];
|
||||
}
|
||||
|
|
|
@ -276,8 +276,9 @@ struct rsnd_mod {
|
|||
/*
|
||||
* status
|
||||
*
|
||||
* 0xH0000CB0
|
||||
* 0xH0000CBA
|
||||
*
|
||||
* A 0: probe 1: remove
|
||||
* B 0: init 1: quit
|
||||
* C 0: start 1: stop
|
||||
*
|
||||
|
@ -287,19 +288,19 @@ struct rsnd_mod {
|
|||
* H 0: fallback
|
||||
* H 0: hw_params
|
||||
*/
|
||||
#define __rsnd_mod_shift_probe 0
|
||||
#define __rsnd_mod_shift_remove 0
|
||||
#define __rsnd_mod_shift_init 4
|
||||
#define __rsnd_mod_shift_quit 4
|
||||
#define __rsnd_mod_shift_start 8
|
||||
#define __rsnd_mod_shift_stop 8
|
||||
#define __rsnd_mod_shift_probe 28 /* always called */
|
||||
#define __rsnd_mod_shift_remove 28 /* always called */
|
||||
#define __rsnd_mod_shift_irq 28 /* always called */
|
||||
#define __rsnd_mod_shift_pcm_new 28 /* always called */
|
||||
#define __rsnd_mod_shift_fallback 28 /* always called */
|
||||
#define __rsnd_mod_shift_hw_params 28 /* always called */
|
||||
|
||||
#define __rsnd_mod_add_probe 0
|
||||
#define __rsnd_mod_add_remove 0
|
||||
#define __rsnd_mod_add_probe 1
|
||||
#define __rsnd_mod_add_remove -1
|
||||
#define __rsnd_mod_add_init 1
|
||||
#define __rsnd_mod_add_quit -1
|
||||
#define __rsnd_mod_add_start 1
|
||||
|
@ -310,7 +311,7 @@ struct rsnd_mod {
|
|||
#define __rsnd_mod_add_hw_params 0
|
||||
|
||||
#define __rsnd_mod_call_probe 0
|
||||
#define __rsnd_mod_call_remove 0
|
||||
#define __rsnd_mod_call_remove 1
|
||||
#define __rsnd_mod_call_init 0
|
||||
#define __rsnd_mod_call_quit 1
|
||||
#define __rsnd_mod_call_start 0
|
||||
|
|
|
@ -572,6 +572,9 @@ int rsnd_src_probe(struct rsnd_priv *priv)
|
|||
|
||||
i = 0;
|
||||
for_each_child_of_node(node, np) {
|
||||
if (!of_device_is_available(np))
|
||||
goto skip;
|
||||
|
||||
src = rsnd_src_get(priv, i);
|
||||
|
||||
snprintf(name, RSND_SRC_NAME_SIZE, "%s.%d",
|
||||
|
@ -595,6 +598,7 @@ int rsnd_src_probe(struct rsnd_priv *priv)
|
|||
if (ret)
|
||||
goto rsnd_src_probe_done;
|
||||
|
||||
skip:
|
||||
i++;
|
||||
}
|
||||
|
||||
|
|
|
@ -1023,6 +1023,11 @@ static int soc_tplg_kcontrol_elems_load(struct soc_tplg *tplg,
|
|||
|
||||
control_hdr = (struct snd_soc_tplg_ctl_hdr *)tplg->pos;
|
||||
|
||||
if (control_hdr->size != sizeof(*control_hdr)) {
|
||||
dev_err(tplg->dev, "ASoC: invalid control size\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (control_hdr->ops.info) {
|
||||
case SND_SOC_TPLG_CTL_VOLSW:
|
||||
case SND_SOC_TPLG_CTL_STROBE:
|
||||
|
@ -1476,6 +1481,8 @@ static int soc_tplg_dapm_widget_create(struct soc_tplg *tplg,
|
|||
widget->dobj.type = SND_SOC_DOBJ_WIDGET;
|
||||
widget->dobj.ops = tplg->ops;
|
||||
widget->dobj.index = tplg->index;
|
||||
kfree(template.sname);
|
||||
kfree(template.name);
|
||||
list_add(&widget->dobj.list, &tplg->comp->dobj_list);
|
||||
return 0;
|
||||
|
||||
|
@ -1499,10 +1506,17 @@ static int soc_tplg_dapm_widget_elems_load(struct soc_tplg *tplg,
|
|||
|
||||
for (i = 0; i < count; i++) {
|
||||
widget = (struct snd_soc_tplg_dapm_widget *) tplg->pos;
|
||||
if (widget->size != sizeof(*widget)) {
|
||||
dev_err(tplg->dev, "ASoC: invalid widget size\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = soc_tplg_dapm_widget_create(tplg, widget);
|
||||
if (ret < 0)
|
||||
if (ret < 0) {
|
||||
dev_err(tplg->dev, "ASoC: failed to load widget %s\n",
|
||||
widget->name);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -1586,6 +1600,7 @@ static int soc_tplg_dai_create(struct soc_tplg *tplg,
|
|||
return snd_soc_register_dai(tplg->comp, dai_drv);
|
||||
}
|
||||
|
||||
/* create the FE DAI link */
|
||||
static int soc_tplg_link_create(struct soc_tplg *tplg,
|
||||
struct snd_soc_tplg_pcm *pcm)
|
||||
{
|
||||
|
@ -1598,6 +1613,16 @@ static int soc_tplg_link_create(struct soc_tplg *tplg,
|
|||
|
||||
link->name = pcm->pcm_name;
|
||||
link->stream_name = pcm->pcm_name;
|
||||
link->id = pcm->pcm_id;
|
||||
|
||||
link->cpu_dai_name = pcm->dai_name;
|
||||
link->codec_name = "snd-soc-dummy";
|
||||
link->codec_dai_name = "snd-soc-dummy-dai";
|
||||
|
||||
/* enable DPCM */
|
||||
link->dynamic = 1;
|
||||
link->dpcm_playback = pcm->playback;
|
||||
link->dpcm_capture = pcm->capture;
|
||||
|
||||
/* pass control to component driver for optional further init */
|
||||
ret = soc_tplg_dai_link_load(tplg, link);
|
||||
|
@ -1639,8 +1664,6 @@ static int soc_tplg_pcm_elems_load(struct soc_tplg *tplg,
|
|||
if (tplg->pass != SOC_TPLG_PASS_PCM_DAI)
|
||||
return 0;
|
||||
|
||||
pcm = (struct snd_soc_tplg_pcm *)tplg->pos;
|
||||
|
||||
if (soc_tplg_check_elem_count(tplg,
|
||||
sizeof(struct snd_soc_tplg_pcm), count,
|
||||
hdr->payload_size, "PCM DAI")) {
|
||||
|
@ -1650,7 +1673,13 @@ static int soc_tplg_pcm_elems_load(struct soc_tplg *tplg,
|
|||
}
|
||||
|
||||
/* create the FE DAIs and DAI links */
|
||||
pcm = (struct snd_soc_tplg_pcm *)tplg->pos;
|
||||
for (i = 0; i < count; i++) {
|
||||
if (pcm->size != sizeof(*pcm)) {
|
||||
dev_err(tplg->dev, "ASoC: invalid pcm size\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
soc_tplg_pcm_create(tplg, pcm);
|
||||
pcm++;
|
||||
}
|
||||
|
@ -1670,6 +1699,11 @@ static int soc_tplg_manifest_load(struct soc_tplg *tplg,
|
|||
return 0;
|
||||
|
||||
manifest = (struct snd_soc_tplg_manifest *)tplg->pos;
|
||||
if (manifest->size != sizeof(*manifest)) {
|
||||
dev_err(tplg->dev, "ASoC: invalid manifest size\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
tplg->pos += sizeof(struct snd_soc_tplg_manifest);
|
||||
|
||||
if (tplg->comp && tplg->ops && tplg->ops->manifest)
|
||||
|
@ -1686,6 +1720,14 @@ static int soc_valid_header(struct soc_tplg *tplg,
|
|||
if (soc_tplg_get_hdr_offset(tplg) >= tplg->fw->size)
|
||||
return 0;
|
||||
|
||||
if (hdr->size != sizeof(*hdr)) {
|
||||
dev_err(tplg->dev,
|
||||
"ASoC: invalid header size for type %d at offset 0x%lx size 0x%zx.\n",
|
||||
hdr->type, soc_tplg_get_hdr_offset(tplg),
|
||||
tplg->fw->size);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* big endian firmware objects not supported atm */
|
||||
if (hdr->magic == cpu_to_be32(SND_SOC_TPLG_MAGIC)) {
|
||||
dev_err(tplg->dev,
|
||||
|
|
|
@ -10,6 +10,142 @@
|
|||
|
||||
#include "uniperif.h"
|
||||
|
||||
/*
|
||||
* User frame size shall be 2, 4, 6 or 8 32-bits words length
|
||||
* (i.e. 8, 16, 24 or 32 bytes)
|
||||
* This constraint comes from allowed values for
|
||||
* UNIPERIF_I2S_FMT_NUM_CH register
|
||||
*/
|
||||
#define UNIPERIF_MAX_FRAME_SZ 0x20
|
||||
#define UNIPERIF_ALLOWED_FRAME_SZ (0x08 | 0x10 | 0x18 | UNIPERIF_MAX_FRAME_SZ)
|
||||
|
||||
int sti_uniperiph_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
|
||||
unsigned int rx_mask, int slots,
|
||||
int slot_width)
|
||||
{
|
||||
struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai);
|
||||
struct uniperif *uni = priv->dai_data.uni;
|
||||
int i, frame_size, avail_slots;
|
||||
|
||||
if (!UNIPERIF_TYPE_IS_TDM(uni)) {
|
||||
dev_err(uni->dev, "cpu dai not in tdm mode\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* store info in unip context */
|
||||
uni->tdm_slot.slots = slots;
|
||||
uni->tdm_slot.slot_width = slot_width;
|
||||
/* unip is unidirectionnal */
|
||||
uni->tdm_slot.mask = (tx_mask != 0) ? tx_mask : rx_mask;
|
||||
|
||||
/* number of available timeslots */
|
||||
for (i = 0, avail_slots = 0; i < uni->tdm_slot.slots; i++) {
|
||||
if ((uni->tdm_slot.mask >> i) & 0x01)
|
||||
avail_slots++;
|
||||
}
|
||||
uni->tdm_slot.avail_slots = avail_slots;
|
||||
|
||||
/* frame size in bytes */
|
||||
frame_size = uni->tdm_slot.avail_slots * uni->tdm_slot.slot_width / 8;
|
||||
|
||||
/* check frame size is allowed */
|
||||
if ((frame_size > UNIPERIF_MAX_FRAME_SZ) ||
|
||||
(frame_size & ~(int)UNIPERIF_ALLOWED_FRAME_SZ)) {
|
||||
dev_err(uni->dev, "frame size not allowed: %d bytes\n",
|
||||
frame_size);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sti_uniperiph_fix_tdm_chan(struct snd_pcm_hw_params *params,
|
||||
struct snd_pcm_hw_rule *rule)
|
||||
{
|
||||
struct uniperif *uni = rule->private;
|
||||
struct snd_interval t;
|
||||
|
||||
t.min = uni->tdm_slot.avail_slots;
|
||||
t.max = uni->tdm_slot.avail_slots;
|
||||
t.openmin = 0;
|
||||
t.openmax = 0;
|
||||
t.integer = 0;
|
||||
|
||||
return snd_interval_refine(hw_param_interval(params, rule->var), &t);
|
||||
}
|
||||
|
||||
int sti_uniperiph_fix_tdm_format(struct snd_pcm_hw_params *params,
|
||||
struct snd_pcm_hw_rule *rule)
|
||||
{
|
||||
struct uniperif *uni = rule->private;
|
||||
struct snd_mask *maskp = hw_param_mask(params, rule->var);
|
||||
u64 format;
|
||||
|
||||
switch (uni->tdm_slot.slot_width) {
|
||||
case 16:
|
||||
format = SNDRV_PCM_FMTBIT_S16_LE;
|
||||
break;
|
||||
case 32:
|
||||
format = SNDRV_PCM_FMTBIT_S32_LE;
|
||||
break;
|
||||
default:
|
||||
dev_err(uni->dev, "format not supported: %d bits\n",
|
||||
uni->tdm_slot.slot_width);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
maskp->bits[0] &= (u_int32_t)format;
|
||||
maskp->bits[1] &= (u_int32_t)(format >> 32);
|
||||
/* clear remaining indexes */
|
||||
memset(maskp->bits + 2, 0, (SNDRV_MASK_MAX - 64) / 8);
|
||||
|
||||
if (!maskp->bits[0] && !maskp->bits[1])
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sti_uniperiph_get_tdm_word_pos(struct uniperif *uni,
|
||||
unsigned int *word_pos)
|
||||
{
|
||||
int slot_width = uni->tdm_slot.slot_width / 8;
|
||||
int slots_num = uni->tdm_slot.slots;
|
||||
unsigned int slots_mask = uni->tdm_slot.mask;
|
||||
int i, j, k;
|
||||
unsigned int word16_pos[4];
|
||||
|
||||
/* word16_pos:
|
||||
* word16_pos[0] = WORDX_LSB
|
||||
* word16_pos[1] = WORDX_MSB,
|
||||
* word16_pos[2] = WORDX+1_LSB
|
||||
* word16_pos[3] = WORDX+1_MSB
|
||||
*/
|
||||
|
||||
/* set unip word position */
|
||||
for (i = 0, j = 0, k = 0; (i < slots_num) && (k < WORD_MAX); i++) {
|
||||
if ((slots_mask >> i) & 0x01) {
|
||||
word16_pos[j] = i * slot_width;
|
||||
|
||||
if (slot_width == 4) {
|
||||
word16_pos[j + 1] = word16_pos[j] + 2;
|
||||
j++;
|
||||
}
|
||||
j++;
|
||||
|
||||
if (j > 3) {
|
||||
word_pos[k] = word16_pos[1] |
|
||||
(word16_pos[0] << 8) |
|
||||
(word16_pos[3] << 16) |
|
||||
(word16_pos[2] << 24);
|
||||
j = 0;
|
||||
k++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* sti_uniperiph_dai_create_ctrl
|
||||
* This function is used to create Ctrl associated to DAI but also pcm device.
|
||||
|
@ -45,10 +181,16 @@ int sti_uniperiph_dai_hw_params(struct snd_pcm_substream *substream,
|
|||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai);
|
||||
struct uniperif *uni = priv->dai_data.uni;
|
||||
struct snd_dmaengine_dai_dma_data *dma_data;
|
||||
int transfer_size;
|
||||
|
||||
transfer_size = params_channels(params) * UNIPERIF_FIFO_FRAMES;
|
||||
if (uni->info->type == SND_ST_UNIPERIF_TYPE_TDM)
|
||||
/* transfer size = user frame size (in 32-bits FIFO cell) */
|
||||
transfer_size = snd_soc_params_to_frame_size(params) / 32;
|
||||
else
|
||||
transfer_size = params_channels(params) * UNIPERIF_FIFO_FRAMES;
|
||||
|
||||
dma_data = snd_soc_dai_get_dma_data(dai, substream);
|
||||
dma_data->maxburst = transfer_size;
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
writel_relaxed((((value) & mask) << shift), ip->base + offset)
|
||||
|
||||
/*
|
||||
* AUD_UNIPERIF_SOFT_RST reg
|
||||
* UNIPERIF_SOFT_RST reg
|
||||
*/
|
||||
|
||||
#define UNIPERIF_SOFT_RST_OFFSET(ip) 0x0000
|
||||
|
@ -50,7 +50,7 @@
|
|||
UNIPERIF_SOFT_RST_SOFT_RST_MASK(ip))
|
||||
|
||||
/*
|
||||
* AUD_UNIPERIF_FIFO_DATA reg
|
||||
* UNIPERIF_FIFO_DATA reg
|
||||
*/
|
||||
|
||||
#define UNIPERIF_FIFO_DATA_OFFSET(ip) 0x0004
|
||||
|
@ -58,7 +58,7 @@
|
|||
writel_relaxed(value, ip->base + UNIPERIF_FIFO_DATA_OFFSET(ip))
|
||||
|
||||
/*
|
||||
* AUD_UNIPERIF_CHANNEL_STA_REGN reg
|
||||
* UNIPERIF_CHANNEL_STA_REGN reg
|
||||
*/
|
||||
|
||||
#define UNIPERIF_CHANNEL_STA_REGN(ip, n) (0x0060 + (4 * n))
|
||||
|
@ -105,7 +105,7 @@
|
|||
writel_relaxed(value, ip->base + UNIPERIF_CHANNEL_STA_REG5_OFFSET(ip))
|
||||
|
||||
/*
|
||||
* AUD_UNIPERIF_ITS reg
|
||||
* UNIPERIF_ITS reg
|
||||
*/
|
||||
|
||||
#define UNIPERIF_ITS_OFFSET(ip) 0x000C
|
||||
|
@ -143,7 +143,7 @@
|
|||
0 : (BIT(UNIPERIF_ITS_UNDERFLOW_REC_FAILED_SHIFT(ip))))
|
||||
|
||||
/*
|
||||
* AUD_UNIPERIF_ITS_BCLR reg
|
||||
* UNIPERIF_ITS_BCLR reg
|
||||
*/
|
||||
|
||||
/* FIFO_ERROR */
|
||||
|
@ -160,7 +160,7 @@
|
|||
writel_relaxed(value, ip->base + UNIPERIF_ITS_BCLR_OFFSET(ip))
|
||||
|
||||
/*
|
||||
* AUD_UNIPERIF_ITM reg
|
||||
* UNIPERIF_ITM reg
|
||||
*/
|
||||
|
||||
#define UNIPERIF_ITM_OFFSET(ip) 0x0018
|
||||
|
@ -188,7 +188,7 @@
|
|||
0 : (BIT(UNIPERIF_ITM_UNDERFLOW_REC_FAILED_SHIFT(ip))))
|
||||
|
||||
/*
|
||||
* AUD_UNIPERIF_ITM_BCLR reg
|
||||
* UNIPERIF_ITM_BCLR reg
|
||||
*/
|
||||
|
||||
#define UNIPERIF_ITM_BCLR_OFFSET(ip) 0x001c
|
||||
|
@ -213,7 +213,7 @@
|
|||
UNIPERIF_ITM_BCLR_DMA_ERROR_MASK(ip))
|
||||
|
||||
/*
|
||||
* AUD_UNIPERIF_ITM_BSET reg
|
||||
* UNIPERIF_ITM_BSET reg
|
||||
*/
|
||||
|
||||
#define UNIPERIF_ITM_BSET_OFFSET(ip) 0x0020
|
||||
|
@ -767,7 +767,7 @@
|
|||
SET_UNIPERIF_REG(ip, \
|
||||
UNIPERIF_CTRL_OFFSET(ip), \
|
||||
UNIPERIF_CTRL_READER_OUT_SEL_SHIFT(ip), \
|
||||
CORAUD_UNIPERIF_CTRL_READER_OUT_SEL_MASK(ip), 1)
|
||||
UNIPERIF_CTRL_READER_OUT_SEL_MASK(ip), 1)
|
||||
|
||||
/* UNDERFLOW_REC_WINDOW */
|
||||
#define UNIPERIF_CTRL_UNDERFLOW_REC_WINDOW_SHIFT(ip) 20
|
||||
|
@ -1046,7 +1046,7 @@
|
|||
UNIPERIF_STATUS_1_UNDERFLOW_DURATION_MASK(ip), value)
|
||||
|
||||
/*
|
||||
* AUD_UNIPERIF_CHANNEL_STA_REGN reg
|
||||
* UNIPERIF_CHANNEL_STA_REGN reg
|
||||
*/
|
||||
|
||||
#define UNIPERIF_CHANNEL_STA_REGN(ip, n) (0x0060 + (4 * n))
|
||||
|
@ -1057,7 +1057,7 @@
|
|||
UNIPERIF_CHANNEL_STA_REGN(ip, n))
|
||||
|
||||
/*
|
||||
* AUD_UNIPERIF_USER_VALIDITY reg
|
||||
* UNIPERIF_USER_VALIDITY reg
|
||||
*/
|
||||
|
||||
#define UNIPERIF_USER_VALIDITY_OFFSET(ip) 0x0090
|
||||
|
@ -1100,6 +1100,118 @@
|
|||
UNIPERIF_DBG_STANDBY_LEFT_SP_SHIFT(ip), \
|
||||
UNIPERIF_DBG_STANDBY_LEFT_SP_MASK(ip), value)
|
||||
|
||||
/*
|
||||
* UNIPERIF_TDM_ENABLE
|
||||
*/
|
||||
#define UNIPERIF_TDM_ENABLE_OFFSET(ip) 0x0118
|
||||
#define GET_UNIPERIF_TDM_ENABLE(ip) \
|
||||
readl_relaxed(ip->base + UNIPERIF_TDM_ENABLE_OFFSET(ip))
|
||||
#define SET_UNIPERIF_TDM_ENABLE(ip, value) \
|
||||
writel_relaxed(value, ip->base + UNIPERIF_TDM_ENABLE_OFFSET(ip))
|
||||
|
||||
/* TDM_ENABLE */
|
||||
#define UNIPERIF_TDM_ENABLE_EN_TDM_SHIFT(ip) 0x0
|
||||
#define UNIPERIF_TDM_ENABLE_EN_TDM_MASK(ip) 0x1
|
||||
#define GET_UNIPERIF_TDM_ENABLE_EN_TDM(ip) \
|
||||
GET_UNIPERIF_REG(ip, \
|
||||
UNIPERIF_TDM_ENABLE_OFFSET(ip), \
|
||||
UNIPERIF_TDM_ENABLE_EN_TDM_SHIFT(ip), \
|
||||
UNIPERIF_TDM_ENABLE_EN_TDM_MASK(ip))
|
||||
#define SET_UNIPERIF_TDM_ENABLE_TDM_ENABLE(ip) \
|
||||
SET_UNIPERIF_REG(ip, \
|
||||
UNIPERIF_TDM_ENABLE_OFFSET(ip), \
|
||||
UNIPERIF_TDM_ENABLE_EN_TDM_SHIFT(ip), \
|
||||
UNIPERIF_TDM_ENABLE_EN_TDM_MASK(ip), 1)
|
||||
#define SET_UNIPERIF_TDM_ENABLE_TDM_DISABLE(ip) \
|
||||
SET_UNIPERIF_REG(ip, \
|
||||
UNIPERIF_TDM_ENABLE_OFFSET(ip), \
|
||||
UNIPERIF_TDM_ENABLE_EN_TDM_SHIFT(ip), \
|
||||
UNIPERIF_TDM_ENABLE_EN_TDM_MASK(ip), 0)
|
||||
|
||||
/*
|
||||
* UNIPERIF_TDM_FS_REF_FREQ
|
||||
*/
|
||||
#define UNIPERIF_TDM_FS_REF_FREQ_OFFSET(ip) 0x011c
|
||||
#define GET_UNIPERIF_TDM_FS_REF_FREQ(ip) \
|
||||
readl_relaxed(ip->base + UNIPERIF_TDM_FS_REF_FREQ_OFFSET(ip))
|
||||
#define SET_UNIPERIF_TDM_FS_REF_FREQ(ip, value) \
|
||||
writel_relaxed(value, ip->base + \
|
||||
UNIPERIF_TDM_FS_REF_FREQ_OFFSET(ip))
|
||||
|
||||
/* REF_FREQ */
|
||||
#define UNIPERIF_TDM_FS_REF_FREQ_REF_FREQ_SHIFT(ip) 0x0
|
||||
#define VALUE_UNIPERIF_TDM_FS_REF_FREQ_8KHZ(ip) 0
|
||||
#define VALUE_UNIPERIF_TDM_FS_REF_FREQ_16KHZ(ip) 1
|
||||
#define VALUE_UNIPERIF_TDM_FS_REF_FREQ_32KHZ(ip) 2
|
||||
#define VALUE_UNIPERIF_TDM_FS_REF_FREQ_48KHZ(ip) 3
|
||||
#define UNIPERIF_TDM_FS_REF_FREQ_REF_FREQ_MASK(ip) 0x3
|
||||
#define GET_UNIPERIF_TDM_FS_REF_FREQ_REF_FREQ(ip) \
|
||||
GET_UNIPERIF_REG(ip, \
|
||||
UNIPERIF_TDM_FS_REF_FREQ_OFFSET(ip), \
|
||||
UNIPERIF_TDM_FS_REF_FREQ_REF_FREQ_SHIFT(ip), \
|
||||
UNIPERIF_TDM_FS_REF_FREQ_REF_FREQ_MASK(ip))
|
||||
#define SET_UNIPERIF_TDM_FS_REF_FREQ_8KHZ(ip) \
|
||||
SET_UNIPERIF_REG(ip, \
|
||||
UNIPERIF_TDM_FS_REF_FREQ_OFFSET(ip), \
|
||||
UNIPERIF_TDM_FS_REF_FREQ_REF_FREQ_SHIFT(ip), \
|
||||
UNIPERIF_TDM_FS_REF_FREQ_REF_FREQ_MASK(ip), \
|
||||
VALUE_UNIPERIF_TDM_FS_REF_FREQ_8KHZ(ip))
|
||||
#define SET_UNIPERIF_TDM_FS_REF_FREQ_16KHZ(ip) \
|
||||
SET_UNIPERIF_REG(ip, \
|
||||
UNIPERIF_TDM_FS_REF_FREQ_OFFSET(ip), \
|
||||
UNIPERIF_TDM_FS_REF_FREQ_REF_FREQ_SHIFT(ip), \
|
||||
UNIPERIF_TDM_FS_REF_FREQ_REF_FREQ_MASK(ip), \
|
||||
VALUE_UNIPERIF_TDM_FS_REF_FREQ_16KHZ(ip))
|
||||
#define SET_UNIPERIF_TDM_FS_REF_FREQ_32KHZ(ip) \
|
||||
SET_UNIPERIF_REG(ip, \
|
||||
UNIPERIF_TDM_FS_REF_FREQ_OFFSET(ip), \
|
||||
UNIPERIF_TDM_FS_REF_FREQ_REF_FREQ_SHIFT(ip), \
|
||||
UNIPERIF_TDM_FS_REF_FREQ_REF_FREQ_MASK(ip), \
|
||||
VALUE_UNIPERIF_TDM_FS_REF_FREQ_32KHZ(ip))
|
||||
#define SET_UNIPERIF_TDM_FS_REF_FREQ_48KHZ(ip) \
|
||||
SET_UNIPERIF_REG(ip, \
|
||||
UNIPERIF_TDM_FS_REF_FREQ_OFFSET(ip), \
|
||||
UNIPERIF_TDM_FS_REF_FREQ_REF_FREQ_SHIFT(ip), \
|
||||
UNIPERIF_TDM_FS_REF_FREQ_REF_FREQ_MASK(ip), \
|
||||
VALUE_UNIPERIF_TDM_FS_REF_FREQ_48KHZ(ip))
|
||||
|
||||
/*
|
||||
* UNIPERIF_TDM_FS_REF_DIV
|
||||
*/
|
||||
#define UNIPERIF_TDM_FS_REF_DIV_OFFSET(ip) 0x0120
|
||||
#define GET_UNIPERIF_TDM_FS_REF_DIV(ip) \
|
||||
readl_relaxed(ip->base + UNIPERIF_TDM_FS_REF_DIV_OFFSET(ip))
|
||||
#define SET_UNIPERIF_TDM_FS_REF_DIV(ip, value) \
|
||||
writel_relaxed(value, ip->base + \
|
||||
UNIPERIF_TDM_FS_REF_DIV_OFFSET(ip))
|
||||
|
||||
/* NUM_TIMESLOT */
|
||||
#define UNIPERIF_TDM_FS_REF_DIV_NUM_TIMESLOT_SHIFT(ip) 0x0
|
||||
#define UNIPERIF_TDM_FS_REF_DIV_NUM_TIMESLOT_MASK(ip) 0xff
|
||||
#define GET_UNIPERIF_TDM_FS_REF_DIV_NUM_TIMESLOT(ip) \
|
||||
GET_UNIPERIF_REG(ip, \
|
||||
UNIPERIF_TDM_FS_REF_DIV_OFFSET(ip), \
|
||||
UNIPERIF_TDM_FS_REF_DIV_NUM_TIMESLOT_SHIFT(ip), \
|
||||
UNIPERIF_TDM_FS_REF_DIV_NUM_TIMESLOT_MASK(ip))
|
||||
#define SET_UNIPERIF_TDM_FS_REF_DIV_NUM_TIMESLOT(ip, value) \
|
||||
SET_UNIPERIF_REG(ip, \
|
||||
UNIPERIF_TDM_FS_REF_DIV_OFFSET(ip), \
|
||||
UNIPERIF_TDM_FS_REF_DIV_NUM_TIMESLOT_SHIFT(ip), \
|
||||
UNIPERIF_TDM_FS_REF_DIV_NUM_TIMESLOT_MASK(ip), value)
|
||||
|
||||
/*
|
||||
* UNIPERIF_TDM_WORD_POS_X_Y
|
||||
* 32 bits of UNIPERIF_TDM_WORD_POS_X_Y register shall be set in 1 shot
|
||||
*/
|
||||
#define UNIPERIF_TDM_WORD_POS_1_2_OFFSET(ip) 0x013c
|
||||
#define UNIPERIF_TDM_WORD_POS_3_4_OFFSET(ip) 0x0140
|
||||
#define UNIPERIF_TDM_WORD_POS_5_6_OFFSET(ip) 0x0144
|
||||
#define UNIPERIF_TDM_WORD_POS_7_8_OFFSET(ip) 0x0148
|
||||
#define GET_UNIPERIF_TDM_WORD_POS(ip, words) \
|
||||
readl_relaxed(ip->base + UNIPERIF_TDM_WORD_POS_##words##_OFFSET(ip))
|
||||
#define SET_UNIPERIF_TDM_WORD_POS(ip, words, value) \
|
||||
writel_relaxed(value, ip->base + \
|
||||
UNIPERIF_TDM_WORD_POS_##words##_OFFSET(ip))
|
||||
/*
|
||||
* uniperipheral IP capabilities
|
||||
*/
|
||||
|
@ -1107,6 +1219,18 @@
|
|||
#define UNIPERIF_FIFO_SIZE 70 /* FIFO is 70 cells deep */
|
||||
#define UNIPERIF_FIFO_FRAMES 4 /* FDMA trigger limit in frames */
|
||||
|
||||
#define UNIPERIF_TYPE_IS_HDMI(p) \
|
||||
((p)->info->type == SND_ST_UNIPERIF_TYPE_HDMI)
|
||||
#define UNIPERIF_TYPE_IS_PCM(p) \
|
||||
((p)->info->type == SND_ST_UNIPERIF_TYPE_PCM)
|
||||
#define UNIPERIF_TYPE_IS_SPDIF(p) \
|
||||
((p)->info->type == SND_ST_UNIPERIF_TYPE_SPDIF)
|
||||
#define UNIPERIF_TYPE_IS_IEC958(p) \
|
||||
(UNIPERIF_TYPE_IS_HDMI(p) || \
|
||||
UNIPERIF_TYPE_IS_SPDIF(p))
|
||||
#define UNIPERIF_TYPE_IS_TDM(p) \
|
||||
((p)->info->type == SND_ST_UNIPERIF_TYPE_TDM)
|
||||
|
||||
/*
|
||||
* Uniperipheral IP revisions
|
||||
*/
|
||||
|
@ -1125,10 +1249,11 @@ enum uniperif_version {
|
|||
};
|
||||
|
||||
enum uniperif_type {
|
||||
SND_ST_UNIPERIF_PLAYER_TYPE_NONE,
|
||||
SND_ST_UNIPERIF_PLAYER_TYPE_HDMI,
|
||||
SND_ST_UNIPERIF_PLAYER_TYPE_PCM,
|
||||
SND_ST_UNIPERIF_PLAYER_TYPE_SPDIF
|
||||
SND_ST_UNIPERIF_TYPE_NONE,
|
||||
SND_ST_UNIPERIF_TYPE_HDMI,
|
||||
SND_ST_UNIPERIF_TYPE_PCM,
|
||||
SND_ST_UNIPERIF_TYPE_SPDIF,
|
||||
SND_ST_UNIPERIF_TYPE_TDM
|
||||
};
|
||||
|
||||
enum uniperif_state {
|
||||
|
@ -1145,9 +1270,17 @@ enum uniperif_iec958_encoding_mode {
|
|||
UNIPERIF_IEC958_ENCODING_MODE_ENCODED
|
||||
};
|
||||
|
||||
enum uniperif_word_pos {
|
||||
WORD_1_2,
|
||||
WORD_3_4,
|
||||
WORD_5_6,
|
||||
WORD_7_8,
|
||||
WORD_MAX
|
||||
};
|
||||
|
||||
struct uniperif_info {
|
||||
int id; /* instance value of the uniperipheral IP */
|
||||
enum uniperif_type player_type;
|
||||
enum uniperif_type type;
|
||||
int underflow_enabled; /* Underflow recovery mode */
|
||||
};
|
||||
|
||||
|
@ -1156,12 +1289,20 @@ struct uniperif_iec958_settings {
|
|||
struct snd_aes_iec958 iec958;
|
||||
};
|
||||
|
||||
struct dai_tdm_slot {
|
||||
unsigned int mask;
|
||||
int slots;
|
||||
int slot_width;
|
||||
unsigned int avail_slots;
|
||||
};
|
||||
|
||||
struct uniperif {
|
||||
/* System information */
|
||||
struct uniperif_info *info;
|
||||
struct device *dev;
|
||||
int ver; /* IP version, used by register access macros */
|
||||
struct regmap_field *clk_sel;
|
||||
struct regmap_field *valid_sel;
|
||||
|
||||
/* capabilities */
|
||||
const struct snd_pcm_hardware *hw;
|
||||
|
@ -1192,6 +1333,7 @@ struct uniperif {
|
|||
|
||||
/* dai properties */
|
||||
unsigned int daifmt;
|
||||
struct dai_tdm_slot tdm_slot;
|
||||
|
||||
/* DAI callbacks */
|
||||
const struct snd_soc_dai_ops *dai_ops;
|
||||
|
@ -1209,6 +1351,28 @@ struct sti_uniperiph_data {
|
|||
struct sti_uniperiph_dai dai_data;
|
||||
};
|
||||
|
||||
static const struct snd_pcm_hardware uni_tdm_hw = {
|
||||
.info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
|
||||
SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_MMAP |
|
||||
SNDRV_PCM_INFO_MMAP_VALID,
|
||||
|
||||
.formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S16_LE,
|
||||
|
||||
.rates = SNDRV_PCM_RATE_CONTINUOUS,
|
||||
.rate_min = 8000,
|
||||
.rate_max = 48000,
|
||||
|
||||
.channels_min = 1,
|
||||
.channels_max = 32,
|
||||
|
||||
.periods_min = 2,
|
||||
.periods_max = 10,
|
||||
|
||||
.period_bytes_min = 128,
|
||||
.period_bytes_max = 64 * PAGE_SIZE,
|
||||
.buffer_bytes_max = 256 * PAGE_SIZE
|
||||
};
|
||||
|
||||
/* uniperiph player*/
|
||||
int uni_player_init(struct platform_device *pdev,
|
||||
struct uniperif *uni_player);
|
||||
|
@ -1226,4 +1390,28 @@ int sti_uniperiph_dai_hw_params(struct snd_pcm_substream *substream,
|
|||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *dai);
|
||||
|
||||
static inline int sti_uniperiph_get_user_frame_size(
|
||||
struct snd_pcm_runtime *runtime)
|
||||
{
|
||||
return (runtime->channels * snd_pcm_format_width(runtime->format) / 8);
|
||||
}
|
||||
|
||||
static inline int sti_uniperiph_get_unip_tdm_frame_size(struct uniperif *uni)
|
||||
{
|
||||
return (uni->tdm_slot.slots * uni->tdm_slot.slot_width / 8);
|
||||
}
|
||||
|
||||
int sti_uniperiph_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
|
||||
unsigned int rx_mask, int slots,
|
||||
int slot_width);
|
||||
|
||||
int sti_uniperiph_get_tdm_word_pos(struct uniperif *uni,
|
||||
unsigned int *word_pos);
|
||||
|
||||
int sti_uniperiph_fix_tdm_chan(struct snd_pcm_hw_params *params,
|
||||
struct snd_pcm_hw_rule *rule);
|
||||
|
||||
int sti_uniperiph_fix_tdm_format(struct snd_pcm_hw_params *params,
|
||||
struct snd_pcm_hw_rule *rule);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -21,23 +21,14 @@
|
|||
|
||||
/* sys config registers definitions */
|
||||
#define SYS_CFG_AUDIO_GLUE 0xA4
|
||||
#define SYS_CFG_AUDI0_GLUE_PCM_CLKX 8
|
||||
|
||||
/*
|
||||
* Driver specific types.
|
||||
*/
|
||||
#define UNIPERIF_PLAYER_TYPE_IS_HDMI(p) \
|
||||
((p)->info->player_type == SND_ST_UNIPERIF_PLAYER_TYPE_HDMI)
|
||||
#define UNIPERIF_PLAYER_TYPE_IS_PCM(p) \
|
||||
((p)->info->player_type == SND_ST_UNIPERIF_PLAYER_TYPE_PCM)
|
||||
#define UNIPERIF_PLAYER_TYPE_IS_SPDIF(p) \
|
||||
((p)->info->player_type == SND_ST_UNIPERIF_PLAYER_TYPE_SPDIF)
|
||||
#define UNIPERIF_PLAYER_TYPE_IS_IEC958(p) \
|
||||
(UNIPERIF_PLAYER_TYPE_IS_HDMI(p) || \
|
||||
UNIPERIF_PLAYER_TYPE_IS_SPDIF(p))
|
||||
|
||||
#define UNIPERIF_PLAYER_CLK_ADJ_MIN -999999
|
||||
#define UNIPERIF_PLAYER_CLK_ADJ_MAX 1000000
|
||||
#define UNIPERIF_PLAYER_I2S_OUT 1 /* player id connected to I2S/TDM TX bus */
|
||||
|
||||
/*
|
||||
* Note: snd_pcm_hardware is linked to DMA controller but is declared here to
|
||||
|
@ -444,18 +435,11 @@ static int uni_player_prepare_pcm(struct uniperif *player,
|
|||
|
||||
/* Force slot width to 32 in I2S mode (HW constraint) */
|
||||
if ((player->daifmt & SND_SOC_DAIFMT_FORMAT_MASK) ==
|
||||
SND_SOC_DAIFMT_I2S) {
|
||||
SND_SOC_DAIFMT_I2S)
|
||||
slot_width = 32;
|
||||
} else {
|
||||
switch (runtime->format) {
|
||||
case SNDRV_PCM_FORMAT_S16_LE:
|
||||
slot_width = 16;
|
||||
break;
|
||||
default:
|
||||
slot_width = 32;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
slot_width = snd_pcm_format_width(runtime->format);
|
||||
|
||||
output_frame_size = slot_width * runtime->channels;
|
||||
|
||||
clk_div = player->mclk / runtime->rate;
|
||||
|
@ -530,7 +514,6 @@ static int uni_player_prepare_pcm(struct uniperif *player,
|
|||
SET_UNIPERIF_CONFIG_ONE_BIT_AUD_DISABLE(player);
|
||||
|
||||
SET_UNIPERIF_I2S_FMT_ORDER_MSB(player);
|
||||
SET_UNIPERIF_I2S_FMT_SCLK_EDGE_FALLING(player);
|
||||
|
||||
/* No iec958 formatting as outputting to DAC */
|
||||
SET_UNIPERIF_CTRL_SPDIF_FMT_OFF(player);
|
||||
|
@ -538,6 +521,55 @@ static int uni_player_prepare_pcm(struct uniperif *player,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int uni_player_prepare_tdm(struct uniperif *player,
|
||||
struct snd_pcm_runtime *runtime)
|
||||
{
|
||||
int tdm_frame_size; /* unip tdm frame size in bytes */
|
||||
int user_frame_size; /* user tdm frame size in bytes */
|
||||
/* default unip TDM_WORD_POS_X_Y */
|
||||
unsigned int word_pos[4] = {
|
||||
0x04060002, 0x0C0E080A, 0x14161012, 0x1C1E181A};
|
||||
int freq, ret;
|
||||
|
||||
tdm_frame_size =
|
||||
sti_uniperiph_get_unip_tdm_frame_size(player);
|
||||
user_frame_size =
|
||||
sti_uniperiph_get_user_frame_size(runtime);
|
||||
|
||||
/* fix 16/0 format */
|
||||
SET_UNIPERIF_CONFIG_MEM_FMT_16_0(player);
|
||||
SET_UNIPERIF_I2S_FMT_DATA_SIZE_32(player);
|
||||
|
||||
/* number of words inserted on the TDM line */
|
||||
SET_UNIPERIF_I2S_FMT_NUM_CH(player, user_frame_size / 4 / 2);
|
||||
|
||||
SET_UNIPERIF_I2S_FMT_ORDER_MSB(player);
|
||||
SET_UNIPERIF_I2S_FMT_ALIGN_LEFT(player);
|
||||
|
||||
/* Enable the tdm functionality */
|
||||
SET_UNIPERIF_TDM_ENABLE_TDM_ENABLE(player);
|
||||
|
||||
/* number of 8 bits timeslots avail in unip tdm frame */
|
||||
SET_UNIPERIF_TDM_FS_REF_DIV_NUM_TIMESLOT(player, tdm_frame_size);
|
||||
|
||||
/* set the timeslot allocation for words in FIFO */
|
||||
sti_uniperiph_get_tdm_word_pos(player, word_pos);
|
||||
SET_UNIPERIF_TDM_WORD_POS(player, 1_2, word_pos[WORD_1_2]);
|
||||
SET_UNIPERIF_TDM_WORD_POS(player, 3_4, word_pos[WORD_3_4]);
|
||||
SET_UNIPERIF_TDM_WORD_POS(player, 5_6, word_pos[WORD_5_6]);
|
||||
SET_UNIPERIF_TDM_WORD_POS(player, 7_8, word_pos[WORD_7_8]);
|
||||
|
||||
/* set unip clk rate (not done vai set_sysclk ops) */
|
||||
freq = runtime->rate * tdm_frame_size * 8;
|
||||
mutex_lock(&player->ctrl_lock);
|
||||
ret = uni_player_clk_set_rate(player, freq);
|
||||
if (!ret)
|
||||
player->mclk = freq;
|
||||
mutex_unlock(&player->ctrl_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* ALSA uniperipheral iec958 controls
|
||||
*/
|
||||
|
@ -668,11 +700,29 @@ static int uni_player_startup(struct snd_pcm_substream *substream,
|
|||
{
|
||||
struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai);
|
||||
struct uniperif *player = priv->dai_data.uni;
|
||||
int ret;
|
||||
|
||||
player->substream = substream;
|
||||
|
||||
player->clk_adj = 0;
|
||||
|
||||
return 0;
|
||||
if (!UNIPERIF_TYPE_IS_TDM(player))
|
||||
return 0;
|
||||
|
||||
/* refine hw constraint in tdm mode */
|
||||
ret = snd_pcm_hw_rule_add(substream->runtime, 0,
|
||||
SNDRV_PCM_HW_PARAM_CHANNELS,
|
||||
sti_uniperiph_fix_tdm_chan,
|
||||
player, SNDRV_PCM_HW_PARAM_CHANNELS,
|
||||
-1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return snd_pcm_hw_rule_add(substream->runtime, 0,
|
||||
SNDRV_PCM_HW_PARAM_FORMAT,
|
||||
sti_uniperiph_fix_tdm_format,
|
||||
player, SNDRV_PCM_HW_PARAM_FORMAT,
|
||||
-1);
|
||||
}
|
||||
|
||||
static int uni_player_set_sysclk(struct snd_soc_dai *dai, int clk_id,
|
||||
|
@ -682,7 +732,7 @@ static int uni_player_set_sysclk(struct snd_soc_dai *dai, int clk_id,
|
|||
struct uniperif *player = priv->dai_data.uni;
|
||||
int ret;
|
||||
|
||||
if (dir == SND_SOC_CLOCK_IN)
|
||||
if (UNIPERIF_TYPE_IS_TDM(player) || (dir == SND_SOC_CLOCK_IN))
|
||||
return 0;
|
||||
|
||||
if (clk_id != 0)
|
||||
|
@ -714,7 +764,13 @@ static int uni_player_prepare(struct snd_pcm_substream *substream,
|
|||
}
|
||||
|
||||
/* Calculate transfer size (in fifo cells and bytes) for frame count */
|
||||
transfer_size = runtime->channels * UNIPERIF_FIFO_FRAMES;
|
||||
if (player->info->type == SND_ST_UNIPERIF_TYPE_TDM) {
|
||||
/* transfer size = user frame size (in 32 bits FIFO cell) */
|
||||
transfer_size =
|
||||
sti_uniperiph_get_user_frame_size(runtime) / 4;
|
||||
} else {
|
||||
transfer_size = runtime->channels * UNIPERIF_FIFO_FRAMES;
|
||||
}
|
||||
|
||||
/* Calculate number of empty cells available before asserting DREQ */
|
||||
if (player->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0) {
|
||||
|
@ -738,16 +794,19 @@ static int uni_player_prepare(struct snd_pcm_substream *substream,
|
|||
SET_UNIPERIF_CONFIG_DMA_TRIG_LIMIT(player, trigger_limit);
|
||||
|
||||
/* Uniperipheral setup depends on player type */
|
||||
switch (player->info->player_type) {
|
||||
case SND_ST_UNIPERIF_PLAYER_TYPE_HDMI:
|
||||
switch (player->info->type) {
|
||||
case SND_ST_UNIPERIF_TYPE_HDMI:
|
||||
ret = uni_player_prepare_iec958(player, runtime);
|
||||
break;
|
||||
case SND_ST_UNIPERIF_PLAYER_TYPE_PCM:
|
||||
case SND_ST_UNIPERIF_TYPE_PCM:
|
||||
ret = uni_player_prepare_pcm(player, runtime);
|
||||
break;
|
||||
case SND_ST_UNIPERIF_PLAYER_TYPE_SPDIF:
|
||||
case SND_ST_UNIPERIF_TYPE_SPDIF:
|
||||
ret = uni_player_prepare_iec958(player, runtime);
|
||||
break;
|
||||
case SND_ST_UNIPERIF_TYPE_TDM:
|
||||
ret = uni_player_prepare_tdm(player, runtime);
|
||||
break;
|
||||
default:
|
||||
dev_err(player->dev, "invalid player type");
|
||||
return -EINVAL;
|
||||
|
@ -852,8 +911,8 @@ static int uni_player_start(struct uniperif *player)
|
|||
* will not take affect and hang the player.
|
||||
*/
|
||||
if (player->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0)
|
||||
if (UNIPERIF_PLAYER_TYPE_IS_IEC958(player))
|
||||
SET_UNIPERIF_CTRL_SPDIF_FMT_ON(player);
|
||||
if (UNIPERIF_TYPE_IS_IEC958(player))
|
||||
SET_UNIPERIF_CTRL_SPDIF_FMT_ON(player);
|
||||
|
||||
/* Force channel status update (no update if clk disable) */
|
||||
if (player->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0)
|
||||
|
@ -954,27 +1013,30 @@ static void uni_player_shutdown(struct snd_pcm_substream *substream,
|
|||
player->substream = NULL;
|
||||
}
|
||||
|
||||
static int uni_player_parse_dt_clk_glue(struct platform_device *pdev,
|
||||
struct uniperif *player)
|
||||
static int uni_player_parse_dt_audio_glue(struct platform_device *pdev,
|
||||
struct uniperif *player)
|
||||
{
|
||||
int bit_offset;
|
||||
struct device_node *node = pdev->dev.of_node;
|
||||
struct regmap *regmap;
|
||||
|
||||
bit_offset = SYS_CFG_AUDI0_GLUE_PCM_CLKX + player->info->id;
|
||||
struct reg_field regfield[2] = {
|
||||
/* PCM_CLK_SEL */
|
||||
REG_FIELD(SYS_CFG_AUDIO_GLUE,
|
||||
8 + player->info->id,
|
||||
8 + player->info->id),
|
||||
/* PCMP_VALID_SEL */
|
||||
REG_FIELD(SYS_CFG_AUDIO_GLUE, 0, 1)
|
||||
};
|
||||
|
||||
regmap = syscon_regmap_lookup_by_phandle(node, "st,syscfg");
|
||||
|
||||
if (regmap) {
|
||||
struct reg_field regfield =
|
||||
REG_FIELD(SYS_CFG_AUDIO_GLUE, bit_offset, bit_offset);
|
||||
|
||||
player->clk_sel = regmap_field_alloc(regmap, regfield);
|
||||
} else {
|
||||
if (!regmap) {
|
||||
dev_err(&pdev->dev, "sti-audio-clk-glue syscf not found\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
player->clk_sel = regmap_field_alloc(regmap, regfield[0]);
|
||||
player->valid_sel = regmap_field_alloc(regmap, regfield[1]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1012,19 +1074,21 @@ static int uni_player_parse_dt(struct platform_device *pdev,
|
|||
}
|
||||
|
||||
if (strcasecmp(mode, "hdmi") == 0)
|
||||
info->player_type = SND_ST_UNIPERIF_PLAYER_TYPE_HDMI;
|
||||
info->type = SND_ST_UNIPERIF_TYPE_HDMI;
|
||||
else if (strcasecmp(mode, "pcm") == 0)
|
||||
info->player_type = SND_ST_UNIPERIF_PLAYER_TYPE_PCM;
|
||||
info->type = SND_ST_UNIPERIF_TYPE_PCM;
|
||||
else if (strcasecmp(mode, "spdif") == 0)
|
||||
info->player_type = SND_ST_UNIPERIF_PLAYER_TYPE_SPDIF;
|
||||
info->type = SND_ST_UNIPERIF_TYPE_SPDIF;
|
||||
else if (strcasecmp(mode, "tdm") == 0)
|
||||
info->type = SND_ST_UNIPERIF_TYPE_TDM;
|
||||
else
|
||||
info->player_type = SND_ST_UNIPERIF_PLAYER_TYPE_NONE;
|
||||
info->type = SND_ST_UNIPERIF_TYPE_NONE;
|
||||
|
||||
/* Save the info structure */
|
||||
player->info = info;
|
||||
|
||||
/* Get the PCM_CLK_SEL bit from audio-glue-ctrl SoC register */
|
||||
if (uni_player_parse_dt_clk_glue(pdev, player))
|
||||
/* Get PCM_CLK_SEL & PCMP_VALID_SEL from audio-glue-ctrl SoC reg */
|
||||
if (uni_player_parse_dt_audio_glue(pdev, player))
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
|
@ -1037,7 +1101,8 @@ static const struct snd_soc_dai_ops uni_player_dai_ops = {
|
|||
.trigger = uni_player_trigger,
|
||||
.hw_params = sti_uniperiph_dai_hw_params,
|
||||
.set_fmt = sti_uniperiph_dai_set_fmt,
|
||||
.set_sysclk = uni_player_set_sysclk
|
||||
.set_sysclk = uni_player_set_sysclk,
|
||||
.set_tdm_slot = sti_uniperiph_set_tdm_slot
|
||||
};
|
||||
|
||||
int uni_player_init(struct platform_device *pdev,
|
||||
|
@ -1047,7 +1112,6 @@ int uni_player_init(struct platform_device *pdev,
|
|||
|
||||
player->dev = &pdev->dev;
|
||||
player->state = UNIPERIF_STATE_STOPPED;
|
||||
player->hw = &uni_player_pcm_hw;
|
||||
player->dai_ops = &uni_player_dai_ops;
|
||||
|
||||
ret = uni_player_parse_dt(pdev, player);
|
||||
|
@ -1057,6 +1121,11 @@ int uni_player_init(struct platform_device *pdev,
|
|||
return ret;
|
||||
}
|
||||
|
||||
if (UNIPERIF_TYPE_IS_TDM(player))
|
||||
player->hw = &uni_tdm_hw;
|
||||
else
|
||||
player->hw = &uni_player_pcm_hw;
|
||||
|
||||
/* Get uniperif resource */
|
||||
player->clk = of_clk_get(pdev->dev.of_node, 0);
|
||||
if (IS_ERR(player->clk))
|
||||
|
@ -1073,6 +1142,17 @@ int uni_player_init(struct platform_device *pdev,
|
|||
}
|
||||
}
|
||||
|
||||
/* connect to I2S/TDM TX bus */
|
||||
if (player->valid_sel &&
|
||||
(player->info->id == UNIPERIF_PLAYER_I2S_OUT)) {
|
||||
ret = regmap_field_write(player->valid_sel, player->info->id);
|
||||
if (ret) {
|
||||
dev_err(player->dev,
|
||||
"%s: unable to connect to tdm bus", __func__);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = devm_request_irq(&pdev->dev, player->irq,
|
||||
uni_player_irq_handler, IRQF_SHARED,
|
||||
dev_name(&pdev->dev), player);
|
||||
|
@ -1087,7 +1167,7 @@ int uni_player_init(struct platform_device *pdev,
|
|||
SET_UNIPERIF_CTRL_SPDIF_LAT_OFF(player);
|
||||
SET_UNIPERIF_CONFIG_IDLE_MOD_DISABLE(player);
|
||||
|
||||
if (UNIPERIF_PLAYER_TYPE_IS_IEC958(player)) {
|
||||
if (UNIPERIF_TYPE_IS_IEC958(player)) {
|
||||
/* Set default iec958 status bits */
|
||||
|
||||
/* Consumer, PCM, copyright, 2ch, mode 0 */
|
||||
|
|
|
@ -73,55 +73,10 @@ static irqreturn_t uni_reader_irq_handler(int irq, void *dev_id)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int uni_reader_prepare(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
static int uni_reader_prepare_pcm(struct snd_pcm_runtime *runtime,
|
||||
struct uniperif *reader)
|
||||
{
|
||||
struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai);
|
||||
struct uniperif *reader = priv->dai_data.uni;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
int transfer_size, trigger_limit;
|
||||
int slot_width;
|
||||
int count = 10;
|
||||
|
||||
/* The reader should be stopped */
|
||||
if (reader->state != UNIPERIF_STATE_STOPPED) {
|
||||
dev_err(reader->dev, "%s: invalid reader state %d", __func__,
|
||||
reader->state);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Calculate transfer size (in fifo cells and bytes) for frame count */
|
||||
transfer_size = runtime->channels * UNIPERIF_FIFO_FRAMES;
|
||||
|
||||
/* Calculate number of empty cells available before asserting DREQ */
|
||||
if (reader->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0)
|
||||
trigger_limit = UNIPERIF_FIFO_SIZE - transfer_size;
|
||||
else
|
||||
/*
|
||||
* Since SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0
|
||||
* FDMA_TRIGGER_LIMIT also controls when the state switches
|
||||
* from OFF or STANDBY to AUDIO DATA.
|
||||
*/
|
||||
trigger_limit = transfer_size;
|
||||
|
||||
/* Trigger limit must be an even number */
|
||||
if ((!trigger_limit % 2) ||
|
||||
(trigger_limit != 1 && transfer_size % 2) ||
|
||||
(trigger_limit > UNIPERIF_CONFIG_DMA_TRIG_LIMIT_MASK(reader))) {
|
||||
dev_err(reader->dev, "invalid trigger limit %d", trigger_limit);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
SET_UNIPERIF_CONFIG_DMA_TRIG_LIMIT(reader, trigger_limit);
|
||||
|
||||
switch (reader->daifmt & SND_SOC_DAIFMT_INV_MASK) {
|
||||
case SND_SOC_DAIFMT_IB_IF:
|
||||
case SND_SOC_DAIFMT_NB_IF:
|
||||
SET_UNIPERIF_I2S_FMT_LR_POL_HIG(reader);
|
||||
break;
|
||||
default:
|
||||
SET_UNIPERIF_I2S_FMT_LR_POL_LOW(reader);
|
||||
}
|
||||
|
||||
/* Force slot width to 32 in I2S mode */
|
||||
if ((reader->daifmt & SND_SOC_DAIFMT_FORMAT_MASK)
|
||||
|
@ -173,6 +128,109 @@ static int uni_reader_prepare(struct snd_pcm_substream *substream,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Number of channels must be even */
|
||||
if ((runtime->channels % 2) || (runtime->channels < 2) ||
|
||||
(runtime->channels > 10)) {
|
||||
dev_err(reader->dev, "%s: invalid nb of channels", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
SET_UNIPERIF_I2S_FMT_NUM_CH(reader, runtime->channels / 2);
|
||||
SET_UNIPERIF_I2S_FMT_ORDER_MSB(reader);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int uni_reader_prepare_tdm(struct snd_pcm_runtime *runtime,
|
||||
struct uniperif *reader)
|
||||
{
|
||||
int frame_size; /* user tdm frame size in bytes */
|
||||
/* default unip TDM_WORD_POS_X_Y */
|
||||
unsigned int word_pos[4] = {
|
||||
0x04060002, 0x0C0E080A, 0x14161012, 0x1C1E181A};
|
||||
|
||||
frame_size = sti_uniperiph_get_user_frame_size(runtime);
|
||||
|
||||
/* fix 16/0 format */
|
||||
SET_UNIPERIF_CONFIG_MEM_FMT_16_0(reader);
|
||||
SET_UNIPERIF_I2S_FMT_DATA_SIZE_32(reader);
|
||||
|
||||
/* number of words inserted on the TDM line */
|
||||
SET_UNIPERIF_I2S_FMT_NUM_CH(reader, frame_size / 4 / 2);
|
||||
|
||||
SET_UNIPERIF_I2S_FMT_ORDER_MSB(reader);
|
||||
SET_UNIPERIF_I2S_FMT_ALIGN_LEFT(reader);
|
||||
SET_UNIPERIF_TDM_ENABLE_TDM_ENABLE(reader);
|
||||
|
||||
/*
|
||||
* set the timeslots allocation for words in FIFO
|
||||
*
|
||||
* HW bug: (LSB word < MSB word) => this config is not possible
|
||||
* So if we want (LSB word < MSB) word, then it shall be
|
||||
* handled by user
|
||||
*/
|
||||
sti_uniperiph_get_tdm_word_pos(reader, word_pos);
|
||||
SET_UNIPERIF_TDM_WORD_POS(reader, 1_2, word_pos[WORD_1_2]);
|
||||
SET_UNIPERIF_TDM_WORD_POS(reader, 3_4, word_pos[WORD_3_4]);
|
||||
SET_UNIPERIF_TDM_WORD_POS(reader, 5_6, word_pos[WORD_5_6]);
|
||||
SET_UNIPERIF_TDM_WORD_POS(reader, 7_8, word_pos[WORD_7_8]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int uni_reader_prepare(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai);
|
||||
struct uniperif *reader = priv->dai_data.uni;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
int transfer_size, trigger_limit, ret;
|
||||
int count = 10;
|
||||
|
||||
/* The reader should be stopped */
|
||||
if (reader->state != UNIPERIF_STATE_STOPPED) {
|
||||
dev_err(reader->dev, "%s: invalid reader state %d", __func__,
|
||||
reader->state);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Calculate transfer size (in fifo cells and bytes) for frame count */
|
||||
if (reader->info->type == SND_ST_UNIPERIF_TYPE_TDM) {
|
||||
/* transfer size = unip frame size (in 32 bits FIFO cell) */
|
||||
transfer_size =
|
||||
sti_uniperiph_get_user_frame_size(runtime) / 4;
|
||||
} else {
|
||||
transfer_size = runtime->channels * UNIPERIF_FIFO_FRAMES;
|
||||
}
|
||||
|
||||
/* Calculate number of empty cells available before asserting DREQ */
|
||||
if (reader->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0)
|
||||
trigger_limit = UNIPERIF_FIFO_SIZE - transfer_size;
|
||||
else
|
||||
/*
|
||||
* Since SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0
|
||||
* FDMA_TRIGGER_LIMIT also controls when the state switches
|
||||
* from OFF or STANDBY to AUDIO DATA.
|
||||
*/
|
||||
trigger_limit = transfer_size;
|
||||
|
||||
/* Trigger limit must be an even number */
|
||||
if ((!trigger_limit % 2) ||
|
||||
(trigger_limit != 1 && transfer_size % 2) ||
|
||||
(trigger_limit > UNIPERIF_CONFIG_DMA_TRIG_LIMIT_MASK(reader))) {
|
||||
dev_err(reader->dev, "invalid trigger limit %d", trigger_limit);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
SET_UNIPERIF_CONFIG_DMA_TRIG_LIMIT(reader, trigger_limit);
|
||||
|
||||
if (UNIPERIF_TYPE_IS_TDM(reader))
|
||||
ret = uni_reader_prepare_tdm(runtime, reader);
|
||||
else
|
||||
ret = uni_reader_prepare_pcm(runtime, reader);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
switch (reader->daifmt & SND_SOC_DAIFMT_FORMAT_MASK) {
|
||||
case SND_SOC_DAIFMT_I2S:
|
||||
SET_UNIPERIF_I2S_FMT_ALIGN_LEFT(reader);
|
||||
|
@ -191,21 +249,26 @@ static int uni_reader_prepare(struct snd_pcm_substream *substream,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
SET_UNIPERIF_I2S_FMT_ORDER_MSB(reader);
|
||||
|
||||
/* Data clocking (changing) on the rising edge */
|
||||
SET_UNIPERIF_I2S_FMT_SCLK_EDGE_RISING(reader);
|
||||
|
||||
/* Number of channels must be even */
|
||||
|
||||
if ((runtime->channels % 2) || (runtime->channels < 2) ||
|
||||
(runtime->channels > 10)) {
|
||||
dev_err(reader->dev, "%s: invalid nb of channels", __func__);
|
||||
return -EINVAL;
|
||||
/* Data clocking (changing) on the rising/falling edge */
|
||||
switch (reader->daifmt & SND_SOC_DAIFMT_INV_MASK) {
|
||||
case SND_SOC_DAIFMT_NB_NF:
|
||||
SET_UNIPERIF_I2S_FMT_LR_POL_LOW(reader);
|
||||
SET_UNIPERIF_I2S_FMT_SCLK_EDGE_RISING(reader);
|
||||
break;
|
||||
case SND_SOC_DAIFMT_NB_IF:
|
||||
SET_UNIPERIF_I2S_FMT_LR_POL_HIG(reader);
|
||||
SET_UNIPERIF_I2S_FMT_SCLK_EDGE_RISING(reader);
|
||||
break;
|
||||
case SND_SOC_DAIFMT_IB_NF:
|
||||
SET_UNIPERIF_I2S_FMT_LR_POL_LOW(reader);
|
||||
SET_UNIPERIF_I2S_FMT_SCLK_EDGE_FALLING(reader);
|
||||
break;
|
||||
case SND_SOC_DAIFMT_IB_IF:
|
||||
SET_UNIPERIF_I2S_FMT_LR_POL_HIG(reader);
|
||||
SET_UNIPERIF_I2S_FMT_SCLK_EDGE_FALLING(reader);
|
||||
break;
|
||||
}
|
||||
|
||||
SET_UNIPERIF_I2S_FMT_NUM_CH(reader, runtime->channels / 2);
|
||||
|
||||
/* Clear any pending interrupts */
|
||||
SET_UNIPERIF_ITS_BCLR(reader, GET_UNIPERIF_ITS(reader));
|
||||
|
||||
|
@ -293,6 +356,32 @@ static int uni_reader_trigger(struct snd_pcm_substream *substream,
|
|||
}
|
||||
}
|
||||
|
||||
static int uni_reader_startup(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai);
|
||||
struct uniperif *reader = priv->dai_data.uni;
|
||||
int ret;
|
||||
|
||||
if (!UNIPERIF_TYPE_IS_TDM(reader))
|
||||
return 0;
|
||||
|
||||
/* refine hw constraint in tdm mode */
|
||||
ret = snd_pcm_hw_rule_add(substream->runtime, 0,
|
||||
SNDRV_PCM_HW_PARAM_CHANNELS,
|
||||
sti_uniperiph_fix_tdm_chan,
|
||||
reader, SNDRV_PCM_HW_PARAM_CHANNELS,
|
||||
-1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return snd_pcm_hw_rule_add(substream->runtime, 0,
|
||||
SNDRV_PCM_HW_PARAM_FORMAT,
|
||||
sti_uniperiph_fix_tdm_format,
|
||||
reader, SNDRV_PCM_HW_PARAM_FORMAT,
|
||||
-1);
|
||||
}
|
||||
|
||||
static void uni_reader_shutdown(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
|
@ -310,6 +399,7 @@ static int uni_reader_parse_dt(struct platform_device *pdev,
|
|||
{
|
||||
struct uniperif_info *info;
|
||||
struct device_node *node = pdev->dev.of_node;
|
||||
const char *mode;
|
||||
|
||||
/* Allocate memory for the info structure */
|
||||
info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
|
||||
|
@ -322,6 +412,17 @@ static int uni_reader_parse_dt(struct platform_device *pdev,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Read the device mode property */
|
||||
if (of_property_read_string(node, "st,mode", &mode)) {
|
||||
dev_err(&pdev->dev, "uniperipheral mode not defined");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (strcasecmp(mode, "tdm") == 0)
|
||||
info->type = SND_ST_UNIPERIF_TYPE_TDM;
|
||||
else
|
||||
info->type = SND_ST_UNIPERIF_TYPE_PCM;
|
||||
|
||||
/* Save the info structure */
|
||||
reader->info = info;
|
||||
|
||||
|
@ -329,11 +430,13 @@ static int uni_reader_parse_dt(struct platform_device *pdev,
|
|||
}
|
||||
|
||||
static const struct snd_soc_dai_ops uni_reader_dai_ops = {
|
||||
.startup = uni_reader_startup,
|
||||
.shutdown = uni_reader_shutdown,
|
||||
.prepare = uni_reader_prepare,
|
||||
.trigger = uni_reader_trigger,
|
||||
.hw_params = sti_uniperiph_dai_hw_params,
|
||||
.set_fmt = sti_uniperiph_dai_set_fmt,
|
||||
.set_tdm_slot = sti_uniperiph_set_tdm_slot
|
||||
};
|
||||
|
||||
int uni_reader_init(struct platform_device *pdev,
|
||||
|
@ -343,7 +446,6 @@ int uni_reader_init(struct platform_device *pdev,
|
|||
|
||||
reader->dev = &pdev->dev;
|
||||
reader->state = UNIPERIF_STATE_STOPPED;
|
||||
reader->hw = &uni_reader_pcm_hw;
|
||||
reader->dai_ops = &uni_reader_dai_ops;
|
||||
|
||||
ret = uni_reader_parse_dt(pdev, reader);
|
||||
|
@ -352,6 +454,11 @@ int uni_reader_init(struct platform_device *pdev,
|
|||
return ret;
|
||||
}
|
||||
|
||||
if (UNIPERIF_TYPE_IS_TDM(reader))
|
||||
reader->hw = &uni_tdm_hw;
|
||||
else
|
||||
reader->hw = &uni_reader_pcm_hw;
|
||||
|
||||
ret = devm_request_irq(&pdev->dev, reader->irq,
|
||||
uni_reader_irq_handler, IRQF_SHARED,
|
||||
dev_name(&pdev->dev), reader);
|
||||
|
|
Loading…
Reference in a new issue