ASoC: Further updates for v4.2

There's a bunch of additional updates and fixes that came in since my
 orignal pull request here, including DT support for rt5645 and fairly
 large serieses of cleanups and improvements to tas2552 and rcar.
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQEcBAABAgAGBQJVh9RgAAoJECTWi3JdVIfQZjYH/3t48LgdB5dxKxIhn70kiaUx
 DqXCIkFOvkum1KUU3VTgcCZAT/dOEvbAY2S8PtxKbPRyRpmY1CqyXDEL7fXICl+y
 kyDBhZYKdzVlf+LBb4TIOibaRONQqJNATRWFN8bJu0L9wzqo2jUjZSYqpJxuvnYD
 U9T4P0dNbIN35ioVrK/0QPt6V2bCPs/qw3UwXmojl2T/4JDATdE+2yJO/SXzdEdR
 qq5aRtS+Ak6yx5DpWw/QBkt85NrbuwyoeYRNQuRXCOAWC8SPmHB/H+aNsvMNAcP1
 UZuRVtxPXjkFp6OVDJ4rIOg8hVb0cfXqUfnj+syEpZxtmRcS7Wwi1Y2M3BJMV1A=
 =H6q+
 -----END PGP SIGNATURE-----

Merge tag 'asoc-v4.2-2' into asoc-next

ASoC: Further updates for v4.2

There's a bunch of additional updates and fixes that came in since my
orignal pull request here, including DT support for rt5645 and fairly
large serieses of cleanups and improvements to tas2552 and rcar.

# gpg: Signature made Mon 22 Jun 2015 10:24:48 BST using RSA key ID 5D5487D0
# gpg: Oops: keyid_from_fingerprint: no pubkey
# gpg: Good signature from "Mark Brown <broonie@sirena.org.uk>"
# gpg:                 aka "Mark Brown <broonie@debian.org>"
# gpg:                 aka "Mark Brown <broonie@kernel.org>"
# gpg:                 aka "Mark Brown <broonie@tardis.ed.ac.uk>"
# gpg:                 aka "Mark Brown <broonie@linaro.org>"
# gpg:                 aka "Mark Brown <Mark.Brown@linaro.org>"
This commit is contained in:
Mark Brown 2015-06-22 11:19:39 +01:00
commit 25305dcf5d
59 changed files with 3720 additions and 898 deletions

View File

@ -0,0 +1,13 @@
MT8173 with MAX98090 CODEC
Required properties:
- compatible : "mediatek,mt8173-max98090"
- mediatek,audio-codec: the phandle of the MAX98090 audio codec
Example:
sound {
compatible = "mediatek,mt8173-max98090";
mediatek,audio-codec = <&max98090>;
};

View File

@ -0,0 +1,13 @@
MT8173 with RT5650 RT5676 CODECS
Required properties:
- compatible : "mediatek,mt8173-rt5650-rt5676"
- mediatek,audio-codec: the phandles of rt5650 and rt5676 codecs
Example:
sound {
compatible = "mediatek,mt8173-rt5650-rt5676";
mediatek,audio-codec = <&rt5650 &rt5676>;
};

View File

@ -0,0 +1,45 @@
Mediatek AFE PCM controller
Required properties:
- compatible = "mediatek,mt8173-afe-pcm";
- reg: register location and size
- interrupts: Should contain AFE interrupt
- clock-names: should have these clock names:
"infra_sys_audio_clk",
"top_pdn_audio",
"top_pdn_aud_intbus",
"bck0",
"bck1",
"i2s0_m",
"i2s1_m",
"i2s2_m",
"i2s3_m",
"i2s3_b";
Example:
afe: mt8173-afe-pcm@11220000 {
compatible = "mediatek,mt8173-afe-pcm";
reg = <0 0x11220000 0 0x1000>;
interrupts = <GIC_SPI 134 IRQ_TYPE_EDGE_FALLING>;
clocks = <&infracfg INFRA_AUDIO>,
<&topckgen TOP_AUDIO_SEL>,
<&topckgen TOP_AUD_INTBUS_SEL>,
<&topckgen TOP_APLL1_DIV0>,
<&topckgen TOP_APLL2_DIV0>,
<&topckgen TOP_I2S0_M_CK_SEL>,
<&topckgen TOP_I2S1_M_CK_SEL>,
<&topckgen TOP_I2S2_M_CK_SEL>,
<&topckgen TOP_I2S3_M_CK_SEL>,
<&topckgen TOP_I2S3_B_CK_SEL>;
clock-names = "infra_sys_audio_clk",
"top_pdn_audio",
"top_pdn_aud_intbus",
"bck0",
"bck1",
"i2s0_m",
"i2s1_m",
"i2s2_m",
"i2s3_m",
"i2s3_b";
};

View File

@ -0,0 +1,60 @@
* Qualcomm Technologies APQ8016 SBC ASoC machine driver
This node models the Qualcomm Technologies APQ8016 SBC ASoC machine driver
Required properties:
- compatible : "qcom,apq8016-sbc-sndcard"
- pinctrl-N : One property must exist for each entry in
pinctrl-names. See ../pinctrl/pinctrl-bindings.txt
for details of the property values.
- pinctrl-names : Must contain a "default" entry.
- reg : Must contain an address for each entry in reg-names.
- reg-names : A list which must include the following entries:
* "mic-iomux"
* "spkr-iomux"
- qcom,model : Name of the sound card.
Dai-link subnode properties and subnodes:
Required dai-link subnodes:
- cpu : CPU sub-node
- codec : CODEC sub-node
Required CPU/CODEC subnodes properties:
-link-name : Name of the dai link.
-sound-dai : phandle and port of CPU/CODEC
-capture-dai : phandle and port of CPU/CODEC
Example:
sound: sound {
compatible = "qcom,apq8016-sbc-sndcard";
reg = <0x07702000 0x4>, <0x07702004 0x4>;
reg-names = "mic-iomux", "spkr-iomux";
qcom,model = "DB410c";
/* I2S - Internal codec */
internal-dai-link@0 {
cpu { /* PRIMARY */
sound-dai = <&lpass MI2S_PRIMARY>;
};
codec {
sound-dai = <&wcd_codec 0>;
};
};
/* External Primary or External Secondary -ADV7533 HDMI */
external-dai-link@0 {
link-name = "ADV7533";
cpu { /* QUAT */
sound-dai = <&lpass MI2S_QUATERNARY>;
};
codec {
sound-dai = <&adv_bridge 0>;
};
};
};

View File

@ -0,0 +1,72 @@
RT5650/RT5645 audio CODEC
This device supports I2C only.
Required properties:
- compatible : One of "realtek,rt5645" or "realtek,rt5650".
- reg : The I2C address of the device.
- interrupts : The CODEC's interrupt output.
Optional properties:
- hp-detect-gpios:
a GPIO spec for the external headphone detect pin. If jd-mode = 0,
we will get the JD status by getting the value of hp-detect-gpios.
- realtek,in2-differential
Boolean. Indicate MIC2 input are differential, rather than single-ended.
- realtek,dmic1-data-pin
0: dmic1 is not used
1: using IN2P pin as dmic1 data pin
2: using GPIO6 pin as dmic1 data pin
3: using GPIO10 pin as dmic1 data pin
4: using GPIO12 pin as dmic1 data pin
- realtek,dmic2-data-pin
0: dmic2 is not used
1: using IN2N pin as dmic2 data pin
2: using GPIO5 pin as dmic2 data pin
3: using GPIO11 pin as dmic2 data pin
-- realtek,jd-mode : The JD mode of rt5645/rt5650
0 : rt5645/rt5650 JD function is not used
1 : Mode-0 (VDD=3.3V), two port jack detection
2 : Mode-1 (VDD=3.3V), one port jack detection
3 : Mode-2 (VDD=1.8V), one port jack detection
Pins on the device (for linking into audio routes) for RT5645/RT5650:
* DMIC L1
* DMIC R1
* DMIC L2
* DMIC R2
* IN1P
* IN1N
* IN2P
* IN2N
* Haptic Generator
* HPOL
* HPOR
* LOUTL
* LOUTR
* PDM1L
* PDM1R
* SPOL
* SPOR
Example:
codec: rt5650@1a {
compatible = "realtek,rt5650";
reg = <0x1a>;
hp-detect-gpios = <&gpio 19 0>;
interrupt-parent = <&gpio>;
interrupts = <7 IRQ_TYPE_EDGE_FALLING>;
realtek,dmic-en = "true";
realtek,en-jd-func = "true";
realtek,jd-mode = <3>;
};

View File

@ -16,7 +16,8 @@ Optional properties:
connection's sink, the second being the connection's
source.
- simple-audio-card,mclk-fs : Multiplication factor between stream rate and codec
mclk.
mclk. When defined, mclk-fs property defined in
dai-link sub nodes are ignored.
- simple-audio-card,hp-det-gpio : Reference to GPIO that signals when
headphones are attached.
- simple-audio-card,mic-det-gpio : Reference to GPIO that signals when
@ -55,6 +56,9 @@ Optional dai-link subnode properties:
dai-link uses bit clock inversion.
- frame-inversion : bool property. Add this if the
dai-link uses frame clock inversion.
- mclk-fs : Multiplication factor between stream
rate and codec mclk, applied only for
the dai-link.
For backward compatibility the frame-master and bitclock-master
properties can be used as booleans in codec subnode to indicate if the

View File

