diff options
Diffstat (limited to 'sound/soc/codecs')
-rw-r--r-- | sound/soc/codecs/Kconfig | 6 | ||||
-rw-r--r-- | sound/soc/codecs/Makefile | 4 | ||||
-rw-r--r-- | sound/soc/codecs/arizona.c | 3 | ||||
-rw-r--r-- | sound/soc/codecs/da7218.c | 47 | ||||
-rw-r--r-- | sound/soc/codecs/da7219.c | 74 | ||||
-rw-r--r-- | sound/soc/codecs/hdac_hdmi.c | 180 | ||||
-rw-r--r-- | sound/soc/codecs/pcm1792a.c | 271 | ||||
-rw-r--r-- | sound/soc/codecs/pcm179x.c | 271 | ||||
-rw-r--r-- | sound/soc/codecs/pcm179x.h (renamed from sound/soc/codecs/pcm1792a.h) | 6 | ||||
-rw-r--r-- | sound/soc/codecs/rt5640.c | 103 | ||||
-rw-r--r-- | sound/soc/codecs/rt5640.h | 17 | ||||
-rw-r--r-- | sound/soc/codecs/rt5645.c | 30 | ||||
-rw-r--r-- | sound/soc/codecs/rt5651.c | 31 | ||||
-rw-r--r-- | sound/soc/codecs/twl6040.c | 3 | ||||
-rw-r--r-- | sound/soc/codecs/wm5110.c | 46 | ||||
-rw-r--r-- | sound/soc/codecs/wm8960.c | 24 | ||||
-rw-r--r-- | sound/soc/codecs/wm_adsp.c | 326 | ||||
-rw-r--r-- | sound/soc/codecs/wm_adsp.h | 5 |
18 files changed, 1052 insertions, 395 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 fb8f18880524..33143fe1de0b 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; 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/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 6ac9926da330..c61d38b585fb 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; @@ -3066,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, @@ -3134,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; @@ -3155,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; @@ -3279,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 */ @@ -3321,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); @@ -3510,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 }, {}, @@ -3787,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/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/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/wm5110.c b/sound/soc/codecs/wm5110.c index c36409601835..6088d30962a9 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/wm8960.c b/sound/soc/codecs/wm8960.c index 5380798883b5..ff237726775a 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 }; @@ -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" }, 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 |