summaryrefslogtreecommitdiff
path: root/sound/soc/codecs
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/codecs')
-rw-r--r--sound/soc/codecs/Kconfig6
-rw-r--r--sound/soc/codecs/Makefile4
-rw-r--r--sound/soc/codecs/arizona.c48
-rw-r--r--sound/soc/codecs/da7218.c47
-rw-r--r--sound/soc/codecs/da7219.c74
-rw-r--r--sound/soc/codecs/hdac_hdmi.c180
-rw-r--r--sound/soc/codecs/pcm1792a.c271
-rw-r--r--sound/soc/codecs/pcm179x.c271
-rw-r--r--sound/soc/codecs/pcm179x.h (renamed from sound/soc/codecs/pcm1792a.h)6
-rw-r--r--sound/soc/codecs/rt286.c26
-rw-r--r--sound/soc/codecs/rt5640.c103
-rw-r--r--sound/soc/codecs/rt5640.h17
-rw-r--r--sound/soc/codecs/rt5645.c36
-rw-r--r--sound/soc/codecs/rt5645.h4
-rw-r--r--sound/soc/codecs/rt5651.c31
-rw-r--r--sound/soc/codecs/rt5659.c31
-rw-r--r--sound/soc/codecs/rt5659.h1
-rw-r--r--sound/soc/codecs/rt5677.c2
-rw-r--r--sound/soc/codecs/sigmadsp-i2c.c5
-rw-r--r--sound/soc/codecs/twl6040.c3
-rw-r--r--sound/soc/codecs/wm5100.c2
-rw-r--r--sound/soc/codecs/wm5110.c46
-rw-r--r--sound/soc/codecs/wm8903.c2
-rw-r--r--sound/soc/codecs/wm8960.c64
-rw-r--r--sound/soc/codecs/wm8962.c2
-rw-r--r--sound/soc/codecs/wm8996.c2
-rw-r--r--sound/soc/codecs/wm_adsp.c326
-rw-r--r--sound/soc/codecs/wm_adsp.h5
28 files changed, 1151 insertions, 464 deletions
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 629ee56a22ce..50693c867e71 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -87,7 +87,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_ML26124 if I2C
select SND_SOC_NAU8825 if I2C
select SND_SOC_PCM1681 if I2C
- select SND_SOC_PCM1792A if SPI_MASTER
+ select SND_SOC_PCM179X if SPI_MASTER
select SND_SOC_PCM3008
select SND_SOC_PCM3168A_I2C if I2C
select SND_SOC_PCM3168A_SPI if SPI_MASTER
@@ -526,8 +526,8 @@ config SND_SOC_PCM1681
tristate "Texas Instruments PCM1681 CODEC"
depends on I2C
-config SND_SOC_PCM1792A
- tristate "Texas Instruments PCM1792A CODEC"
+config SND_SOC_PCM179X
+ tristate "Texas Instruments PCM179X CODEC"
depends on SPI_MASTER
config SND_SOC_PCM3008
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index fb846e2ad33b..d44f7d347183 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -80,7 +80,7 @@ snd-soc-mc13783-objs := mc13783.o
snd-soc-ml26124-objs := ml26124.o
snd-soc-nau8825-objs := nau8825.o
snd-soc-pcm1681-objs := pcm1681.o
-snd-soc-pcm1792a-codec-objs := pcm1792a.o
+snd-soc-pcm179x-codec-objs := pcm179x.o
snd-soc-pcm3008-objs := pcm3008.o
snd-soc-pcm3168a-objs := pcm3168a.o
snd-soc-pcm3168a-i2c-objs := pcm3168a-i2c.o
@@ -284,7 +284,7 @@ obj-$(CONFIG_SND_SOC_MC13783) += snd-soc-mc13783.o
obj-$(CONFIG_SND_SOC_ML26124) += snd-soc-ml26124.o
obj-$(CONFIG_SND_SOC_NAU8825) += snd-soc-nau8825.o
obj-$(CONFIG_SND_SOC_PCM1681) += snd-soc-pcm1681.o
-obj-$(CONFIG_SND_SOC_PCM1792A) += snd-soc-pcm1792a-codec.o
+obj-$(CONFIG_SND_SOC_PCM179X) += snd-soc-pcm179x-codec.o
obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o
obj-$(CONFIG_SND_SOC_PCM3168A) += snd-soc-pcm3168a.o
obj-$(CONFIG_SND_SOC_PCM3168A_I2C) += snd-soc-pcm3168a-i2c.o
diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c
index 38a73e3da508..91785318b283 100644
--- a/sound/soc/codecs/arizona.c
+++ b/sound/soc/codecs/arizona.c
@@ -1494,6 +1494,9 @@ static int arizona_startup(struct snd_pcm_substream *substream,
const struct snd_pcm_hw_constraint_list *constraint;
unsigned int base_rate;
+ if (!substream->runtime)
+ return 0;
+
switch (dai_priv->clk) {
case ARIZONA_CLK_SYSCLK:
base_rate = priv->sysclk;
@@ -1656,7 +1659,7 @@ static int arizona_hw_params(struct snd_pcm_substream *substream,
bool reconfig;
unsigned int aif_tx_state, aif_rx_state;
- if (params_rate(params) % 8000)
+ if (params_rate(params) % 4000)
rates = &arizona_44k1_bclk_rates[0];
else
rates = &arizona_48k_bclk_rates[0];
@@ -1926,6 +1929,25 @@ static struct {
{ 1000000, 13500000, 0, 1 },
};
+static const unsigned int pseudo_fref_max[ARIZONA_FLL_MAX_FRATIO] = {
+ 13500000,
+ 6144000,
+ 6144000,
+ 3072000,
+ 3072000,
+ 2822400,
+ 2822400,
+ 1536000,
+ 1536000,
+ 1536000,
+ 1536000,
+ 1536000,
+ 1536000,
+ 1536000,
+ 1536000,
+ 768000,
+};
+
static struct {
unsigned int min;
unsigned int max;
@@ -2039,16 +2061,32 @@ static int arizona_calc_fratio(struct arizona_fll *fll,
/* Adjust FRATIO/refdiv to avoid integer mode if possible */
refdiv = cfg->refdiv;
+ arizona_fll_dbg(fll, "pseudo: initial ratio=%u fref=%u refdiv=%u\n",
+ init_ratio, Fref, refdiv);
+
while (div <= ARIZONA_FLL_MAX_REFDIV) {
for (ratio = init_ratio; ratio <= ARIZONA_FLL_MAX_FRATIO;
ratio++) {
if ((ARIZONA_FLL_VCO_CORNER / 2) /
- (fll->vco_mult * ratio) < Fref)
+ (fll->vco_mult * ratio) < Fref) {
+ arizona_fll_dbg(fll, "pseudo: hit VCO corner\n");
break;
+ }
+
+ if (Fref > pseudo_fref_max[ratio - 1]) {
+ arizona_fll_dbg(fll,
+ "pseudo: exceeded max fref(%u) for ratio=%u\n",
+ pseudo_fref_max[ratio - 1],
+ ratio);
+ break;
+ }
if (target % (ratio * Fref)) {
cfg->refdiv = refdiv;
cfg->fratio = ratio - 1;
+ arizona_fll_dbg(fll,
+ "pseudo: found fref=%u refdiv=%d(%d) ratio=%d\n",
+ Fref, refdiv, div, ratio);
return ratio;
}
}
@@ -2057,6 +2095,9 @@ static int arizona_calc_fratio(struct arizona_fll *fll,
if (target % (ratio * Fref)) {
cfg->refdiv = refdiv;
cfg->fratio = ratio - 1;
+ arizona_fll_dbg(fll,
+ "pseudo: found fref=%u refdiv=%d(%d) ratio=%d\n",
+ Fref, refdiv, div, ratio);
return ratio;
}
}
@@ -2065,6 +2106,9 @@ static int arizona_calc_fratio(struct arizona_fll *fll,
Fref /= 2;
refdiv++;
init_ratio = arizona_find_fratio(Fref, NULL);
+ arizona_fll_dbg(fll,
+ "pseudo: change fref=%u refdiv=%d(%d) ratio=%u\n",
+ Fref, refdiv, div, init_ratio);
}
arizona_fll_warn(fll, "Falling back to integer mode operation\n");
diff --git a/sound/soc/codecs/da7218.c b/sound/soc/codecs/da7218.c
index 72686517ff54..93575f251866 100644
--- a/sound/soc/codecs/da7218.c
+++ b/sound/soc/codecs/da7218.c
@@ -1954,17 +1954,44 @@ static int da7218_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
return -EINVAL;
}
- switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
- case SND_SOC_DAIFMT_NB_NF:
- break;
- case SND_SOC_DAIFMT_NB_IF:
- dai_clk_mode |= DA7218_DAI_WCLK_POL_INV;
- break;
- case SND_SOC_DAIFMT_IB_NF:
- dai_clk_mode |= DA7218_DAI_CLK_POL_INV;
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ case SND_SOC_DAIFMT_LEFT_J:
+ case SND_SOC_DAIFMT_RIGHT_J:
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ dai_clk_mode |= DA7218_DAI_WCLK_POL_INV;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ dai_clk_mode |= DA7218_DAI_CLK_POL_INV;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ dai_clk_mode |= DA7218_DAI_WCLK_POL_INV |
+ DA7218_DAI_CLK_POL_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
break;
- case SND_SOC_DAIFMT_IB_IF:
- dai_clk_mode |= DA7218_DAI_WCLK_POL_INV | DA7218_DAI_CLK_POL_INV;
+ case SND_SOC_DAIFMT_DSP_B:
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ dai_clk_mode |= DA7218_DAI_CLK_POL_INV;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ dai_clk_mode |= DA7218_DAI_WCLK_POL_INV |
+ DA7218_DAI_CLK_POL_INV;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ dai_clk_mode |= DA7218_DAI_WCLK_POL_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
break;
default:
return -EINVAL;
diff --git a/sound/soc/codecs/da7219.c b/sound/soc/codecs/da7219.c
index c6d3b32bb4ae..81c0708b85c1 100644
--- a/sound/soc/codecs/da7219.c
+++ b/sound/soc/codecs/da7219.c
@@ -1156,18 +1156,44 @@ static int da7219_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
return -EINVAL;
}
- switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
- case SND_SOC_DAIFMT_NB_NF:
- break;
- case SND_SOC_DAIFMT_NB_IF:
- dai_clk_mode |= DA7219_DAI_WCLK_POL_INV;
- break;
- case SND_SOC_DAIFMT_IB_NF:
- dai_clk_mode |= DA7219_DAI_CLK_POL_INV;
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ case SND_SOC_DAIFMT_LEFT_J:
+ case SND_SOC_DAIFMT_RIGHT_J:
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ dai_clk_mode |= DA7219_DAI_WCLK_POL_INV;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ dai_clk_mode |= DA7219_DAI_CLK_POL_INV;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ dai_clk_mode |= DA7219_DAI_WCLK_POL_INV |
+ DA7219_DAI_CLK_POL_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
break;
- case SND_SOC_DAIFMT_IB_IF:
- dai_clk_mode |= DA7219_DAI_WCLK_POL_INV |
- DA7219_DAI_CLK_POL_INV;
+ case SND_SOC_DAIFMT_DSP_B:
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ dai_clk_mode |= DA7219_DAI_CLK_POL_INV;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ dai_clk_mode |= DA7219_DAI_WCLK_POL_INV |
+ DA7219_DAI_CLK_POL_INV;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ dai_clk_mode |= DA7219_DAI_WCLK_POL_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
break;
default:
return -EINVAL;
@@ -1592,9 +1618,14 @@ static void da7219_handle_pdata(struct snd_soc_codec *codec)
}
}
+static struct reg_sequence da7219_rev_aa_patch[] = {
+ { DA7219_REFERENCES, 0x08 },
+};
+
static int da7219_probe(struct snd_soc_codec *codec)
{
struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ unsigned int rev;
int ret;
mutex_init(&da7219->lock);
@@ -1604,6 +1635,26 @@ static int da7219_probe(struct snd_soc_codec *codec)
if (ret)
return ret;
+ ret = regmap_read(da7219->regmap, DA7219_CHIP_REVISION, &rev);
+ if (ret) {
+ dev_err(codec->dev, "Failed to read chip revision: %d\n", ret);
+ goto err_disable_reg;
+ }
+
+ switch (rev & DA7219_CHIP_MINOR_MASK) {
+ case 0:
+ ret = regmap_register_patch(da7219->regmap, da7219_rev_aa_patch,
+ ARRAY_SIZE(da7219_rev_aa_patch));
+ if (ret) {
+ dev_err(codec->dev, "Failed to register AA patch: %d\n",
+ ret);
+ goto err_disable_reg;
+ }
+ break;
+ default:
+ break;
+ }
+
/* Handle DT/Platform data */
if (codec->dev->of_node)
da7219->pdata = da7219_of_to_pdata(codec);
@@ -1774,7 +1825,6 @@ static struct reg_default da7219_reg_defaults[] = {
{ DA7219_MIXOUT_R_CTRL, 0x10 },
{ DA7219_CHIP_ID1, 0x23 },
{ DA7219_CHIP_ID2, 0x93 },
- { DA7219_CHIP_REVISION, 0x00 },
{ DA7219_IO_CTRL, 0x00 },
{ DA7219_GAIN_RAMP_CTRL, 0x00 },
{ DA7219_PC_COUNT, 0x02 },
diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c
index 1a2f33b4abfc..5a1ec0f7a1a6 100644
--- a/sound/soc/codecs/hdac_hdmi.c
+++ b/sound/soc/codecs/hdac_hdmi.c
@@ -43,11 +43,13 @@ struct hdac_hdmi_cvt_params {
};
struct hdac_hdmi_cvt {
+ struct list_head head;
hda_nid_t nid;
struct hdac_hdmi_cvt_params params;
};
struct hdac_hdmi_pin {
+ struct list_head head;
hda_nid_t nid;
int num_mux_nids;
hda_nid_t mux_nids[HDA_MAX_CONNECTIONS];
@@ -55,21 +57,23 @@ struct hdac_hdmi_pin {
struct hdac_hdmi_dai_pin_map {
int dai_id;
- struct hdac_hdmi_pin pin;
- struct hdac_hdmi_cvt cvt;
+ struct hdac_hdmi_pin *pin;
+ struct hdac_hdmi_cvt *cvt;
};
struct hdac_hdmi_priv {
- hda_nid_t pin_nid[3];
- hda_nid_t cvt_nid[3];
struct hdac_hdmi_dai_pin_map dai_map[3];
+ struct list_head pin_list;
+ struct list_head cvt_list;
+ int num_pin;
+ int num_cvt;
};
static inline struct hdac_ext_device *to_hda_ext_device(struct device *dev)
{
- struct hdac_device *hdac = container_of(dev, struct hdac_device, dev);
+ struct hdac_device *hdac = dev_to_hdac_dev(dev);
- return container_of(hdac, struct hdac_ext_device, hdac);
+ return to_ehdac_device(hdac);
}
static int hdac_hdmi_setup_stream(struct hdac_ext_device *hdac,
@@ -149,13 +153,15 @@ static void hdac_hdmi_set_power_state(struct hdac_ext_device *edev,
struct hdac_hdmi_dai_pin_map *dai_map, unsigned int pwr_state)
{
/* Power up pin widget */
- if (!snd_hdac_check_power_state(&edev->hdac, dai_map->pin.nid, pwr_state))
- snd_hdac_codec_write(&edev->hdac, dai_map->pin.nid, 0,
+ if (!snd_hdac_check_power_state(&edev->hdac, dai_map->pin->nid,
+ pwr_state))
+ snd_hdac_codec_write(&edev->hdac, dai_map->pin->nid, 0,
AC_VERB_SET_POWER_STATE, pwr_state);
/* Power up converter */
- if (!snd_hdac_check_power_state(&edev->hdac, dai_map->cvt.nid, pwr_state))
- snd_hdac_codec_write(&edev->hdac, dai_map->cvt.nid, 0,
+ if (!snd_hdac_check_power_state(&edev->hdac, dai_map->cvt->nid,
+ pwr_state))
+ snd_hdac_codec_write(&edev->hdac, dai_map->cvt->nid, 0,
AC_VERB_SET_POWER_STATE, pwr_state);
}
@@ -179,13 +185,13 @@ static int hdac_hdmi_playback_prepare(struct snd_pcm_substream *substream,
dev_dbg(&hdac->hdac.dev, "stream tag from cpu dai %d format in cvt 0x%x\n",
dd->stream_tag, dd->format);
- ret = hdac_hdmi_setup_audio_infoframe(hdac, dai_map->cvt.nid,
- dai_map->pin.nid);
+ ret = hdac_hdmi_setup_audio_infoframe(hdac, dai_map->cvt->nid,
+ dai_map->pin->nid);
if (ret < 0)
return ret;
- return hdac_hdmi_setup_stream(hdac, dai_map->cvt.nid, dai_map->pin.nid,
- dd->stream_tag, dd->format);
+ return hdac_hdmi_setup_stream(hdac, dai_map->cvt->nid,
+ dai_map->pin->nid, dd->stream_tag, dd->format);
}
static int hdac_hdmi_set_hw_params(struct snd_pcm_substream *substream,
@@ -221,9 +227,9 @@ static int hdac_hdmi_playback_cleanup(struct snd_pcm_substream *substream,
dai_map = &hdmi->dai_map[dai->id];
- snd_hdac_codec_write(&edev->hdac, dai_map->cvt.nid, 0,
+ snd_hdac_codec_write(&edev->hdac, dai_map->cvt->nid, 0,
AC_VERB_SET_CHANNEL_STREAMID, 0);
- snd_hdac_codec_write(&edev->hdac, dai_map->cvt.nid, 0,
+ snd_hdac_codec_write(&edev->hdac, dai_map->cvt->nid, 0,
AC_VERB_SET_STREAM_FORMAT, 0);
dd = (struct hdac_ext_dma_params *)snd_soc_dai_get_dma_data(dai, substream);
@@ -249,7 +255,7 @@ static int hdac_hdmi_pcm_open(struct snd_pcm_substream *substream,
dai_map = &hdmi->dai_map[dai->id];
- val = snd_hdac_codec_read(&hdac->hdac, dai_map->pin.nid, 0,
+ val = snd_hdac_codec_read(&hdac->hdac, dai_map->pin->nid, 0,
AC_VERB_GET_PIN_SENSE, 0);
dev_info(&hdac->hdac.dev, "Val for AC_VERB_GET_PIN_SENSE: %x\n", val);
@@ -260,7 +266,7 @@ static int hdac_hdmi_pcm_open(struct snd_pcm_substream *substream,
hdac_hdmi_set_power_state(hdac, dai_map, AC_PWRST_D0);
- snd_hdac_codec_write(&hdac->hdac, dai_map->pin.nid, 0,
+ snd_hdac_codec_write(&hdac->hdac, dai_map->pin->nid, 0,
AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
snd_pcm_hw_constraint_step(substream->runtime, 0,
@@ -280,7 +286,7 @@ static void hdac_hdmi_pcm_close(struct snd_pcm_substream *substream,
hdac_hdmi_set_power_state(hdac, dai_map, AC_PWRST_D3);
- snd_hdac_codec_write(&hdac->hdac, dai_map->pin.nid, 0,
+ snd_hdac_codec_write(&hdac->hdac, dai_map->pin->nid, 0,
AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
}
@@ -304,26 +310,6 @@ hdac_hdmi_query_cvt_params(struct hdac_device *hdac, struct hdac_hdmi_cvt *cvt)
return err;
}
-static int hdac_hdmi_query_pin_connlist(struct hdac_ext_device *hdac,
- struct hdac_hdmi_pin *pin)
-{
- if (!(get_wcaps(&hdac->hdac, pin->nid) & AC_WCAP_CONN_LIST)) {
- dev_warn(&hdac->hdac.dev,
- "HDMI: pin %d wcaps %#x does not support connection list\n",
- pin->nid, get_wcaps(&hdac->hdac, pin->nid));
- return -EINVAL;
- }
-
- pin->num_mux_nids = snd_hdac_get_connections(&hdac->hdac, pin->nid,
- pin->mux_nids, HDA_MAX_CONNECTIONS);
- if (pin->num_mux_nids == 0) {
- dev_err(&hdac->hdac.dev, "No connections found\n");
- return -ENODEV;
- }
-
- return pin->num_mux_nids;
-}
-
static void hdac_hdmi_fill_widget_info(struct snd_soc_dapm_widget *w,
enum snd_soc_dapm_type id,
const char *wname, const char *stream)
@@ -366,40 +352,79 @@ static void create_fill_widget_route_map(struct snd_soc_dapm_context *dapm,
snd_soc_dapm_add_routes(dapm, route, ARRAY_SIZE(route));
}
-static int hdac_hdmi_init_dai_map(struct hdac_ext_device *edev,
- struct hdac_hdmi_dai_pin_map *dai_map,
- hda_nid_t pin_nid, hda_nid_t cvt_nid, int dai_id)
+static int hdac_hdmi_init_dai_map(struct hdac_ext_device *edev)
{
- int ret;
+ struct hdac_hdmi_priv *hdmi = edev->private_data;
+ struct hdac_hdmi_dai_pin_map *dai_map = &hdmi->dai_map[0];
+ struct hdac_hdmi_cvt *cvt;
+ struct hdac_hdmi_pin *pin;
- dai_map->dai_id = dai_id;
- dai_map->pin.nid = pin_nid;
+ if (list_empty(&hdmi->cvt_list) || list_empty(&hdmi->pin_list))
+ return -EINVAL;
- ret = hdac_hdmi_query_pin_connlist(edev, &dai_map->pin);
- if (ret < 0) {
- dev_err(&edev->hdac.dev,
- "Error querying connection list: %d\n", ret);
- return ret;
- }
+ /*
+ * Currently on board only 1 pin and 1 converter is enabled for
+ * simplification, more will be added eventually
+ * So using fixed map for dai_id:pin:cvt
+ */
+ cvt = list_first_entry(&hdmi->cvt_list, struct hdac_hdmi_cvt, head);
+ pin = list_first_entry(&hdmi->pin_list, struct hdac_hdmi_pin, head);
+
+ dai_map->dai_id = 0;
+ dai_map->pin = pin;
- dai_map->cvt.nid = cvt_nid;
+ dai_map->cvt = cvt;
/* Enable out path for this pin widget */
- snd_hdac_codec_write(&edev->hdac, pin_nid, 0,
+ snd_hdac_codec_write(&edev->hdac, pin->nid, 0,
AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
/* Enable transmission */
- snd_hdac_codec_write(&edev->hdac, cvt_nid, 0,
+ snd_hdac_codec_write(&edev->hdac, cvt->nid, 0,
AC_VERB_SET_DIGI_CONVERT_1, 1);
/* Category Code (CC) to zero */
- snd_hdac_codec_write(&edev->hdac, cvt_nid, 0,
+ snd_hdac_codec_write(&edev->hdac, cvt->nid, 0,
AC_VERB_SET_DIGI_CONVERT_2, 0);
- snd_hdac_codec_write(&edev->hdac, pin_nid, 0,
+ snd_hdac_codec_write(&edev->hdac, pin->nid, 0,
AC_VERB_SET_CONNECT_SEL, 0);
- return hdac_hdmi_query_cvt_params(&edev->hdac, &dai_map->cvt);
+ return 0;
+}
+
+static int hdac_hdmi_add_cvt(struct hdac_ext_device *edev, hda_nid_t nid)
+{
+ struct hdac_hdmi_priv *hdmi = edev->private_data;
+ struct hdac_hdmi_cvt *cvt;
+
+ cvt = kzalloc(sizeof(*cvt), GFP_KERNEL);
+ if (!cvt)
+ return -ENOMEM;
+
+ cvt->nid = nid;
+
+ list_add_tail(&cvt->head, &hdmi->cvt_list);
+ hdmi->num_cvt++;
+
+ return hdac_hdmi_query_cvt_params(&edev->hdac, cvt);
+}
+
+static int hdac_hdmi_add_pin(struct hdac_ext_device *edev, hda_nid_t nid)
+{
+ struct hdac_hdmi_priv *hdmi = edev->private_data;
+ struct hdac_hdmi_pin *pin;
+
+ pin = kzalloc(sizeof(*pin), GFP_KERNEL);
+ if (!pin)
+ return -ENOMEM;
+
+ pin->nid = nid;
+
+ list_add_tail(&pin->head, &hdmi->pin_list);
+ hdmi->num_pin++;
+
+ return 0;
}
/*
@@ -412,10 +437,10 @@ static int hdac_hdmi_parse_and_map_nid(struct hdac_ext_device *edev)
int i, num_nodes;
struct hdac_device *hdac = &edev->hdac;
struct hdac_hdmi_priv *hdmi = edev->private_data;
- int cvt_nid = 0, pin_nid = 0;
+ int ret;
num_nodes = snd_hdac_get_sub_nodes(hdac, hdac->afg, &nid);
- if (!nid || num_nodes < 0) {
+ if (!nid || num_nodes <= 0) {
dev_warn(&hdac->dev, "HDMI: failed to get afg sub nodes\n");
return -EINVAL;
}
@@ -436,29 +461,25 @@ static int hdac_hdmi_parse_and_map_nid(struct hdac_ext_device *edev)
switch (type) {
case AC_WID_AUD_OUT:
- hdmi->cvt_nid[cvt_nid] = nid;
- cvt_nid++;
+ ret = hdac_hdmi_add_cvt(edev, nid);
+ if (ret < 0)
+ return ret;
break;
case AC_WID_PIN:
- hdmi->pin_nid[pin_nid] = nid;
- pin_nid++;
+ ret = hdac_hdmi_add_pin(edev, nid);
+ if (ret < 0)
+ return ret;
break;
}
}
hdac->end_nid = nid;
- if (!pin_nid || !cvt_nid)
+ if (!hdmi->num_pin || !hdmi->num_cvt)
return -EIO;
- /*
- * Currently on board only 1 pin and 1 converter is enabled for
- * simplification, more will be added eventually
- * So using fixed map for dai_id:pin:cvt
- */
- return hdac_hdmi_init_dai_map(edev, &hdmi->dai_map[0], hdmi->pin_nid[0],
- hdmi->cvt_nid[0], 0);
+ return hdac_hdmi_init_dai_map(edev);
}
static int hdmi_codec_probe(struct snd_soc_codec *codec)
@@ -542,6 +563,9 @@ static int hdac_hdmi_dev_probe(struct hdac_ext_device *edev)
dev_set_drvdata(&codec->dev, edev);
+ INIT_LIST_HEAD(&hdmi_priv->pin_list);
+ INIT_LIST_HEAD(&hdmi_priv->cvt_list);
+
ret = hdac_hdmi_parse_and_map_nid(edev);
if (ret < 0)
return ret;
@@ -553,8 +577,22 @@ static int hdac_hdmi_dev_probe(struct hdac_ext_device *edev)
static int hdac_hdmi_dev_remove(struct hdac_ext_device *edev)
{
+ struct hdac_hdmi_priv *hdmi = edev->private_data;
+ struct hdac_hdmi_pin *pin, *pin_next;
+ struct hdac_hdmi_cvt *cvt, *cvt_next;
+
snd_soc_unregister_codec(&edev->hdac.dev);
+ list_for_each_entry_safe(cvt, cvt_next, &hdmi->cvt_list, head) {
+ list_del(&cvt->head);
+ kfree(cvt);
+ }
+
+ list_for_each_entry_safe(pin, pin_next, &hdmi->pin_list, head) {
+ list_del(&pin->head);
+ kfree(pin);
+ }
+
return 0;
}
diff --git a/sound/soc/codecs/pcm1792a.c b/sound/soc/codecs/pcm1792a.c
deleted file mode 100644
index 08bb4863e96f..000000000000
--- a/sound/soc/codecs/pcm1792a.c
+++ /dev/null
@@ -1,271 +0,0 @@
-/*
- * PCM1792A ASoC codec driver
- *
- * Copyright (c) Amarula Solutions B.V. 2013
- *
- * Michael Trimarchi <michael@amarulasolutions.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/kernel.h>
-#include <linux/device.h>
-#include <linux/spi/spi.h>
-
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/initval.h>
-#include <sound/soc.h>
-#include <sound/tlv.h>
-#include <linux/of.h>
-#include <linux/of_device.h>
-
-#include "pcm1792a.h"
-
-#define PCM1792A_DAC_VOL_LEFT 0x10
-#define PCM1792A_DAC_VOL_RIGHT 0x11
-#define PCM1792A_FMT_CONTROL 0x12
-#define PCM1792A_MODE_CONTROL 0x13
-#define PCM1792A_SOFT_MUTE PCM1792A_FMT_CONTROL
-
-#define PCM1792A_FMT_MASK 0x70
-#define PCM1792A_FMT_SHIFT 4
-#define PCM1792A_MUTE_MASK 0x01
-#define PCM1792A_MUTE_SHIFT 0
-#define PCM1792A_ATLD_ENABLE (1 << 7)
-
-static const struct reg_default pcm1792a_reg_defaults[] = {
- { 0x10, 0xff },
- { 0x11, 0xff },
- { 0x12, 0x50 },
- { 0x13, 0x00 },
- { 0x14, 0x00 },
- { 0x15, 0x01 },
- { 0x16, 0x00 },
- { 0x17, 0x00 },
-};
-
-static bool pcm1792a_accessible_reg(struct device *dev, unsigned int reg)
-{
- return reg >= 0x10 && reg <= 0x17;
-}
-
-static bool pcm1792a_writeable_reg(struct device *dev, unsigned register reg)
-{
- bool accessible;
-
- accessible = pcm1792a_accessible_reg(dev, reg);
-
- return accessible && reg != 0x16 && reg != 0x17;
-}
-
-struct pcm1792a_private {
- struct regmap *regmap;
- unsigned int format;
- unsigned int rate;
-};
-
-static int pcm1792a_set_dai_fmt(struct snd_soc_dai *codec_dai,
- unsigned int format)
-{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct pcm1792a_private *priv = snd_soc_codec_get_drvdata(codec);
-
- priv->format = format;
-
- return 0;
-}
-
-static int pcm1792a_digital_mute(struct snd_soc_dai *dai, int mute)
-{
- struct snd_soc_codec *codec = dai->codec;
- struct pcm1792a_private *priv = snd_soc_codec_get_drvdata(codec);
- int ret;
-
- ret = regmap_update_bits(priv->regmap, PCM1792A_SOFT_MUTE,
- PCM1792A_MUTE_MASK, !!mute);
- if (ret < 0)
- return ret;
-
- return 0;
-}
-
-static int pcm1792a_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 pcm1792a_private *priv = snd_soc_codec_get_drvdata(codec);
- int val = 0, ret;
-
- priv->rate = params_rate(params);
-
- switch (priv->format & SND_SOC_DAIFMT_FORMAT_MASK) {
- case SND_SOC_DAIFMT_RIGHT_J:
- switch (params_width(params)) {
- case 24:
- case 32:
- val = 2;
- break;
- case 16:
- val = 0;
- break;
- default:
- return -EINVAL;
- }
- break;
- case SND_SOC_DAIFMT_I2S:
- switch (params_width(params)) {
- case 24:
- case 32:
- val = 5;
- break;
- case 16:
- val = 4;
- break;
- default:
- return -EINVAL;
- }
- break;
- default:
- dev_err(codec->dev, "Invalid DAI format\n");
- return -EINVAL;
- }
-
- val = val << PCM1792A_FMT_SHIFT | PCM1792A_ATLD_ENABLE;
-
- ret = regmap_update_bits(priv->regmap, PCM1792A_FMT_CONTROL,
- PCM1792A_FMT_MASK | PCM1792A_ATLD_ENABLE, val);
- if (ret < 0)
- return ret;
-
- return 0;
-}
-
-static const struct snd_soc_dai_ops pcm1792a_dai_ops = {
- .set_fmt = pcm1792a_set_dai_fmt,
- .hw_params = pcm1792a_hw_params,
- .digital_mute = pcm1792a_digital_mute,
-};
-
-static const DECLARE_TLV_DB_SCALE(pcm1792a_dac_tlv, -12000, 50, 1);
-
-static const struct snd_kcontrol_new pcm1792a_controls[] = {
- SOC_DOUBLE_R_RANGE_TLV("DAC Playback Volume", PCM1792A_DAC_VOL_LEFT,
- PCM1792A_DAC_VOL_RIGHT, 0, 0xf, 0xff, 0,
- pcm1792a_dac_tlv),
- SOC_SINGLE("DAC Invert Output Switch", PCM1792A_MODE_CONTROL, 7, 1, 0),
- SOC_SINGLE("DAC Rolloff Filter Switch", PCM1792A_MODE_CONTROL, 1, 1, 0),
-};
-
-static const struct snd_soc_dapm_widget pcm1792a_dapm_widgets[] = {
-SND_SOC_DAPM_OUTPUT("IOUTL+"),
-SND_SOC_DAPM_OUTPUT("IOUTL-"),
-SND_SOC_DAPM_OUTPUT("IOUTR+"),
-SND_SOC_DAPM_OUTPUT("IOUTR-"),
-};
-
-static const struct snd_soc_dapm_route pcm1792a_dapm_routes[] = {
- { "IOUTL+", NULL, "Playback" },
- { "IOUTL-", NULL, "Playback" },
- { "IOUTR+", NULL, "Playback" },
- { "IOUTR-", NULL, "Playback" },
-};
-
-static struct snd_soc_dai_driver pcm1792a_dai = {
- .name = "pcm1792a-hifi",
- .playback = {
- .stream_name = "Playback",
- .channels_min = 2,
- .channels_max = 2,
- .rates = PCM1792A_RATES,
- .formats = PCM1792A_FORMATS, },
- .ops = &pcm1792a_dai_ops,
-};
-
-static const struct of_device_id pcm1792a_of_match[] = {
- { .compatible = "ti,pcm1792a", },
- { }
-};
-MODULE_DEVICE_TABLE(of, pcm1792a_of_match);
-
-static const struct regmap_config pcm1792a_regmap = {
- .reg_bits = 8,
- .val_bits = 8,
- .max_register = 23,
- .reg_defaults = pcm1792a_reg_defaults,
- .num_reg_defaults = ARRAY_SIZE(pcm1792a_reg_defaults),
- .writeable_reg = pcm1792a_writeable_reg,
- .readable_reg = pcm1792a_accessible_reg,
-};
-
-static struct snd_soc_codec_driver soc_codec_dev_pcm1792a = {
- .controls = pcm1792a_controls,
- .num_controls = ARRAY_SIZE(pcm1792a_controls),
- .dapm_widgets = pcm1792a_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(pcm1792a_dapm_widgets),
- .dapm_routes = pcm1792a_dapm_routes,
- .num_dapm_routes = ARRAY_SIZE(pcm1792a_dapm_routes),
-};
-
-static int pcm1792a_spi_probe(struct spi_device *spi)
-{
- struct pcm1792a_private *pcm1792a;
- int ret;
-
- pcm1792a = devm_kzalloc(&spi->dev, sizeof(struct pcm1792a_private),
- GFP_KERNEL);
- if (!pcm1792a)
- return -ENOMEM;
-
- spi_set_drvdata(spi, pcm1792a);
-
- pcm1792a->regmap = devm_regmap_init_spi(spi, &pcm1792a_regmap);
- if (IS_ERR(pcm1792a->regmap)) {
- ret = PTR_ERR(pcm1792a->regmap);
- dev_err(&spi->dev, "Failed to register regmap: %d\n", ret);
- return ret;
- }
-
- return snd_soc_register_codec(&spi->dev,
- &soc_codec_dev_pcm1792a, &pcm1792a_dai, 1);
-}
-
-static int pcm1792a_spi_remove(struct spi_device *spi)
-{
- snd_soc_unregister_codec(&spi->dev);
- return 0;
-}
-
-static const struct spi_device_id pcm1792a_spi_ids[] = {
- { "pcm1792a", 0 },
- { },
-};
-MODULE_DEVICE_TABLE(spi, pcm1792a_spi_ids);
-
-static struct spi_driver pcm1792a_codec_driver = {
- .driver = {
- .name = "pcm1792a",
- .of_match_table = of_match_ptr(pcm1792a_of_match),
- },
- .id_table = pcm1792a_spi_ids,
- .probe = pcm1792a_spi_probe,
- .remove = pcm1792a_spi_remove,
-};
-
-module_spi_driver(pcm1792a_codec_driver);
-
-MODULE_DESCRIPTION("ASoC PCM1792A driver");
-MODULE_AUTHOR("Michael Trimarchi <michael@amarulasolutions.com>");
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/pcm179x.c b/sound/soc/codecs/pcm179x.c
new file mode 100644
index 000000000000..a56c7b767d90
--- /dev/null
+++ b/sound/soc/codecs/pcm179x.c
@@ -0,0 +1,271 @@
+/*
+ * PCM179X ASoC codec driver
+ *
+ * Copyright (c) Amarula Solutions B.V. 2013
+ *
+ * Michael Trimarchi <michael@amarulasolutions.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/spi/spi.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+
+#include "pcm179x.h"
+
+#define PCM179X_DAC_VOL_LEFT 0x10
+#define PCM179X_DAC_VOL_RIGHT 0x11
+#define PCM179X_FMT_CONTROL 0x12
+#define PCM179X_MODE_CONTROL 0x13
+#define PCM179X_SOFT_MUTE PCM179X_FMT_CONTROL
+
+#define PCM179X_FMT_MASK 0x70
+#define PCM179X_FMT_SHIFT 4
+#define PCM179X_MUTE_MASK 0x01
+#define PCM179X_MUTE_SHIFT 0
+#define PCM179X_ATLD_ENABLE (1 << 7)
+
+static const struct reg_default pcm179x_reg_defaults[] = {
+ { 0x10, 0xff },
+ { 0x11, 0xff },
+ { 0x12, 0x50 },
+ { 0x13, 0x00 },
+ { 0x14, 0x00 },
+ { 0x15, 0x01 },
+ { 0x16, 0x00 },
+ { 0x17, 0x00 },
+};
+
+static bool pcm179x_accessible_reg(struct device *dev, unsigned int reg)
+{
+ return reg >= 0x10 && reg <= 0x17;
+}
+
+static bool pcm179x_writeable_reg(struct device *dev, unsigned register reg)
+{
+ bool accessible;
+
+ accessible = pcm179x_accessible_reg(dev, reg);
+
+ return accessible && reg != 0x16 && reg != 0x17;
+}
+
+struct pcm179x_private {
+ struct regmap *regmap;
+ unsigned int format;
+ unsigned int rate;
+};
+
+static int pcm179x_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int format)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct pcm179x_private *priv = snd_soc_codec_get_drvdata(codec);
+
+ priv->format = format;
+
+ return 0;
+}
+
+static int pcm179x_digital_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct pcm179x_private *priv = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ ret = regmap_update_bits(priv->regmap, PCM179X_SOFT_MUTE,
+ PCM179X_MUTE_MASK, !!mute);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int pcm179x_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 pcm179x_private *priv = snd_soc_codec_get_drvdata(codec);
+ int val = 0, ret;
+
+ priv->rate = params_rate(params);
+
+ switch (priv->format & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_RIGHT_J:
+ switch (params_width(params)) {
+ case 24:
+ case 32:
+ val = 2;
+ break;
+ case 16:
+ val = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ switch (params_width(params)) {
+ case 24:
+ case 32:
+ val = 5;
+ break;
+ case 16:
+ val = 4;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ default:
+ dev_err(codec->dev, "Invalid DAI format\n");
+ return -EINVAL;
+ }
+
+ val = val << PCM179X_FMT_SHIFT | PCM179X_ATLD_ENABLE;
+
+ ret = regmap_update_bits(priv->regmap, PCM179X_FMT_CONTROL,
+ PCM179X_FMT_MASK | PCM179X_ATLD_ENABLE, val);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops pcm179x_dai_ops = {
+ .set_fmt = pcm179x_set_dai_fmt,
+ .hw_params = pcm179x_hw_params,
+ .digital_mute = pcm179x_digital_mute,
+};
+
+static const DECLARE_TLV_DB_SCALE(pcm179x_dac_tlv, -12000, 50, 1);
+
+static const struct snd_kcontrol_new pcm179x_controls[] = {
+ SOC_DOUBLE_R_RANGE_TLV("DAC Playback Volume", PCM179X_DAC_VOL_LEFT,
+ PCM179X_DAC_VOL_RIGHT, 0, 0xf, 0xff, 0,
+ pcm179x_dac_tlv),
+ SOC_SINGLE("DAC Invert Output Switch", PCM179X_MODE_CONTROL, 7, 1, 0),
+ SOC_SINGLE("DAC Rolloff Filter Switch", PCM179X_MODE_CONTROL, 1, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget pcm179x_dapm_widgets[] = {
+SND_SOC_DAPM_OUTPUT("IOUTL+"),
+SND_SOC_DAPM_OUTPUT("IOUTL-"),
+SND_SOC_DAPM_OUTPUT("IOUTR+"),
+SND_SOC_DAPM_OUTPUT("IOUTR-"),
+};
+
+static const struct snd_soc_dapm_route pcm179x_dapm_routes[] = {
+ { "IOUTL+", NULL, "Playback" },
+ { "IOUTL-", NULL, "Playback" },
+ { "IOUTR+", NULL, "Playback" },
+ { "IOUTR-", NULL, "Playback" },
+};
+
+static struct snd_soc_dai_driver pcm179x_dai = {
+ .name = "pcm179x-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = PCM1792A_RATES,
+ .formats = PCM1792A_FORMATS, },
+ .ops = &pcm179x_dai_ops,
+};
+
+static const struct of_device_id pcm179x_of_match[] = {
+ { .compatible = "ti,pcm1792a", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, pcm179x_of_match);
+
+static const struct regmap_config pcm179x_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = 23,
+ .reg_defaults = pcm179x_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(pcm179x_reg_defaults),
+ .writeable_reg = pcm179x_writeable_reg,
+ .readable_reg = pcm179x_accessible_reg,
+};
+
+static struct snd_soc_codec_driver soc_codec_dev_pcm179x = {
+ .controls = pcm179x_controls,
+ .num_controls = ARRAY_SIZE(pcm179x_controls),
+ .dapm_widgets = pcm179x_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(pcm179x_dapm_widgets),
+ .dapm_routes = pcm179x_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(pcm179x_dapm_routes),
+};
+
+static int pcm179x_spi_probe(struct spi_device *spi)
+{
+ struct pcm179x_private *pcm179x;
+ int ret;
+
+ pcm179x = devm_kzalloc(&spi->dev, sizeof(struct pcm179x_private),
+ GFP_KERNEL);
+ if (!pcm179x)
+ return -ENOMEM;
+
+ spi_set_drvdata(spi, pcm179x);
+
+ pcm179x->regmap = devm_regmap_init_spi(spi, &pcm179x_regmap);
+ if (IS_ERR(pcm179x->regmap)) {
+ ret = PTR_ERR(pcm179x->regmap);
+ dev_err(&spi->dev, "Failed to register regmap: %d\n", ret);
+ return ret;
+ }
+
+ return snd_soc_register_codec(&spi->dev,
+ &soc_codec_dev_pcm179x, &pcm179x_dai, 1);
+}
+
+static int pcm179x_spi_remove(struct spi_device *spi)
+{
+ snd_soc_unregister_codec(&spi->dev);
+ return 0;
+}
+
+static const struct spi_device_id pcm179x_spi_ids[] = {
+ { "pcm179x", 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(spi, pcm179x_spi_ids);
+
+static struct spi_driver pcm179x_codec_driver = {
+ .driver = {
+ .name = "pcm179x",
+ .of_match_table = of_match_ptr(pcm179x_of_match),
+ },
+ .id_table = pcm179x_spi_ids,
+ .probe = pcm179x_spi_probe,
+ .remove = pcm179x_spi_remove,
+};
+
+module_spi_driver(pcm179x_codec_driver);
+
+MODULE_DESCRIPTION("ASoC PCM179X driver");
+MODULE_AUTHOR("Michael Trimarchi <michael@amarulasolutions.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/pcm1792a.h b/sound/soc/codecs/pcm179x.h
index 51d5470fee16..c6fdc062a497 100644
--- a/sound/soc/codecs/pcm1792a.h
+++ b/sound/soc/codecs/pcm179x.h
@@ -1,5 +1,5 @@
/*
- * definitions for PCM1792A
+ * definitions for PCM179X
*
* Copyright 2013 Amarula Solutions
*
@@ -14,8 +14,8 @@
* GNU General Public License for more details.
*/
-#ifndef __PCM1792A_H__
-#define __PCM1792A_H__
+#ifndef __PCM179X_H__
+#define __PCM179X_H__
#define PCM1792A_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_8000_48000 | \
SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | \
diff --git a/sound/soc/codecs/rt286.c b/sound/soc/codecs/rt286.c
index bc08f0c5a5f6..1bd31644a782 100644
--- a/sound/soc/codecs/rt286.c
+++ b/sound/soc/codecs/rt286.c
@@ -266,6 +266,8 @@ static int rt286_jack_detect(struct rt286_priv *rt286, bool *hp, bool *mic)
} else {
*mic = false;
regmap_write(rt286->regmap, RT286_SET_MIC1, 0x20);
+ regmap_update_bits(rt286->regmap,
+ RT286_CBJ_CTRL1, 0x0400, 0x0000);
}
} else {
regmap_read(rt286->regmap, RT286_GET_HP_SENSE, &buf);
@@ -470,24 +472,6 @@ static int rt286_set_dmic1_event(struct snd_soc_dapm_widget *w,
return 0;
}
-static int rt286_vref_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event)
-{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
-
- switch (event) {
- case SND_SOC_DAPM_PRE_PMU:
- snd_soc_update_bits(codec,
- RT286_CBJ_CTRL1, 0x0400, 0x0000);
- mdelay(50);
- break;
- default:
- return 0;
- }
-
- return 0;
-}
-
static int rt286_ldo2_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
@@ -536,7 +520,7 @@ static const struct snd_soc_dapm_widget rt286_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY_S("HV", 1, RT286_POWER_CTRL1,
12, 1, NULL, 0),
SND_SOC_DAPM_SUPPLY("VREF", RT286_POWER_CTRL1,
- 0, 1, rt286_vref_event, SND_SOC_DAPM_PRE_PMU),
+ 0, 1, NULL, 0),
SND_SOC_DAPM_SUPPLY_S("LDO1", 1, RT286_POWER_CTRL2,
2, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY_S("LDO2", 2, RT286_POWER_CTRL1,
@@ -911,8 +895,6 @@ static int rt286_set_bias_level(struct snd_soc_codec *codec,
case SND_SOC_BIAS_ON:
mdelay(10);
snd_soc_update_bits(codec,
- RT286_CBJ_CTRL1, 0x0400, 0x0400);
- snd_soc_update_bits(codec,
RT286_DC_GAIN, 0x200, 0x0);
break;
@@ -920,8 +902,6 @@ static int rt286_set_bias_level(struct snd_soc_codec *codec,
case SND_SOC_BIAS_STANDBY:
snd_soc_write(codec,
RT286_SET_AUDIO_POWER, AC_PWRST_D3);
- snd_soc_update_bits(codec,
- RT286_CBJ_CTRL1, 0x0400, 0x0000);
break;
default:
diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c
index f2beb1aa5763..11d032cdc658 100644
--- a/sound/soc/codecs/rt5640.c
+++ b/sound/soc/codecs/rt5640.c
@@ -488,6 +488,18 @@ static int is_sys_clk_from_pll(struct snd_soc_dapm_widget *source,
return 0;
}
+static int is_using_asrc(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
+ struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+
+ if (!rt5640->asrc_en)
+ return 0;
+
+ return 1;
+}
+
/* Digital Mixer */
static const struct snd_kcontrol_new rt5640_sto_adc_l_mix[] = {
SOC_DAPM_SINGLE("ADC1 Switch", RT5640_STO_ADC_MIXER,
@@ -1059,6 +1071,20 @@ static int rt5640_hp_post_event(struct snd_soc_dapm_widget *w,
static const struct snd_soc_dapm_widget rt5640_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY("PLL1", RT5640_PWR_ANLG2,
RT5640_PWR_PLL_BIT, 0, NULL, 0),
+
+ /* ASRC */
+ SND_SOC_DAPM_SUPPLY_S("Stereo Filter ASRC", 1, RT5640_ASRC_1,
+ 15, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("I2S2 Filter ASRC", 1, RT5640_ASRC_1,
+ 12, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("I2S2 ASRC", 1, RT5640_ASRC_1,
+ 11, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("DMIC1 ASRC", 1, RT5640_ASRC_1,
+ 9, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("DMIC2 ASRC", 1, RT5640_ASRC_1,
+ 8, 0, NULL, 0),
+
+
/* Input Side */
/* micbias */
SND_SOC_DAPM_SUPPLY("LDO2", RT5640_PWR_ANLG1,
@@ -1319,6 +1345,12 @@ static const struct snd_soc_dapm_widget rt5639_specific_dapm_widgets[] = {
};
static const struct snd_soc_dapm_route rt5640_dapm_routes[] = {
+ { "I2S1", NULL, "Stereo Filter ASRC", is_using_asrc },
+ { "I2S2", NULL, "I2S2 ASRC", is_using_asrc },
+ { "I2S2", NULL, "I2S2 Filter ASRC", is_using_asrc },
+ { "DMIC1", NULL, "DMIC1 ASRC", is_using_asrc },
+ { "DMIC2", NULL, "DMIC2 ASRC", is_using_asrc },
+
{"IN1P", NULL, "LDO2"},
{"IN2P", NULL, "LDO2"},
{"IN3P", NULL, "LDO2"},
@@ -1981,6 +2013,76 @@ int rt5640_dmic_enable(struct snd_soc_codec *codec,
}
EXPORT_SYMBOL_GPL(rt5640_dmic_enable);
+int rt5640_sel_asrc_clk_src(struct snd_soc_codec *codec,
+ unsigned int filter_mask, unsigned int clk_src)
+{
+ struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+ unsigned int asrc2_mask = 0;
+ unsigned int asrc2_value = 0;
+
+ switch (clk_src) {
+ case RT5640_CLK_SEL_SYS:
+ case RT5640_CLK_SEL_ASRC:
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ if (!filter_mask)
+ return -EINVAL;
+
+ if (filter_mask & RT5640_DA_STEREO_FILTER) {
+ asrc2_mask |= RT5640_STO_DAC_M_MASK;
+ asrc2_value = (asrc2_value & ~RT5640_STO_DAC_M_MASK)
+ | (clk_src << RT5640_STO_DAC_M_SFT);
+ }
+
+ if (filter_mask & RT5640_DA_MONO_L_FILTER) {
+ asrc2_mask |= RT5640_MDA_L_M_MASK;
+ asrc2_value = (asrc2_value & ~RT5640_MDA_L_M_MASK)
+ | (clk_src << RT5640_MDA_L_M_SFT);
+ }
+
+ if (filter_mask & RT5640_DA_MONO_R_FILTER) {
+ asrc2_mask |= RT5640_MDA_R_M_MASK;
+ asrc2_value = (asrc2_value & ~RT5640_MDA_R_M_MASK)
+ | (clk_src << RT5640_MDA_R_M_SFT);
+ }
+
+ if (filter_mask & RT5640_AD_STEREO_FILTER) {
+ asrc2_mask |= RT5640_ADC_M_MASK;
+ asrc2_value = (asrc2_value & ~RT5640_ADC_M_MASK)
+ | (clk_src << RT5640_ADC_M_SFT);
+ }
+
+ if (filter_mask & RT5640_AD_MONO_L_FILTER) {
+ asrc2_mask |= RT5640_MAD_L_M_MASK;
+ asrc2_value = (asrc2_value & ~RT5640_MAD_L_M_MASK)
+ | (clk_src << RT5640_MAD_L_M_SFT);
+ }
+
+ if (filter_mask & RT5640_AD_MONO_R_FILTER) {
+ asrc2_mask |= RT5640_MAD_R_M_MASK;
+ asrc2_value = (asrc2_value & ~RT5640_MAD_R_M_MASK)
+ | (clk_src << RT5640_MAD_R_M_SFT);
+ }
+
+ snd_soc_update_bits(codec, RT5640_ASRC_2,
+ asrc2_mask, asrc2_value);
+
+ if (snd_soc_read(codec, RT5640_ASRC_2)) {
+ rt5640->asrc_en = true;
+ snd_soc_update_bits(codec, RT5640_JD_CTRL, 0x3, 0x3);
+ } else {
+ rt5640->asrc_en = false;
+ snd_soc_update_bits(codec, RT5640_JD_CTRL, 0x3, 0x0);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rt5640_sel_asrc_clk_src);
+
static int rt5640_probe(struct snd_soc_codec *codec)
{
struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
@@ -2175,6 +2277,7 @@ static const struct acpi_device_id rt5640_acpi_match[] = {
{ "INT33CA", 0 },
{ "10EC5640", 0 },
{ "10EC5642", 0 },
+ { "INTCCFFD", 0 },
{ },
};
MODULE_DEVICE_TABLE(acpi, rt5640_acpi_match);
diff --git a/sound/soc/codecs/rt5640.h b/sound/soc/codecs/rt5640.h
index 3deb8babeabb..83a7150ddc24 100644
--- a/sound/soc/codecs/rt5640.h
+++ b/sound/soc/codecs/rt5640.h
@@ -1033,6 +1033,10 @@
#define RT5640_DMIC_2_M_NOR (0x0 << 8)
#define RT5640_DMIC_2_M_ASYN (0x1 << 8)
+/* ASRC clock source selection (0x84) */
+#define RT5640_CLK_SEL_SYS (0x0)
+#define RT5640_CLK_SEL_ASRC (0x1)
+
/* ASRC Control 2 (0x84) */
#define RT5640_MDA_L_M_MASK (0x1 << 15)
#define RT5640_MDA_L_M_SFT 15
@@ -2079,6 +2083,16 @@ enum {
RT5640_DMIC2,
};
+/* filter mask */
+enum {
+ RT5640_DA_STEREO_FILTER = 0x1,
+ RT5640_DA_MONO_L_FILTER = (0x1 << 1),
+ RT5640_DA_MONO_R_FILTER = (0x1 << 2),
+ RT5640_AD_STEREO_FILTER = (0x1 << 3),
+ RT5640_AD_MONO_L_FILTER = (0x1 << 4),
+ RT5640_AD_MONO_R_FILTER = (0x1 << 5),
+};
+
struct rt5640_priv {
struct snd_soc_codec *codec;
struct rt5640_platform_data pdata;
@@ -2095,9 +2109,12 @@ struct rt5640_priv {
int pll_out;
bool hp_mute;
+ bool asrc_en;
};
int rt5640_dmic_enable(struct snd_soc_codec *codec,
bool dmic1_data_pin, bool dmic2_data_pin);
+int rt5640_sel_asrc_clk_src(struct snd_soc_codec *codec,
+ unsigned int filter_mask, unsigned int clk_src);
#endif
diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c
index 3e8d66661b7e..93e8c9017633 100644
--- a/sound/soc/codecs/rt5645.c
+++ b/sound/soc/codecs/rt5645.c
@@ -64,7 +64,6 @@ static const struct reg_sequence init_list[] = {
{RT5645_PR_BASE + 0x21, 0x4040},
{RT5645_PR_BASE + 0x23, 0x0004},
};
-#define RT5645_INIT_REG_LEN ARRAY_SIZE(init_list)
static const struct reg_sequence rt5650_init_list[] = {
{0xf6, 0x0100},
@@ -405,6 +404,7 @@ struct rt5645_priv {
struct delayed_work jack_detect_work, rcclock_work;
struct regulator_bulk_data supplies[ARRAY_SIZE(rt5645_supply_names)];
struct rt5645_eq_param_s *eq_param;
+ struct timer_list btn_check_timer;
int codec_type;
int sysclk;
@@ -776,7 +776,7 @@ static const struct snd_kcontrol_new rt5645_snd_controls[] = {
/* IN1/IN2 Control */
SOC_SINGLE_TLV("IN1 Boost", RT5645_IN1_CTRL1,
- RT5645_BST_SFT1, 8, 0, bst_tlv),
+ RT5645_BST_SFT1, 12, 0, bst_tlv),
SOC_SINGLE_TLV("IN2 Boost", RT5645_IN2_CTRL,
RT5645_BST_SFT2, 8, 0, bst_tlv),
@@ -1822,9 +1822,13 @@ static int rt5645_spk_event(struct snd_soc_dapm_widget *w,
RT5645_PWR_CLS_D_L,
RT5645_PWR_CLS_D | RT5645_PWR_CLS_D_R |
RT5645_PWR_CLS_D_L);
+ snd_soc_update_bits(codec, RT5645_GEN_CTRL3,
+ RT5645_DET_CLK_MASK, RT5645_DET_CLK_MODE1);
break;
case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_update_bits(codec, RT5645_GEN_CTRL3,
+ RT5645_DET_CLK_MASK, RT5645_DET_CLK_DIS);
snd_soc_write(codec, RT5645_EQ_CTRL2, 0);
snd_soc_update_bits(codec, RT5645_PWR_DIG1,
RT5645_PWR_CLS_D | RT5645_PWR_CLS_D_R |
@@ -3062,6 +3066,7 @@ static void rt5645_enable_push_button_irq(struct snd_soc_codec *codec,
snd_soc_dapm_force_enable_pin(dapm, "ADC R power");
snd_soc_dapm_sync(dapm);
+ snd_soc_update_bits(codec, RT5650_4BTN_IL_CMD1, 0x3, 0x3);
snd_soc_update_bits(codec,
RT5645_INT_IRQ_ST, 0x8, 0x8);
snd_soc_update_bits(codec,
@@ -3130,7 +3135,7 @@ static int rt5645_jack_detect(struct snd_soc_codec *codec, int jack_insert)
}
if (rt5645->pdata.jd_invert)
regmap_update_bits(rt5645->regmap, RT5645_IRQ_CTRL2,
- RT5645_JD_1_1_MASK, RT5645_JD_1_1_INV);
+ RT5645_JD_1_1_MASK, RT5645_JD_1_1_NOR);
} else { /* jack out */
rt5645->jack_type = 0;
@@ -3151,7 +3156,7 @@ static int rt5645_jack_detect(struct snd_soc_codec *codec, int jack_insert)
snd_soc_dapm_sync(dapm);
if (rt5645->pdata.jd_invert)
regmap_update_bits(rt5645->regmap, RT5645_IRQ_CTRL2,
- RT5645_JD_1_1_MASK, RT5645_JD_1_1_NOR);
+ RT5645_JD_1_1_MASK, RT5645_JD_1_1_INV);
}
return rt5645->jack_type;
@@ -3275,6 +3280,12 @@ static void rt5645_jack_detect_work(struct work_struct *work)
}
if (btn_type == 0)/* button release */
report = rt5645->jack_type;
+ else {
+ if (rt5645->pdata.jd_invert) {
+ mod_timer(&rt5645->btn_check_timer,
+ msecs_to_jiffies(100));
+ }
+ }
break;
/* jack out */
@@ -3317,6 +3328,14 @@ static irqreturn_t rt5645_irq(int irq, void *data)
return IRQ_HANDLED;
}
+static void rt5645_btn_check_callback(unsigned long data)
+{
+ struct rt5645_priv *rt5645 = (struct rt5645_priv *)data;
+
+ queue_delayed_work(system_power_efficient_wq,
+ &rt5645->jack_detect_work, msecs_to_jiffies(5));
+}
+
static int rt5645_probe(struct snd_soc_codec *codec)
{
struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
@@ -3506,7 +3525,7 @@ static const struct i2c_device_id rt5645_i2c_id[] = {
MODULE_DEVICE_TABLE(i2c, rt5645_i2c_id);
#ifdef CONFIG_ACPI
-static struct acpi_device_id rt5645_acpi_match[] = {
+static const struct acpi_device_id rt5645_acpi_match[] = {
{ "10EC5645", 0 },
{ "10EC5650", 0 },
{},
@@ -3783,6 +3802,13 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
}
}
+ if (rt5645->pdata.jd_invert) {
+ regmap_update_bits(rt5645->regmap, RT5645_IRQ_CTRL2,
+ RT5645_JD_1_1_MASK, RT5645_JD_1_1_INV);
+ setup_timer(&rt5645->btn_check_timer,
+ rt5645_btn_check_callback, (unsigned long)rt5645);
+ }
+
INIT_DELAYED_WORK(&rt5645->jack_detect_work, rt5645_jack_detect_work);
INIT_DELAYED_WORK(&rt5645->rcclock_work, rt5645_rcclock_work);
diff --git a/sound/soc/codecs/rt5645.h b/sound/soc/codecs/rt5645.h
index 093e46d559fb..205e0715c99a 100644
--- a/sound/soc/codecs/rt5645.h
+++ b/sound/soc/codecs/rt5645.h
@@ -2122,6 +2122,10 @@ enum {
/* General Control3 (0xfc) */
#define RT5645_JD_PSV_MODE (0x1 << 12)
#define RT5645_IRQ_CLK_GATE_CTRL (0x1 << 11)
+#define RT5645_DET_CLK_MASK (0x3 << 9)
+#define RT5645_DET_CLK_DIS (0x0 << 9)
+#define RT5645_DET_CLK_MODE1 (0x1 << 9)
+#define RT5645_DET_CLK_MODE2 (0x2 << 9)
#define RT5645_MICINDET_MANU (0x1 << 7)
#define RT5645_RING2_SLEEVE_GND (0x1 << 5)
diff --git a/sound/soc/codecs/rt5651.c b/sound/soc/codecs/rt5651.c
index 1d4031818966..7a6197042423 100644
--- a/sound/soc/codecs/rt5651.c
+++ b/sound/soc/codecs/rt5651.c
@@ -18,6 +18,7 @@
#include <linux/regmap.h>
#include <linux/platform_device.h>
#include <linux/spi/spi.h>
+#include <linux/acpi.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -1735,12 +1736,38 @@ static const struct regmap_config rt5651_regmap = {
.num_ranges = ARRAY_SIZE(rt5651_ranges),
};
+#if defined(CONFIG_OF)
+static const struct of_device_id rt5651_of_match[] = {
+ { .compatible = "realtek,rt5651", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, rt5651_of_match);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id rt5651_acpi_match[] = {
+ { "10EC5651", 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(acpi, rt5651_acpi_match);
+#endif
+
static const struct i2c_device_id rt5651_i2c_id[] = {
{ "rt5651", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, rt5651_i2c_id);
+static int rt5651_parse_dt(struct rt5651_priv *rt5651, struct device_node *np)
+{
+ rt5651->pdata.in2_diff = of_property_read_bool(np,
+ "realtek,in2-differential");
+ rt5651->pdata.dmic_en = of_property_read_bool(np,
+ "realtek,dmic-en");
+
+ return 0;
+}
+
static int rt5651_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
@@ -1757,6 +1784,8 @@ static int rt5651_i2c_probe(struct i2c_client *i2c,
if (pdata)
rt5651->pdata = *pdata;
+ else if (i2c->dev.of_node)
+ rt5651_parse_dt(rt5651, i2c->dev.of_node);
rt5651->regmap = devm_regmap_init_i2c(i2c, &rt5651_regmap);
if (IS_ERR(rt5651->regmap)) {
@@ -1806,6 +1835,8 @@ static int rt5651_i2c_remove(struct i2c_client *i2c)
static struct i2c_driver rt5651_i2c_driver = {
.driver = {
.name = "rt5651",
+ .acpi_match_table = ACPI_PTR(rt5651_acpi_match),
+ .of_match_table = of_match_ptr(rt5651_of_match),
},
.probe = rt5651_i2c_probe,
.remove = rt5651_i2c_remove,
diff --git a/sound/soc/codecs/rt5659.c b/sound/soc/codecs/rt5659.c
index 820d8fa62b5e..fb8ea05c0de1 100644
--- a/sound/soc/codecs/rt5659.c
+++ b/sound/soc/codecs/rt5659.c
@@ -3985,7 +3985,6 @@ static int rt5659_i2c_probe(struct i2c_client *i2c,
if (rt5659 == NULL)
return -ENOMEM;
- rt5659->i2c = i2c;
i2c_set_clientdata(i2c, rt5659);
if (pdata)
@@ -4157,24 +4156,17 @@ static int rt5659_i2c_probe(struct i2c_client *i2c,
INIT_DELAYED_WORK(&rt5659->jack_detect_work, rt5659_jack_detect_work);
- if (rt5659->i2c->irq) {
- ret = request_threaded_irq(rt5659->i2c->irq, NULL, rt5659_irq,
- IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING
+ if (i2c->irq) {
+ ret = devm_request_threaded_irq(&i2c->dev, i2c->irq, NULL,
+ rt5659_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING
| IRQF_ONESHOT, "rt5659", rt5659);
if (ret)
dev_err(&i2c->dev, "Failed to reguest IRQ: %d\n", ret);
}
- ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5659,
+ return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5659,
rt5659_dai, ARRAY_SIZE(rt5659_dai));
-
- if (ret) {
- if (rt5659->i2c->irq)
- free_irq(rt5659->i2c->irq, rt5659);
- }
-
- return 0;
}
static int rt5659_i2c_remove(struct i2c_client *i2c)
@@ -4191,24 +4183,29 @@ void rt5659_i2c_shutdown(struct i2c_client *client)
regmap_write(rt5659->regmap, RT5659_RESET, 0);
}
+#ifdef CONFIG_OF
static const struct of_device_id rt5659_of_match[] = {
{ .compatible = "realtek,rt5658", },
{ .compatible = "realtek,rt5659", },
- {},
+ { },
};
+MODULE_DEVICE_TABLE(of, rt5659_of_match);
+#endif
+#ifdef CONFIG_ACPI
static struct acpi_device_id rt5659_acpi_match[] = {
- { "10EC5658", 0},
- { "10EC5659", 0},
- { },
+ { "10EC5658", 0, },
+ { "10EC5659", 0, },
+ { },
};
MODULE_DEVICE_TABLE(acpi, rt5659_acpi_match);
+#endif
struct i2c_driver rt5659_i2c_driver = {
.driver = {
.name = "rt5659",
.owner = THIS_MODULE,
- .of_match_table = rt5659_of_match,
+ .of_match_table = of_match_ptr(rt5659_of_match),
.acpi_match_table = ACPI_PTR(rt5659_acpi_match),
},
.probe = rt5659_i2c_probe,
diff --git a/sound/soc/codecs/rt5659.h b/sound/soc/codecs/rt5659.h
index 8f07ee903eaa..d31c9e5bcec8 100644
--- a/sound/soc/codecs/rt5659.h
+++ b/sound/soc/codecs/rt5659.h
@@ -1792,7 +1792,6 @@ struct rt5659_priv {
struct snd_soc_codec *codec;
struct rt5659_platform_data pdata;
struct regmap *regmap;
- struct i2c_client *i2c;
struct gpio_desc *gpiod_ldo1_en;
struct gpio_desc *gpiod_reset;
struct snd_soc_jack *hs_jack;
diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c
index 967678e7f48e..33e290b703df 100644
--- a/sound/soc/codecs/rt5677.c
+++ b/sound/soc/codecs/rt5677.c
@@ -4694,7 +4694,7 @@ static void rt5677_init_gpio(struct i2c_client *i2c)
rt5677->gpio_chip = rt5677_template_chip;
rt5677->gpio_chip.ngpio = RT5677_GPIO_NUM;
- rt5677->gpio_chip.dev = &i2c->dev;
+ rt5677->gpio_chip.parent = &i2c->dev;
rt5677->gpio_chip.base = -1;
ret = gpiochip_add(&rt5677->gpio_chip);
diff --git a/sound/soc/codecs/sigmadsp-i2c.c b/sound/soc/codecs/sigmadsp-i2c.c
index 21ca3a5e9f66..d374c18d4db7 100644
--- a/sound/soc/codecs/sigmadsp-i2c.c
+++ b/sound/soc/codecs/sigmadsp-i2c.c
@@ -31,7 +31,10 @@ static int sigmadsp_write_i2c(void *control_data,
kfree(buf);
- return ret;
+ if (ret < 0)
+ return ret;
+
+ return 0;
}
static int sigmadsp_read_i2c(void *control_data,
diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c
index 4cad8929d262..bc3de2e844e6 100644
--- a/sound/soc/codecs/twl6040.c
+++ b/sound/soc/codecs/twl6040.c
@@ -1097,8 +1097,7 @@ static int twl6040_probe(struct snd_soc_codec *codec)
{
struct twl6040_data *priv;
struct twl6040 *twl6040 = dev_get_drvdata(codec->dev->parent);
- struct platform_device *pdev = container_of(codec->dev,
- struct platform_device, dev);
+ struct platform_device *pdev = to_platform_device(codec->dev);
int ret = 0;
priv = devm_kzalloc(codec->dev, sizeof(*priv), GFP_KERNEL);
diff --git a/sound/soc/codecs/wm5100.c b/sound/soc/codecs/wm5100.c
index c2cdcae18ff6..171a23ddd15d 100644
--- a/sound/soc/codecs/wm5100.c
+++ b/sound/soc/codecs/wm5100.c
@@ -2306,7 +2306,7 @@ static void wm5100_init_gpio(struct i2c_client *i2c)
wm5100->gpio_chip = wm5100_template_chip;
wm5100->gpio_chip.ngpio = 6;
- wm5100->gpio_chip.dev = &i2c->dev;
+ wm5100->gpio_chip.parent = &i2c->dev;
if (wm5100->pdata.gpio_base)
wm5100->gpio_chip.base = wm5100->pdata.gpio_base;
diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c
index cd1b3080a497..97c0f1e23886 100644
--- a/sound/soc/codecs/wm5110.c
+++ b/sound/soc/codecs/wm5110.c
@@ -360,15 +360,13 @@ static int wm5110_hp_ev(struct snd_soc_dapm_widget *w,
static int wm5110_clear_pga_volume(struct arizona *arizona, int output)
{
- struct reg_sequence clear_pga = {
- ARIZONA_OUTPUT_PATH_CONFIG_1L + output * 4, 0x80
- };
+ unsigned int reg = ARIZONA_OUTPUT_PATH_CONFIG_1L + output * 4;
int ret;
- ret = regmap_multi_reg_write_bypassed(arizona->regmap, &clear_pga, 1);
+ ret = regmap_write(arizona->regmap, reg, 0x80);
if (ret)
dev_err(arizona->dev, "Failed to clear PGA (0x%x): %d\n",
- clear_pga.reg, ret);
+ reg, ret);
return ret;
}
@@ -439,18 +437,17 @@ static int wm5110_in_pga_get(struct snd_kcontrol *kcontrol,
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
- struct snd_soc_card *card = dapm->card;
int ret;
/*
* PGA Volume is also used as part of the enable sequence, so
* usage of it should be avoided whilst that is running.
*/
- mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
+ snd_soc_dapm_mutex_lock(dapm);
ret = snd_soc_get_volsw_range(kcontrol, ucontrol);
- mutex_unlock(&card->dapm_mutex);
+ snd_soc_dapm_mutex_unlock(dapm);
return ret;
}
@@ -460,18 +457,17 @@ static int wm5110_in_pga_put(struct snd_kcontrol *kcontrol,
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
- struct snd_soc_card *card = dapm->card;
int ret;
/*
* PGA Volume is also used as part of the enable sequence, so
* usage of it should be avoided whilst that is running.
*/
- mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
+ snd_soc_dapm_mutex_lock(dapm);
ret = snd_soc_put_volsw_range(kcontrol, ucontrol);
- mutex_unlock(&card->dapm_mutex);
+ snd_soc_dapm_mutex_unlock(dapm);
return ret;
}
@@ -2177,10 +2173,23 @@ static int wm5110_open(struct snd_compr_stream *stream)
return wm_adsp_compr_open(&priv->core.adsp[n_adsp], stream);
}
+static irqreturn_t wm5110_adsp2_irq(int irq, void *data)
+{
+ struct wm5110_priv *florida = data;
+ int ret;
+
+ ret = wm_adsp_compr_handle_irq(&florida->core.adsp[2]);
+ if (ret == -ENODEV)
+ return IRQ_NONE;
+
+ return IRQ_HANDLED;
+}
+
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);
+ struct arizona *arizona = priv->core.arizona;
int i, ret;
priv->core.arizona->dapm = dapm;
@@ -2189,6 +2198,14 @@ static int wm5110_codec_probe(struct snd_soc_codec *codec)
arizona_init_gpio(codec);
arizona_init_mono(codec);
+ ret = arizona_request_irq(arizona, ARIZONA_IRQ_DSP_IRQ1,
+ "ADSP2 Compressed IRQ", wm5110_adsp2_irq,
+ priv);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to request DSP IRQ: %d\n", ret);
+ return ret;
+ }
+
for (i = 0; i < WM5110_NUM_ADSP; ++i) {
ret = wm_adsp2_codec_probe(&priv->core.adsp[i], codec);
if (ret)
@@ -2209,12 +2226,15 @@ err_adsp2_codec_probe:
for (--i; i >= 0; --i)
wm_adsp2_codec_remove(&priv->core.adsp[i], codec);
+ arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, priv);
+
return ret;
}
static int wm5110_codec_remove(struct snd_soc_codec *codec)
{
struct wm5110_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct arizona *arizona = priv->core.arizona;
int i;
for (i = 0; i < WM5110_NUM_ADSP; ++i)
@@ -2222,6 +2242,8 @@ static int wm5110_codec_remove(struct snd_soc_codec *codec)
priv->core.arizona->dapm = NULL;
+ arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, priv);
+
return 0;
}
@@ -2273,6 +2295,8 @@ static struct snd_compr_ops wm5110_compr_ops = {
.set_params = wm_adsp_compr_set_params,
.get_caps = wm_adsp_compr_get_caps,
.trigger = wm_adsp_compr_trigger,
+ .pointer = wm_adsp_compr_pointer,
+ .copy = wm_adsp_compr_copy,
};
static struct snd_soc_platform_driver wm5110_compr_platform = {
diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c
index 2ed6419c181e..a82b8bc2cfc0 100644
--- a/sound/soc/codecs/wm8903.c
+++ b/sound/soc/codecs/wm8903.c
@@ -1853,7 +1853,7 @@ static void wm8903_init_gpio(struct wm8903_priv *wm8903)
wm8903->gpio_chip = wm8903_template_chip;
wm8903->gpio_chip.ngpio = WM8903_NUM_GPIO;
- wm8903->gpio_chip.dev = wm8903->dev;
+ wm8903->gpio_chip.parent = wm8903->dev;
if (pdata->gpio_base)
wm8903->gpio_chip.base = pdata->gpio_base;
diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c
index 5380798883b5..d7f444f87460 100644
--- a/sound/soc/codecs/wm8960.c
+++ b/sound/soc/codecs/wm8960.c
@@ -147,6 +147,13 @@ static const char *wm8960_3d_upper_cutoff[] = {"High", "Low"};
static const char *wm8960_3d_lower_cutoff[] = {"Low", "High"};
static const char *wm8960_alcfunc[] = {"Off", "Right", "Left", "Stereo"};
static const char *wm8960_alcmode[] = {"ALC", "Limiter"};
+static const char *wm8960_adc_data_output_sel[] = {
+ "Left Data = Left ADC; Right Data = Right ADC",
+ "Left Data = Left ADC; Right Data = Left ADC",
+ "Left Data = Right ADC; Right Data = Right ADC",
+ "Left Data = Right ADC; Right Data = Left ADC",
+};
+static const char *wm8960_dmonomix[] = {"Stereo", "Mono"};
static const struct soc_enum wm8960_enum[] = {
SOC_ENUM_SINGLE(WM8960_DACCTL1, 5, 4, wm8960_polarity),
@@ -155,6 +162,8 @@ static const struct soc_enum wm8960_enum[] = {
SOC_ENUM_SINGLE(WM8960_3D, 5, 2, wm8960_3d_lower_cutoff),
SOC_ENUM_SINGLE(WM8960_ALC1, 7, 4, wm8960_alcfunc),
SOC_ENUM_SINGLE(WM8960_ALC3, 8, 2, wm8960_alcmode),
+ SOC_ENUM_SINGLE(WM8960_ADDCTL1, 2, 4, wm8960_adc_data_output_sel),
+ SOC_ENUM_SINGLE(WM8960_ADDCTL1, 4, 2, wm8960_dmonomix),
};
static const int deemph_settings[] = { 0, 32000, 44100, 48000 };
@@ -231,13 +240,13 @@ SOC_DOUBLE_R("Capture Volume ZC Switch", WM8960_LINVOL, WM8960_RINVOL,
SOC_DOUBLE_R("Capture Switch", WM8960_LINVOL, WM8960_RINVOL,
7, 1, 1),
-SOC_SINGLE_TLV("Right Input Boost Mixer RINPUT3 Volume",
+SOC_SINGLE_TLV("Left Input Boost Mixer LINPUT3 Volume",
WM8960_INBMIX1, 4, 7, 0, lineinboost_tlv),
-SOC_SINGLE_TLV("Right Input Boost Mixer RINPUT2 Volume",
+SOC_SINGLE_TLV("Left Input Boost Mixer LINPUT2 Volume",
WM8960_INBMIX1, 1, 7, 0, lineinboost_tlv),
-SOC_SINGLE_TLV("Left Input Boost Mixer LINPUT3 Volume",
+SOC_SINGLE_TLV("Right Input Boost Mixer RINPUT3 Volume",
WM8960_INBMIX2, 4, 7, 0, lineinboost_tlv),
-SOC_SINGLE_TLV("Left Input Boost Mixer LINPUT2 Volume",
+SOC_SINGLE_TLV("Right Input Boost Mixer RINPUT2 Volume",
WM8960_INBMIX2, 1, 7, 0, lineinboost_tlv),
SOC_SINGLE_TLV("Right Input Boost Mixer RINPUT1 Volume",
WM8960_RINPATH, 4, 3, 0, micboost_tlv),
@@ -295,6 +304,9 @@ SOC_SINGLE_TLV("Right Output Mixer Boost Bypass Volume",
WM8960_BYPASS2, 4, 7, 1, bypass_tlv),
SOC_SINGLE_TLV("Right Output Mixer RINPUT3 Volume",
WM8960_ROUTMIX, 4, 7, 1, bypass_tlv),
+
+SOC_ENUM("ADC Data Output Select", wm8960_enum[6]),
+SOC_ENUM("DAC Mono Mix", wm8960_enum[7]),
};
static const struct snd_kcontrol_new wm8960_lin_boost[] = {
@@ -401,8 +413,8 @@ static const struct snd_soc_dapm_route audio_paths[] = {
{ "Left Boost Mixer", "LINPUT2 Switch", "LINPUT2" },
{ "Left Boost Mixer", "LINPUT3 Switch", "LINPUT3" },
- { "Left Input Mixer", "Boost Switch", "Left Boost Mixer", },
- { "Left Input Mixer", NULL, "LINPUT1", }, /* Really Boost Switch */
+ { "Left Input Mixer", "Boost Switch", "Left Boost Mixer" },
+ { "Left Input Mixer", "Boost Switch", "LINPUT1" }, /* Really Boost Switch */
{ "Left Input Mixer", NULL, "LINPUT2" },
{ "Left Input Mixer", NULL, "LINPUT3" },
@@ -410,8 +422,8 @@ static const struct snd_soc_dapm_route audio_paths[] = {
{ "Right Boost Mixer", "RINPUT2 Switch", "RINPUT2" },
{ "Right Boost Mixer", "RINPUT3 Switch", "RINPUT3" },
- { "Right Input Mixer", "Boost Switch", "Right Boost Mixer", },
- { "Right Input Mixer", NULL, "RINPUT1", }, /* Really Boost Switch */
+ { "Right Input Mixer", "Boost Switch", "Right Boost Mixer" },
+ { "Right Input Mixer", "Boost Switch", "RINPUT1" }, /* Really Boost Switch */
{ "Right Input Mixer", NULL, "RINPUT2" },
{ "Right Input Mixer", NULL, "RINPUT3" },
@@ -419,11 +431,11 @@ static const struct snd_soc_dapm_route audio_paths[] = {
{ "Right ADC", NULL, "Right Input Mixer" },
{ "Left Output Mixer", "LINPUT3 Switch", "LINPUT3" },
- { "Left Output Mixer", "Boost Bypass Switch", "Left Boost Mixer"} ,
+ { "Left Output Mixer", "Boost Bypass Switch", "Left Boost Mixer" },
{ "Left Output Mixer", "PCM Playback Switch", "Left DAC" },
{ "Right Output Mixer", "RINPUT3 Switch", "RINPUT3" },
- { "Right Output Mixer", "Boost Bypass Switch", "Right Boost Mixer" } ,
+ { "Right Output Mixer", "Boost Bypass Switch", "Right Boost Mixer" },
{ "Right Output Mixer", "PCM Playback Switch", "Right DAC" },
{ "LOUT1 PGA", NULL, "Left Output Mixer" },
@@ -631,29 +643,31 @@ static int wm8960_configure_clocking(struct snd_soc_codec *codec)
return -EINVAL;
}
- /* check if the sysclk frequency is available. */
- for (i = 0; i < ARRAY_SIZE(sysclk_divs); ++i) {
- if (sysclk_divs[i] == -1)
- continue;
- sysclk = freq_out / sysclk_divs[i];
- for (j = 0; j < ARRAY_SIZE(dac_divs); ++j) {
- if (sysclk == dac_divs[j] * lrclk) {
+ if (wm8960->clk_id != WM8960_SYSCLK_PLL) {
+ /* check if the sysclk frequency is available. */
+ for (i = 0; i < ARRAY_SIZE(sysclk_divs); ++i) {
+ if (sysclk_divs[i] == -1)
+ continue;
+ sysclk = freq_out / sysclk_divs[i];
+ for (j = 0; j < ARRAY_SIZE(dac_divs); ++j) {
+ if (sysclk != dac_divs[j] * lrclk)
+ continue;
for (k = 0; k < ARRAY_SIZE(bclk_divs); ++k)
if (sysclk == bclk * bclk_divs[k] / 10)
break;
if (k != ARRAY_SIZE(bclk_divs))
break;
}
+ if (j != ARRAY_SIZE(dac_divs))
+ break;
}
- if (j != ARRAY_SIZE(dac_divs))
- break;
- }
- if (i != ARRAY_SIZE(sysclk_divs)) {
- goto configure_clock;
- } else if (wm8960->clk_id != WM8960_SYSCLK_AUTO) {
- dev_err(codec->dev, "failed to configure clock\n");
- return -EINVAL;
+ if (i != ARRAY_SIZE(sysclk_divs)) {
+ goto configure_clock;
+ } else if (wm8960->clk_id != WM8960_SYSCLK_AUTO) {
+ dev_err(codec->dev, "failed to configure clock\n");
+ return -EINVAL;
+ }
}
/* get a available pll out frequency and set pll */
for (i = 0; i < ARRAY_SIZE(sysclk_divs); ++i) {
diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c
index 949f632fc3f8..88223608a33f 100644
--- a/sound/soc/codecs/wm8962.c
+++ b/sound/soc/codecs/wm8962.c
@@ -3379,7 +3379,7 @@ static void wm8962_init_gpio(struct snd_soc_codec *codec)
wm8962->gpio_chip = wm8962_template_chip;
wm8962->gpio_chip.ngpio = WM8962_MAX_GPIO;
- wm8962->gpio_chip.dev = codec->dev;
+ wm8962->gpio_chip.parent = codec->dev;
if (pdata->gpio_base)
wm8962->gpio_chip.base = pdata->gpio_base;
diff --git a/sound/soc/codecs/wm8996.c b/sound/soc/codecs/wm8996.c
index f7ccd9fc5808..8d7d6c01a2f7 100644
--- a/sound/soc/codecs/wm8996.c
+++ b/sound/soc/codecs/wm8996.c
@@ -2204,7 +2204,7 @@ static void wm8996_init_gpio(struct wm8996_priv *wm8996)
wm8996->gpio_chip = wm8996_template_chip;
wm8996->gpio_chip.ngpio = 5;
- wm8996->gpio_chip.dev = wm8996->dev;
+ wm8996->gpio_chip.parent = wm8996->dev;
if (wm8996->pdata.gpio_base)
wm8996->gpio_chip.base = wm8996->pdata.gpio_base;
diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c
index ac879d16c6a6..33806d487b8a 100644
--- a/sound/soc/codecs/wm_adsp.c
+++ b/sound/soc/codecs/wm_adsp.c
@@ -279,6 +279,11 @@ struct wm_adsp_compr_buf {
struct wm_adsp_buffer_region *regions;
u32 host_buf_ptr;
+
+ u32 error;
+ u32 irq_count;
+ int read_index;
+ int avail;
};
struct wm_adsp_compr {
@@ -287,6 +292,9 @@ struct wm_adsp_compr {
struct snd_compr_stream *stream;
struct snd_compressed_buffer size;
+
+ u32 *raw_buf;
+ unsigned int copied_total;
};
#define WM_ADSP_DATA_WORD_SIZE 3
@@ -2378,6 +2386,7 @@ int wm_adsp_compr_free(struct snd_compr_stream *stream)
dsp->compr = NULL;
+ kfree(compr->raw_buf);
kfree(compr);
mutex_unlock(&dsp->pwr_lock);
@@ -2436,10 +2445,16 @@ static int wm_adsp_compr_check_params(struct snd_compr_stream *stream,
return -EINVAL;
}
+static inline unsigned int wm_adsp_compr_frag_words(struct wm_adsp_compr *compr)
+{
+ return compr->size.fragment_size / WM_ADSP_DATA_WORD_SIZE;
+}
+
int wm_adsp_compr_set_params(struct snd_compr_stream *stream,
struct snd_compr_params *params)
{
struct wm_adsp_compr *compr = stream->runtime->private_data;
+ unsigned int size;
int ret;
ret = wm_adsp_compr_check_params(stream, params);
@@ -2451,6 +2466,11 @@ int wm_adsp_compr_set_params(struct snd_compr_stream *stream,
adsp_dbg(compr->dsp, "fragment_size=%d fragments=%d\n",
compr->size.fragment_size, compr->size.fragments);
+ size = wm_adsp_compr_frag_words(compr) * sizeof(*compr->raw_buf);
+ compr->raw_buf = kmalloc(size, GFP_DMA | GFP_KERNEL);
+ if (!compr->raw_buf)
+ return -ENOMEM;
+
return 0;
}
EXPORT_SYMBOL_GPL(wm_adsp_compr_set_params);
@@ -2622,6 +2642,8 @@ static int wm_adsp_buffer_init(struct wm_adsp *dsp)
return -ENOMEM;
buf->dsp = dsp;
+ buf->read_index = -1;
+ buf->irq_count = 0xFFFFFFFF;
ret = wm_adsp_buffer_locate(buf);
if (ret < 0) {
@@ -2705,6 +2727,16 @@ int wm_adsp_compr_trigger(struct snd_compr_stream *stream, int cmd)
ret);
break;
}
+
+ /* Trigger the IRQ at one fragment of data */
+ ret = wm_adsp_buffer_write(compr->buf,
+ HOST_BUFFER_FIELD(high_water_mark),
+ wm_adsp_compr_frag_words(compr));
+ if (ret < 0) {
+ adsp_err(dsp, "Failed to set high water mark: %d\n",
+ ret);
+ break;
+ }
break;
case SNDRV_PCM_TRIGGER_STOP:
break;
@@ -2719,4 +2751,298 @@ int wm_adsp_compr_trigger(struct snd_compr_stream *stream, int cmd)
}
EXPORT_SYMBOL_GPL(wm_adsp_compr_trigger);
+static inline int wm_adsp_buffer_size(struct wm_adsp_compr_buf *buf)
+{
+ int last_region = wm_adsp_fw[buf->dsp->fw].caps->num_regions - 1;
+
+ return buf->regions[last_region].cumulative_size;
+}
+
+static int wm_adsp_buffer_update_avail(struct wm_adsp_compr_buf *buf)
+{
+ u32 next_read_index, next_write_index;
+ int write_index, read_index, avail;
+ int ret;
+
+ /* Only sync read index if we haven't already read a valid index */
+ if (buf->read_index < 0) {
+ ret = wm_adsp_buffer_read(buf,
+ HOST_BUFFER_FIELD(next_read_index),
+ &next_read_index);
+ if (ret < 0)
+ return ret;
+
+ read_index = sign_extend32(next_read_index, 23);
+
+ if (read_index < 0) {
+ adsp_dbg(buf->dsp, "Avail check on unstarted stream\n");
+ return 0;
+ }
+
+ buf->read_index = read_index;
+ }
+
+ ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(next_write_index),
+ &next_write_index);
+ if (ret < 0)
+ return ret;
+
+ write_index = sign_extend32(next_write_index, 23);
+
+ avail = write_index - buf->read_index;
+ if (avail < 0)
+ avail += wm_adsp_buffer_size(buf);
+
+ adsp_dbg(buf->dsp, "readindex=0x%x, writeindex=0x%x, avail=%d\n",
+ buf->read_index, write_index, avail);
+
+ buf->avail = avail;
+
+ return 0;
+}
+
+int wm_adsp_compr_handle_irq(struct wm_adsp *dsp)
+{
+ struct wm_adsp_compr_buf *buf = dsp->buffer;
+ struct wm_adsp_compr *compr = dsp->compr;
+ int ret = 0;
+
+ mutex_lock(&dsp->pwr_lock);
+
+ if (!buf) {
+ adsp_err(dsp, "Spurious buffer IRQ\n");
+ ret = -ENODEV;
+ goto out;
+ }
+
+ adsp_dbg(dsp, "Handling buffer IRQ\n");
+
+ ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(error), &buf->error);
+ if (ret < 0) {
+ adsp_err(dsp, "Failed to check buffer error: %d\n", ret);
+ goto out;
+ }
+ if (buf->error != 0) {
+ adsp_err(dsp, "Buffer error occurred: %d\n", buf->error);
+ ret = -EIO;
+ goto out;
+ }
+
+ ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(irq_count),
+ &buf->irq_count);
+ if (ret < 0) {
+ adsp_err(dsp, "Failed to get irq_count: %d\n", ret);
+ goto out;
+ }
+
+ ret = wm_adsp_buffer_update_avail(buf);
+ if (ret < 0) {
+ adsp_err(dsp, "Error reading avail: %d\n", ret);
+ goto out;
+ }
+
+ if (compr->stream)
+ snd_compr_fragment_elapsed(compr->stream);
+
+out:
+ mutex_unlock(&dsp->pwr_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(wm_adsp_compr_handle_irq);
+
+static int wm_adsp_buffer_reenable_irq(struct wm_adsp_compr_buf *buf)
+{
+ if (buf->irq_count & 0x01)
+ return 0;
+
+ adsp_dbg(buf->dsp, "Enable IRQ(0x%x) for next fragment\n",
+ buf->irq_count);
+
+ buf->irq_count |= 0x01;
+
+ return wm_adsp_buffer_write(buf, HOST_BUFFER_FIELD(irq_ack),
+ buf->irq_count);
+}
+
+int wm_adsp_compr_pointer(struct snd_compr_stream *stream,
+ struct snd_compr_tstamp *tstamp)
+{
+ struct wm_adsp_compr *compr = stream->runtime->private_data;
+ struct wm_adsp_compr_buf *buf = compr->buf;
+ struct wm_adsp *dsp = compr->dsp;
+ int ret = 0;
+
+ adsp_dbg(dsp, "Pointer request\n");
+
+ mutex_lock(&dsp->pwr_lock);
+
+ if (!compr->buf) {
+ ret = -ENXIO;
+ goto out;
+ }
+
+ if (compr->buf->error) {
+ ret = -EIO;
+ goto out;
+ }
+
+ if (buf->avail < wm_adsp_compr_frag_words(compr)) {
+ ret = wm_adsp_buffer_update_avail(buf);
+ if (ret < 0) {
+ adsp_err(dsp, "Error reading avail: %d\n", ret);
+ goto out;
+ }
+
+ /*
+ * If we really have less than 1 fragment available tell the
+ * DSP to inform us once a whole fragment is available.
+ */
+ if (buf->avail < wm_adsp_compr_frag_words(compr)) {
+ ret = wm_adsp_buffer_reenable_irq(buf);
+ if (ret < 0) {
+ adsp_err(dsp,
+ "Failed to re-enable buffer IRQ: %d\n",
+ ret);
+ goto out;
+ }
+ }
+ }
+
+ tstamp->copied_total = compr->copied_total;
+ tstamp->copied_total += buf->avail * WM_ADSP_DATA_WORD_SIZE;
+
+out:
+ mutex_unlock(&dsp->pwr_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(wm_adsp_compr_pointer);
+
+static int wm_adsp_buffer_capture_block(struct wm_adsp_compr *compr, int target)
+{
+ struct wm_adsp_compr_buf *buf = compr->buf;
+ u8 *pack_in = (u8 *)compr->raw_buf;
+ u8 *pack_out = (u8 *)compr->raw_buf;
+ unsigned int adsp_addr;
+ int mem_type, nwords, max_read;
+ int i, j, ret;
+
+ /* Calculate read parameters */
+ for (i = 0; i < wm_adsp_fw[buf->dsp->fw].caps->num_regions; ++i)
+ if (buf->read_index < buf->regions[i].cumulative_size)
+ break;
+
+ if (i == wm_adsp_fw[buf->dsp->fw].caps->num_regions)
+ return -EINVAL;
+
+ mem_type = buf->regions[i].mem_type;
+ adsp_addr = buf->regions[i].base_addr +
+ (buf->read_index - buf->regions[i].offset);
+
+ max_read = wm_adsp_compr_frag_words(compr);
+ nwords = buf->regions[i].cumulative_size - buf->read_index;
+
+ if (nwords > target)
+ nwords = target;
+ if (nwords > buf->avail)
+ nwords = buf->avail;
+ if (nwords > max_read)
+ nwords = max_read;
+ if (!nwords)
+ return 0;
+
+ /* Read data from DSP */
+ ret = wm_adsp_read_data_block(buf->dsp, mem_type, adsp_addr,
+ nwords, compr->raw_buf);
+ if (ret < 0)
+ return ret;
+
+ /* Remove the padding bytes from the data read from the DSP */
+ for (i = 0; i < nwords; i++) {
+ for (j = 0; j < WM_ADSP_DATA_WORD_SIZE; j++)
+ *pack_out++ = *pack_in++;
+
+ pack_in += sizeof(*(compr->raw_buf)) - WM_ADSP_DATA_WORD_SIZE;
+ }
+
+ /* update read index to account for words read */
+ buf->read_index += nwords;
+ if (buf->read_index == wm_adsp_buffer_size(buf))
+ buf->read_index = 0;
+
+ ret = wm_adsp_buffer_write(buf, HOST_BUFFER_FIELD(next_read_index),
+ buf->read_index);
+ if (ret < 0)
+ return ret;
+
+ /* update avail to account for words read */
+ buf->avail -= nwords;
+
+ return nwords;
+}
+
+static int wm_adsp_compr_read(struct wm_adsp_compr *compr,
+ char __user *buf, size_t count)
+{
+ struct wm_adsp *dsp = compr->dsp;
+ int ntotal = 0;
+ int nwords, nbytes;
+
+ adsp_dbg(dsp, "Requested read of %zu bytes\n", count);
+
+ if (!compr->buf)
+ return -ENXIO;
+
+ if (compr->buf->error)
+ return -EIO;
+
+ count /= WM_ADSP_DATA_WORD_SIZE;
+
+ do {
+ nwords = wm_adsp_buffer_capture_block(compr, count);
+ if (nwords < 0) {
+ adsp_err(dsp, "Failed to capture block: %d\n", nwords);
+ return nwords;
+ }
+
+ nbytes = nwords * WM_ADSP_DATA_WORD_SIZE;
+
+ adsp_dbg(dsp, "Read %d bytes\n", nbytes);
+
+ if (copy_to_user(buf + ntotal, compr->raw_buf, nbytes)) {
+ adsp_err(dsp, "Failed to copy data to user: %d, %d\n",
+ ntotal, nbytes);
+ return -EFAULT;
+ }
+
+ count -= nwords;
+ ntotal += nbytes;
+ } while (nwords > 0 && count > 0);
+
+ compr->copied_total += ntotal;
+
+ return ntotal;
+}
+
+int wm_adsp_compr_copy(struct snd_compr_stream *stream, char __user *buf,
+ size_t count)
+{
+ struct wm_adsp_compr *compr = stream->runtime->private_data;
+ struct wm_adsp *dsp = compr->dsp;
+ int ret;
+
+ mutex_lock(&dsp->pwr_lock);
+
+ if (stream->direction == SND_COMPRESS_CAPTURE)
+ ret = wm_adsp_compr_read(compr, buf, count);
+ else
+ ret = -ENOTSUPP;
+
+ mutex_unlock(&dsp->pwr_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(wm_adsp_compr_copy);
+
MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/wm_adsp.h b/sound/soc/codecs/wm_adsp.h
index 43af093fafcf..1a928ec54741 100644
--- a/sound/soc/codecs/wm_adsp.h
+++ b/sound/soc/codecs/wm_adsp.h
@@ -112,5 +112,10 @@ extern int wm_adsp_compr_set_params(struct snd_compr_stream *stream,
extern int wm_adsp_compr_get_caps(struct snd_compr_stream *stream,
struct snd_compr_caps *caps);
extern int wm_adsp_compr_trigger(struct snd_compr_stream *stream, int cmd);
+extern int wm_adsp_compr_handle_irq(struct wm_adsp *dsp);
+extern int wm_adsp_compr_pointer(struct snd_compr_stream *stream,
+ struct snd_compr_tstamp *tstamp);
+extern int wm_adsp_compr_copy(struct snd_compr_stream *stream,
+ char __user *buf, size_t count);
#endif