@ -14,6 +14,12 @@ Required properties:
Optional properties:
- enable-gpio - gpio pin to enable/disable the device
tas2552 can receive it's reference clock via MCLK, BCLK, IVCLKIN pin or use the
internal 1.8MHz. This CLKIN is used by the PLL. In addition to PLL, the PDM
reference clock is also selectable: PLL, IVCLKIN, BCLK or MCLK.
For system integration the dt-bindings/sound/tas2552.h header file provides
defined values to selct and configure the PLL and PDM reference clocks.
Example:
tas2552: tas2552@41 {

View File

@ -20,9 +20,6 @@ struct rt5645_platform_data {
unsigned int dmic2_data_pin;
/* 0 = IN2P; 1 = GPIO6; 2 = GPIO10; 3 = GPIO12 */
unsigned int hp_det_gpio;
bool gpio_hp_det_active_high;
unsigned int jd_mode;
};

View File

@ -45,6 +45,7 @@ source "sound/soc/nuc900/Kconfig"
source "sound/soc/omap/Kconfig"
source "sound/soc/kirkwood/Kconfig"
source "sound/soc/intel/Kconfig"
source "sound/soc/mediatek/Kconfig"
source "sound/soc/mxs/Kconfig"
source "sound/soc/pxa/Kconfig"
source "sound/soc/qcom/Kconfig"

View File

@ -24,6 +24,7 @@ obj-$(CONFIG_SND_SOC) += dwc/
obj-$(CONFIG_SND_SOC) += fsl/
obj-$(CONFIG_SND_SOC) += jz4740/
obj-$(CONFIG_SND_SOC) += intel/
obj-$(CONFIG_SND_SOC) += mediatek/
obj-$(CONFIG_SND_SOC) += mxs/
obj-$(CONFIG_SND_SOC) += nuc900/
obj-$(CONFIG_SND_SOC) += omap/

View File

@ -9,21 +9,32 @@ config SND_ATMEL_SOC
if SND_ATMEL_SOC
config SND_ATMEL_SOC_PDC
bool
tristate
default m if SND_ATMEL_SOC_SSC_PDC=m && SND_ATMEL_SOC_SSC=m
default y if SND_ATMEL_SOC_SSC_PDC=y || (SND_ATMEL_SOC_SSC_PDC=m && SND_ATMEL_SOC_SSC=y)
config SND_ATMEL_SOC_SSC_PDC
tristate
config SND_ATMEL_SOC_DMA
bool
tristate
select SND_SOC_GENERIC_DMAENGINE_PCM
default m if SND_ATMEL_SOC_SSC_DMA=m && SND_ATMEL_SOC_SSC=m
default y if SND_ATMEL_SOC_SSC_DMA=y || (SND_ATMEL_SOC_SSC_DMA=m && SND_ATMEL_SOC_SSC=y)
config SND_ATMEL_SOC_SSC_DMA
tristate
config SND_ATMEL_SOC_SSC
tristate
default y if SND_ATMEL_SOC_SSC_DMA=y || SND_ATMEL_SOC_SSC_PDC=y
default m if SND_ATMEL_SOC_SSC_DMA=m || SND_ATMEL_SOC_SSC_PDC=m
config SND_AT91_SOC_SAM9G20_WM8731
tristate "SoC Audio support for WM8731-based At91sam9g20 evaluation board"
depends on ARCH_AT91 || COMPILE_TEST
depends on ATMEL_SSC && SND_SOC_I2C_AND_SPI
select SND_ATMEL_SOC_PDC
select SND_ATMEL_SOC_SSC
select SND_ATMEL_SOC_SSC_PDC
select SND_SOC_WM8731
help
Say Y if you want to add support for SoC audio on WM8731-based
@ -33,8 +44,7 @@ config SND_ATMEL_SOC_WM8904
tristate "Atmel ASoC driver for boards using WM8904 codec"
depends on ARCH_AT91 || COMPILE_TEST
depends on ATMEL_SSC && I2C
select SND_ATMEL_SOC_SSC
select SND_ATMEL_SOC_DMA
select SND_ATMEL_SOC_SSC_DMA
select SND_SOC_WM8904
help
Say Y if you want to add support for Atmel ASoC driver for boards using
@ -44,8 +54,7 @@ config SND_AT91_SOC_SAM9X5_WM8731
tristate "SoC Audio support for WM8731-based at91sam9x5 board"
depends on ARCH_AT91 || COMPILE_TEST
depends on ATMEL_SSC && SND_SOC_I2C_AND_SPI
select SND_ATMEL_SOC_SSC
select SND_ATMEL_SOC_DMA
select SND_ATMEL_SOC_SSC_DMA
select SND_SOC_WM8731
help
Say Y if you want to add support for audio SoC on an

View File

@ -1,8 +1,10 @@
# AT91 Platform Support
snd-soc-atmel-pcm-$(CONFIG_SND_ATMEL_SOC_PDC) := atmel-pcm-pdc.o
snd-soc-atmel-pcm-$(CONFIG_SND_ATMEL_SOC_DMA) += atmel-pcm-dma.o
snd-soc-atmel_ssc_dai-objs := atmel_ssc_dai.o $(snd-soc-atmel-pcm-y)
snd-soc-atmel-pcm-pdc-objs := atmel-pcm-pdc.o
snd-soc-atmel-pcm-dma-objs := atmel-pcm-dma.o
snd-soc-atmel_ssc_dai-objs := atmel_ssc_dai.o
obj-$(CONFIG_SND_ATMEL_SOC_PDC) += snd-soc-atmel-pcm-pdc.o
obj-$(CONFIG_SND_ATMEL_SOC_DMA) += snd-soc-atmel-pcm-dma.o
obj-$(CONFIG_SND_ATMEL_SOC_SSC) += snd-soc-atmel_ssc_dai.o
# AT91 Machine Support

View File

@ -509,6 +509,11 @@ config SND_SOC_RL6231
default m if SND_SOC_RT5670=m
default m if SND_SOC_RT5677=m
config SND_SOC_RL6347A
tristate
default y if SND_SOC_RT286=y
default m if SND_SOC_RT286=m
config SND_SOC_RT286
tristate
depends on I2C

View File

@ -77,6 +77,7 @@ snd-soc-pcm512x-objs := pcm512x.o
snd-soc-pcm512x-i2c-objs := pcm512x-i2c.o
snd-soc-pcm512x-spi-objs := pcm512x-spi.o
snd-soc-rl6231-objs := rl6231.o
snd-soc-rl6347a-objs := rl6347a.o
snd-soc-rt286-objs := rt286.o
snd-soc-rt5631-objs := rt5631.o
snd-soc-rt5640-objs := rt5640.o
@ -263,6 +264,7 @@ obj-$(CONFIG_SND_SOC_PCM512x) += snd-soc-pcm512x.o
obj-$(CONFIG_SND_SOC_PCM512x_I2C) += snd-soc-pcm512x-i2c.o
obj-$(CONFIG_SND_SOC_PCM512x_SPI) += snd-soc-pcm512x-spi.o
obj-$(CONFIG_SND_SOC_RL6231) += snd-soc-rl6231.o
obj-$(CONFIG_SND_SOC_RL6347A) += snd-soc-rl6347a.o
obj-$(CONFIG_SND_SOC_RT286) += snd-soc-rt286.o
obj-$(CONFIG_SND_SOC_RT5631) += snd-soc-rt5631.o
obj-$(CONFIG_SND_SOC_RT5640) += snd-soc-rt5640.o

View File

@ -27,7 +27,7 @@
#include "max98090.h"
/* Allows for sparsely populated register maps */
static struct reg_default max98090_reg[] = {
static const struct reg_default max98090_reg[] = {
{ 0x00, 0x00 }, /* 00 Software Reset */
{ 0x03, 0x04 }, /* 03 Interrupt Masks */
{ 0x04, 0x00 }, /* 04 System Clock Quick */
@ -2704,7 +2704,7 @@ static const struct of_device_id max98090_of_match[] = {
MODULE_DEVICE_TABLE(of, max98090_of_match);
#ifdef CONFIG_ACPI
static struct acpi_device_id max98090_acpi_match[] = {
static const struct acpi_device_id max98090_acpi_match[] = {
{ "193C9890", MAX98090 },
{ }
};

View File

@ -341,6 +341,7 @@ static int ml26124_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_codec *codec = dai->codec;
struct ml26124_priv *priv = snd_soc_codec_get_drvdata(codec);
int i = get_coeff(priv->mclk, params_rate(hw_params));
int srate;
if (i < 0)
return i;
@ -370,53 +371,16 @@ static int ml26124_hw_params(struct snd_pcm_substream *substream,
BIT(0) | BIT(1), 0);
}
switch (params_rate(hw_params)) {
case 16000:
snd_soc_update_bits(codec, ML26124_SMPLING_RATE, 0xf,
get_srate(params_rate(hw_params)));
snd_soc_update_bits(codec, ML26124_PLLNL, 0xff,
coeff_div[i].pllnl);
snd_soc_update_bits(codec, ML26124_PLLNH, 0x1,
coeff_div[i].pllnh);
snd_soc_update_bits(codec, ML26124_PLLML, 0xff,
coeff_div[i].pllml);
snd_soc_update_bits(codec, ML26124_PLLMH, 0x3f,
coeff_div[i].pllmh);
snd_soc_update_bits(codec, ML26124_PLLDIV, 0x1f,
coeff_div[i].plldiv);
break;
case 32000:
snd_soc_update_bits(codec, ML26124_SMPLING_RATE, 0xf,
get_srate(params_rate(hw_params)));
snd_soc_update_bits(codec, ML26124_PLLNL, 0xff,
coeff_div[i].pllnl);
snd_soc_update_bits(codec, ML26124_PLLNH, 0x1,
coeff_div[i].pllnh);
snd_soc_update_bits(codec, ML26124_PLLML, 0xff,
coeff_div[i].pllml);
snd_soc_update_bits(codec, ML26124_PLLMH, 0x3f,
coeff_div[i].pllmh);
snd_soc_update_bits(codec, ML26124_PLLDIV, 0x1f,
coeff_div[i].plldiv);
break;
case 48000:
snd_soc_update_bits(codec, ML26124_SMPLING_RATE, 0xf,
get_srate(params_rate(hw_params)));
snd_soc_update_bits(codec, ML26124_PLLNL, 0xff,
coeff_div[i].pllnl);
snd_soc_update_bits(codec, ML26124_PLLNH, 0x1,
coeff_div[i].pllnh);
snd_soc_update_bits(codec, ML26124_PLLML, 0xff,
coeff_div[i].pllml);
snd_soc_update_bits(codec, ML26124_PLLMH, 0x3f,
coeff_div[i].pllmh);
snd_soc_update_bits(codec, ML26124_PLLDIV, 0x1f,
coeff_div[i].plldiv);
break;
default:
pr_err("%s:this rate is no support for ml26124\n", __func__);
return -EINVAL;
}
srate = get_srate(params_rate(hw_params));
if (srate < 0)
return srate;
snd_soc_update_bits(codec, ML26124_SMPLING_RATE, 0xf, srate);
snd_soc_update_bits(codec, ML26124_PLLNL, 0xff, coeff_div[i].pllnl);
snd_soc_update_bits(codec, ML26124_PLLNH, 0x1, coeff_div[i].pllnh);
snd_soc_update_bits(codec, ML26124_PLLML, 0xff, coeff_div[i].pllml);
snd_soc_update_bits(codec, ML26124_PLLMH, 0x3f, coeff_div[i].pllmh);
snd_soc_update_bits(codec, ML26124_PLLDIV, 0x1f, coeff_div[i].plldiv);
return 0;
}

128
sound/soc/codecs/rl6347a.c Normal file
View File

@ -0,0 +1,128 @@
/*
* rl6347a.c - RL6347A class device shared support
*
* Copyright 2015 Realtek Semiconductor Corp.
*
* Author: Oder Chiou <oder_chiou@realtek.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.
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/i2c.h>
#include <linux/platform_device.h>
#include <linux/spi/spi.h>
#include <linux/dmi.h>
#include <linux/acpi.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <sound/initval.h>
#include <sound/tlv.h>
#include <sound/jack.h>
#include <linux/workqueue.h>
#include <sound/hda_verbs.h>
#include "rl6347a.h"
int rl6347a_hw_write(void *context, unsigned int reg, unsigned int value)
{
struct i2c_client *client = context;
struct rl6347a_priv *rl6347a = i2c_get_clientdata(client);
u8 data[4];
int ret, i;
/* handle index registers */
if (reg <= 0xff) {
rl6347a_hw_write(client, RL6347A_COEF_INDEX, reg);
for (i = 0; i < rl6347a->index_cache_size; i++) {
if (reg == rl6347a->index_cache[i].reg) {
rl6347a->index_cache[i].def = value;
break;
}
}
reg = RL6347A_PROC_COEF;
}
data[0] = (reg >> 24) & 0xff;
data[1] = (reg >> 16) & 0xff;
/*
* 4 bit VID: reg should be 0
* 12 bit VID: value should be 0
* So we use an OR operator to handle it rather than use if condition.
*/
data[2] = ((reg >> 8) & 0xff) | ((value >> 8) & 0xff);
data[3] = value & 0xff;
ret = i2c_master_send(client, data, 4);
if (ret == 4)
return 0;
else
pr_err("ret=%d\n", ret);
if (ret < 0)
return ret;
else
return -EIO;
}
EXPORT_SYMBOL_GPL(rl6347a_hw_write);
int rl6347a_hw_read(void *context, unsigned int reg, unsigned int *value)
{
struct i2c_client *client = context;
struct i2c_msg xfer[2];
int ret;
__be32 be_reg;
unsigned int index, vid, buf = 0x0;
/* handle index registers */
if (reg <= 0xff) {
rl6347a_hw_write(client, RL6347A_COEF_INDEX, reg);
reg = RL6347A_PROC_COEF;
}
reg = reg | 0x80000;
vid = (reg >> 8) & 0xfff;
if (AC_VERB_GET_AMP_GAIN_MUTE == (vid & 0xf00)) {
index = (reg >> 8) & 0xf;
reg = (reg & ~0xf0f) | index;
}
be_reg = cpu_to_be32(reg);
/* Write register */
xfer[0].addr = client->addr;
xfer[0].flags = 0;
xfer[0].len = 4;
xfer[0].buf = (u8 *)&be_reg;
/* Read data */
xfer[1].addr = client->addr;
xfer[1].flags = I2C_M_RD;
xfer[1].len = 4;
xfer[1].buf = (u8 *)&buf;
ret = i2c_transfer(client->adapter, xfer, 2);
if (ret < 0)
return ret;
else if (ret != 2)
return -EIO;
*value = be32_to_cpu(buf);
return 0;
}
EXPORT_SYMBOL_GPL(rl6347a_hw_read);
MODULE_DESCRIPTION("RL6347A class device shared support");
MODULE_AUTHOR("Oder Chiou <oder_chiou@realtek.com>");
MODULE_LICENSE("GPL v2");

View File

@ -0,0 +1,32 @@
/*
* rl6347a.h - RL6347A class device shared support
*
* Copyright 2015 Realtek Semiconductor Corp.
*
* Author: Oder Chiou <oder_chiou@realtek.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.
*/
#ifndef __RL6347A_H__
#define __RL6347A_H__
#define VERB_CMD(V, N, D) ((N << 20) | (V << 8) | D)
#define RL6347A_VENDOR_REGISTERS 0x20
#define RL6347A_COEF_INDEX\
VERB_CMD(AC_VERB_SET_COEF_INDEX, RL6347A_VENDOR_REGISTERS, 0)
#define RL6347A_PROC_COEF\
VERB_CMD(AC_VERB_SET_PROC_COEF, RL6347A_VENDOR_REGISTERS, 0)
struct rl6347a_priv {
struct reg_default *index_cache;
int index_cache_size;
};
int rl6347a_hw_write(void *context, unsigned int reg, unsigned int value);
int rl6347a_hw_read(void *context, unsigned int reg, unsigned int *value);
#endif /* __RL6347A_H__ */

View File

@ -31,12 +31,15 @@
#include <sound/rt286.h>
#include <sound/hda_verbs.h>
#include "rl6347a.h"
#include "rt286.h"
#define RT286_VENDOR_ID 0x10ec0286
#define RT288_VENDOR_ID 0x10ec0288
struct rt286_priv {
struct reg_default *index_cache;
int index_cache_size;
struct regmap *regmap;
struct snd_soc_codec *codec;
struct rt286_platform_data pdata;
@ -45,7 +48,6 @@ struct rt286_priv {
struct delayed_work jack_detect_work;
int sys_clk;
int clk_id;
struct reg_default *index_cache;
};
static struct reg_default rt286_index_def[] = {
@ -185,94 +187,6 @@ static bool rt286_readable_register(struct device *dev, unsigned int reg)
}
}
static int rt286_hw_write(void *context, unsigned int reg, unsigned int value)
{
struct i2c_client *client = context;
struct rt286_priv *rt286 = i2c_get_clientdata(client);
u8 data[4];
int ret, i;
/* handle index registers */
if (reg <= 0xff) {
rt286_hw_write(client, RT286_COEF_INDEX, reg);
for (i = 0; i < INDEX_CACHE_SIZE; i++) {
if (reg == rt286->index_cache[i].reg) {
rt286->index_cache[i].def = value;
break;
}
}
reg = RT286_PROC_COEF;
}
data[0] = (reg >> 24) & 0xff;
data[1] = (reg >> 16) & 0xff;
/*
* 4 bit VID: reg should be 0
* 12 bit VID: value should be 0
* So we use an OR operator to handle it rather than use if condition.
*/
data[2] = ((reg >> 8) & 0xff) | ((value >> 8) & 0xff);
data[3] = value & 0xff;
ret = i2c_master_send(client, data, 4);
if (ret == 4)
return 0;
else
pr_err("ret=%d\n", ret);
if (ret < 0)
return ret;
else
return -EIO;
}
static int rt286_hw_read(void *context, unsigned int reg, unsigned int *value)
{
struct i2c_client *client = context;
struct i2c_msg xfer[2];
int ret;
__be32 be_reg;
unsigned int index, vid, buf = 0x0;
/* handle index registers */
if (reg <= 0xff) {
rt286_hw_write(client, RT286_COEF_INDEX, reg);
reg = RT286_PROC_COEF;
}
reg = reg | 0x80000;
vid = (reg >> 8) & 0xfff;
if (AC_VERB_GET_AMP_GAIN_MUTE == (vid & 0xf00)) {
index = (reg >> 8) & 0xf;
reg = (reg & ~0xf0f) | index;
}
be_reg = cpu_to_be32(reg);
/* Write register */
xfer[0].addr = client->addr;
xfer[0].flags = 0;
xfer[0].len = 4;
xfer[0].buf = (u8 *)&be_reg;
/* Read data */
xfer[1].addr = client->addr;
xfer[1].flags = I2C_M_RD;
xfer[1].len = 4;
xfer[1].buf = (u8 *)&buf;
ret = i2c_transfer(client->adapter, xfer, 2);
if (ret < 0)
return ret;
else if (ret != 2)
return -EIO;
*value = be32_to_cpu(buf);
return 0;
}
#ifdef CONFIG_PM
static void rt286_index_sync(struct snd_soc_codec *codec)
{
@ -1174,8 +1088,8 @@ static const struct regmap_config rt286_regmap = {
.max_register = 0x02370100,
.volatile_reg = rt286_volatile_register,
.readable_reg = rt286_readable_register,
.reg_write = rt286_hw_write,
.reg_read = rt286_hw_read,
.reg_write = rl6347a_hw_write,
.reg_read = rl6347a_hw_read,
.cache_type = REGCACHE_RBTREE,
.reg_defaults = rt286_reg,
.num_reg_defaults = ARRAY_SIZE(rt286_reg),
@ -1248,6 +1162,7 @@ static int rt286_i2c_probe(struct i2c_client *i2c,
}
rt286->index_cache = rt286_index_def;
rt286->index_cache_size = INDEX_CACHE_SIZE;
rt286->i2c = i2c;
i2c_set_clientdata(i2c, rt286);

View File

@ -51,7 +51,7 @@ static const struct regmap_range_cfg rt5640_ranges[] = {
.window_len = 0x1, },
};
static struct reg_default init_list[] = {
static const struct reg_default init_list[] = {
{RT5640_PR_BASE + 0x3d, 0x3600},
{RT5640_PR_BASE + 0x12, 0x0aa8},
{RT5640_PR_BASE + 0x14, 0x0aaa},
@ -59,7 +59,6 @@ static struct reg_default init_list[] = {
{RT5640_PR_BASE + 0x21, 0xe0e0},
{RT5640_PR_BASE + 0x23, 0x1804},
};
#define RT5640_INIT_REG_LEN ARRAY_SIZE(init_list)
static const struct reg_default rt5640_reg[] = {
{ 0x00, 0x000e },
@ -2122,7 +2121,7 @@ MODULE_DEVICE_TABLE(of, rt5640_of_match);
#endif
#ifdef CONFIG_ACPI
static struct acpi_device_id rt5640_acpi_match[] = {
static const struct acpi_device_id rt5640_acpi_match[] = {
{ "INT33CA", 0 },
{ "10EC5640", 0 },
{ "10EC5642", 0 },

View File

@ -349,6 +349,7 @@ static bool rt5645_readable_register(struct device *dev, unsigned int reg)
case RT5645_TDM_CTRL_1:
case RT5645_TDM_CTRL_2:
case RT5645_TDM_CTRL_3:
case RT5650_TDM_CTRL_4:
case RT5645_GLB_CLK:
case RT5645_PLL_CTRL1:
case RT5645_PLL_CTRL2:
@ -1705,15 +1706,6 @@ static const struct snd_soc_dapm_widget rt5645_dapm_widgets[] = {
SND_SOC_DAPM_MUX("RT5645 IF1 ADC Mux", SND_SOC_NOPM,
0, 0, &rt5645_if1_adc_in_mux),
SND_SOC_DAPM_MUX("RT5650 IF1 ADC1 Swap Mux", SND_SOC_NOPM,
0, 0, &rt5650_if1_adc1_in_mux),
SND_SOC_DAPM_MUX("RT5650 IF1 ADC2 Swap Mux", SND_SOC_NOPM,
0, 0, &rt5650_if1_adc2_in_mux),
SND_SOC_DAPM_MUX("RT5650 IF1 ADC3 Swap Mux", SND_SOC_NOPM,
0, 0, &rt5650_if1_adc3_in_mux),
SND_SOC_DAPM_MUX("RT5650 IF1 ADC Mux", SND_SOC_NOPM,
0, 0, &rt5650_if1_adc_in_mux),
SND_SOC_DAPM_MUX("IF2 ADC Mux", SND_SOC_NOPM,
0, 0, &rt5645_if2_adc_in_mux),
@ -1732,14 +1724,6 @@ static const struct snd_soc_dapm_widget rt5645_dapm_widgets[] = {
&rt5645_if1_dac2_tdm_sel_mux),
SND_SOC_DAPM_MUX("RT5645 IF1 DAC2 R Mux", SND_SOC_NOPM, 0, 0,
&rt5645_if1_dac3_tdm_sel_mux),
SND_SOC_DAPM_MUX("RT5650 IF1 DAC1 L Mux", SND_SOC_NOPM, 0, 0,
&rt5650_if1_dac0_tdm_sel_mux),
SND_SOC_DAPM_MUX("RT5650 IF1 DAC1 R Mux", SND_SOC_NOPM, 0, 0,
&rt5650_if1_dac1_tdm_sel_mux),
SND_SOC_DAPM_MUX("RT5650 IF1 DAC2 L Mux", SND_SOC_NOPM, 0, 0,
&rt5650_if1_dac2_tdm_sel_mux),
SND_SOC_DAPM_MUX("RT5650 IF1 DAC2 R Mux", SND_SOC_NOPM, 0, 0,
&rt5650_if1_dac3_tdm_sel_mux),
SND_SOC_DAPM_PGA("IF1 ADC", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_PGA("IF1 ADC L", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_PGA("IF1 ADC R", SND_SOC_NOPM, 0, 0, NULL, 0),
@ -1881,6 +1865,24 @@ static const struct snd_soc_dapm_widget rt5650_specific_dapm_widgets[] = {
0, 0, &rt5650_a_dac2_l_mux),
SND_SOC_DAPM_MUX("A DAC2 R Mux", SND_SOC_NOPM,
0, 0, &rt5650_a_dac2_r_mux),
SND_SOC_DAPM_MUX("RT5650 IF1 ADC1 Swap Mux", SND_SOC_NOPM,
0, 0, &rt5650_if1_adc1_in_mux),
SND_SOC_DAPM_MUX("RT5650 IF1 ADC2 Swap Mux", SND_SOC_NOPM,
0, 0, &rt5650_if1_adc2_in_mux),
SND_SOC_DAPM_MUX("RT5650 IF1 ADC3 Swap Mux", SND_SOC_NOPM,
0, 0, &rt5650_if1_adc3_in_mux),
SND_SOC_DAPM_MUX("RT5650 IF1 ADC Mux", SND_SOC_NOPM,
0, 0, &rt5650_if1_adc_in_mux),
SND_SOC_DAPM_MUX("RT5650 IF1 DAC1 L Mux", SND_SOC_NOPM, 0, 0,
&rt5650_if1_dac0_tdm_sel_mux),
SND_SOC_DAPM_MUX("RT5650 IF1 DAC1 R Mux", SND_SOC_NOPM, 0, 0,
&rt5650_if1_dac1_tdm_sel_mux),
SND_SOC_DAPM_MUX("RT5650 IF1 DAC2 L Mux", SND_SOC_NOPM, 0, 0,
&rt5650_if1_dac2_tdm_sel_mux),
SND_SOC_DAPM_MUX("RT5650 IF1 DAC2 R Mux", SND_SOC_NOPM, 0, 0,
&rt5650_if1_dac3_tdm_sel_mux),
};
static const struct snd_soc_dapm_route rt5645_dapm_routes[] = {
@ -2761,6 +2763,7 @@ static void rt5645_enable_push_button_irq(struct snd_soc_codec *codec,
struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
if (enable) {
snd_soc_dapm_mutex_lock(&codec->dapm);
snd_soc_dapm_force_enable_pin_unlocked(&codec->dapm,
"ADC L power");
snd_soc_dapm_force_enable_pin_unlocked(&codec->dapm,
@ -2770,6 +2773,8 @@ static void rt5645_enable_push_button_irq(struct snd_soc_codec *codec,
snd_soc_dapm_force_enable_pin_unlocked(&codec->dapm,
"Mic Det Power");
snd_soc_dapm_sync_unlocked(&codec->dapm);
snd_soc_dapm_mutex_unlock(&codec->dapm);
snd_soc_update_bits(codec,
RT5645_INT_IRQ_ST, 0x8, 0x8);
snd_soc_update_bits(codec,
@ -2780,6 +2785,8 @@ static void rt5645_enable_push_button_irq(struct snd_soc_codec *codec,
} else {
snd_soc_update_bits(codec, RT5650_4BTN_IL_CMD2, 0x8000, 0x0);
snd_soc_update_bits(codec, RT5645_INT_IRQ_ST, 0x8, 0x0);
snd_soc_dapm_mutex_lock(&codec->dapm);
snd_soc_dapm_disable_pin_unlocked(&codec->dapm,
"ADC L power");
snd_soc_dapm_disable_pin_unlocked(&codec->dapm,
@ -2790,6 +2797,7 @@ static void rt5645_enable_push_button_irq(struct snd_soc_codec *codec,
snd_soc_dapm_disable_pin_unlocked(&codec->dapm,
"Mic Det Power");
snd_soc_dapm_sync_unlocked(&codec->dapm);
snd_soc_dapm_mutex_unlock(&codec->dapm);
}
}
@ -2937,17 +2945,11 @@ static int rt5645_irq_detection(struct rt5645_priv *rt5645)
switch (rt5645->pdata.jd_mode) {
case 0: /* Not using rt5645 JD */
if (gpio_is_valid(rt5645->pdata.hp_det_gpio)) {
gpio_state = gpio_get_value(rt5645->pdata.hp_det_gpio);
dev_dbg(rt5645->codec->dev, "gpio = %d(%d)\n",
rt5645->pdata.hp_det_gpio, gpio_state);
}
if ((rt5645->pdata.gpio_hp_det_active_high && gpio_state) ||
(!rt5645->pdata.gpio_hp_det_active_high &&
!gpio_state)) {
report = rt5645_jack_detect(rt5645->codec, 1);
} else {
report = rt5645_jack_detect(rt5645->codec, 0);
if (rt5645->gpiod_hp_det) {
gpio_state = gpiod_get_value(rt5645->gpiod_hp_det);
dev_dbg(rt5645->codec->dev, "gpio_state = %d\n",
gpio_state);
report = rt5645_jack_detect(rt5645->codec, gpio_state);
}
snd_soc_jack_report(rt5645->hp_jack,
report, SND_JACK_HEADPHONE);
@ -3230,6 +3232,20 @@ static struct dmi_system_id dmi_platform_intel_braswell[] = {
{ }
};
static int rt5645_parse_dt(struct rt5645_priv *rt5645, struct device *dev)
{
rt5645->pdata.in2_diff = device_property_read_bool(dev,
"realtek,in2-differential");
device_property_read_u32(dev,
"realtek,dmic1-data-pin", &rt5645->pdata.dmic1_data_pin);
device_property_read_u32(dev,
"realtek,dmic2-data-pin", &rt5645->pdata.dmic2_data_pin);
device_property_read_u32(dev,
"realtek,jd-mode", &rt5645->pdata.jd_mode);
return 0;
}
static int rt5645_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
@ -3237,7 +3253,6 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
struct rt5645_priv *rt5645;
int ret;
unsigned int val;
struct gpio_desc *gpiod;
rt5645 = devm_kzalloc(&i2c->dev, sizeof(struct rt5645_priv),
GFP_KERNEL);
@ -3247,22 +3262,19 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
rt5645->i2c = i2c;
i2c_set_clientdata(i2c, rt5645);
if (pdata) {
if (pdata)
rt5645->pdata = *pdata;
} else {
if (dmi_check_system(dmi_platform_intel_braswell)) {
rt5645->pdata = *rt5645_pdata;
gpiod = devm_gpiod_get_index(&i2c->dev, "rt5645", 0);
else if (dmi_check_system(dmi_platform_intel_braswell))
rt5645->pdata = *rt5645_pdata;
else
rt5645_parse_dt(rt5645, &i2c->dev);
if (IS_ERR(gpiod) || gpiod_direction_input(gpiod)) {
rt5645->pdata.hp_det_gpio = -1;
dev_err(&i2c->dev, "failed to initialize gpiod\n");
} else {
rt5645->pdata.hp_det_gpio = desc_to_gpio(gpiod);
rt5645->pdata.gpio_hp_det_active_high
= !gpiod_is_active_low(gpiod);
}
}
rt5645->gpiod_hp_det = devm_gpiod_get_optional(&i2c->dev, "hp-detect",
GPIOD_IN);
if (IS_ERR(rt5645->gpiod_hp_det)) {
dev_err(&i2c->dev, "failed to initialize gpiod\n");
return PTR_ERR(rt5645->gpiod_hp_det);
}
rt5645->regmap = devm_regmap_init_i2c(i2c, &rt5645_regmap);
@ -3426,16 +3438,6 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
dev_err(&i2c->dev, "Failed to reguest IRQ: %d\n", ret);
}
if (gpio_is_valid(rt5645->pdata.hp_det_gpio)) {
ret = gpio_request(rt5645->pdata.hp_det_gpio, "rt5645");
if (ret)
dev_err(&i2c->dev, "Fail gpio_request hp_det_gpio\n");
ret = gpio_direction_input(rt5645->pdata.hp_det_gpio);
if (ret)
dev_err(&i2c->dev, "Fail gpio_direction hp_det_gpio\n");
}
return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5645,
rt5645_dai, ARRAY_SIZE(rt5645_dai));
}
@ -3449,9 +3451,6 @@ static int rt5645_i2c_remove(struct i2c_client *i2c)
cancel_delayed_work_sync(&rt5645->jack_detect_work);
if (gpio_is_valid(rt5645->pdata.hp_det_gpio))
gpio_free(rt5645->pdata.hp_det_gpio);
snd_soc_unregister_codec(&i2c->dev);
return 0;

View File

@ -2182,6 +2182,7 @@ struct rt5645_priv {
struct rt5645_platform_data pdata;
struct regmap *regmap;
struct i2c_client *i2c;
struct gpio_desc *gpiod_hp_det;
struct snd_soc_jack *hp_jack;
struct snd_soc_jack *mic_jack;
struct snd_soc_jack *btn_jack;

View File

@ -51,12 +51,11 @@ static const struct regmap_range_cfg rt5670_ranges[] = {
.window_len = 0x1, },
};
static struct reg_default init_list[] = {
static const struct reg_default init_list[] = {
{ RT5670_PR_BASE + 0x14, 0x9a8a },
{ RT5670_PR_BASE + 0x38, 0x3ba1 },
{ RT5670_PR_BASE + 0x3d, 0x3640 },
};
#define RT5670_INIT_REG_LEN ARRAY_SIZE(init_list)
static const struct reg_default rt5670_reg[] = {
{ 0x00, 0x0000 },
@ -2809,7 +2808,7 @@ static const struct i2c_device_id rt5670_i2c_id[] = {
MODULE_DEVICE_TABLE(i2c, rt5670_i2c_id);
#ifdef CONFIG_ACPI
static struct acpi_device_id rt5670_acpi_match[] = {
static const struct acpi_device_id rt5670_acpi_match[] = {
{ "10EC5670", 0},
{ },
};

View File

@ -45,7 +45,7 @@ static struct reg_default tas2552_reg_defs[] = {
{TAS2552_OUTPUT_DATA, 0xc0},
{TAS2552_PDM_CFG, 0x01},
{TAS2552_PGA_GAIN, 0x00},
{TAS2552_BOOST_PT_CTRL, 0x0f},
{TAS2552_BOOST_APT_CTRL, 0x0f},
{TAS2552_RESERVED_0D, 0xbe},
{TAS2552_LIMIT_RATE_HYS, 0x08},
{TAS2552_CFG_2, 0xef},
@ -77,7 +77,9 @@ struct tas2552_data {
struct gpio_desc *enable_gpio;
unsigned char regs[TAS2552_VBAT_DATA];
unsigned int pll_clkin;
int pll_clk_id;
unsigned int pdm_clk;
int pdm_clk_id;
unsigned int dai_fmt;
unsigned int tdm_delay;
@ -143,31 +145,105 @@ static const struct snd_soc_dapm_route tas2552_audio_map[] = {
};
#ifdef CONFIG_PM
static void tas2552_sw_shutdown(struct tas2552_data *tas_data, int sw_shutdown)
static void tas2552_sw_shutdown(struct tas2552_data *tas2552, int sw_shutdown)
{
u8 cfg1_reg = 0;
if (!tas_data->codec)
if (!tas2552->codec)
return;
if (sw_shutdown)
cfg1_reg = TAS2552_SWS;
snd_soc_update_bits(tas_data->codec, TAS2552_CFG_1, TAS2552_SWS,
snd_soc_update_bits(tas2552->codec, TAS2552_CFG_1, TAS2552_SWS,
cfg1_reg);
}
#endif
static int tas2552_setup_pll(struct snd_soc_codec *codec,
struct snd_pcm_hw_params *params)
{
struct tas2552_data *tas2552 = dev_get_drvdata(codec->dev);
bool bypass_pll = false;
unsigned int pll_clk = params_rate(params) * 512;
unsigned int pll_clkin = tas2552->pll_clkin;
u8 pll_enable;
if (!pll_clkin) {
if (tas2552->pll_clk_id != TAS2552_PLL_CLKIN_BCLK)
return -EINVAL;
pll_clkin = snd_soc_params_to_bclk(params);
pll_clkin += tas2552->tdm_delay;
}
pll_enable = snd_soc_read(codec, TAS2552_CFG_2) & TAS2552_PLL_ENABLE;
snd_soc_update_bits(codec, TAS2552_CFG_2, TAS2552_PLL_ENABLE, 0);
if (pll_clkin == pll_clk)
bypass_pll = true;
if (bypass_pll) {
/* By pass the PLL configuration */
snd_soc_update_bits(codec, TAS2552_PLL_CTRL_2,
TAS2552_PLL_BYPASS, TAS2552_PLL_BYPASS);
} else {
/* Fill in the PLL control registers for J & D
* pll_clk = (.5 * pll_clkin * J.D) / 2^p
* Need to fill in J and D here based on incoming freq
*/
unsigned int d;
u8 j;
u8 pll_sel = (tas2552->pll_clk_id << 3) & TAS2552_PLL_SRC_MASK;
u8 p = snd_soc_read(codec, TAS2552_PLL_CTRL_1);
p = (p >> 7);
recalc:
j = (pll_clk * 2 * (1 << p)) / pll_clkin;
d = (pll_clk * 2 * (1 << p)) % pll_clkin;
d /= (pll_clkin / 10000);
if (d && (pll_clkin < 512000 || pll_clkin > 9200000)) {
if (tas2552->pll_clk_id == TAS2552_PLL_CLKIN_BCLK) {
pll_clkin = 1800000;
pll_sel = (TAS2552_PLL_CLKIN_1_8_FIXED << 3) &
TAS2552_PLL_SRC_MASK;
} else {
pll_clkin = snd_soc_params_to_bclk(params);
pll_clkin += tas2552->tdm_delay;
pll_sel = (TAS2552_PLL_CLKIN_BCLK << 3) &
TAS2552_PLL_SRC_MASK;
}
goto recalc;
}
snd_soc_update_bits(codec, TAS2552_CFG_1, TAS2552_PLL_SRC_MASK,
pll_sel);
snd_soc_update_bits(codec, TAS2552_PLL_CTRL_1,
TAS2552_PLL_J_MASK, j);
/* Will clear the PLL_BYPASS bit */
snd_soc_write(codec, TAS2552_PLL_CTRL_2,
TAS2552_PLL_D_UPPER(d));
snd_soc_write(codec, TAS2552_PLL_CTRL_3,
TAS2552_PLL_D_LOWER(d));
}
/* Restore PLL status */
snd_soc_update_bits(codec, TAS2552_CFG_2, TAS2552_PLL_ENABLE,
pll_enable);
return 0;
}
static int tas2552_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 tas2552_data *tas2552 = dev_get_drvdata(codec->dev);
int sample_rate, pll_clk;
int d;
int cpf;
u8 p, j;
u8 ser_ctrl1_reg, wclk_rate;
switch (params_width(params)) {
@ -245,49 +321,7 @@ static int tas2552_hw_params(struct snd_pcm_substream *substream,
snd_soc_update_bits(codec, TAS2552_CFG_3, TAS2552_WCLK_FREQ_MASK,
wclk_rate);
if (!tas2552->pll_clkin)
return -EINVAL;
snd_soc_update_bits(codec, TAS2552_CFG_2, TAS2552_PLL_ENABLE, 0);
if (tas2552->pll_clkin == TAS2552_245MHZ_CLK ||
tas2552->pll_clkin == TAS2552_225MHZ_CLK) {
/* By pass the PLL configuration */
snd_soc_update_bits(codec, TAS2552_PLL_CTRL_2,
TAS2552_PLL_BYPASS_MASK,
TAS2552_PLL_BYPASS);
} else {
/* Fill in the PLL control registers for J & D
* PLL_CLK = (.5 * freq * J.D) / 2^p
* Need to fill in J and D here based on incoming freq
*/
p = snd_soc_read(codec, TAS2552_PLL_CTRL_1);
p = (p >> 7);
sample_rate = params_rate(params);
if (sample_rate == 48000)
pll_clk = TAS2552_245MHZ_CLK;
else if (sample_rate == 44100)
pll_clk = TAS2552_225MHZ_CLK;
else {
dev_vdbg(codec->dev, "Substream sample rate is not found %i\n",
params_rate(params));
return -EINVAL;
}
j = (pll_clk * 2 * (1 << p)) / tas2552->pll_clkin;
d = (pll_clk * 2 * (1 << p)) % tas2552->pll_clkin;
snd_soc_update_bits(codec, TAS2552_PLL_CTRL_1,
TAS2552_PLL_J_MASK, j);
snd_soc_write(codec, TAS2552_PLL_CTRL_2,
(d >> 7) & TAS2552_PLL_D_UPPER_MASK);
snd_soc_write(codec, TAS2552_PLL_CTRL_3,
d & TAS2552_PLL_D_LOWER_MASK);
}
return 0;
return tas2552_setup_pll(codec, params);
}
#define TAS2552_DAI_FMT_MASK (TAS2552_BCLKDIR | \
@ -370,12 +404,21 @@ static int tas2552_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
switch (clk_id) {
case TAS2552_PLL_CLKIN_MCLK:
case TAS2552_PLL_CLKIN_BCLK:
case TAS2552_PLL_CLKIN_IVCLKIN:
if (freq < 512000 || freq > 24576000) {
/* out of range PLL_CLKIN, fall back to use BCLK */
dev_warn(codec->dev, "Out of range PLL_CLKIN: %u\n",
freq);
clk_id = TAS2552_PLL_CLKIN_BCLK;
freq = 0;
}
/* fall through */
case TAS2552_PLL_CLKIN_BCLK:
case TAS2552_PLL_CLKIN_1_8_FIXED:
mask = TAS2552_PLL_SRC_MASK;
val = (clk_id << 3) & mask; /* bit 4:5 in the register */
reg = TAS2552_CFG_1;
tas2552->pll_clk_id = clk_id;
tas2552->pll_clkin = freq;
break;
case TAS2552_PDM_CLK_PLL:
@ -385,6 +428,7 @@ static int tas2552_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
mask = TAS2552_PDM_CLK_SEL_MASK;
val = (clk_id >> 1) & mask; /* bit 0:1 in the register */
reg = TAS2552_PDM_CFG;
tas2552->pdm_clk_id = clk_id;
tas2552->pdm_clk = freq;
break;
default:
@ -509,9 +553,20 @@ static struct snd_soc_dai_driver tas2552_dai[] = {
*/
static DECLARE_TLV_DB_SCALE(dac_tlv, -7, 100, 0);
static const char * const tas2552_din_source_select[] = {
"Muted",
"Left",
"Right",
"Left + Right average",
};
static SOC_ENUM_SINGLE_DECL(tas2552_din_source_enum,
TAS2552_CFG_3, 3,
tas2552_din_source_select);
static const struct snd_kcontrol_new tas2552_snd_controls[] = {
SOC_SINGLE_TLV("Speaker Driver Playback Volume",
TAS2552_PGA_GAIN, 0, 0x1f, 0, dac_tlv),
SOC_ENUM("DIN source", tas2552_din_source_enum),
};
static int tas2552_codec_probe(struct snd_soc_codec *codec)
@ -543,13 +598,14 @@ static int tas2552_codec_probe(struct snd_soc_codec *codec)
snd_soc_update_bits(codec, TAS2552_CFG_1, TAS2552_MUTE, TAS2552_MUTE);
snd_soc_write(codec, TAS2552_CFG_3, TAS2552_I2S_OUT_SEL |
TAS2552_DIN_SRC_SEL_AVG_L_R);
snd_soc_write(codec, TAS2552_DOUT, TAS2552_PDM_DATA_I);
snd_soc_write(codec, TAS2552_OUTPUT_DATA, TAS2552_PDM_DATA_V_I | 0x8);
snd_soc_write(codec, TAS2552_BOOST_PT_CTRL, TAS2552_APT_DELAY_200 |
TAS2552_APT_THRESH_2_1_7);
snd_soc_write(codec, TAS2552_OUTPUT_DATA,
TAS2552_PDM_DATA_SEL_V_I |
TAS2552_R_DATA_OUT(TAS2552_DATA_OUT_V_DATA));
snd_soc_write(codec, TAS2552_BOOST_APT_CTRL, TAS2552_APT_DELAY_200 |
TAS2552_APT_THRESH_20_17);
snd_soc_write(codec, TAS2552_CFG_2, TAS2552_BOOST_EN |
TAS2552_APT_EN | TAS2552_LIM_EN);
snd_soc_write(codec, TAS2552_CFG_2, TAS2552_BOOST_EN | TAS2552_APT_EN |
TAS2552_LIM_EN);
return 0;
@ -647,13 +703,10 @@ static int tas2552_probe(struct i2c_client *client,
if (data == NULL)
return -ENOMEM;
data->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW);
if (IS_ERR(data->enable_gpio)) {
if (PTR_ERR(data->enable_gpio) == -EPROBE_DEFER)
return -EPROBE_DEFER;
data->enable_gpio = NULL;;
}
data->enable_gpio = devm_gpiod_get_optional(dev, "enable",
GPIOD_OUT_LOW);
if (IS_ERR(data->enable_gpio))
return PTR_ERR(data->enable_gpio);
data->tas2552_client = client;
data->regmap = devm_regmap_init_i2c(client, &tas2552_regmap_config);
@ -695,6 +748,7 @@ static int tas2552_probe(struct i2c_client *client,
static int tas2552_i2c_remove(struct i2c_client *client)
{
snd_soc_unregister_codec(&client->dev);
pm_runtime_disable(&client->dev);
return 0;
}

View File

@ -19,7 +19,7 @@
#define __TAS2552_H__
/* Register Address Map */
#define TAS2552_DEVICE_STATUS 0x00
#define TAS2552_DEVICE_STATUS 0x00
#define TAS2552_CFG_1 0x01
#define TAS2552_CFG_2 0x02
#define TAS2552_CFG_3 0x03
@ -33,13 +33,13 @@
#define TAS2552_BTIP 0x0b
#define TAS2552_BTS_CTRL 0x0c
#define TAS2552_RESERVED_0D 0x0d
#define TAS2552_LIMIT_RATE_HYS 0x0e
#define TAS2552_LIMIT_RELEASE 0x0f
#define TAS2552_LIMIT_INT_COUNT 0x10
#define TAS2552_LIMIT_RATE_HYS 0x0e
#define TAS2552_LIMIT_RELEASE 0x0f
#define TAS2552_LIMIT_INT_COUNT 0x10
#define TAS2552_PDM_CFG 0x11
#define TAS2552_PGA_GAIN 0x12
#define TAS2552_EDGE_RATE_CTRL 0x13
#define TAS2552_BOOST_PT_CTRL 0x14
#define TAS2552_EDGE_RATE_CTRL 0x13
#define TAS2552_BOOST_APT_CTRL 0x14
#define TAS2552_VER_NUM 0x16
#define TAS2552_VBAT_DATA 0x19
#define TAS2552_MAX_REG 0x20
@ -103,10 +103,21 @@
#define TAS2552_WCLKDIR (1 << 7)
/* OUTPUT_DATA register */
#define TAS2552_PDM_DATA_I 0x00
#define TAS2552_PDM_DATA_V (1 << 6)
#define TAS2552_PDM_DATA_I_V (1 << 7)
#define TAS2552_PDM_DATA_V_I (0x11 << 6)
#define TAS2552_DATA_OUT_I_DATA (0x0)
#define TAS2552_DATA_OUT_V_DATA (0x1)
#define TAS2552_DATA_OUT_VBAT_DATA (0x2)
#define TAS2552_DATA_OUT_VBOOST_DATA (0x3)
#define TAS2552_DATA_OUT_PGA_GAIN (0x4)
#define TAS2552_DATA_OUT_IV_DATA (0x5)
#define TAS2552_DATA_OUT_VBAT_VBOOST_GAIN (0x6)
#define TAS2552_DATA_OUT_DISABLED (0x7)
#define TAS2552_L_DATA_OUT(x) ((x) << 0)
#define TAS2552_R_DATA_OUT(x) ((x) << 3)
#define TAS2552_PDM_DATA_SEL_I (0x0 << 6)
#define TAS2552_PDM_DATA_SEL_V (0x1 << 6)
#define TAS2552_PDM_DATA_SEL_I_V (0x2 << 6)
#define TAS2552_PDM_DATA_SEL_V_I (0x3 << 6)
#define TAS2552_PDM_DATA_SEL_MASK TAS2552_PDM_DATA_SEL_V_I
/* PDM CFG Register */
#define TAS2552_PDM_CLK_SEL_PLL (0x0 << 0)
@ -116,24 +127,20 @@
#define TAS2552_PDM_CLK_SEL_MASK TAS2552_PDM_CLK_SEL_MCLK
#define TAS2552_PDM_DATA_ES (1 << 2)
/* Boost pass-through register */
#define TAS2552_APT_DELAY_50 0x00
#define TAS2552_APT_DELAY_75 (1 << 1)
#define TAS2552_APT_DELAY_125 (1 << 2)
#define TAS2552_APT_DELAY_200 (1 << 3)
#define TAS2552_APT_THRESH_2_5 0x00
#define TAS2552_APT_THRESH_1_7 (1 << 3)
#define TAS2552_APT_THRESH_1_4_1_1 (1 << 4)
#define TAS2552_APT_THRESH_2_1_7 (0x11 << 2)
/* Boost Auto-pass through register */
#define TAS2552_APT_DELAY_50 (0x0 << 0)
#define TAS2552_APT_DELAY_75 (0x1 << 0)
#define TAS2552_APT_DELAY_125 (0x2 << 0)
#define TAS2552_APT_DELAY_200 (0x3 << 0)
#define TAS2552_APT_THRESH_05_02 (0x0 << 2)
#define TAS2552_APT_THRESH_10_07 (0x1 << 2)
#define TAS2552_APT_THRESH_14_11 (0x2 << 2)
#define TAS2552_APT_THRESH_20_17 (0x3 << 2)
/* PLL Control Register */
#define TAS2552_245MHZ_CLK 24576000
#define TAS2552_225MHZ_CLK 22579200
#define TAS2552_PLL_J_MASK 0x7f
#define TAS2552_PLL_D_UPPER_MASK 0x3f
#define TAS2552_PLL_D_LOWER_MASK 0xff
#define TAS2552_PLL_BYPASS_MASK 0x80
#define TAS2552_PLL_BYPASS 0x80
#define TAS2552_PLL_J_MASK 0x7f
#define TAS2552_PLL_D_UPPER(x) (((x) >> 8) & 0x3f)
#define TAS2552_PLL_D_LOWER(x) ((x) & 0xff)
#define TAS2552_PLL_BYPASS (1 << 7)
#endif

View File

@ -1876,8 +1876,8 @@ static int wm5102_codec_probe(struct snd_soc_codec *codec)
struct wm5102_priv *priv = snd_soc_codec_get_drvdata(codec);
int ret;
ret = snd_soc_add_codec_controls(codec, wm_adsp2_fw_controls, 2);
if (ret != 0)
ret = wm_adsp2_codec_probe(&priv->core.adsp[0], codec);
if (ret)
return ret;
arizona_init_spk(codec);
@ -1894,6 +1894,8 @@ static int wm5102_codec_remove(struct snd_soc_codec *codec)
{
struct wm5102_priv *priv = snd_soc_codec_get_drvdata(codec);
wm_adsp2_codec_remove(&priv->core.adsp[0], codec);
priv->core.arizona->dapm = NULL;
return 0;

View File

@ -1600,7 +1600,7 @@ static int wm5110_codec_probe(struct snd_soc_codec *codec)
{
struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
struct wm5110_priv *priv = snd_soc_codec_get_drvdata(codec);
int ret;
int i, ret;
priv->core.arizona->dapm = dapm;
@ -1608,9 +1608,11 @@ static int wm5110_codec_probe(struct snd_soc_codec *codec)
arizona_init_gpio(codec);
arizona_init_mono(codec);
ret = snd_soc_add_codec_controls(codec, wm_adsp2_fw_controls, 8);
if (ret != 0)
return ret;
for (i = 0; i < WM5110_NUM_ADSP; ++i) {
ret = wm_adsp2_codec_probe(&priv->core.adsp[i], codec);
if (ret)
return ret;
}
snd_soc_dapm_disable_pin(dapm, "HAPTICS");
@ -1620,6 +1622,10 @@ static int wm5110_codec_probe(struct snd_soc_codec *codec)
static int wm5110_codec_remove(struct snd_soc_codec *codec)
{
struct wm5110_priv *priv = snd_soc_codec_get_drvdata(codec);
int i;
for (i = 0; i < WM5110_NUM_ADSP; ++i)
wm_adsp2_codec_remove(&priv->core.adsp[i], codec);
priv->core.arizona->dapm = NULL;

View File

@ -113,6 +113,15 @@ static struct {
{ 7, 1152 },
};
static struct {
int value;
int ratio;
} bclk_ratios[WM8523_NUM_RATES] = {
{ 2, 32 },
{ 3, 64 },
{ 4, 128 },
};
static int wm8523_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
@ -162,6 +171,23 @@ static int wm8523_hw_params(struct snd_pcm_substream *substream,
aifctrl2 &= ~WM8523_SR_MASK;
aifctrl2 |= lrclk_ratios[i].value;
if (aifctrl1 & WM8523_AIF_MSTR) {
/* Find a fs->bclk ratio */
for (i = 0; i < ARRAY_SIZE(bclk_ratios); i++)
if (params_width(params) * 2 <= bclk_ratios[i].ratio)
break;
if (i == ARRAY_SIZE(bclk_ratios)) {
dev_err(codec->dev,
"No matching BCLK/fs ratio for word length %d\n",
params_width(params));
return -EINVAL;
}
aifctrl2 &= ~WM8523_BCLKDIV_MASK;
aifctrl2 |= bclk_ratios[i].value << WM8523_BCLKDIV_SHIFT;
}
aifctrl1 &= ~WM8523_WL_MASK;
switch (params_width(params)) {
case 16:

View File

@ -125,18 +125,6 @@ static const struct snd_soc_dapm_route wm8741_dapm_routes[] = {
{ "VOUTRN", NULL, "DACR" },
};
static struct {
int value;
int ratio;
} lrclk_ratios[WM8741_NUM_RATES] = {
{ 1, 128 },
{ 2, 192 },
{ 3, 256 },
{ 4, 384 },
{ 5, 512 },
{ 6, 768 },
};
static const unsigned int rates_11289[] = {
44100, 88200,
};
@ -209,25 +197,16 @@ static const struct snd_pcm_hw_constraint_list constraints_36864 = {
.list = rates_36864,
};
static int wm8741_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
struct wm8741_priv *wm8741 = snd_soc_codec_get_drvdata(codec);
/* The set of sample rates that can be supported depends on the
* MCLK supplied to the CODEC - enforce this.
*/
if (!wm8741->sysclk) {
dev_err(codec->dev,
"No MCLK configured, call set_sysclk() on init\n");
return -EINVAL;
}
snd_pcm_hw_constraint_list(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_RATE,
wm8741->sysclk_constraints);
if (wm8741->sysclk)
snd_pcm_hw_constraint_list(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_RATE,
wm8741->sysclk_constraints);
return 0;
}
@ -241,17 +220,24 @@ static int wm8741_hw_params(struct snd_pcm_substream *substream,
u16 iface = snd_soc_read(codec, WM8741_FORMAT_CONTROL) & 0x1FC;
int i;
/* Find a supported LRCLK ratio */
for (i = 0; i < ARRAY_SIZE(lrclk_ratios); i++) {
if (wm8741->sysclk / params_rate(params) ==
lrclk_ratios[i].ratio)
/* The set of sample rates that can be supported depends on the
* MCLK supplied to the CODEC - enforce this.
*/
if (!wm8741->sysclk) {
dev_err(codec->dev,
"No MCLK configured, call set_sysclk() on init or in hw_params\n");
return -EINVAL;
}
/* Find a supported LRCLK rate */
for (i = 0; i < wm8741->sysclk_constraints->count; i++) {
if (wm8741->sysclk_constraints->list[i] == params_rate(params))
break;
}
/* Should never happen, should be handled by constraints */
if (i == ARRAY_SIZE(lrclk_ratios)) {
dev_err(codec->dev, "MCLK/fs ratio %d unsupported\n",
wm8741->sysclk / params_rate(params));
if (i == wm8741->sysclk_constraints->count) {
dev_err(codec->dev, "LRCLK %d unsupported with MCLK %d\n",
params_rate(params), wm8741->sysclk);
return -EINVAL;
}
@ -274,8 +260,8 @@ static int wm8741_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
dev_dbg(codec->dev, "wm8741_hw_params: bit size param = %d",
params_width(params));
dev_dbg(codec->dev, "wm8741_hw_params: bit size param = %d, rate param = %d",
params_width(params), params_rate(params));
snd_soc_write(codec, WM8741_FORMAT_CONTROL, iface);
return 0;
@ -290,6 +276,11 @@ static int wm8741_set_dai_sysclk(struct snd_soc_dai *codec_dai,
dev_dbg(codec->dev, "wm8741_set_dai_sysclk info: freq=%dHz\n", freq);
switch (freq) {
case 0:
wm8741->sysclk_constraints = NULL;
wm8741->sysclk = freq;
return 0;
case 11289600:
wm8741->sysclk_constraints = &constraints_11289;
wm8741->sysclk = freq;

View File

@ -1930,7 +1930,7 @@ static int wm8995_set_dai_sysclk(struct snd_soc_dai *dai,
dai->id + 1, freq);
break;
case WM8995_SYSCLK_MCLK2:
wm8995->sysclk[dai->id] = WM8995_SYSCLK_MCLK1;
wm8995->sysclk[dai->id] = WM8995_SYSCLK_MCLK2;
wm8995->mclk[1] = freq;
dev_dbg(dai->dev, "AIF%d using MCLK2 at %uHz\n",
dai->id + 1, freq);

View File

@ -23,6 +23,7 @@
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/workqueue.h>
#include <linux/debugfs.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@ -248,6 +249,175 @@ struct wm_coeff_ctl {
unsigned int flags;
};
#ifdef CONFIG_DEBUG_FS
static void wm_adsp_debugfs_save_wmfwname(struct wm_adsp *dsp, const char *s)
{
char *tmp = kasprintf(GFP_KERNEL, "%s\n", s);
mutex_lock(&dsp->debugfs_lock);
kfree(dsp->wmfw_file_name);
dsp->wmfw_file_name = tmp;
mutex_unlock(&dsp->debugfs_lock);
}
static void wm_adsp_debugfs_save_binname(struct wm_adsp *dsp, const char *s)
{
char *tmp = kasprintf(GFP_KERNEL, "%s\n", s);
mutex_lock(&dsp->debugfs_lock);
kfree(dsp->bin_file_name);
dsp->bin_file_name = tmp;
mutex_unlock(&dsp->debugfs_lock);
}
static void wm_adsp_debugfs_clear(struct wm_adsp *dsp)
{
mutex_lock(&dsp->debugfs_lock);
kfree(dsp->wmfw_file_name);
kfree(dsp->bin_file_name);
dsp->wmfw_file_name = NULL;
dsp->bin_file_name = NULL;
mutex_unlock(&dsp->debugfs_lock);
}
static ssize_t wm_adsp_debugfs_wmfw_read(struct file *file,
char __user *user_buf,
size_t count, loff_t *ppos)
{
struct wm_adsp *dsp = file->private_data;
ssize_t ret;
mutex_lock(&dsp->debugfs_lock);
if (!dsp->wmfw_file_name || !dsp->running)
ret = 0;
else
ret = simple_read_from_buffer(user_buf, count, ppos,
dsp->wmfw_file_name,
strlen(dsp->wmfw_file_name));
mutex_unlock(&dsp->debugfs_lock);
return ret;
}
static ssize_t wm_adsp_debugfs_bin_read(struct file *file,
char __user *user_buf,
size_t count, loff_t *ppos)
{
struct wm_adsp *dsp = file->private_data;
ssize_t ret;
mutex_lock(&dsp->debugfs_lock);
if (!dsp->bin_file_name || !dsp->running)
ret = 0;
else
ret = simple_read_from_buffer(user_buf, count, ppos,
dsp->bin_file_name,
strlen(dsp->bin_file_name));
mutex_unlock(&dsp->debugfs_lock);
return ret;
}
static const struct {
const char *name;
const struct file_operations fops;
} wm_adsp_debugfs_fops[] = {
{
.name = "wmfw_file_name",
.fops = {
.open = simple_open,
.read = wm_adsp_debugfs_wmfw_read,
},
},
{
.name = "bin_file_name",
.fops = {
.open = simple_open,
.read = wm_adsp_debugfs_bin_read,
},
},
};
static void wm_adsp2_init_debugfs(struct wm_adsp *dsp,
struct snd_soc_codec *codec)
{
struct dentry *root = NULL;
char *root_name;
int i;
if (!codec->component.debugfs_root) {
adsp_err(dsp, "No codec debugfs root\n");
goto err;
}
root_name = kmalloc(PAGE_SIZE, GFP_KERNEL);
if (!root_name)
goto err;
snprintf(root_name, PAGE_SIZE, "dsp%d", dsp->num);
root = debugfs_create_dir(root_name, codec->component.debugfs_root);
kfree(root_name);
if (!root)
goto err;
if (!debugfs_create_bool("running", S_IRUGO, root, &dsp->running))
goto err;
if (!debugfs_create_x32("fw_id", S_IRUGO, root, &dsp->fw_id))
goto err;
if (!debugfs_create_x32("fw_version", S_IRUGO, root,
&dsp->fw_id_version))
goto err;
for (i = 0; i < ARRAY_SIZE(wm_adsp_debugfs_fops); ++i) {
if (!debugfs_create_file(wm_adsp_debugfs_fops[i].name,
S_IRUGO, root, dsp,
&wm_adsp_debugfs_fops[i].fops))
goto err;
}
dsp->debugfs_root = root;
return;
err:
debugfs_remove_recursive(root);
adsp_err(dsp, "Failed to create debugfs\n");
}
static void wm_adsp2_cleanup_debugfs(struct wm_adsp *dsp)
{
wm_adsp_debugfs_clear(dsp);
debugfs_remove_recursive(dsp->debugfs_root);
}
#else
static inline void wm_adsp2_init_debugfs(struct wm_adsp *dsp,
struct snd_soc_codec *codec)
{
}
static inline void wm_adsp2_cleanup_debugfs(struct wm_adsp *dsp)
{
}
static inline void wm_adsp_debugfs_save_wmfwname(struct wm_adsp *dsp,
const char *s)
{
}
static inline void wm_adsp_debugfs_save_binname(struct wm_adsp *dsp,
const char *s)
{
}
static inline void wm_adsp_debugfs_clear(struct wm_adsp *dsp)
{
}
#endif
static int wm_adsp_fw_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@ -298,7 +468,6 @@ const struct snd_kcontrol_new wm_adsp1_fw_controls[] = {
};
EXPORT_SYMBOL_GPL(wm_adsp1_fw_controls);
#if IS_ENABLED(CONFIG_SND_SOC_ARIZONA)
static const struct soc_enum wm_adsp2_rate_enum[] = {
SOC_VALUE_ENUM_SINGLE(ARIZONA_DSP1_CONTROL_1,
ARIZONA_DSP1_RATE_SHIFT, 0xf,
@ -318,22 +487,28 @@ static const struct soc_enum wm_adsp2_rate_enum[] = {
arizona_rate_text, arizona_rate_val),
};
const struct snd_kcontrol_new wm_adsp2_fw_controls[] = {
SOC_ENUM_EXT("DSP1 Firmware", wm_adsp_fw_enum[0],
wm_adsp_fw_get, wm_adsp_fw_put),
SOC_ENUM("DSP1 Rate", wm_adsp2_rate_enum[0]),
SOC_ENUM_EXT("DSP2 Firmware", wm_adsp_fw_enum[1],
wm_adsp_fw_get, wm_adsp_fw_put),
SOC_ENUM("DSP2 Rate", wm_adsp2_rate_enum[1]),
SOC_ENUM_EXT("DSP3 Firmware", wm_adsp_fw_enum[2],
wm_adsp_fw_get, wm_adsp_fw_put),
SOC_ENUM("DSP3 Rate", wm_adsp2_rate_enum[2]),
SOC_ENUM_EXT("DSP4 Firmware", wm_adsp_fw_enum[3],
wm_adsp_fw_get, wm_adsp_fw_put),
SOC_ENUM("DSP4 Rate", wm_adsp2_rate_enum[3]),
static const struct snd_kcontrol_new wm_adsp2_fw_controls[4][2] = {
{
SOC_ENUM_EXT("DSP1 Firmware", wm_adsp_fw_enum[0],
wm_adsp_fw_get, wm_adsp_fw_put),
SOC_ENUM("DSP1 Rate", wm_adsp2_rate_enum[0]),
},
{
SOC_ENUM_EXT("DSP2 Firmware", wm_adsp_fw_enum[1],
wm_adsp_fw_get, wm_adsp_fw_put),
SOC_ENUM("DSP2 Rate", wm_adsp2_rate_enum[1]),
},
{
SOC_ENUM_EXT("DSP3 Firmware", wm_adsp_fw_enum[2],
wm_adsp_fw_get, wm_adsp_fw_put),
SOC_ENUM("DSP3 Rate", wm_adsp2_rate_enum[2]),
},
{
SOC_ENUM_EXT("DSP4 Firmware", wm_adsp_fw_enum[3],
wm_adsp_fw_get, wm_adsp_fw_put),
SOC_ENUM("DSP4 Rate", wm_adsp2_rate_enum[3]),
},
};
EXPORT_SYMBOL_GPL(wm_adsp2_fw_controls);
#endif
static struct wm_adsp_region const *wm_adsp_find_region(struct wm_adsp *dsp,
int type)
@ -1128,6 +1303,8 @@ static int wm_adsp_load(struct wm_adsp *dsp)
adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n",
file, regions, pos - firmware->size);
wm_adsp_debugfs_save_wmfwname(dsp, file);
out_fw:
regmap_async_complete(regmap);
wm_adsp_buf_free(&buf_list);
@ -1345,11 +1522,12 @@ static int wm_adsp2_setup_algs(struct wm_adsp *dsp)
n_algs = be32_to_cpu(adsp2_id.n_algs);
dsp->fw_id = be32_to_cpu(adsp2_id.fw.id);
dsp->fw_id_version = be32_to_cpu(adsp2_id.fw.ver);
adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n",
dsp->fw_id,
(be32_to_cpu(adsp2_id.fw.ver) & 0xff0000) >> 16,
(be32_to_cpu(adsp2_id.fw.ver) & 0xff00) >> 8,
be32_to_cpu(adsp2_id.fw.ver) & 0xff,
(dsp->fw_id_version & 0xff0000) >> 16,
(dsp->fw_id_version & 0xff00) >> 8,
dsp->fw_id_version & 0xff,
n_algs);
alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_XM,
@ -1625,6 +1803,8 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp)
adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n",
file, blocks, pos - firmware->size);
wm_adsp_debugfs_save_binname(dsp, file);
out_fw:
regmap_async_complete(regmap);
release_firmware(firmware);
@ -1638,6 +1818,9 @@ int wm_adsp1_init(struct wm_adsp *dsp)
{
INIT_LIST_HEAD(&dsp->alg_regions);
#ifdef CONFIG_DEBUG_FS
mutex_init(&dsp->debugfs_lock);
#endif
return 0;
}
EXPORT_SYMBOL_GPL(wm_adsp1_init);
@ -1896,6 +2079,10 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
/* Log firmware state, it can be useful for analysis */
wm_adsp2_show_fw_status(dsp);
wm_adsp_debugfs_clear(dsp);
dsp->fw_id = 0;
dsp->fw_id_version = 0;
dsp->running = false;
regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
@ -1933,6 +2120,24 @@ err:
}
EXPORT_SYMBOL_GPL(wm_adsp2_event);
int wm_adsp2_codec_probe(struct wm_adsp *dsp, struct snd_soc_codec *codec)
{
wm_adsp2_init_debugfs(dsp, codec);
return snd_soc_add_codec_controls(codec,
wm_adsp2_fw_controls[dsp->num - 1],
ARRAY_SIZE(wm_adsp2_fw_controls[0]));
}
EXPORT_SYMBOL_GPL(wm_adsp2_codec_probe);
int wm_adsp2_codec_remove(struct wm_adsp *dsp, struct snd_soc_codec *codec)
{
wm_adsp2_cleanup_debugfs(dsp);
return 0;
}
EXPORT_SYMBOL_GPL(wm_adsp2_codec_remove);
int wm_adsp2_init(struct wm_adsp *dsp)
{
int ret;
@ -1952,6 +2157,9 @@ int wm_adsp2_init(struct wm_adsp *dsp)
INIT_LIST_HEAD(&dsp->ctl_list);
INIT_WORK(&dsp->boot_work, wm_adsp2_boot_work);
#ifdef CONFIG_DEBUG_FS
mutex_init(&dsp->debugfs_lock);
#endif
return 0;
}
EXPORT_SYMBOL_GPL(wm_adsp2_init);

View File

@ -46,17 +46,26 @@ struct wm_adsp {
struct list_head alg_regions;
int fw_id;
int fw_id_version;
const struct wm_adsp_region *mem;
int num_mems;
int fw;
int fw_ver;
bool running;
u32 running;
struct list_head ctl_list;
struct work_struct boot_work;
#ifdef CONFIG_DEBUG_FS
struct dentry *debugfs_root;
struct mutex debugfs_lock;
char *wmfw_file_name;
char *bin_file_name;
#endif
};
#define WM_ADSP1(wname, num) \
@ -75,10 +84,11 @@ struct wm_adsp {
WM_ADSP2_E(wname, num, wm_adsp2_early_event)
extern const struct snd_kcontrol_new wm_adsp1_fw_controls[];
extern const struct snd_kcontrol_new wm_adsp2_fw_controls[];
int wm_adsp1_init(struct wm_adsp *dsp);
int wm_adsp2_init(struct wm_adsp *dsp);
int wm_adsp2_codec_probe(struct wm_adsp *dsp, struct snd_soc_codec *codec);
int wm_adsp2_codec_remove(struct wm_adsp *dsp, struct snd_soc_codec *codec);
int wm_adsp1_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event);
int wm_adsp2_early_event(struct snd_soc_dapm_widget *w,

View File

@ -686,6 +686,8 @@ static int mcasp_common_hw_param(struct davinci_mcasp *mcasp, int stream,
if (mcasp->serial_dir[i] == TX_MODE &&
tx_ser < max_active_serializers) {
mcasp_set_bits(mcasp, DAVINCI_MCASP_PDIR_REG, AXR(i));
mcasp_mod_bits(mcasp, DAVINCI_MCASP_XRSRCTL_REG(i),
DISMOD_LOW, DISMOD_MASK);
tx_ser++;
} else if (mcasp->serial_dir[i] == RX_MODE &&
rx_ser < max_active_serializers) {
@ -1565,6 +1567,49 @@ static int davinci_mcasp_init_ch_constraints(struct davinci_mcasp *mcasp)
return ret;
}
enum {
PCM_EDMA,
PCM_SDMA,
};
static const char *sdma_prefix = "ti,omap";
static int davinci_mcasp_get_dma_type(struct davinci_mcasp *mcasp)
{
struct dma_chan *chan;
const char *tmp;
int ret = PCM_EDMA;
if (!mcasp->dev->of_node)
return PCM_EDMA;
tmp = mcasp->dma_data[SNDRV_PCM_STREAM_PLAYBACK].filter_data;
chan = dma_request_slave_channel_reason(mcasp->dev, tmp);
if (IS_ERR(chan)) {
if (PTR_ERR(chan) != -EPROBE_DEFER)
dev_err(mcasp->dev,
"Can't verify DMA configuration (%ld)\n",
PTR_ERR(chan));
return PTR_ERR(chan);
}
BUG_ON(!chan->device || !chan->device->dev);
if (chan->device->dev->of_node)
ret = of_property_read_string(chan->device->dev->of_node,
"compatible", &tmp);
else
dev_dbg(mcasp->dev, "DMA controller has no of-node\n");
dma_release_channel(chan);
if (ret)
return ret;
dev_dbg(mcasp->dev, "DMA controller compatible = \"%s\"\n", tmp);
if (!strncmp(tmp, sdma_prefix, strlen(sdma_prefix)))
return PCM_SDMA;
return PCM_EDMA;
}
static int davinci_mcasp_probe(struct platform_device *pdev)
{
struct snd_dmaengine_dai_dma_data *dma_data;
@ -1763,27 +1808,34 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
if (ret != 0)
goto err;
switch (mcasp->version) {
ret = davinci_mcasp_get_dma_type(mcasp);
switch (ret) {
case PCM_EDMA:
#if IS_BUILTIN(CONFIG_SND_EDMA_SOC) || \
(IS_MODULE(CONFIG_SND_DAVINCI_SOC_MCASP) && \
IS_MODULE(CONFIG_SND_EDMA_SOC))
case MCASP_VERSION_1:
case MCASP_VERSION_2:
case MCASP_VERSION_3:
ret = edma_pcm_platform_register(&pdev->dev);
break;
#else
dev_err(&pdev->dev, "Missing SND_EDMA_SOC\n");
ret = -EINVAL;
goto err;
#endif
break;
case PCM_SDMA:
#if IS_BUILTIN(CONFIG_SND_OMAP_SOC) || \
(IS_MODULE(CONFIG_SND_DAVINCI_SOC_MCASP) && \
IS_MODULE(CONFIG_SND_OMAP_SOC))
case MCASP_VERSION_4:
ret = omap_pcm_platform_register(&pdev->dev);
break;
#endif
default:
dev_err(&pdev->dev, "Invalid McASP version: %d\n",
mcasp->version);
#else
dev_err(&pdev->dev, "Missing SND_SDMA_SOC\n");
ret = -EINVAL;
goto err;
#endif
break;
default:
dev_err(&pdev->dev, "No DMA controller found (%d)\n", ret);
case -EPROBE_DEFER:
goto err;
break;
}

View File

@ -215,7 +215,10 @@
* DAVINCI_MCASP_XRSRCTL_BASE_REG - Serializer Control Register Bits
*/
#define MODE(val) (val)
#define DISMOD (val)(val<<2)
#define DISMOD_3STATE (0x0)
#define DISMOD_LOW (0x2 << 2)
#define DISMOD_HIGH (0x3 << 2)
#define DISMOD_MASK DISMOD_HIGH
#define TXSTATE BIT(4)
#define RXSTATE BIT(5)
#define SRMOD_MASK 3

View File

@ -190,7 +190,7 @@ static int imx_wm8962_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "audmux internal port setup failed\n");
return ret;
}
imx_audmux_v2_configure_port(ext_port,
ret = imx_audmux_v2_configure_port(ext_port,
IMX_AUDMUX_V2_PTCR_SYN,
IMX_AUDMUX_V2_PDCR_RXDSEL(int_port));
if (ret) {

View File

@ -26,6 +26,7 @@ struct simple_card_data {
struct simple_dai_props {
struct asoc_simple_dai cpu_dai;
struct asoc_simple_dai codec_dai;
unsigned int mclk_fs;
} *dai_props;
unsigned int mclk_fs;
int gpio_hp_det;
@ -76,11 +77,18 @@ static int asoc_simple_card_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *codec_dai = rtd->codec_dai;
struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
unsigned int mclk;
struct simple_dai_props *dai_props =
&priv->dai_props[rtd - rtd->card->rtd];
unsigned int mclk, mclk_fs = 0;
int ret = 0;
if (priv->mclk_fs) {
mclk = params_rate(params) * priv->mclk_fs;
if (priv->mclk_fs)
mclk_fs = priv->mclk_fs;
else if (dai_props->mclk_fs)
mclk_fs = dai_props->mclk_fs;
if (mclk_fs) {
mclk = params_rate(params) * mclk_fs;
ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk,
SND_SOC_CLOCK_IN);
}
@ -313,6 +321,7 @@ static int asoc_simple_card_dai_link_of(struct device_node *node,
char prop[128];
char *prefix = "";
int ret, cpu_args;
u32 val;
/* For single DAI link & old style of DT node */
if (is_top_level_node)
@ -338,6 +347,9 @@ static int asoc_simple_card_dai_link_of(struct device_node *node,
if (ret < 0)
goto dai_link_of_err;
if (!of_property_read_u32(node, "mclk-fs", &val))
dai_props->mclk_fs = val;
ret = asoc_simple_card_sub_parse_of(cpu, &dai_props->cpu_dai,
&dai_link->cpu_of_node,
&dai_link->cpu_dai_name,

View File

@ -112,7 +112,7 @@ config SND_SOC_INTEL_CHT_BSW_RT5672_MACH
config SND_SOC_INTEL_CHT_BSW_RT5645_MACH
tristate "ASoC Audio driver for Intel Cherrytrail & Braswell with RT5645/5650 codec"
depends on X86_INTEL_LPSS
depends on X86_INTEL_LPSS && I2C
select SND_SOC_RT5645
select SND_SST_MFLD_PLATFORM
select SND_SST_IPC_ACPI
@ -123,7 +123,7 @@ config SND_SOC_INTEL_CHT_BSW_RT5645_MACH
config SND_SOC_INTEL_CHT_BSW_MAX98090_TI_MACH
tristate "ASoC Audio driver for Intel Cherrytrail & Braswell with MAX98090 & TI codec"
depends on X86_INTEL_LPSS
depends on X86_INTEL_LPSS && I2C
select SND_SOC_MAX98090
select SND_SOC_TS3A227E
select SND_SST_MFLD_PLATFORM

View File

@ -1401,36 +1401,32 @@ static int sst_fill_widget_module_info(struct snd_soc_dapm_widget *w,
down_read(&card->controls_rwsem);
list_for_each_entry(kctl, &card->controls, list) {
idx = strstr(kctl->id.name, " ");
idx = strchr(kctl->id.name, ' ');
if (idx == NULL)
continue;
index = strlen(kctl->id.name) - strlen(idx);
index = idx - (char*)kctl->id.name;
if (strncmp(kctl->id.name, w->name, index))
continue;
if (strstr(kctl->id.name, "Volume") &&
!strncmp(kctl->id.name, w->name, index))
if (strstr(kctl->id.name, "Volume"))
ret = sst_fill_module_list(kctl, w, SST_MODULE_GAIN);
else if (strstr(kctl->id.name, "params") &&
!strncmp(kctl->id.name, w->name, index))
else if (strstr(kctl->id.name, "params"))
ret = sst_fill_module_list(kctl, w, SST_MODULE_ALGO);
else if (strstr(kctl->id.name, "Switch") &&
!strncmp(kctl->id.name, w->name, index) &&
strstr(kctl->id.name, "Gain")) {
struct sst_gain_mixer_control *mc =
(void *)kctl->private_value;
mc->w = w;
} else if (strstr(kctl->id.name, "interleaver") &&
!strncmp(kctl->id.name, w->name, index)) {
} else if (strstr(kctl->id.name, "interleaver")) {
struct sst_enum *e = (void *)kctl->private_value;
e->w = w;
} else if (strstr(kctl->id.name, "deinterleaver") &&
!strncmp(kctl->id.name, w->name, index)) {
} else if (strstr(kctl->id.name, "deinterleaver")) {
struct sst_enum *e = (void *)kctl->private_value;
e->w = w;

View File

@ -368,8 +368,8 @@ static inline void sst_restore_shim64(struct intel_sst_drv *ctx,
* initialize by FW or driver when firmware is loaded
*/
spin_lock_irqsave(&ctx->ipc_spin_lock, irq_flags);
sst_shim_write64(shim, SST_IMRX, shim_regs->imrx),
sst_shim_write64(shim, SST_CSR, shim_regs->csr),
sst_shim_write64(shim, SST_IMRX, shim_regs->imrx);
sst_shim_write64(shim, SST_CSR, shim_regs->csr);
spin_unlock_irqrestore(&ctx->ipc_spin_lock, irq_flags);
}

View File

@ -101,6 +101,33 @@ static int cht_aif1_hw_params(struct snd_pcm_substream *substream,
return 0;
}
static int cht_ti_jack_event(struct notifier_block *nb,
unsigned long event, void *data)
{
struct snd_soc_jack *jack = (struct snd_soc_jack *)data;
struct snd_soc_dai *codec_dai = jack->card->rtd->codec_dai;
struct snd_soc_codec *codec = codec_dai->codec;
if (event & SND_JACK_MICROPHONE) {
snd_soc_dapm_force_enable_pin(&codec->dapm, "SHDN");
snd_soc_dapm_force_enable_pin(&codec->dapm, "MICBIAS");
snd_soc_dapm_sync(&codec->dapm);
} else {
snd_soc_dapm_disable_pin(&codec->dapm, "MICBIAS");
snd_soc_dapm_disable_pin(&codec->dapm, "SHDN");
snd_soc_dapm_sync(&codec->dapm);
}
return 0;
}
static struct notifier_block cht_jack_nb = {
.notifier_call = cht_ti_jack_event,
};
static int cht_codec_init(struct snd_soc_pcm_runtime *runtime)
{
int ret;
@ -130,6 +157,9 @@ static int cht_codec_init(struct snd_soc_pcm_runtime *runtime)
return ret;
}
if (ctx->ts3a227e_present)
snd_soc_jack_notifier_register(jack, &cht_jack_nb);
return ret;
}

View File

@ -263,7 +263,7 @@ static struct sst_acpi_desc sst_acpi_baytrail_desc = {
.resindex_dma_base = -1,
};
static struct acpi_device_id sst_acpi_match[] = {
static const struct acpi_device_id sst_acpi_match[] = {
{ "INT33C8", (unsigned long)&sst_acpi_haswell_desc },
{ "INT3438", (unsigned long)&sst_acpi_broadwell_desc },
{ "80860F28", (unsigned long)&sst_acpi_baytrail_desc },

View File

@ -0,0 +1,30 @@
config SND_SOC_MEDIATEK
tristate "ASoC support for Mediatek chip"
depends on ARCH_MEDIATEK
help
This adds ASoC platform driver support for Mediatek chip
that can be used with other codecs.
Select Y if you have such device.
Ex: MT8173
config SND_SOC_MT8173_MAX98090
tristate "ASoC Audio driver for MT8173 with MAX98090 codec"
depends on SND_SOC_MEDIATEK
select SND_SOC_MAX98090
help
This adds ASoC driver for Mediatek MT8173 boards
with the MAX98090 audio codec.
Select Y if you have such device.
If unsure select "N".
config SND_SOC_MT8173_RT5650_RT5676
tristate "ASoC Audio driver for MT8173 with RT5650 RT5676 codecs"
depends on SND_SOC_MEDIATEK
select SND_SOC_RT5645
select SND_SOC_RT5677
help
This adds ASoC driver for Mediatek MT8173 boards
with the RT5650 and RT5676 codecs.
Select Y if you have such device.
If unsure select "N".

View File

@ -0,0 +1,5 @@
# MTK Platform Support
obj-$(CONFIG_SND_SOC_MEDIATEK) += mtk-afe-pcm.o
# Machine support
obj-$(CONFIG_SND_SOC_MT8173_MAX98090) += mt8173-max98090.o
obj-$(CONFIG_SND_SOC_MT8173_RT5650_RT5676) += mt8173-rt5650-rt5676.o

View File

@ -0,0 +1,213 @@
/*
* mt8173-max98090.c -- MT8173 MAX98090 ALSA SoC machine driver
*
* Copyright (c) 2015 MediaTek Inc.
* Author: Koro Chen <koro.chen@mediatek.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 and
* only 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 <sound/soc.h>
#include <sound/jack.h>
#include <linux/gpio.h>
#include "../codecs/max98090.h"
static struct snd_soc_jack mt8173_max98090_jack;
static struct snd_soc_jack_pin mt8173_max98090_jack_pins[] = {
{
.pin = "Headphone",
.mask = SND_JACK_HEADPHONE,
},
{
.pin = "Headset Mic",
.mask = SND_JACK_MICROPHONE,
},
};
static const struct snd_soc_dapm_widget mt8173_max98090_widgets[] = {
SND_SOC_DAPM_SPK("Speaker", NULL),
SND_SOC_DAPM_MIC("Int Mic", NULL),
SND_SOC_DAPM_HP("Headphone", NULL),
SND_SOC_DAPM_MIC("Headset Mic", NULL),
};
static const struct snd_soc_dapm_route mt8173_max98090_routes[] = {
{"Speaker", NULL, "SPKL"},
{"Speaker", NULL, "SPKR"},
{"DMICL", NULL, "Int Mic"},
{"Headphone", NULL, "HPL"},
{"Headphone", NULL, "HPR"},
{"Headset Mic", NULL, "MICBIAS"},
{"IN34", NULL, "Headset Mic"},
};
static const struct snd_kcontrol_new mt8173_max98090_controls[] = {
SOC_DAPM_PIN_SWITCH("Speaker"),
SOC_DAPM_PIN_SWITCH("Int Mic"),
SOC_DAPM_PIN_SWITCH("Headphone"),
SOC_DAPM_PIN_SWITCH("Headset Mic"),
};
static int mt8173_max98090_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *codec_dai = rtd->codec_dai;
return snd_soc_dai_set_sysclk(codec_dai, 0, params_rate(params) * 256,
SND_SOC_CLOCK_IN);
}
static struct snd_soc_ops mt8173_max98090_ops = {
.hw_params = mt8173_max98090_hw_params,
};
static int mt8173_max98090_init(struct snd_soc_pcm_runtime *runtime)
{
int ret;
struct snd_soc_card *card = runtime->card;
struct snd_soc_codec *codec = runtime->codec;
/* enable jack detection */
ret = snd_soc_card_jack_new(card, "Headphone", SND_JACK_HEADPHONE,
&mt8173_max98090_jack, NULL, 0);
if (ret) {
dev_err(card->dev, "Can't snd_soc_jack_new %d\n", ret);
return ret;
}
ret = snd_soc_jack_add_pins(&mt8173_max98090_jack,
ARRAY_SIZE(mt8173_max98090_jack_pins),
mt8173_max98090_jack_pins);
if (ret) {
dev_err(card->dev, "Can't snd_soc_jack_add_pins %d\n", ret);
return ret;
}
return max98090_mic_detect(codec, &mt8173_max98090_jack);
}
/* Digital audio interface glue - connects codec <---> CPU */
static struct snd_soc_dai_link mt8173_max98090_dais[] = {
/* Front End DAI links */
{
.name = "MAX98090 Playback",
.stream_name = "MAX98090 Playback",
.cpu_dai_name = "DL1",
.platform_name = "11220000.mt8173-afe-pcm",
.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,
},
{
.name = "MAX98090 Capture",
.stream_name = "MAX98090 Capture",
.cpu_dai_name = "VUL",
.platform_name = "11220000.mt8173-afe-pcm",
.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_capture = 1,
},
/* Back End DAI links */
{
.name = "Codec",
.cpu_dai_name = "I2S",
.platform_name = "11220000.mt8173-afe-pcm",
.no_pcm = 1,
.codec_dai_name = "HiFi",
.init = mt8173_max98090_init,
.ops = &mt8173_max98090_ops,
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBS_CFS,
.dpcm_playback = 1,
.dpcm_capture = 1,
},
};
static struct snd_soc_card mt8173_max98090_card = {
.name = "mt8173-max98090",
.dai_link = mt8173_max98090_dais,
.num_links = ARRAY_SIZE(mt8173_max98090_dais),
.controls = mt8173_max98090_controls,
.num_controls = ARRAY_SIZE(mt8173_max98090_controls),
.dapm_widgets = mt8173_max98090_widgets,
.num_dapm_widgets = ARRAY_SIZE(mt8173_max98090_widgets),
.dapm_routes = mt8173_max98090_routes,
.num_dapm_routes = ARRAY_SIZE(mt8173_max98090_routes),
};
static int mt8173_max98090_dev_probe(struct platform_device *pdev)
{
struct snd_soc_card *card = &mt8173_max98090_card;
struct device_node *codec_node;
int ret, i;
codec_node = of_parse_phandle(pdev->dev.of_node,
"mediatek,audio-codec", 0);
if (!codec_node) {
dev_err(&pdev->dev,
"Property 'audio-codec' missing or invalid\n");
return -EINVAL;
}
for (i = 0; i < card->num_links; i++) {
if (mt8173_max98090_dais[i].codec_name)
continue;
mt8173_max98090_dais[i].codec_of_node = codec_node;
}
card->dev = &pdev->dev;
ret = snd_soc_register_card(card);
if (ret)
dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n",
__func__, ret);
return ret;
}
static int mt8173_max98090_dev_remove(struct platform_device *pdev)
{
struct snd_soc_card *card = platform_get_drvdata(pdev);
snd_soc_unregister_card(card);
return 0;
}
static const struct of_device_id mt8173_max98090_dt_match[] = {
{ .compatible = "mediatek,mt8173-max98090", },
{ }
};
MODULE_DEVICE_TABLE(of, mt8173_max98090_dt_match);
static struct platform_driver mt8173_max98090_driver = {
.driver = {
.name = "mt8173-max98090",
.owner = THIS_MODULE,
.of_match_table = mt8173_max98090_dt_match,
#ifdef CONFIG_PM
.pm = &snd_soc_pm_ops,
#endif
},
.probe = mt8173_max98090_dev_probe,
.remove = mt8173_max98090_dev_remove,
};
module_platform_driver(mt8173_max98090_driver);
/* Module information */
MODULE_DESCRIPTION("MT8173 MAX98090 ALSA SoC machine driver");
MODULE_AUTHOR("Koro Chen <koro.chen@mediatek.com>");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:mt8173-max98090");

View File

@ -0,0 +1,278 @@
/*
* mt8173-rt5650-rt5676.c -- MT8173 machine driver with RT5650/5676 codecs
*
* Copyright (c) 2015 MediaTek Inc.
* Author: Koro Chen <koro.chen@mediatek.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 and
* only 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/gpio.h>
#include <linux/of_gpio.h>
#include <sound/soc.h>
#include <sound/jack.h>
#include "../codecs/rt5645.h"
#include "../codecs/rt5677.h"
#define MCLK_FOR_CODECS 12288000
static const struct snd_soc_dapm_widget mt8173_rt5650_rt5676_widgets[] = {
SND_SOC_DAPM_SPK("Speaker", NULL),
SND_SOC_DAPM_MIC("Int Mic", NULL),
SND_SOC_DAPM_HP("Headphone", NULL),
SND_SOC_DAPM_MIC("Headset Mic", NULL),
};
static const struct snd_soc_dapm_route mt8173_rt5650_rt5676_routes[] = {
{"Speaker", NULL, "SPOL"},
{"Speaker", NULL, "SPOR"},
{"Speaker", NULL, "Sub AIF2TX"}, /* IF2 ADC to 5650 */
{"Sub DMIC L1", NULL, "Int Mic"}, /* DMIC from 5676 */
{"Sub DMIC R1", NULL, "Int Mic"},
{"Headphone", NULL, "HPOL"},
{"Headphone", NULL, "HPOR"},
{"Headphone", NULL, "Sub AIF2TX"}, /* IF2 ADC to 5650 */
{"Headset Mic", NULL, "micbias1"},
{"Headset Mic", NULL, "micbias2"},
{"IN1P", NULL, "Headset Mic"},
{"IN1N", NULL, "Headset Mic"},
{"Sub AIF2RX", NULL, "Headset Mic"}, /* IF2 DAC from 5650 */
};
static const struct snd_kcontrol_new mt8173_rt5650_rt5676_controls[] = {
SOC_DAPM_PIN_SWITCH("Speaker"),
SOC_DAPM_PIN_SWITCH("Int Mic"),
SOC_DAPM_PIN_SWITCH("Headphone"),
SOC_DAPM_PIN_SWITCH("Headset Mic"),
};
static int mt8173_rt5650_rt5676_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
int i, ret;
for (i = 0; i < rtd->num_codecs; i++) {
struct snd_soc_dai *codec_dai = rtd->codec_dais[i];
/* pll from mclk 12.288M */
ret = snd_soc_dai_set_pll(codec_dai, 0, 0, MCLK_FOR_CODECS,
params_rate(params) * 512);
if (ret)
return ret;
/* sysclk from pll */
ret = snd_soc_dai_set_sysclk(codec_dai, 1,
params_rate(params) * 512,
SND_SOC_CLOCK_IN);
if (ret)
return ret;
}
return 0;
}
static struct snd_soc_ops mt8173_rt5650_rt5676_ops = {
.hw_params = mt8173_rt5650_rt5676_hw_params,
};
static struct snd_soc_jack mt8173_rt5650_rt5676_jack;
static int mt8173_rt5650_rt5676_init(struct snd_soc_pcm_runtime *runtime)
{
struct snd_soc_card *card = runtime->card;
struct snd_soc_codec *codec = runtime->codec_dais[0]->codec;
struct snd_soc_codec *codec_sub = runtime->codec_dais[1]->codec;
int ret;
rt5645_sel_asrc_clk_src(codec,
RT5645_DA_STEREO_FILTER |
RT5645_AD_STEREO_FILTER,
RT5645_CLK_SEL_I2S1_ASRC);
rt5677_sel_asrc_clk_src(codec_sub,
RT5677_DA_STEREO_FILTER |
RT5677_AD_STEREO1_FILTER,
RT5677_CLK_SEL_I2S1_ASRC);
rt5677_sel_asrc_clk_src(codec_sub,
RT5677_AD_STEREO2_FILTER |
RT5677_I2S2_SOURCE,
RT5677_CLK_SEL_I2S2_ASRC);
/* enable jack detection */
ret = snd_soc_card_jack_new(card, "Headset Jack",
SND_JACK_HEADPHONE | SND_JACK_MICROPHONE |
SND_JACK_BTN_0 | SND_JACK_BTN_1 |
SND_JACK_BTN_2 | SND_JACK_BTN_3,
&mt8173_rt5650_rt5676_jack, NULL, 0);
if (ret) {
dev_err(card->dev, "Can't new Headset Jack %d\n", ret);
return ret;
}
return rt5645_set_jack_detect(codec,
&mt8173_rt5650_rt5676_jack,
&mt8173_rt5650_rt5676_jack,
&mt8173_rt5650_rt5676_jack);
}
static struct snd_soc_dai_link_component mt8173_rt5650_rt5676_codecs[] = {
{
.dai_name = "rt5645-aif1",
},
{
.dai_name = "rt5677-aif1",
},
};
/* Digital audio interface glue - connects codec <---> CPU */
static struct snd_soc_dai_link mt8173_rt5650_rt5676_dais[] = {
/* Front End DAI links */
{
.name = "rt5650_rt5676 Playback",
.stream_name = "rt5650_rt5676 Playback",
.cpu_dai_name = "DL1",
.platform_name = "11220000.mt8173-afe-pcm",
.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,
},
{
.name = "rt5650_rt5676 Capture",
.stream_name = "rt5650_rt5676 Capture",
.cpu_dai_name = "VUL",
.platform_name = "11220000.mt8173-afe-pcm",
.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_capture = 1,
},
/* Back End DAI links */
{
.name = "Codec",
.cpu_dai_name = "I2S",
.platform_name = "11220000.mt8173-afe-pcm",
.no_pcm = 1,
.codecs = mt8173_rt5650_rt5676_codecs,
.num_codecs = 2,
.init = mt8173_rt5650_rt5676_init,
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBS_CFS,
.ops = &mt8173_rt5650_rt5676_ops,
.ignore_pmdown_time = 1,
.dpcm_playback = 1,
.dpcm_capture = 1,
},
{ /* rt5676 <-> rt5650 intercodec link: Sets rt5676 I2S2 as master */
.name = "rt5650_rt5676 intercodec",
.stream_name = "rt5650_rt5676 intercodec",
.cpu_dai_name = "snd-soc-dummy-dai",
.platform_name = "snd-soc-dummy",
.no_pcm = 1,
.codec_dai_name = "rt5677-aif2",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBM_CFM,
},
};
static struct snd_soc_codec_conf mt8173_rt5650_rt5676_codec_conf[] = {
{
.name_prefix = "Sub",
},
};
static struct snd_soc_card mt8173_rt5650_rt5676_card = {
.name = "mtk-rt5650-rt5676",
.dai_link = mt8173_rt5650_rt5676_dais,
.num_links = ARRAY_SIZE(mt8173_rt5650_rt5676_dais),
.codec_conf = mt8173_rt5650_rt5676_codec_conf,
.num_configs = ARRAY_SIZE(mt8173_rt5650_rt5676_codec_conf),
.controls = mt8173_rt5650_rt5676_controls,
.num_controls = ARRAY_SIZE(mt8173_rt5650_rt5676_controls),
.dapm_widgets = mt8173_rt5650_rt5676_widgets,
.num_dapm_widgets = ARRAY_SIZE(mt8173_rt5650_rt5676_widgets),
.dapm_routes = mt8173_rt5650_rt5676_routes,
.num_dapm_routes = ARRAY_SIZE(mt8173_rt5650_rt5676_routes),
};
static int mt8173_rt5650_rt5676_dev_probe(struct platform_device *pdev)
{
struct snd_soc_card *card = &mt8173_rt5650_rt5676_card;
int ret;
mt8173_rt5650_rt5676_codecs[0].of_node =
of_parse_phandle(pdev->dev.of_node, "mediatek,audio-codec", 0);
if (!mt8173_rt5650_rt5676_codecs[0].of_node) {
dev_err(&pdev->dev,
"Property 'audio-codec' missing or invalid\n");
return -EINVAL;
}
mt8173_rt5650_rt5676_codecs[1].of_node =
of_parse_phandle(pdev->dev.of_node, "mediatek,audio-codec", 1);
if (!mt8173_rt5650_rt5676_codecs[1].of_node) {
dev_err(&pdev->dev,
"Property 'audio-codec' missing or invalid\n");
return -EINVAL;
}
mt8173_rt5650_rt5676_codec_conf[0].of_node =
mt8173_rt5650_rt5676_codecs[1].of_node;
mt8173_rt5650_rt5676_dais[3].codec_of_node =
mt8173_rt5650_rt5676_codecs[1].of_node;
card->dev = &pdev->dev;
platform_set_drvdata(pdev, card);
ret = snd_soc_register_card(card);
if (ret)
dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n",
__func__, ret);
return ret;
}
static int mt8173_rt5650_rt5676_dev_remove(struct platform_device *pdev)
{
struct snd_soc_card *card = platform_get_drvdata(pdev);
snd_soc_unregister_card(card);
return 0;
}
static const struct of_device_id mt8173_rt5650_rt5676_dt_match[] = {
{ .compatible = "mediatek,mt8173-rt5650-rt5676", },
{ }
};
MODULE_DEVICE_TABLE(of, mt8173_rt5650_rt5676_dt_match);
static struct platform_driver mt8173_rt5650_rt5676_driver = {
.driver = {
.name = "mtk-rt5650-rt5676",
.owner = THIS_MODULE,
.of_match_table = mt8173_rt5650_rt5676_dt_match,
#ifdef CONFIG_PM
.pm = &snd_soc_pm_ops,
#endif
},
.probe = mt8173_rt5650_rt5676_dev_probe,
.remove = mt8173_rt5650_rt5676_dev_remove,
};
module_platform_driver(mt8173_rt5650_rt5676_driver);
/* Module information */
MODULE_DESCRIPTION("MT8173 RT5650 and RT5676 SoC machine driver");
MODULE_AUTHOR("Koro Chen <koro.chen@mediatek.com>");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:mtk-rt5650-rt5676");

View File

@ -0,0 +1,109 @@
/*
* mtk_afe_common.h -- Mediatek audio driver common definitions
*
* Copyright (c) 2015 MediaTek Inc.
* Author: Koro Chen <koro.chen@mediatek.com>
* Sascha Hauer <s.hauer@pengutronix.de>
* Hidalgo Huang <hidalgo.huang@mediatek.com>
* Ir Lian <ir.lian@mediatek.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 and
* only 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 _MTK_AFE_COMMON_H_
#define _MTK_AFE_COMMON_H_
#include <linux/clk.h>
#include <linux/regmap.h>
enum {
MTK_AFE_MEMIF_DL1,
MTK_AFE_MEMIF_DL2,
MTK_AFE_MEMIF_VUL,
MTK_AFE_MEMIF_DAI,
MTK_AFE_MEMIF_AWB,
MTK_AFE_MEMIF_MOD_DAI,
MTK_AFE_MEMIF_HDMI,
MTK_AFE_MEMIF_NUM,
MTK_AFE_IO_MOD_PCM1 = MTK_AFE_MEMIF_NUM,
MTK_AFE_IO_MOD_PCM2,
MTK_AFE_IO_PMIC,
MTK_AFE_IO_I2S,
MTK_AFE_IO_2ND_I2S,
MTK_AFE_IO_HW_GAIN1,
MTK_AFE_IO_HW_GAIN2,
MTK_AFE_IO_MRG_O,
MTK_AFE_IO_MRG_I,
MTK_AFE_IO_DAIBT,
MTK_AFE_IO_HDMI,
};
enum {
MTK_AFE_IRQ_1,
MTK_AFE_IRQ_2,
MTK_AFE_IRQ_3,
MTK_AFE_IRQ_4,
MTK_AFE_IRQ_5,
MTK_AFE_IRQ_6,
MTK_AFE_IRQ_7,
MTK_AFE_IRQ_8,
MTK_AFE_IRQ_NUM,
};
enum {
MTK_CLK_INFRASYS_AUD,
MTK_CLK_TOP_PDN_AUD,
MTK_CLK_TOP_PDN_AUD_BUS,
MTK_CLK_I2S0_M,
MTK_CLK_I2S1_M,
MTK_CLK_I2S2_M,
MTK_CLK_I2S3_M,
MTK_CLK_I2S3_B,
MTK_CLK_BCK0,
MTK_CLK_BCK1,
MTK_CLK_NUM
};
struct mtk_afe;
struct snd_pcm_substream;
struct mtk_afe_memif_data {
int id;
const char *name;
int reg_ofs_base;
int reg_ofs_cur;
int fs_shift;
int mono_shift;
int enable_shift;
int irq_reg_cnt;
int irq_cnt_shift;
int irq_en_shift;
int irq_fs_shift;
int irq_clr_shift;
};
struct mtk_afe_memif {
unsigned int phys_buf_addr;
int buffer_size;
unsigned int hw_ptr; /* Previous IRQ's HW ptr */
struct snd_pcm_substream *substream;
const struct mtk_afe_memif_data *data;
const struct mtk_afe_irq_data *irqdata;
};
struct mtk_afe {
/* address for ioremap audio hardware register */
void __iomem *base_addr;
struct device *dev;
struct regmap *regmap;
struct mtk_afe_memif memif[MTK_AFE_MEMIF_NUM];
struct clk *clocks[MTK_CLK_NUM];
};
#endif

File diff suppressed because it is too large Load Diff

View File

@ -245,6 +245,8 @@ static const struct snd_soc_dapm_widget aic34_dapm_widgets[] = {
static const struct snd_soc_dapm_route audio_map[] = {
{"Ext Spk", NULL, "HPLOUT"},
{"Ext Spk", NULL, "HPROUT"},
{"Ext Spk", NULL, "HPLCOM"},
{"Ext Spk", NULL, "HPRCOM"},
{"Headphone Jack", NULL, "LLOUT"},
{"Headphone Jack", NULL, "RLOUT"},
{"FM Transmitter", NULL, "LLOUT"},
@ -288,15 +290,8 @@ static int rx51_aic34_init(struct snd_soc_pcm_runtime *rtd)
struct snd_soc_codec *codec = rtd->codec;
struct snd_soc_card *card = rtd->card;
struct rx51_audio_pdata *pdata = snd_soc_card_get_drvdata(card);
struct snd_soc_dapm_context *dapm = &codec->dapm;
int err;
/* Set up NC codec pins */
snd_soc_dapm_nc_pin(dapm, "MIC3L");
snd_soc_dapm_nc_pin(dapm, "MIC3R");
snd_soc_dapm_nc_pin(dapm, "LINE1R");
err = tpa6130a2_add_controls(codec);
if (err < 0) {
dev_err(card->dev, "Failed to add TPA6130A2 controls\n");
@ -383,6 +378,7 @@ static struct snd_soc_card rx51_sound_card = {
.num_aux_devs = ARRAY_SIZE(rx51_aux_dev),
.codec_conf = rx51_codec_conf,
.num_configs = ARRAY_SIZE(rx51_codec_conf),
.fully_routed = true,
.controls = aic34_rx51_controls,
.num_controls = ARRAY_SIZE(aic34_rx51_controls),

View File

@ -32,3 +32,12 @@ config SND_SOC_STORM
help
Say Y or M if you want add support for SoC audio on the
Qualcomm Technologies IPQ806X-based Storm board.
config SND_SOC_APQ8016_SBC
tristate "SoC Audio support for APQ8016 SBC platforms"
depends on SND_SOC_QCOM && (ARCH_QCOM || COMPILE_TEST)
select SND_SOC_LPASS_APQ8016
help
Support for Qualcomm Technologies LPASS audio block in
APQ8016 SOC-based systems.
Say Y if you want to use audio devices on MI2S.

View File

@ -11,5 +11,7 @@ obj-$(CONFIG_SND_SOC_LPASS_APQ8016) += snd-soc-lpass-apq8016.o
# Machine
snd-soc-storm-objs := storm.o
snd-soc-apq8016-sbc-objs := apq8016_sbc.o
obj-$(CONFIG_SND_SOC_STORM) += snd-soc-storm.o
obj-$(CONFIG_SND_SOC_APQ8016_SBC) += snd-soc-apq8016-sbc.o

View File

@ -0,0 +1,198 @@
/*
* Copyright (c) 2015 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only 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/device.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/clk.h>
#include <linux/platform_device.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <dt-bindings/sound/apq8016-lpass.h>
struct apq8016_sbc_data {
void __iomem *mic_iomux;
void __iomem *spkr_iomux;
struct snd_soc_dai_link dai_link[]; /* dynamically allocated */
};
#define MIC_CTRL_QUA_WS_SLAVE_SEL_10 BIT(17)
#define MIC_CTRL_TLMM_SCLK_EN BIT(1)
#define SPKR_CTL_PRI_WS_SLAVE_SEL_11 (BIT(17) | BIT(16))
static int apq8016_sbc_dai_init(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
struct snd_soc_card *card = rtd->card;
struct apq8016_sbc_data *pdata = snd_soc_card_get_drvdata(card);
int rval = 0;
switch (cpu_dai->id) {
case MI2S_PRIMARY:
writel(readl(pdata->spkr_iomux) | SPKR_CTL_PRI_WS_SLAVE_SEL_11,
pdata->spkr_iomux);
break;
case MI2S_QUATERNARY:
/* Configure the Quat MI2S to TLMM */
writel(readl(pdata->mic_iomux) | MIC_CTRL_QUA_WS_SLAVE_SEL_10 |
MIC_CTRL_TLMM_SCLK_EN,
pdata->mic_iomux);
break;
default:
dev_err(card->dev, "unsupported cpu dai configuration\n");
rval = -EINVAL;
break;
}
return rval;
}
static struct apq8016_sbc_data *apq8016_sbc_parse_of(struct snd_soc_card *card)
{
struct device *dev = card->dev;
struct snd_soc_dai_link *link;
struct device_node *np, *codec, *cpu, *node = dev->of_node;
struct apq8016_sbc_data *data;
int ret, num_links;
ret = snd_soc_of_parse_card_name(card, "qcom,model");
if (ret) {
dev_err(dev, "Error parsing card name: %d\n", ret);
return ERR_PTR(ret);
}
/* Populate links */
num_links = of_get_child_count(node);
/* Allocate the private data and the DAI link array */
data = devm_kzalloc(dev, sizeof(*data) + sizeof(*link) * num_links,
GFP_KERNEL);
if (!data)
return ERR_PTR(-ENOMEM);
card->dai_link = &data->dai_link[0];
card->num_links = num_links;
link = data->dai_link;
for_each_child_of_node(node, np) {
cpu = of_get_child_by_name(np, "cpu");
codec = of_get_child_by_name(np, "codec");
if (!cpu || !codec) {
dev_err(dev, "Can't find cpu/codec DT node\n");
return ERR_PTR(-EINVAL);
}
link->cpu_of_node = of_parse_phandle(cpu, "sound-dai", 0);
if (!link->cpu_of_node) {
dev_err(card->dev, "error getting cpu phandle\n");
return ERR_PTR(-EINVAL);
}
link->codec_of_node = of_parse_phandle(codec, "sound-dai", 0);
if (!link->codec_of_node) {
dev_err(card->dev, "error getting codec phandle\n");
return ERR_PTR(-EINVAL);
}
ret = snd_soc_of_get_dai_name(cpu, &link->cpu_dai_name);
if (ret) {
dev_err(card->dev, "error getting cpu dai name\n");
return ERR_PTR(ret);
}
ret = snd_soc_of_get_dai_name(codec, &link->codec_dai_name);
if (ret) {
dev_err(card->dev, "error getting codec dai name\n");
return ERR_PTR(ret);
}
link->platform_of_node = link->cpu_of_node;
/* For now we only support playback */
link->playback_only = true;
ret = of_property_read_string(np, "link-name", &link->name);
if (ret) {
dev_err(card->dev, "error getting codec dai_link name\n");
return ERR_PTR(ret);
}
link->stream_name = link->name;
link->init = apq8016_sbc_dai_init;
link++;
}
return data;
}
static int apq8016_sbc_platform_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct snd_soc_card *card;
struct apq8016_sbc_data *data;
struct resource *res;
card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
if (!card)
return -ENOMEM;
card->dev = dev;
data = apq8016_sbc_parse_of(card);
if (IS_ERR(data)) {
dev_err(&pdev->dev, "Error resolving dai links: %ld\n",
PTR_ERR(data));
return PTR_ERR(data);
}
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mic-iomux");
data->mic_iomux = devm_ioremap_resource(dev, res);
if (IS_ERR(data->mic_iomux))
return PTR_ERR(data->mic_iomux);
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "spkr-iomux");
data->spkr_iomux = devm_ioremap_resource(dev, res);
if (IS_ERR(data->spkr_iomux))
return PTR_ERR(data->spkr_iomux);
platform_set_drvdata(pdev, data);
snd_soc_card_set_drvdata(card, data);
return devm_snd_soc_register_card(&pdev->dev, card);
}
static const struct of_device_id apq8016_sbc_device_id[] = {
{ .compatible = "qcom,apq8016-sbc-sndcard" },
{},
};
MODULE_DEVICE_TABLE(of, apq8016_sbc_device_id);
static struct platform_driver apq8016_sbc_platform_driver = {
.driver = {
.name = "qcom-apq8016-sbc",
.of_match_table = of_match_ptr(apq8016_sbc_device_id),
},
.probe = apq8016_sbc_platform_probe,
};
module_platform_driver(apq8016_sbc_platform_driver);
MODULE_AUTHOR("Srinivas Kandagatla <srinivas.kandagatla@linaro.org");
MODULE_DESCRIPTION("APQ8016 ASoC Machine Driver");
MODULE_LICENSE("GPL v2");

View File

@ -69,11 +69,6 @@ static struct snd_soc_dai_link storm_dai_link = {
.ops = &storm_soc_ops,
};
static struct snd_soc_card storm_soc_card = {
.name = "ipq806x-storm",
.dev = NULL,
};
static int storm_parse_of(struct snd_soc_card *card)
{
struct snd_soc_dai_link *dai_link = card->dai_link;
@ -99,14 +94,13 @@ static int storm_parse_of(struct snd_soc_card *card)
static int storm_platform_probe(struct platform_device *pdev)
{
struct snd_soc_card *card = &storm_soc_card;
struct snd_soc_card *card;
int ret;
if (card->dev) {
dev_err(&pdev->dev, "%s() error, existing soundcard\n",
__func__);
return -ENODEV;
}
card = devm_kzalloc(&pdev->dev, sizeof(*card), GFP_KERNEL);
if (!card)
return -ENOMEM;
card->dev = &pdev->dev;
platform_set_drvdata(pdev, card);
@ -128,16 +122,12 @@ static int storm_platform_probe(struct platform_device *pdev)
}
ret = devm_snd_soc_register_card(&pdev->dev, card);
if (ret == -EPROBE_DEFER) {
card->dev = NULL;
return ret;
} else if (ret) {
if (ret)
dev_err(&pdev->dev, "%s() error registering soundcard: %d\n",
__func__, ret);
return ret;
}
return 0;
return ret;
}
#ifdef CONFIG_OF

View File

@ -137,15 +137,17 @@ char *rsnd_mod_name(struct rsnd_mod *mod)
return mod->ops->name;
}
struct dma_chan *rsnd_mod_dma_req(struct rsnd_mod *mod)
struct dma_chan *rsnd_mod_dma_req(struct rsnd_dai_stream *io,
struct rsnd_mod *mod)
{
if (!mod || !mod->ops || !mod->ops->dma_req)
return NULL;
return mod->ops->dma_req(mod);
return mod->ops->dma_req(io, mod);
}
int rsnd_mod_init(struct rsnd_mod *mod,
int rsnd_mod_init(struct rsnd_priv *priv,
struct rsnd_mod *mod,
struct rsnd_mod_ops *ops,
struct clk *clk,
enum rsnd_mod_type type,
@ -160,6 +162,7 @@ int rsnd_mod_init(struct rsnd_mod *mod,
mod->ops = ops;
mod->type = type;
mod->clk = clk;
mod->priv = priv;
return ret;
}
@ -170,10 +173,31 @@ void rsnd_mod_quit(struct rsnd_mod *mod)
clk_unprepare(mod->clk);
}
int rsnd_mod_is_working(struct rsnd_mod *mod)
void rsnd_mod_interrupt(struct rsnd_mod *mod,
void (*callback)(struct rsnd_mod *mod,
struct rsnd_dai_stream *io))
{
struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
struct rsnd_dai_stream *io;
struct rsnd_dai *rdai;
int i, j;
for_each_rsnd_dai(rdai, priv, j) {
for (i = 0; i < RSND_MOD_MAX; i++) {
io = &rdai->playback;
if (mod == io->mod[i])
callback(mod, io);
io = &rdai->capture;
if (mod == io->mod[i])
callback(mod, io);
}
}
}
int rsnd_io_is_working(struct rsnd_dai_stream *io)
{
/* see rsnd_dai_stream_init/quit() */
return !!io->substream;
}
@ -181,10 +205,9 @@ int rsnd_mod_is_working(struct rsnd_mod *mod)
/*
* settting function
*/
u32 rsnd_get_adinr(struct rsnd_mod *mod)
u32 rsnd_get_adinr(struct rsnd_mod *mod, struct rsnd_dai_stream *io)
{
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
struct device *dev = rsnd_priv_to_dev(priv);
u32 adinr = runtime->channels;
@ -207,26 +230,31 @@ u32 rsnd_get_adinr(struct rsnd_mod *mod)
/*
* rsnd_dai functions
*/
#define __rsnd_mod_call(mod, func, param...) \
#define __rsnd_mod_call(mod, io, func, param...) \
({ \
struct rsnd_priv *priv = rsnd_mod_to_priv(mod); \
struct device *dev = rsnd_priv_to_dev(priv); \
u32 mask = (1 << __rsnd_mod_shift_##func) & ~(1 << 31); \
u32 call = __rsnd_mod_call_##func << __rsnd_mod_shift_##func; \
u32 mask = 0xF << __rsnd_mod_shift_##func; \
u8 val = (mod->status >> __rsnd_mod_shift_##func) & 0xF; \
u8 add = ((val + __rsnd_mod_add_##func) & 0xF); \
int ret = 0; \
if ((mod->status & mask) == call) { \
dev_dbg(dev, "%s[%d] %s\n", \
rsnd_mod_name(mod), rsnd_mod_id(mod), #func); \
ret = (mod)->ops->func(mod, param); \
mod->status = (mod->status & ~mask) | (~call & mask); \
int called = 0; \
if (val == __rsnd_mod_call_##func) { \
called = 1; \
ret = (mod)->ops->func(mod, io, param); \
mod->status = (mod->status & ~mask) + \
(add << __rsnd_mod_shift_##func); \
} \
dev_dbg(dev, "%s[%d] 0x%08x %s\n", \
rsnd_mod_name(mod), rsnd_mod_id(mod), mod->status, \
called ? #func : ""); \
ret; \
})
#define rsnd_mod_call(mod, func, param...) \
#define rsnd_mod_call(mod, io, func, param...) \
(!(mod) ? -ENODEV : \
!((mod)->ops->func) ? 0 : \
__rsnd_mod_call(mod, func, param))
__rsnd_mod_call(mod, io, func, param))
#define rsnd_dai_call(fn, io, param...) \
({ \
@ -236,7 +264,7 @@ u32 rsnd_get_adinr(struct rsnd_mod *mod)
mod = (io)->mod[i]; \
if (!mod) \
continue; \
ret = rsnd_mod_call(mod, fn, param); \
ret = rsnd_mod_call(mod, io, fn, param); \
if (ret < 0) \
break; \
} \
@ -260,7 +288,6 @@ static int rsnd_dai_connect(struct rsnd_mod *mod,
}
io->mod[mod->type] = mod;
mod->io = io;
return 0;
}
@ -268,7 +295,6 @@ static int rsnd_dai_connect(struct rsnd_mod *mod,
static void rsnd_dai_disconnect(struct rsnd_mod *mod,
struct rsnd_dai_stream *io)
{
mod->io = NULL;
io->mod[mod->type] = NULL;
}
@ -302,7 +328,7 @@ int rsnd_dai_pointer_offset(struct rsnd_dai_stream *io, int additional)
return pos;
}
void rsnd_dai_pointer_update(struct rsnd_dai_stream *io, int byte)
bool rsnd_dai_pointer_update(struct rsnd_dai_stream *io, int byte)
{
io->byte_pos += byte;
@ -319,8 +345,24 @@ void rsnd_dai_pointer_update(struct rsnd_dai_stream *io, int byte)
io->next_period_byte = io->byte_per_period;
}
snd_pcm_period_elapsed(substream);
return true;
}
return false;
}
void rsnd_dai_period_elapsed(struct rsnd_dai_stream *io)
{
struct snd_pcm_substream *substream = io->substream;
/*
* this function should be called...
*
* - if rsnd_dai_pointer_update() returns true
* - without spin lock
*/
snd_pcm_period_elapsed(substream);
}
static void rsnd_dai_stream_init(struct rsnd_dai_stream *io,
@ -834,16 +876,18 @@ static int rsnd_kctrl_put(struct snd_kcontrol *kctrl,
}
if (change)
cfg->update(mod);
cfg->update(cfg->io, mod);
return change;
}
static int __rsnd_kctrl_new(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct snd_soc_pcm_runtime *rtd,
const unsigned char *name,
struct rsnd_kctrl_cfg *cfg,
void (*update)(struct rsnd_mod *mod))
void (*update)(struct rsnd_dai_stream *io,
struct rsnd_mod *mod))
{
struct snd_soc_card *soc_card = rtd->card;
struct snd_card *card = rtd->card->snd_card;
@ -872,6 +916,7 @@ static int __rsnd_kctrl_new(struct rsnd_mod *mod,
cfg->update = update;
cfg->card = card;
cfg->kctrl = kctrl;
cfg->io = io;
return 0;
}
@ -882,36 +927,42 @@ void _rsnd_kctrl_remove(struct rsnd_kctrl_cfg *cfg)
}
int rsnd_kctrl_new_m(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct snd_soc_pcm_runtime *rtd,
const unsigned char *name,
void (*update)(struct rsnd_mod *mod),
void (*update)(struct rsnd_dai_stream *io,
struct rsnd_mod *mod),
struct rsnd_kctrl_cfg_m *_cfg,
u32 max)
{
_cfg->cfg.max = max;
_cfg->cfg.size = RSND_DVC_CHANNELS;
_cfg->cfg.val = _cfg->val;
return __rsnd_kctrl_new(mod, rtd, name, &_cfg->cfg, update);
return __rsnd_kctrl_new(mod, io, rtd, name, &_cfg->cfg, update);
}
int rsnd_kctrl_new_s(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct snd_soc_pcm_runtime *rtd,
const unsigned char *name,
void (*update)(struct rsnd_mod *mod),
void (*update)(struct rsnd_dai_stream *io,
struct rsnd_mod *mod),
struct rsnd_kctrl_cfg_s *_cfg,
u32 max)
{
_cfg->cfg.max = max;
_cfg->cfg.size = 1;
_cfg->cfg.val = &_cfg->val;
return __rsnd_kctrl_new(mod, rtd, name, &_cfg->cfg, update);
return __rsnd_kctrl_new(mod, io, rtd, name, &_cfg->cfg, update);
}
int rsnd_kctrl_new_e(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct snd_soc_pcm_runtime *rtd,
const unsigned char *name,
struct rsnd_kctrl_cfg_s *_cfg,
void (*update)(struct rsnd_mod *mod),
void (*update)(struct rsnd_dai_stream *io,
struct rsnd_mod *mod),
const char * const *texts,
u32 max)
{
@ -919,7 +970,7 @@ int rsnd_kctrl_new_e(struct rsnd_mod *mod,
_cfg->cfg.size = 1;
_cfg->cfg.val = &_cfg->val;
_cfg->cfg.texts = texts;
return __rsnd_kctrl_new(mod, rtd, name, &_cfg->cfg, update);
return __rsnd_kctrl_new(mod, io, rtd, name, &_cfg->cfg, update);
}
/*

View File

@ -32,11 +32,12 @@ struct rsnd_dma_ctrl {
/*
* Audio DMAC
*/
static void rsnd_dmaen_complete(void *data)
static void __rsnd_dmaen_complete(struct rsnd_mod *mod,
struct rsnd_dai_stream *io)
{
struct rsnd_dma *dma = (struct rsnd_dma *)data;
struct rsnd_mod *mod = rsnd_dma_to_mod(dma);
struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
bool elapsed = false;
unsigned long flags;
/*
* Renesas sound Gen1 needs 1 DMAC,
@ -49,23 +50,36 @@ static void rsnd_dmaen_complete(void *data)
* rsnd_dai_pointer_update() will be called twice,
* ant it will breaks io->byte_pos
*/
spin_lock_irqsave(&priv->lock, flags);
rsnd_dai_pointer_update(io, io->byte_per_period);
if (rsnd_io_is_working(io))
elapsed = rsnd_dai_pointer_update(io, io->byte_per_period);
spin_unlock_irqrestore(&priv->lock, flags);
if (elapsed)
rsnd_dai_period_elapsed(io);
}
static void rsnd_dmaen_stop(struct rsnd_dma *dma)
static void rsnd_dmaen_complete(void *data)
{
struct rsnd_mod *mod = data;
rsnd_mod_interrupt(mod, __rsnd_dmaen_complete);
}
static void rsnd_dmaen_stop(struct rsnd_dai_stream *io, struct rsnd_dma *dma)
{
struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma);
dmaengine_terminate_all(dmaen->chan);
}
static void rsnd_dmaen_start(struct rsnd_dma *dma)
static void rsnd_dmaen_start(struct rsnd_dai_stream *io, struct rsnd_dma *dma)
{
struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma);
struct rsnd_mod *mod = rsnd_dma_to_mod(dma);
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
struct snd_pcm_substream *substream = io->substream;
struct device *dev = rsnd_priv_to_dev(priv);
struct dma_async_tx_descriptor *desc;
@ -84,7 +98,7 @@ static void rsnd_dmaen_start(struct rsnd_dma *dma)
}
desc->callback = rsnd_dmaen_complete;
desc->callback_param = dma;
desc->callback_param = mod;
if (dmaengine_submit(desc) < 0) {
dev_err(dev, "dmaengine_submit() fail\n");
@ -115,7 +129,8 @@ struct dma_chan *rsnd_dma_request_channel(struct device_node *of_node,
return chan;
}
static struct dma_chan *rsnd_dmaen_request_channel(struct rsnd_mod *mod_from,
static struct dma_chan *rsnd_dmaen_request_channel(struct rsnd_dai_stream *io,
struct rsnd_mod *mod_from,
struct rsnd_mod *mod_to)
{
if ((!mod_from && !mod_to) ||
@ -123,19 +138,19 @@ static struct dma_chan *rsnd_dmaen_request_channel(struct rsnd_mod *mod_from,
return NULL;
if (mod_from)
return rsnd_mod_dma_req(mod_from);
return rsnd_mod_dma_req(io, mod_from);
else
return rsnd_mod_dma_req(mod_to);
return rsnd_mod_dma_req(io, mod_to);
}
static int rsnd_dmaen_init(struct rsnd_priv *priv, struct rsnd_dma *dma, int id,
static int rsnd_dmaen_init(struct rsnd_dai_stream *io,
struct rsnd_dma *dma, int id,
struct rsnd_mod *mod_from, struct rsnd_mod *mod_to)
{
struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma);
struct rsnd_priv *priv = rsnd_io_to_priv(io);
struct device *dev = rsnd_priv_to_dev(priv);
struct dma_slave_config cfg = {};
struct rsnd_mod *mod = rsnd_dma_to_mod(dma);
struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
int is_play = rsnd_io_is_play(io);
int ret;
@ -145,7 +160,7 @@ static int rsnd_dmaen_init(struct rsnd_priv *priv, struct rsnd_dma *dma, int id,
}
if (dev->of_node) {
dmaen->chan = rsnd_dmaen_request_channel(mod_from, mod_to);
dmaen->chan = rsnd_dmaen_request_channel(io, mod_from, mod_to);
} else {
dma_cap_mask_t mask;
@ -177,7 +192,7 @@ static int rsnd_dmaen_init(struct rsnd_priv *priv, struct rsnd_dma *dma, int id,
return 0;
rsnd_dma_init_err:
rsnd_dma_quit(dma);
rsnd_dma_quit(io, dma);
rsnd_dma_channel_err:
/*
@ -189,7 +204,7 @@ rsnd_dma_channel_err:
return -EAGAIN;
}
static void rsnd_dmaen_quit(struct rsnd_dma *dma)
static void rsnd_dmaen_quit(struct rsnd_dai_stream *io, struct rsnd_dma *dma)
{
struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma);
@ -238,9 +253,9 @@ static const u8 gen2_id_table_cmd[] = {
0x38, /* SCU_CMD1 */
};
static u32 rsnd_dmapp_get_id(struct rsnd_mod *mod)
static u32 rsnd_dmapp_get_id(struct rsnd_dai_stream *io,
struct rsnd_mod *mod)
{
struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
struct rsnd_mod *ssi = rsnd_io_to_mod_ssi(io);
struct rsnd_mod *src = rsnd_io_to_mod_src(io);
struct rsnd_mod *dvc = rsnd_io_to_mod_dvc(io);
@ -268,11 +283,12 @@ static u32 rsnd_dmapp_get_id(struct rsnd_mod *mod)
return entry[id];
}
static u32 rsnd_dmapp_get_chcr(struct rsnd_mod *mod_from,
static u32 rsnd_dmapp_get_chcr(struct rsnd_dai_stream *io,
struct rsnd_mod *mod_from,
struct rsnd_mod *mod_to)
{
return (rsnd_dmapp_get_id(mod_from) << 24) +
(rsnd_dmapp_get_id(mod_to) << 16);
return (rsnd_dmapp_get_id(io, mod_from) << 24) +
(rsnd_dmapp_get_id(io, mod_to) << 16);
}
#define rsnd_dmapp_addr(dmac, dma, reg) \
@ -299,7 +315,7 @@ static u32 rsnd_dmapp_read(struct rsnd_dma *dma, u32 reg)
return ioread32(rsnd_dmapp_addr(dmac, dma, reg));
}
static void rsnd_dmapp_stop(struct rsnd_dma *dma)
static void rsnd_dmapp_stop(struct rsnd_dai_stream *io, struct rsnd_dma *dma)
{
int i;
@ -312,7 +328,7 @@ static void rsnd_dmapp_stop(struct rsnd_dma *dma)
}
}
static void rsnd_dmapp_start(struct rsnd_dma *dma)
static void rsnd_dmapp_start(struct rsnd_dai_stream *io, struct rsnd_dma *dma)
{
struct rsnd_dmapp *dmapp = rsnd_dma_to_dmapp(dma);
@ -321,19 +337,21 @@ static void rsnd_dmapp_start(struct rsnd_dma *dma)
rsnd_dmapp_write(dma, dmapp->chcr, PDMACHCR);
}
static int rsnd_dmapp_init(struct rsnd_priv *priv, struct rsnd_dma *dma, int id,
static int rsnd_dmapp_init(struct rsnd_dai_stream *io,
struct rsnd_dma *dma, int id,
struct rsnd_mod *mod_from, struct rsnd_mod *mod_to)
{
struct rsnd_dmapp *dmapp = rsnd_dma_to_dmapp(dma);
struct rsnd_priv *priv = rsnd_io_to_priv(io);
struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv);
struct device *dev = rsnd_priv_to_dev(priv);
dmapp->dmapp_id = dmac->dmapp_num;
dmapp->chcr = rsnd_dmapp_get_chcr(mod_from, mod_to) | PDMACHCR_DE;
dmapp->chcr = rsnd_dmapp_get_chcr(io, mod_from, mod_to) | PDMACHCR_DE;
dmac->dmapp_num++;
rsnd_dmapp_stop(dma);
rsnd_dmapp_stop(io, dma);
dev_dbg(dev, "id/src/dst/chcr = %d/%pad/%pad/%08x\n",
dmapp->dmapp_id, &dma->src_addr, &dma->dst_addr, dmapp->chcr);
@ -386,12 +404,12 @@ static struct rsnd_dma_ops rsnd_dmapp_ops = {
#define RDMA_CMD_O_P(addr, i) (addr ##_reg - 0x001f8000 + (0x400 * i))
static dma_addr_t
rsnd_gen2_dma_addr(struct rsnd_priv *priv,
rsnd_gen2_dma_addr(struct rsnd_dai_stream *io,
struct rsnd_mod *mod,
int is_play, int is_from)
{
struct rsnd_priv *priv = rsnd_io_to_priv(io);
struct device *dev = rsnd_priv_to_dev(priv);
struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
phys_addr_t ssi_reg = rsnd_gen_get_phy_addr(priv, RSND_GEN2_SSI);
phys_addr_t src_reg = rsnd_gen_get_phy_addr(priv, RSND_GEN2_SCU);
int is_ssi = !!(rsnd_io_to_mod_ssi(io) == mod);
@ -438,7 +456,7 @@ rsnd_gen2_dma_addr(struct rsnd_priv *priv,
dev_err(dev, "DVC is selected without SRC\n");
/* use SSIU or SSI ? */
if (is_ssi && rsnd_ssi_use_busif(mod))
if (is_ssi && rsnd_ssi_use_busif(io, mod))
is_ssi++;
return (is_from) ?
@ -446,10 +464,12 @@ rsnd_gen2_dma_addr(struct rsnd_priv *priv,
dma_addrs[is_ssi][is_play][use_src + use_dvc].in_addr;
}
static dma_addr_t rsnd_dma_addr(struct rsnd_priv *priv,
static dma_addr_t rsnd_dma_addr(struct rsnd_dai_stream *io,
struct rsnd_mod *mod,
int is_play, int is_from)
{
struct rsnd_priv *priv = rsnd_io_to_priv(io);
/*
* gen1 uses default DMA addr
*/
@ -459,17 +479,17 @@ static dma_addr_t rsnd_dma_addr(struct rsnd_priv *priv,
if (!mod)
return 0;
return rsnd_gen2_dma_addr(priv, mod, is_play, is_from);
return rsnd_gen2_dma_addr(io, mod, is_play, is_from);
}
#define MOD_MAX 4 /* MEM/SSI/SRC/DVC */
static void rsnd_dma_of_path(struct rsnd_dma *dma,
struct rsnd_dai_stream *io,
int is_play,
struct rsnd_mod **mod_from,
struct rsnd_mod **mod_to)
{
struct rsnd_mod *this = rsnd_dma_to_mod(dma);
struct rsnd_dai_stream *io = rsnd_mod_to_io(this);
struct rsnd_mod *ssi = rsnd_io_to_mod_ssi(io);
struct rsnd_mod *src = rsnd_io_to_mod_src(io);
struct rsnd_mod *dvc = rsnd_io_to_mod_dvc(io);
@ -524,17 +544,17 @@ static void rsnd_dma_of_path(struct rsnd_dma *dma,
}
}
void rsnd_dma_stop(struct rsnd_dma *dma)
void rsnd_dma_stop(struct rsnd_dai_stream *io, struct rsnd_dma *dma)
{
dma->ops->stop(dma);
dma->ops->stop(io, dma);
}
void rsnd_dma_start(struct rsnd_dma *dma)
void rsnd_dma_start(struct rsnd_dai_stream *io, struct rsnd_dma *dma)
{
dma->ops->start(dma);
dma->ops->start(io, dma);
}
void rsnd_dma_quit(struct rsnd_dma *dma)
void rsnd_dma_quit(struct rsnd_dai_stream *io, struct rsnd_dma *dma)
{
struct rsnd_mod *mod = rsnd_dma_to_mod(dma);
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
@ -543,15 +563,14 @@ void rsnd_dma_quit(struct rsnd_dma *dma)
if (!dmac)
return;
dma->ops->quit(dma);
dma->ops->quit(io, dma);
}
int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma, int id)
int rsnd_dma_init(struct rsnd_dai_stream *io, struct rsnd_dma *dma, int id)
{
struct rsnd_mod *mod = rsnd_dma_to_mod(dma);
struct rsnd_mod *mod_from;
struct rsnd_mod *mod_to;
struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
struct rsnd_priv *priv = rsnd_io_to_priv(io);
struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv);
int is_play = rsnd_io_is_play(io);
@ -564,10 +583,10 @@ int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma, int id)
if (!dmac)
return -EAGAIN;
rsnd_dma_of_path(dma, is_play, &mod_from, &mod_to);
rsnd_dma_of_path(dma, io, is_play, &mod_from, &mod_to);
dma->src_addr = rsnd_dma_addr(priv, mod_from, is_play, 1);
dma->dst_addr = rsnd_dma_addr(priv, mod_to, is_play, 0);
dma->src_addr = rsnd_dma_addr(io, mod_from, is_play, 1);
dma->dst_addr = rsnd_dma_addr(io, mod_to, is_play, 0);
/* for Gen2 */
if (mod_from && mod_to)
@ -579,7 +598,7 @@ int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma, int id)
if (rsnd_is_gen1(priv))
dma->ops = &rsnd_dmaen_ops;
return dma->ops->init(priv, dma, id, mod_from, mod_to);
return dma->ops->init(io, dma, id, mod_from, mod_to);
}
int rsnd_dma_probe(struct platform_device *pdev,

View File

@ -63,7 +63,8 @@ static const char * const dvc_ramp_rate[] = {
"0.125 dB/8192 steps", /* 10111 */
};
static void rsnd_dvc_volume_update(struct rsnd_mod *mod)
static void rsnd_dvc_volume_update(struct rsnd_dai_stream *io,
struct rsnd_mod *mod)
{
struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod);
u32 val[RSND_DVC_CHANNELS];
@ -120,6 +121,7 @@ static void rsnd_dvc_volume_update(struct rsnd_mod *mod)
}
static int rsnd_dvc_remove_gen2(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod);
@ -134,9 +136,9 @@ static int rsnd_dvc_remove_gen2(struct rsnd_mod *mod,
}
static int rsnd_dvc_init(struct rsnd_mod *dvc_mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
struct rsnd_dai_stream *io = rsnd_mod_to_io(dvc_mod);
struct rsnd_mod *src_mod = rsnd_io_to_mod_src(io);
struct device *dev = rsnd_priv_to_dev(priv);
int dvc_id = rsnd_mod_id(dvc_mod);
@ -168,10 +170,10 @@ static int rsnd_dvc_init(struct rsnd_mod *dvc_mod,
rsnd_mod_write(dvc_mod, DVC_DVUIR, 1);
rsnd_mod_write(dvc_mod, DVC_ADINR, rsnd_get_adinr(dvc_mod));
rsnd_mod_write(dvc_mod, DVC_ADINR, rsnd_get_adinr(dvc_mod, io));
/* ch0/ch1 Volume */
rsnd_dvc_volume_update(dvc_mod);
rsnd_dvc_volume_update(io, dvc_mod);
rsnd_mod_write(dvc_mod, DVC_DVUIR, 0);
@ -181,6 +183,7 @@ static int rsnd_dvc_init(struct rsnd_mod *dvc_mod,
}
static int rsnd_dvc_quit(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
rsnd_mod_hw_stop(mod);
@ -189,6 +192,7 @@ static int rsnd_dvc_quit(struct rsnd_mod *mod,
}
static int rsnd_dvc_start(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
rsnd_mod_write(mod, CMD_CTRL, 0x10);
@ -197,6 +201,7 @@ static int rsnd_dvc_start(struct rsnd_mod *mod,
}
static int rsnd_dvc_stop(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
rsnd_mod_write(mod, CMD_CTRL, 0);
@ -205,15 +210,15 @@ static int rsnd_dvc_stop(struct rsnd_mod *mod,
}
static int rsnd_dvc_pcm_new(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct snd_soc_pcm_runtime *rtd)
{
struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod);
int is_play = rsnd_io_is_play(io);
int ret;
/* Volume */
ret = rsnd_kctrl_new_m(mod, rtd,
ret = rsnd_kctrl_new_m(mod, io, rtd,
is_play ?
"DVC Out Playback Volume" : "DVC In Capture Volume",
rsnd_dvc_volume_update,
@ -222,7 +227,7 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod,
return ret;
/* Mute */
ret = rsnd_kctrl_new_m(mod, rtd,
ret = rsnd_kctrl_new_m(mod, io, rtd,
is_play ?
"DVC Out Mute Switch" : "DVC In Mute Switch",
rsnd_dvc_volume_update,
@ -231,7 +236,7 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod,
return ret;
/* Ramp */
ret = rsnd_kctrl_new_s(mod, rtd,
ret = rsnd_kctrl_new_s(mod, io, rtd,
is_play ?
"DVC Out Ramp Switch" : "DVC In Ramp Switch",
rsnd_dvc_volume_update,
@ -239,7 +244,7 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod,
if (ret < 0)
return ret;
ret = rsnd_kctrl_new_e(mod, rtd,
ret = rsnd_kctrl_new_e(mod, io, rtd,
is_play ?
"DVC Out Ramp Up Rate" : "DVC In Ramp Up Rate",
&dvc->rup,
@ -248,7 +253,7 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod,
if (ret < 0)
return ret;
ret = rsnd_kctrl_new_e(mod, rtd,
ret = rsnd_kctrl_new_e(mod, io, rtd,
is_play ?
"DVC Out Ramp Down Rate" : "DVC In Ramp Down Rate",
&dvc->rdown,
@ -261,7 +266,8 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod,
return 0;
}
static struct dma_chan *rsnd_dvc_dma_req(struct rsnd_mod *mod)
static struct dma_chan *rsnd_dvc_dma_req(struct rsnd_dai_stream *io,
struct rsnd_mod *mod)
{
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
@ -366,7 +372,7 @@ int rsnd_dvc_probe(struct platform_device *pdev,
dvc->info = &info->dvc_info[i];
ret = rsnd_mod_init(&dvc->mod, &rsnd_dvc_ops,
ret = rsnd_mod_init(priv, &dvc->mod, &rsnd_dvc_ops,
clk, RSND_MOD_DVC, i);
if (ret)
return ret;

View File

@ -165,18 +165,18 @@ void rsnd_write(struct rsnd_priv *priv, struct rsnd_mod *mod,
enum rsnd_reg reg, u32 data);
void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod, enum rsnd_reg reg,
u32 mask, u32 data);
u32 rsnd_get_adinr(struct rsnd_mod *mod);
u32 rsnd_get_adinr(struct rsnd_mod *mod, struct rsnd_dai_stream *io);
/*
* R-Car DMA
*/
struct rsnd_dma;
struct rsnd_dma_ops {
void (*start)(struct rsnd_dma *dma);
void (*stop)(struct rsnd_dma *dma);
int (*init)(struct rsnd_priv *priv, struct rsnd_dma *dma, int id,
void (*start)(struct rsnd_dai_stream *io, struct rsnd_dma *dma);
void (*stop)(struct rsnd_dai_stream *io, struct rsnd_dma *dma);
int (*init)(struct rsnd_dai_stream *io, struct rsnd_dma *dma, int id,
struct rsnd_mod *mod_from, struct rsnd_mod *mod_to);
void (*quit)(struct rsnd_dma *dma);
void (*quit)(struct rsnd_dai_stream *io, struct rsnd_dma *dma);
};
struct rsnd_dmaen {
@ -200,10 +200,10 @@ struct rsnd_dma {
#define rsnd_dma_to_dmaen(dma) (&(dma)->dma.en)
#define rsnd_dma_to_dmapp(dma) (&(dma)->dma.pp)
void rsnd_dma_start(struct rsnd_dma *dma);
void rsnd_dma_stop(struct rsnd_dma *dma);
int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma, int id);
void rsnd_dma_quit(struct rsnd_dma *dma);
void rsnd_dma_start(struct rsnd_dai_stream *io, struct rsnd_dma *dma);
void rsnd_dma_stop(struct rsnd_dai_stream *io, struct rsnd_dma *dma);
int rsnd_dma_init(struct rsnd_dai_stream *io, struct rsnd_dma *dma, int id);
void rsnd_dma_quit(struct rsnd_dai_stream *io, struct rsnd_dma *dma);
int rsnd_dma_probe(struct platform_device *pdev,
const struct rsnd_of_data *of_data,
struct rsnd_priv *priv);
@ -224,25 +224,35 @@ enum rsnd_mod_type {
struct rsnd_mod_ops {
char *name;
struct dma_chan* (*dma_req)(struct rsnd_mod *mod);
struct dma_chan* (*dma_req)(struct rsnd_dai_stream *io,
struct rsnd_mod *mod);
int (*probe)(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv);
int (*remove)(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv);
int (*init)(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv);
int (*quit)(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv);
int (*start)(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv);
int (*stop)(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv);
int (*pcm_new)(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct snd_soc_pcm_runtime *rtd);
int (*hw_params)(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params);
int (*fallback)(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv);
};
@ -252,32 +262,43 @@ struct rsnd_mod {
enum rsnd_mod_type type;
struct rsnd_mod_ops *ops;
struct rsnd_dma dma;
struct rsnd_dai_stream *io;
struct rsnd_priv *priv;
struct clk *clk;
u32 status;
};
/*
* status
*
* bit
* 0 0: probe 1: remove
* 1 0: init 1: quit
* 2 0: start 1: stop
* 3 0: pcm_new
* 4 0: fallback
* 0xH0000CBA
*
* 31 bit is always called (see __rsnd_mod_call)
* 31 0: hw_params
* A 0: probe 1: remove
* B 0: init 1: quit
* C 0: start 1: stop
*
* H is always called (see __rsnd_mod_call)
* H 0: pcm_new
* H 0: fallback
* H 0: hw_params
*/
#define __rsnd_mod_shift_probe 0
#define __rsnd_mod_shift_remove 0
#define __rsnd_mod_shift_init 1
#define __rsnd_mod_shift_quit 1
#define __rsnd_mod_shift_start 2
#define __rsnd_mod_shift_stop 2
#define __rsnd_mod_shift_pcm_new 3
#define __rsnd_mod_shift_fallback 4
#define __rsnd_mod_shift_hw_params 31 /* always called */
#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_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 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
#define __rsnd_mod_add_stop -1
#define __rsnd_mod_add_pcm_new 0
#define __rsnd_mod_add_fallback 0
#define __rsnd_mod_add_hw_params 0
#define __rsnd_mod_call_probe 0
#define __rsnd_mod_call_remove 1
@ -289,22 +310,25 @@ struct rsnd_mod {
#define __rsnd_mod_call_fallback 0
#define __rsnd_mod_call_hw_params 0
#define rsnd_mod_to_priv(mod) (rsnd_io_to_priv(rsnd_mod_to_io(mod)))
#define rsnd_mod_to_priv(mod) ((mod)->priv)
#define rsnd_mod_to_dma(mod) (&(mod)->dma)
#define rsnd_mod_to_io(mod) ((mod)->io)
#define rsnd_mod_id(mod) ((mod)->id)
#define rsnd_mod_hw_start(mod) clk_enable((mod)->clk)
#define rsnd_mod_hw_stop(mod) clk_disable((mod)->clk)
int rsnd_mod_init(struct rsnd_mod *mod,
int rsnd_mod_init(struct rsnd_priv *priv,
struct rsnd_mod *mod,
struct rsnd_mod_ops *ops,
struct clk *clk,
enum rsnd_mod_type type,
int id);
void rsnd_mod_quit(struct rsnd_mod *mod);
char *rsnd_mod_name(struct rsnd_mod *mod);
int rsnd_mod_is_working(struct rsnd_mod *mod);
struct dma_chan *rsnd_mod_dma_req(struct rsnd_mod *mod);
struct dma_chan *rsnd_mod_dma_req(struct rsnd_dai_stream *io,
struct rsnd_mod *mod);
void rsnd_mod_interrupt(struct rsnd_mod *mod,
void (*callback)(struct rsnd_mod *mod,
struct rsnd_dai_stream *io));
/*
* R-Car sound DAI
@ -329,7 +353,7 @@ struct rsnd_dai_stream {
#define rsnd_io_is_play(io) (&rsnd_io_to_rdai(io)->playback == io)
#define rsnd_io_to_runtime(io) ((io)->substream ? \
(io)->substream->runtime : NULL)
int rsnd_io_is_working(struct rsnd_dai_stream *io);
struct rsnd_dai {
char name[RSND_DAI_NAME_SIZE];
@ -355,7 +379,8 @@ struct rsnd_dai {
struct rsnd_dai *rsnd_rdai_get(struct rsnd_priv *priv, int id);
void rsnd_dai_pointer_update(struct rsnd_dai_stream *io, int cnt);
bool rsnd_dai_pointer_update(struct rsnd_dai_stream *io, int cnt);
void rsnd_dai_period_elapsed(struct rsnd_dai_stream *io);
int rsnd_dai_pointer_offset(struct rsnd_dai_stream *io, int additional);
/*
@ -459,7 +484,8 @@ struct rsnd_kctrl_cfg {
unsigned int size;
u32 *val;
const char * const *texts;
void (*update)(struct rsnd_mod *mod);
void (*update)(struct rsnd_dai_stream *io, struct rsnd_mod *mod);
struct rsnd_dai_stream *io;
struct snd_card *card;
struct snd_kcontrol *kctrl;
};
@ -479,22 +505,28 @@ void _rsnd_kctrl_remove(struct rsnd_kctrl_cfg *cfg);
#define rsnd_kctrl_remove(_cfg) _rsnd_kctrl_remove(&((_cfg).cfg))
int rsnd_kctrl_new_m(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct snd_soc_pcm_runtime *rtd,
const unsigned char *name,
void (*update)(struct rsnd_mod *mod),
void (*update)(struct rsnd_dai_stream *io,
struct rsnd_mod *mod),
struct rsnd_kctrl_cfg_m *_cfg,
u32 max);
int rsnd_kctrl_new_s(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct snd_soc_pcm_runtime *rtd,
const unsigned char *name,
void (*update)(struct rsnd_mod *mod),
void (*update)(struct rsnd_dai_stream *io,
struct rsnd_mod *mod),
struct rsnd_kctrl_cfg_s *_cfg,
u32 max);
int rsnd_kctrl_new_e(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct snd_soc_pcm_runtime *rtd,
const unsigned char *name,
struct rsnd_kctrl_cfg_s *_cfg,
void (*update)(struct rsnd_mod *mod),
void (*update)(struct rsnd_dai_stream *io,
struct rsnd_mod *mod),
const char * const *texts,
u32 max);
@ -511,8 +543,10 @@ unsigned int rsnd_src_get_ssi_rate(struct rsnd_priv *priv,
struct rsnd_dai_stream *io,
struct snd_pcm_runtime *runtime);
int rsnd_src_ssiu_start(struct rsnd_mod *ssi_mod,
struct rsnd_dai_stream *io,
int use_busif);
int rsnd_src_ssiu_stop(struct rsnd_mod *ssi_mod);
int rsnd_src_ssiu_stop(struct rsnd_mod *ssi_mod,
struct rsnd_dai_stream *io);
int rsnd_src_ssi_irq_enable(struct rsnd_mod *ssi_mod);
int rsnd_src_ssi_irq_disable(struct rsnd_mod *ssi_mod);
@ -529,7 +563,7 @@ void rsnd_ssi_remove(struct platform_device *pdev,
struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id);
int rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod);
int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod);
int rsnd_ssi_use_busif(struct rsnd_mod *mod);
int rsnd_ssi_use_busif(struct rsnd_dai_stream *io, struct rsnd_mod *mod);
/*
* R-Car DVC

View File

@ -45,61 +45,50 @@ static const struct of_device_id rsrc_card_of_match[] = {
};
MODULE_DEVICE_TABLE(of, rsrc_card_of_match);
#define DAI_NAME_NUM 32
struct rsrc_card_dai {
const char *name;
unsigned int fmt;
unsigned int sysclk;
struct clk *clk;
char dai_name[DAI_NAME_NUM];
};
#define RSRC_FB_NUM 2 /* FE/BE */
#define IDX_CPU 0
#define IDX_CODEC 1
struct rsrc_card_priv {
struct snd_soc_card snd_card;
struct rsrc_card_dai_props {
struct rsrc_card_dai cpu_dai;
struct rsrc_card_dai codec_dai;
} dai_props[RSRC_FB_NUM];
struct snd_soc_codec_conf codec_conf;
struct snd_soc_dai_link dai_link[RSRC_FB_NUM];
struct rsrc_card_dai *dai_props;
struct snd_soc_dai_link *dai_link;
int dai_num;
u32 convert_rate;
};
#define rsrc_priv_to_dev(priv) ((priv)->snd_card.dev)
#define rsrc_priv_to_link(priv, i) ((priv)->snd_card.dai_link + i)
#define rsrc_priv_to_props(priv, i) ((priv)->dai_props + i)
#define rsrc_priv_to_link(priv, i) ((priv)->snd_card.dai_link + (i))
#define rsrc_priv_to_props(priv, i) ((priv)->dai_props + (i))
#define rsrc_dev_to_of_data(dev) (of_match_device(rsrc_card_of_match, (dev))->data)
static int rsrc_card_startup(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct rsrc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card);
struct rsrc_card_dai_props *dai_props =
&priv->dai_props[rtd - rtd->card->rtd];
struct rsrc_card_dai *dai_props =
rsrc_priv_to_props(priv, rtd - rtd->card->rtd);
int ret;
ret = clk_prepare_enable(dai_props->cpu_dai.clk);
if (ret)
return ret;
ret = clk_prepare_enable(dai_props->codec_dai.clk);
if (ret)
clk_disable_unprepare(dai_props->cpu_dai.clk);
return ret;
return clk_prepare_enable(dai_props->clk);
}
static void rsrc_card_shutdown(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct rsrc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card);
struct rsrc_card_dai_props *dai_props =
&priv->dai_props[rtd - rtd->card->rtd];
struct rsrc_card_dai *dai_props =
rsrc_priv_to_props(priv, rtd - rtd->card->rtd);
clk_disable_unprepare(dai_props->cpu_dai.clk);
clk_disable_unprepare(dai_props->codec_dai.clk);
clk_disable_unprepare(dai_props->clk);
}
static struct snd_soc_ops rsrc_card_ops = {
@ -107,21 +96,31 @@ static struct snd_soc_ops rsrc_card_ops = {
.shutdown = rsrc_card_shutdown,
};
static int __rsrc_card_dai_init(struct snd_soc_dai *dai,
struct rsrc_card_dai *set)
static int rsrc_card_dai_init(struct snd_soc_pcm_runtime *rtd)
{
struct rsrc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card);
struct snd_soc_dai *dai;
struct snd_soc_dai_link *dai_link;
struct rsrc_card_dai *dai_props;
int num = rtd - rtd->card->rtd;
int ret;
if (set->fmt) {
ret = snd_soc_dai_set_fmt(dai, set->fmt);
dai_link = rsrc_priv_to_link(priv, num);
dai_props = rsrc_priv_to_props(priv, num);
dai = dai_link->dynamic ?
rtd->cpu_dai :
rtd->codec_dai;
if (dai_props->fmt) {
ret = snd_soc_dai_set_fmt(dai, dai_props->fmt);
if (ret && ret != -ENOTSUPP) {
dev_err(dai->dev, "set_fmt error\n");
goto err;
}
}
if (set->sysclk) {
ret = snd_soc_dai_set_sysclk(dai, 0, set->sysclk, 0);
if (dai_props->sysclk) {
ret = snd_soc_dai_set_sysclk(dai, 0, dai_props->sysclk, 0);
if (ret && ret != -ENOTSUPP) {
dev_err(dai->dev, "set_sysclk error\n");
goto err;
@ -134,27 +133,6 @@ err:
return ret;
}
static int rsrc_card_dai_init(struct snd_soc_pcm_runtime *rtd)
{
struct rsrc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card);
struct snd_soc_dai *codec = rtd->codec_dai;
struct snd_soc_dai *cpu = rtd->cpu_dai;
struct rsrc_card_dai_props *dai_props;
int num, ret;
num = rtd - rtd->card->rtd;
dai_props = &priv->dai_props[num];
ret = __rsrc_card_dai_init(codec, &dai_props->codec_dai);
if (ret < 0)
return ret;
ret = __rsrc_card_dai_init(cpu, &dai_props->cpu_dai);
if (ret < 0)
return ret;
return 0;
}
static int rsrc_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
struct snd_pcm_hw_params *params)
{
@ -170,111 +148,15 @@ static int rsrc_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
return 0;
}
static int
rsrc_card_sub_parse_of(struct rsrc_card_priv *priv,
struct device_node *np,
struct rsrc_card_dai *dai,
struct snd_soc_dai_link *dai_link,
int *args_count)
{
struct device *dev = rsrc_priv_to_dev(priv);
const struct rsrc_card_of_data *of_data = rsrc_dev_to_of_data(dev);
struct of_phandle_args args;
struct device_node **p_node;
struct clk *clk;
const char **dai_name;
const char **name;
u32 val;
int ret;
if (args_count) {
p_node = &dai_link->cpu_of_node;
dai_name = &dai_link->cpu_dai_name;
name = &dai_link->cpu_name;
} else {
p_node = &dai_link->codec_of_node;
dai_name = &dai_link->codec_dai_name;
name = &dai_link->codec_name;
}
if (!np) {
/* use snd-soc-dummy */
*p_node = NULL;
*dai_name = "snd-soc-dummy-dai";
*name = "snd-soc-dummy";
return 0;
}
/*
* Get node via "sound-dai = <&phandle port>"
* it will be used as xxx_of_node on soc_bind_dai_link()
*/
ret = of_parse_phandle_with_args(np, "sound-dai",
"#sound-dai-cells", 0, &args);
if (ret)
return ret;
*p_node = args.np;
/* Get dai->name */
ret = snd_soc_of_get_dai_name(np, dai_name);
if (ret < 0)
return ret;
/*
* FIXME
*
* rsrc assumes DPCM playback/capture
*/
dai_link->dpcm_playback = 1;
dai_link->dpcm_capture = 1;
if (args_count) {
*args_count = args.args_count;
dai_link->dynamic = 1;
dai_link->dpcm_merged_format = 1;
} else {
dai_link->no_pcm = 1;
priv->codec_conf.of_node = (*p_node);
priv->codec_conf.name_prefix = of_data->prefix;
}
/*
* Parse dai->sysclk come from "clocks = <&xxx>"
* (if system has common clock)
* or "system-clock-frequency = <xxx>"
* or device's module clock.
*/
if (of_property_read_bool(np, "clocks")) {
clk = of_clk_get(np, 0);
if (IS_ERR(clk)) {
ret = PTR_ERR(clk);
return ret;
}
dai->sysclk = clk_get_rate(clk);
dai->clk = clk;
} else if (!of_property_read_u32(np, "system-clock-frequency", &val)) {
dai->sysclk = val;
} else {
clk = of_clk_get(args.np, 0);
if (!IS_ERR(clk))
dai->sysclk = clk_get_rate(clk);
}
return 0;
}
static int rsrc_card_parse_daifmt(struct device_node *node,
struct device_node *np,
struct rsrc_card_priv *priv,
struct device_node *codec,
int idx)
int idx, bool is_fe)
{
struct rsrc_card_dai *dai_props = rsrc_priv_to_props(priv, idx);
struct device_node *bitclkmaster = NULL;
struct device_node *framemaster = NULL;
struct rsrc_card_dai_props *dai_props = rsrc_priv_to_props(priv, idx);
struct rsrc_card_dai *cpu_dai = &dai_props->cpu_dai;
struct rsrc_card_dai *codec_dai = &dai_props->codec_dai;
struct device_node *codec = is_fe ? NULL : np;
unsigned int daifmt;
daifmt = snd_soc_of_parse_daifmt(node, NULL,
@ -291,8 +173,7 @@ static int rsrc_card_parse_daifmt(struct device_node *node,
daifmt |= (codec == framemaster) ?
SND_SOC_DAIFMT_CBS_CFM : SND_SOC_DAIFMT_CBS_CFS;
cpu_dai->fmt = daifmt;
codec_dai->fmt = daifmt;
dai_props->fmt = daifmt;
of_node_put(bitclkmaster);
of_node_put(framemaster);
@ -300,120 +181,194 @@ static int rsrc_card_parse_daifmt(struct device_node *node,
return 0;
}
static int rsrc_card_parse_links(struct device_node *np,
struct rsrc_card_priv *priv,
int idx, bool is_fe)
{
struct snd_soc_dai_link *dai_link = rsrc_priv_to_link(priv, idx);
struct rsrc_card_dai *dai_props = rsrc_priv_to_props(priv, idx);
struct of_phandle_args args;
int ret;
/*
* Get node via "sound-dai = <&phandle port>"
* it will be used as xxx_of_node on soc_bind_dai_link()
*/
ret = of_parse_phandle_with_args(np, "sound-dai",
"#sound-dai-cells", 0, &args);
if (ret)
return ret;
if (is_fe) {
/* BE is dummy */
dai_link->codec_of_node = NULL;
dai_link->codec_dai_name = "snd-soc-dummy-dai";
dai_link->codec_name = "snd-soc-dummy";
/* FE settings */
dai_link->dynamic = 1;
dai_link->dpcm_merged_format = 1;
dai_link->cpu_of_node = args.np;
snd_soc_of_get_dai_name(np, &dai_link->cpu_dai_name);
/* set dai_name */
snprintf(dai_props->dai_name, DAI_NAME_NUM, "fe.%s",
dai_link->cpu_dai_name);
/*
* In soc_bind_dai_link() will check cpu name after
* of_node matching if dai_link has cpu_dai_name.
* but, it will never match if name was created by
* fmt_single_name() remove cpu_dai_name if cpu_args
* was 0. See:
* fmt_single_name()
* fmt_multiple_name()
*/
if (!args.args_count)
dai_link->cpu_dai_name = NULL;
} else {
struct device *dev = rsrc_priv_to_dev(priv);
const struct rsrc_card_of_data *of_data;
of_data = rsrc_dev_to_of_data(dev);
/* FE is dummy */
dai_link->cpu_of_node = NULL;
dai_link->cpu_dai_name = "snd-soc-dummy-dai";
dai_link->cpu_name = "snd-soc-dummy";
/* BE settings */
dai_link->no_pcm = 1;
dai_link->be_hw_params_fixup = rsrc_card_be_hw_params_fixup;
dai_link->codec_of_node = args.np;
snd_soc_of_get_dai_name(np, &dai_link->codec_dai_name);
/* additional name prefix */
priv->codec_conf.of_node = dai_link->codec_of_node;
priv->codec_conf.name_prefix = of_data->prefix;
/* set dai_name */
snprintf(dai_props->dai_name, DAI_NAME_NUM, "be.%s",
dai_link->codec_dai_name);
}
/* Simple Card assumes platform == cpu */
dai_link->platform_of_node = dai_link->cpu_of_node;
dai_link->dpcm_playback = 1;
dai_link->dpcm_capture = 1;
dai_link->name = dai_props->dai_name;
dai_link->stream_name = dai_props->dai_name;
dai_link->ops = &rsrc_card_ops;
dai_link->init = rsrc_card_dai_init;
return 0;
}
static int rsrc_card_parse_clk(struct device_node *np,
struct rsrc_card_priv *priv,
int idx, bool is_fe)
{
struct snd_soc_dai_link *dai_link = rsrc_priv_to_link(priv, idx);
struct rsrc_card_dai *dai_props = rsrc_priv_to_props(priv, idx);
struct clk *clk;
struct device_node *of_np = is_fe ? dai_link->cpu_of_node :
dai_link->codec_of_node;
u32 val;
/*
* Parse dai->sysclk come from "clocks = <&xxx>"
* (if system has common clock)
* or "system-clock-frequency = <xxx>"
* or device's module clock.
*/
if (of_property_read_bool(np, "clocks")) {
clk = of_clk_get(np, 0);
if (IS_ERR(clk))
return PTR_ERR(clk);
dai_props->sysclk = clk_get_rate(clk);
dai_props->clk = clk;
} else if (!of_property_read_u32(np, "system-clock-frequency", &val)) {
dai_props->sysclk = val;
} else {
clk = of_clk_get(of_np, 0);
if (!IS_ERR(clk))
dai_props->sysclk = clk_get_rate(clk);
}
return 0;
}
static int rsrc_card_dai_link_of(struct device_node *node,
struct device_node *np,
struct rsrc_card_priv *priv,
int idx)
{
struct device *dev = rsrc_priv_to_dev(priv);
struct snd_soc_dai_link *dai_link = rsrc_priv_to_link(priv, idx);
struct rsrc_card_dai_props *dai_props = rsrc_priv_to_props(priv, idx);
struct device_node *cpu = NULL;
struct device_node *codec = NULL;
char *name;
char prop[128];
int ret, cpu_args;
struct rsrc_card_dai *dai_props = rsrc_priv_to_props(priv, idx);
bool is_fe = false;
int ret;
cpu = of_get_child_by_name(node, "cpu");
codec = of_get_child_by_name(node, "codec");
if (0 == strcmp(np->name, "cpu"))
is_fe = true;
if (!cpu || !codec) {
ret = -EINVAL;
dev_err(dev, "%s: Can't find %s DT node\n", __func__, prop);
goto dai_link_of_err;
}
ret = rsrc_card_parse_daifmt(node, priv, codec, idx);
ret = rsrc_card_parse_daifmt(node, np, priv, idx, is_fe);
if (ret < 0)
goto dai_link_of_err;
return ret;
ret = rsrc_card_sub_parse_of(priv, (idx == IDX_CPU) ? cpu : NULL,
&dai_props->cpu_dai,
dai_link,
&cpu_args);
ret = rsrc_card_parse_links(np, priv, idx, is_fe);
if (ret < 0)
goto dai_link_of_err;
return ret;
ret = rsrc_card_sub_parse_of(priv, (idx == IDX_CODEC) ? codec : NULL,
&dai_props->codec_dai,
dai_link,
NULL);
ret = rsrc_card_parse_clk(np, priv, idx, is_fe);
if (ret < 0)
goto dai_link_of_err;
return ret;
if (!dai_link->cpu_dai_name || !dai_link->codec_dai_name) {
ret = -EINVAL;
goto dai_link_of_err;
}
/* Simple Card assumes platform == cpu */
dai_link->platform_of_node = dai_link->cpu_of_node;
/* DAI link name is created from CPU/CODEC dai name */
name = devm_kzalloc(dev,
strlen(dai_link->cpu_dai_name) +
strlen(dai_link->codec_dai_name) + 2,
GFP_KERNEL);
if (!name) {
ret = -ENOMEM;
goto dai_link_of_err;
}
sprintf(name, "%s-%s", dai_link->cpu_dai_name,
dai_link->codec_dai_name);
dai_link->name = dai_link->stream_name = name;
dai_link->ops = &rsrc_card_ops;
dai_link->init = rsrc_card_dai_init;
if (idx == IDX_CODEC)
dai_link->be_hw_params_fixup = rsrc_card_be_hw_params_fixup;
dev_dbg(dev, "\tname : %s\n", dai_link->stream_name);
dev_dbg(dev, "\tcpu : %s / %04x / %d\n",
dai_link->cpu_dai_name,
dai_props->cpu_dai.fmt,
dai_props->cpu_dai.sysclk);
dev_dbg(dev, "\tcodec : %s / %04x / %d\n",
dai_link->codec_dai_name,
dai_props->codec_dai.fmt,
dai_props->codec_dai.sysclk);
/*
* In soc_bind_dai_link() will check cpu name after
* of_node matching if dai_link has cpu_dai_name.
* but, it will never match if name was created by
* fmt_single_name() remove cpu_dai_name if cpu_args
* was 0. See:
* fmt_single_name()
* fmt_multiple_name()
*/
if (!cpu_args)
dai_link->cpu_dai_name = NULL;
dai_link_of_err:
of_node_put(cpu);
of_node_put(codec);
dev_dbg(dev, "\t%s / %04x / %d\n",
dai_props->dai_name,
dai_props->fmt,
dai_props->sysclk);
return ret;
}
static int rsrc_card_parse_of(struct device_node *node,
struct rsrc_card_priv *priv)
struct rsrc_card_priv *priv,
struct device *dev)
{
struct device *dev = rsrc_priv_to_dev(priv);
const struct rsrc_card_of_data *of_data = rsrc_dev_to_of_data(dev);
struct rsrc_card_dai *props;
struct snd_soc_dai_link *links;
struct device_node *np;
int ret;
int i;
int i, num;
if (!node)
return -EINVAL;
/* Parse the card name from DT */
snd_soc_of_parse_card_name(&priv->snd_card, "card-name");
num = of_get_child_count(node);
props = devm_kzalloc(dev, sizeof(*props) * num, GFP_KERNEL);
links = devm_kzalloc(dev, sizeof(*links) * num, GFP_KERNEL);
if (!props || !links)
return -ENOMEM;
/* DAPM routes */
priv->dai_props = props;
priv->dai_link = links;
priv->dai_num = num;
/* Init snd_soc_card */
priv->snd_card.owner = THIS_MODULE;
priv->snd_card.dev = dev;
priv->snd_card.dai_link = priv->dai_link;
priv->snd_card.num_links = num;
priv->snd_card.codec_conf = &priv->codec_conf;
priv->snd_card.num_configs = 1;
priv->snd_card.of_dapm_routes = of_data->routes;
priv->snd_card.num_of_dapm_routes = of_data->num_routes;
/* Parse the card name from DT */
snd_soc_of_parse_card_name(&priv->snd_card, "card-name");
/* sampling rate convert */
of_property_read_u32(node, "convert-rate", &priv->convert_rate);
@ -421,11 +376,12 @@ static int rsrc_card_parse_of(struct device_node *node,
priv->snd_card.name ? priv->snd_card.name : "",
priv->convert_rate);
/* FE/BE */
for (i = 0; i < RSRC_FB_NUM; i++) {
ret = rsrc_card_dai_link_of(node, priv, i);
i = 0;
for_each_child_of_node(node, np) {
ret = rsrc_card_dai_link_of(node, np, priv, i);
if (ret < 0)
return ret;
i++;
}
if (!priv->snd_card.name)
@ -452,7 +408,6 @@ static int rsrc_card_unref(struct snd_soc_card *card)
static int rsrc_card_probe(struct platform_device *pdev)
{
struct rsrc_card_priv *priv;
struct snd_soc_dai_link *dai_link;
struct device_node *np = pdev->dev.of_node;
struct device *dev = &pdev->dev;
int ret;
@ -462,16 +417,7 @@ static int rsrc_card_probe(struct platform_device *pdev)
if (!priv)
return -ENOMEM;
/* Init snd_soc_card */
priv->snd_card.owner = THIS_MODULE;
priv->snd_card.dev = dev;
dai_link = priv->dai_link;
priv->snd_card.dai_link = dai_link;
priv->snd_card.num_links = RSRC_FB_NUM;
priv->snd_card.codec_conf = &priv->codec_conf;
priv->snd_card.num_configs = 1;
ret = rsrc_card_parse_of(np, priv);
ret = rsrc_card_parse_of(np, priv, dev);
if (ret < 0) {
if (ret != -EPROBE_DEFER)
dev_err(dev, "parse error %d\n", ret);

View File

@ -117,10 +117,10 @@ struct rsnd_src {
/*
* Gen1/Gen2 common functions
*/
static struct dma_chan *rsnd_src_dma_req(struct rsnd_mod *mod)
static struct dma_chan *rsnd_src_dma_req(struct rsnd_dai_stream *io,
struct rsnd_mod *mod)
{
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
int is_play = rsnd_io_is_play(io);
return rsnd_dma_request_channel(rsnd_src_of_node(priv),
@ -129,9 +129,9 @@ static struct dma_chan *rsnd_src_dma_req(struct rsnd_mod *mod)
}
int rsnd_src_ssiu_start(struct rsnd_mod *ssi_mod,
struct rsnd_dai_stream *io,
int use_busif)
{
struct rsnd_dai_stream *io = rsnd_mod_to_io(ssi_mod);
struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
int ssi_id = rsnd_mod_id(ssi_mod);
@ -174,7 +174,7 @@ int rsnd_src_ssiu_start(struct rsnd_mod *ssi_mod,
u32 mask = ~0;
rsnd_mod_write(ssi_mod, SSI_BUSIF_ADINR,
rsnd_get_adinr(ssi_mod));
rsnd_get_adinr(ssi_mod, io));
rsnd_mod_write(ssi_mod, SSI_BUSIF_MODE, 1);
rsnd_mod_write(ssi_mod, SSI_CTRL, 0x1);
@ -196,7 +196,8 @@ int rsnd_src_ssiu_start(struct rsnd_mod *ssi_mod,
return 0;
}
int rsnd_src_ssiu_stop(struct rsnd_mod *ssi_mod)
int rsnd_src_ssiu_stop(struct rsnd_mod *ssi_mod,
struct rsnd_dai_stream *io)
{
/*
* DMA settings for SSIU
@ -235,10 +236,9 @@ int rsnd_src_ssi_irq_disable(struct rsnd_mod *ssi_mod)
return 0;
}
static u32 rsnd_src_convert_rate(struct rsnd_src *src)
static u32 rsnd_src_convert_rate(struct rsnd_dai_stream *io,
struct rsnd_src *src)
{
struct rsnd_mod *mod = &src->mod;
struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
u32 convert_rate;
@ -274,7 +274,7 @@ unsigned int rsnd_src_get_ssi_rate(struct rsnd_priv *priv,
* return convert rate if SRC is used,
* otherwise, return runtime->rate as usual
*/
rate = rsnd_src_convert_rate(src);
rate = rsnd_src_convert_rate(io, src);
}
if (!rate)
@ -283,12 +283,12 @@ unsigned int rsnd_src_get_ssi_rate(struct rsnd_priv *priv,
return rate;
}
static int rsnd_src_set_convert_rate(struct rsnd_mod *mod)
static int rsnd_src_set_convert_rate(struct rsnd_mod *mod,
struct rsnd_dai_stream *io)
{
struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
struct rsnd_src *src = rsnd_mod_to_src(mod);
u32 convert_rate = rsnd_src_convert_rate(src);
u32 convert_rate = rsnd_src_convert_rate(io, src);
u32 fsrate = 0;
if (convert_rate)
@ -299,7 +299,7 @@ static int rsnd_src_set_convert_rate(struct rsnd_mod *mod)
rsnd_mod_write(mod, SRC_SWRSR, 1);
/* Set channel number and output bit length */
rsnd_mod_write(mod, SRC_ADINR, rsnd_get_adinr(mod));
rsnd_mod_write(mod, SRC_ADINR, rsnd_get_adinr(mod, io));
/* Enable the initial value of IFS */
if (fsrate) {
@ -316,6 +316,7 @@ static int rsnd_src_set_convert_rate(struct rsnd_mod *mod)
}
static int rsnd_src_hw_params(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *fe_params)
{
@ -372,6 +373,7 @@ static int rsnd_src_init(struct rsnd_mod *mod,
}
static int rsnd_src_quit(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
struct rsnd_src *src = rsnd_mod_to_src(mod);
@ -411,9 +413,9 @@ static int rsnd_src_stop(struct rsnd_mod *mod)
/*
* Gen1 functions
*/
static int rsnd_src_set_route_gen1(struct rsnd_mod *mod)
static int rsnd_src_set_route_gen1(struct rsnd_dai_stream *io,
struct rsnd_mod *mod)
{
struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
struct src_route_config {
u32 mask;
int shift;
@ -448,13 +450,13 @@ static int rsnd_src_set_route_gen1(struct rsnd_mod *mod)
return 0;
}
static int rsnd_src_set_convert_timing_gen1(struct rsnd_mod *mod)
static int rsnd_src_set_convert_timing_gen1(struct rsnd_dai_stream *io,
struct rsnd_mod *mod)
{
struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
struct rsnd_src *src = rsnd_mod_to_src(mod);
struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
u32 convert_rate = rsnd_src_convert_rate(src);
u32 convert_rate = rsnd_src_convert_rate(io, src);
u32 mask;
u32 val;
int shift;
@ -506,12 +508,13 @@ static int rsnd_src_set_convert_timing_gen1(struct rsnd_mod *mod)
return 0;
}
static int rsnd_src_set_convert_rate_gen1(struct rsnd_mod *mod)
static int rsnd_src_set_convert_rate_gen1(struct rsnd_mod *mod,
struct rsnd_dai_stream *io)
{
struct rsnd_src *src = rsnd_mod_to_src(mod);
int ret;
ret = rsnd_src_set_convert_rate(mod);
ret = rsnd_src_set_convert_rate(mod, io);
if (ret < 0)
return ret;
@ -523,7 +526,7 @@ static int rsnd_src_set_convert_rate_gen1(struct rsnd_mod *mod)
rsnd_mod_read(mod, SRC_IFSVR) / 100 * 98);
/* Gen1/Gen2 are not compatible */
if (rsnd_src_convert_rate(src))
if (rsnd_src_convert_rate(io, src))
rsnd_mod_write(mod, SRC_ROUTE_MODE0, 1);
/* no SRC_BFSSR settings, since SRC_SRCCR::BUFMD is 0 */
@ -532,6 +535,7 @@ static int rsnd_src_set_convert_rate_gen1(struct rsnd_mod *mod)
}
static int rsnd_src_init_gen1(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
int ret;
@ -540,15 +544,15 @@ static int rsnd_src_init_gen1(struct rsnd_mod *mod,
if (ret < 0)
return ret;
ret = rsnd_src_set_route_gen1(mod);
ret = rsnd_src_set_route_gen1(io, mod);
if (ret < 0)
return ret;
ret = rsnd_src_set_convert_rate_gen1(mod);
ret = rsnd_src_set_convert_rate_gen1(mod, io);
if (ret < 0)
return ret;
ret = rsnd_src_set_convert_timing_gen1(mod);
ret = rsnd_src_set_convert_timing_gen1(io, mod);
if (ret < 0)
return ret;
@ -556,6 +560,7 @@ static int rsnd_src_init_gen1(struct rsnd_mod *mod,
}
static int rsnd_src_start_gen1(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
int id = rsnd_mod_id(mod);
@ -566,6 +571,7 @@ static int rsnd_src_start_gen1(struct rsnd_mod *mod,
}
static int rsnd_src_stop_gen1(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
int id = rsnd_mod_id(mod);
@ -643,9 +649,9 @@ static bool rsnd_src_error_record_gen2(struct rsnd_mod *mod)
return ret;
}
static int _rsnd_src_start_gen2(struct rsnd_mod *mod)
static int _rsnd_src_start_gen2(struct rsnd_mod *mod,
struct rsnd_dai_stream *io)
{
struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
u32 val = rsnd_io_to_mod_dvc(io) ? 0x01 : 0x11;
rsnd_mod_write(mod, SRC_CTRL, val);
@ -670,15 +676,15 @@ static int _rsnd_src_stop_gen2(struct rsnd_mod *mod)
return rsnd_src_stop(mod);
}
static irqreturn_t rsnd_src_interrupt_gen2(int irq, void *data)
static void __rsnd_src_interrupt_gen2(struct rsnd_mod *mod,
struct rsnd_dai_stream *io)
{
struct rsnd_mod *mod = data;
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
spin_lock(&priv->lock);
/* ignore all cases if not working */
if (!rsnd_mod_is_working(mod))
if (!rsnd_io_is_working(io))
goto rsnd_src_interrupt_gen2_out;
if (rsnd_src_error_record_gen2(mod)) {
@ -691,24 +697,32 @@ static irqreturn_t rsnd_src_interrupt_gen2(int irq, void *data)
_rsnd_src_stop_gen2(mod);
if (src->err < 1024)
_rsnd_src_start_gen2(mod);
_rsnd_src_start_gen2(mod, io);
else
dev_warn(dev, "no more SRC restart\n");
}
rsnd_src_interrupt_gen2_out:
spin_unlock(&priv->lock);
}
static irqreturn_t rsnd_src_interrupt_gen2(int irq, void *data)
{
struct rsnd_mod *mod = data;
rsnd_mod_interrupt(mod, __rsnd_src_interrupt_gen2);
return IRQ_HANDLED;
}
static int rsnd_src_set_convert_rate_gen2(struct rsnd_mod *mod)
static int rsnd_src_set_convert_rate_gen2(struct rsnd_mod *mod,
struct rsnd_dai_stream *io)
{
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
struct device *dev = rsnd_priv_to_dev(priv);
struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
struct rsnd_src *src = rsnd_mod_to_src(mod);
u32 convert_rate = rsnd_src_convert_rate(src);
u32 convert_rate = rsnd_src_convert_rate(io, src);
u32 cr, route;
uint ratio;
int ret;
@ -726,7 +740,7 @@ static int rsnd_src_set_convert_rate_gen2(struct rsnd_mod *mod)
return -EINVAL;
}
ret = rsnd_src_set_convert_rate(mod);
ret = rsnd_src_set_convert_rate(mod, io);
if (ret < 0)
return ret;
@ -762,12 +776,12 @@ static int rsnd_src_set_convert_rate_gen2(struct rsnd_mod *mod)
return 0;
}
static int rsnd_src_set_convert_timing_gen2(struct rsnd_mod *mod)
static int rsnd_src_set_convert_timing_gen2(struct rsnd_dai_stream *io,
struct rsnd_mod *mod)
{
struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
struct rsnd_src *src = rsnd_mod_to_src(mod);
u32 convert_rate = rsnd_src_convert_rate(src);
u32 convert_rate = rsnd_src_convert_rate(io, src);
int ret;
if (convert_rate)
@ -781,6 +795,7 @@ static int rsnd_src_set_convert_timing_gen2(struct rsnd_mod *mod)
}
static int rsnd_src_probe_gen2(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
struct rsnd_src *src = rsnd_mod_to_src(mod);
@ -802,7 +817,7 @@ static int rsnd_src_probe_gen2(struct rsnd_mod *mod,
return ret;
}
ret = rsnd_dma_init(priv,
ret = rsnd_dma_init(io,
rsnd_mod_to_dma(mod),
src->info->dma_id);
@ -810,14 +825,16 @@ static int rsnd_src_probe_gen2(struct rsnd_mod *mod,
}
static int rsnd_src_remove_gen2(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
rsnd_dma_quit(rsnd_mod_to_dma(mod));
rsnd_dma_quit(io, rsnd_mod_to_dma(mod));
return 0;
}
static int rsnd_src_init_gen2(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
int ret;
@ -826,11 +843,11 @@ static int rsnd_src_init_gen2(struct rsnd_mod *mod,
if (ret < 0)
return ret;
ret = rsnd_src_set_convert_rate_gen2(mod);
ret = rsnd_src_set_convert_rate_gen2(mod, io);
if (ret < 0)
return ret;
ret = rsnd_src_set_convert_timing_gen2(mod);
ret = rsnd_src_set_convert_timing_gen2(io, mod);
if (ret < 0)
return ret;
@ -838,31 +855,33 @@ static int rsnd_src_init_gen2(struct rsnd_mod *mod,
}
static int rsnd_src_start_gen2(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
rsnd_dma_start(rsnd_mod_to_dma(mod));
rsnd_dma_start(io, rsnd_mod_to_dma(mod));
return _rsnd_src_start_gen2(mod);
return _rsnd_src_start_gen2(mod, io);
}
static int rsnd_src_stop_gen2(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
int ret;
ret = _rsnd_src_stop_gen2(mod);
rsnd_dma_stop(rsnd_mod_to_dma(mod));
rsnd_dma_stop(io, rsnd_mod_to_dma(mod));
return ret;
}
static void rsnd_src_reconvert_update(struct rsnd_mod *mod)
static void rsnd_src_reconvert_update(struct rsnd_dai_stream *io,
struct rsnd_mod *mod)
{
struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
struct rsnd_src *src = rsnd_mod_to_src(mod);
u32 convert_rate = rsnd_src_convert_rate(src);
u32 convert_rate = rsnd_src_convert_rate(io, src);
u32 fsrate;
if (!runtime)
@ -878,10 +897,10 @@ static void rsnd_src_reconvert_update(struct rsnd_mod *mod)
}
static int rsnd_src_pcm_new(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct snd_soc_pcm_runtime *rtd)
{
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
struct rsnd_src *src = rsnd_mod_to_src(mod);
int ret;
@ -912,7 +931,7 @@ static int rsnd_src_pcm_new(struct rsnd_mod *mod,
/*
* enable sync convert
*/
ret = rsnd_kctrl_new_s(mod, rtd,
ret = rsnd_kctrl_new_s(mod, io, rtd,
rsnd_io_is_play(io) ?
"SRC Out Rate Switch" :
"SRC In Rate Switch",
@ -921,7 +940,7 @@ static int rsnd_src_pcm_new(struct rsnd_mod *mod,
if (ret < 0)
return ret;
ret = rsnd_kctrl_new_s(mod, rtd,
ret = rsnd_kctrl_new_s(mod, io, rtd,
rsnd_io_is_play(io) ?
"SRC Out Rate" :
"SRC In Rate",
@ -1046,7 +1065,7 @@ int rsnd_src_probe(struct platform_device *pdev,
src->info = &info->src_info[i];
ret = rsnd_mod_init(&src->mod, ops, clk, RSND_MOD_SRC, i);
ret = rsnd_mod_init(priv, &src->mod, ops, clk, RSND_MOD_SRC, i);
if (ret)
return ret;
}

View File

@ -87,10 +87,9 @@ struct rsnd_ssi {
#define rsnd_ssi_of_node(priv) \
of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,ssi")
int rsnd_ssi_use_busif(struct rsnd_mod *mod)
int rsnd_ssi_use_busif(struct rsnd_dai_stream *io, struct rsnd_mod *mod)
{
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
int use_busif = 0;
if (!rsnd_ssi_is_dma_mode(mod))
@ -199,15 +198,17 @@ static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi,
}
}
cr_mode = rsnd_ssi_is_dma_mode(&ssi->mod) ?
DMEN : /* DMA : enable DMA */
DIEN; /* PIO : enable Data interrupt */
if (rsnd_ssi_is_dma_mode(&ssi->mod)) {
cr_mode = UIEN | OIEN | /* over/under run */
DMEN; /* DMA : enable DMA */
} else {
cr_mode = DIEN; /* PIO : enable Data interrupt */
}
cr = ssi->cr_own |
ssi->cr_clk |
cr_mode |
UIEN | OIEN | EN;
EN;
rsnd_mod_write(&ssi->mod, SSICR, cr);
@ -224,10 +225,9 @@ static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi,
rsnd_mod_name(&ssi->mod), rsnd_mod_id(&ssi->mod));
}
static void rsnd_ssi_hw_stop(struct rsnd_ssi *ssi)
static void rsnd_ssi_hw_stop(struct rsnd_dai_stream *io, struct rsnd_ssi *ssi)
{
struct rsnd_priv *priv = rsnd_mod_to_priv(&ssi->mod);
struct rsnd_dai_stream *io = rsnd_mod_to_io(&ssi->mod);
struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
struct device *dev = rsnd_priv_to_dev(priv);
u32 cr;
@ -261,7 +261,7 @@ static void rsnd_ssi_hw_stop(struct rsnd_ssi *ssi)
struct rsnd_ssi *ssi_parent = rsnd_ssi_parent(ssi);
if (ssi_parent)
rsnd_ssi_hw_stop(ssi_parent);
rsnd_ssi_hw_stop(io, ssi_parent);
else
rsnd_ssi_master_clk_stop(ssi);
}
@ -279,10 +279,10 @@ static void rsnd_ssi_hw_stop(struct rsnd_ssi *ssi)
* SSI mod common functions
*/
static int rsnd_ssi_init(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
u32 cr;
@ -330,6 +330,7 @@ static int rsnd_ssi_init(struct rsnd_mod *mod,
}
static int rsnd_ssi_quit(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
@ -346,6 +347,7 @@ static int rsnd_ssi_quit(struct rsnd_mod *mod,
}
static int rsnd_ssi_hw_params(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
@ -369,7 +371,8 @@ static int rsnd_ssi_hw_params(struct rsnd_mod *mod,
/* It will be removed on rsnd_ssi_hw_stop */
ssi->chan = chan;
if (ssi_parent)
return rsnd_ssi_hw_params(&ssi_parent->mod, substream, params);
return rsnd_ssi_hw_params(&ssi_parent->mod, io,
substream, params);
return 0;
}
@ -386,12 +389,12 @@ static void rsnd_ssi_record_error(struct rsnd_ssi *ssi, u32 status)
}
static int rsnd_ssi_start(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
rsnd_src_ssiu_start(mod, rsnd_ssi_use_busif(mod));
rsnd_src_ssiu_start(mod, io, rsnd_ssi_use_busif(io, mod));
rsnd_ssi_hw_start(ssi, io);
@ -401,6 +404,7 @@ static int rsnd_ssi_start(struct rsnd_mod *mod,
}
static int rsnd_ssi_stop(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
@ -409,26 +413,26 @@ static int rsnd_ssi_stop(struct rsnd_mod *mod,
rsnd_ssi_record_error(ssi, rsnd_mod_read(mod, SSISR));
rsnd_ssi_hw_stop(ssi);
rsnd_ssi_hw_stop(io, ssi);
rsnd_src_ssiu_stop(mod);
rsnd_src_ssiu_stop(mod, io);
return 0;
}
static irqreturn_t rsnd_ssi_interrupt(int irq, void *data)
static void __rsnd_ssi_interrupt(struct rsnd_mod *mod,
struct rsnd_dai_stream *io)
{
struct rsnd_ssi *ssi = data;
struct rsnd_mod *mod = &ssi->mod;
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
int is_dma = rsnd_ssi_is_dma_mode(mod);
u32 status;
bool elapsed = false;
spin_lock(&priv->lock);
/* ignore all cases if not working */
if (!rsnd_mod_is_working(mod))
if (!rsnd_io_is_working(io))
goto rsnd_ssi_interrupt_out;
status = rsnd_mod_read(mod, SSISR);
@ -449,11 +453,11 @@ static irqreturn_t rsnd_ssi_interrupt(int irq, void *data)
else
*buf = rsnd_mod_read(mod, SSIRDR);
rsnd_dai_pointer_update(io, sizeof(*buf));
elapsed = rsnd_dai_pointer_update(io, sizeof(*buf));
}
/* PIO / DMA */
if (status & (UIRQ | OIRQ)) {
/* DMA only */
if (is_dma && (status & (UIRQ | OIRQ))) {
struct device *dev = rsnd_priv_to_dev(priv);
/*
@ -462,9 +466,9 @@ static irqreturn_t rsnd_ssi_interrupt(int irq, void *data)
dev_dbg(dev, "%s[%d] restart\n",
rsnd_mod_name(mod), rsnd_mod_id(mod));
rsnd_ssi_stop(mod, priv);
rsnd_ssi_stop(mod, io, priv);
if (ssi->err < 1024)
rsnd_ssi_start(mod, priv);
rsnd_ssi_start(mod, io, priv);
else
dev_warn(dev, "no more SSI restart\n");
}
@ -474,6 +478,16 @@ static irqreturn_t rsnd_ssi_interrupt(int irq, void *data)
rsnd_ssi_interrupt_out:
spin_unlock(&priv->lock);
if (elapsed)
rsnd_dai_period_elapsed(io);
}
static irqreturn_t rsnd_ssi_interrupt(int irq, void *data)
{
struct rsnd_mod *mod = data;
rsnd_mod_interrupt(mod, __rsnd_ssi_interrupt);
return IRQ_HANDLED;
}
@ -481,6 +495,7 @@ rsnd_ssi_interrupt_out:
* SSI PIO
*/
static int rsnd_ssi_pio_probe(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
struct device *dev = rsnd_priv_to_dev(priv);
@ -490,7 +505,7 @@ static int rsnd_ssi_pio_probe(struct rsnd_mod *mod,
ret = devm_request_irq(dev, ssi->info->irq,
rsnd_ssi_interrupt,
IRQF_SHARED,
dev_name(dev), ssi);
dev_name(dev), mod);
return ret;
}
@ -506,6 +521,7 @@ static struct rsnd_mod_ops rsnd_ssi_pio_ops = {
};
static int rsnd_ssi_dma_probe(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
@ -516,25 +532,26 @@ static int rsnd_ssi_dma_probe(struct rsnd_mod *mod,
ret = devm_request_irq(dev, ssi->info->irq,
rsnd_ssi_interrupt,
IRQF_SHARED,
dev_name(dev), ssi);
dev_name(dev), mod);
if (ret)
return ret;
ret = rsnd_dma_init(
priv, rsnd_mod_to_dma(mod),
io, rsnd_mod_to_dma(mod),
dma_id);
return ret;
}
static int rsnd_ssi_dma_remove(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
struct device *dev = rsnd_priv_to_dev(priv);
int irq = ssi->info->irq;
rsnd_dma_quit(rsnd_mod_to_dma(mod));
rsnd_dma_quit(io, rsnd_mod_to_dma(mod));
/* PIO will request IRQ again */
devm_free_irq(dev, irq, ssi);
@ -543,6 +560,7 @@ static int rsnd_ssi_dma_remove(struct rsnd_mod *mod,
}
static int rsnd_ssi_fallback(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
struct device *dev = rsnd_priv_to_dev(priv);
@ -563,37 +581,39 @@ static int rsnd_ssi_fallback(struct rsnd_mod *mod,
}
static int rsnd_ssi_dma_start(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
struct rsnd_dma *dma = rsnd_mod_to_dma(mod);
rsnd_dma_start(dma);
rsnd_dma_start(io, dma);
rsnd_ssi_start(mod, priv);
rsnd_ssi_start(mod, io, priv);
return 0;
}
static int rsnd_ssi_dma_stop(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
struct rsnd_dma *dma = rsnd_mod_to_dma(mod);
rsnd_ssi_stop(mod, priv);
rsnd_ssi_stop(mod, io, priv);
rsnd_dma_stop(dma);
rsnd_dma_stop(io, dma);
return 0;
}
static struct dma_chan *rsnd_ssi_dma_req(struct rsnd_mod *mod)
static struct dma_chan *rsnd_ssi_dma_req(struct rsnd_dai_stream *io,
struct rsnd_mod *mod)
{
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
int is_play = rsnd_io_is_play(io);
char *name;
if (rsnd_ssi_use_busif(mod))
if (rsnd_ssi_use_busif(io, mod))
name = is_play ? "rxu" : "txu";
else
name = is_play ? "rx" : "tx";
@ -776,7 +796,7 @@ int rsnd_ssi_probe(struct platform_device *pdev,
else if (rsnd_ssi_pio_available(ssi))
ops = &rsnd_ssi_pio_ops;
ret = rsnd_mod_init(&ssi->mod, ops, clk, RSND_MOD_SSI, i);
ret = rsnd_mod_init(priv, &ssi->mod, ops, clk, RSND_MOD_SSI, i);
if (ret)
return ret;