Merge remote-tracking branches 'asoc/topic/max98095', 'asoc/topic/omap', 'asoc/topic/pxa', 'asoc/topic/qcom' and 'asoc/topic/rcar' into asoc-next

This commit is contained in:
Mark Brown 2015-06-05 18:54:57 +01:00
24 changed files with 846 additions and 367 deletions

View File

@ -4,12 +4,21 @@ This node models the Qualcomm Technologies Low-Power Audio SubSystem (LPASS).
Required properties:
- compatible : "qcom,lpass-cpu"
- compatible : "qcom,lpass-cpu" or "qcom,apq8016-lpass-cpu"
- clocks : Must contain an entry for each entry in clock-names.
- clock-names : A list which must include the following entries:
* "ahbix-clk"
* "mi2s-osr-clk"
* "mi2s-bit-clk"
: required clocks for "qcom,lpass-cpu-apq8016"
* "ahbix-clk"
* "mi2s-bit-clk0"
* "mi2s-bit-clk1"
* "mi2s-bit-clk2"
* "mi2s-bit-clk3"
* "pcnoc-mport-clk"
* "pcnoc-sway-clk"
- interrupts : Must contain an entry for each entry in
interrupt-names.
- interrupt-names : A list which must include the following entries:
@ -22,6 +31,8 @@ Required properties:
- reg-names : A list which must include the following entries:
* "lpass-lpaif"
Optional properties:
- qcom,adsp : Phandle for the audio DSP node

View File

@ -48,7 +48,7 @@ DAI subnode properties:
Example:
rcar_sound: rcar_sound@ec500000 {
rcar_sound: sound@ec500000 {
#sound-dai-cells = <1>;
compatible = "renesas,rcar_sound-r8a7791", "renesas,rcar_sound-gen2";
reg = <0 0xec500000 0 0x1000>, /* SCU */

View File

@ -465,6 +465,7 @@ static dma_cookie_t rcar_dmac_tx_submit(struct dma_async_tx_descriptor *tx)
static int rcar_dmac_desc_alloc(struct rcar_dmac_chan *chan, gfp_t gfp)
{
struct rcar_dmac_desc_page *page;
unsigned long flags;
LIST_HEAD(list);
unsigned int i;
@ -482,10 +483,10 @@ static int rcar_dmac_desc_alloc(struct rcar_dmac_chan *chan, gfp_t gfp)
list_add_tail(&desc->node, &list);
}
spin_lock_irq(&chan->lock);
spin_lock_irqsave(&chan->lock, flags);
list_splice_tail(&list, &chan->desc.free);
list_add_tail(&page->node, &chan->desc.pages);
spin_unlock_irq(&chan->lock);
spin_unlock_irqrestore(&chan->lock, flags);
return 0;
}
@ -516,6 +517,7 @@ static void rcar_dmac_desc_put(struct rcar_dmac_chan *chan,
static void rcar_dmac_desc_recycle_acked(struct rcar_dmac_chan *chan)
{
struct rcar_dmac_desc *desc, *_desc;
unsigned long flags;
LIST_HEAD(list);
/*
@ -524,9 +526,9 @@ static void rcar_dmac_desc_recycle_acked(struct rcar_dmac_chan *chan)
* list_for_each_entry_safe, isn't safe if we release the channel lock
* around the rcar_dmac_desc_put() call.
*/
spin_lock_irq(&chan->lock);
spin_lock_irqsave(&chan->lock, flags);
list_splice_init(&chan->desc.wait, &list);
spin_unlock_irq(&chan->lock);
spin_unlock_irqrestore(&chan->lock, flags);
list_for_each_entry_safe(desc, _desc, &list, node) {
if (async_tx_test_ack(&desc->async_tx)) {
@ -539,9 +541,9 @@ static void rcar_dmac_desc_recycle_acked(struct rcar_dmac_chan *chan)
return;
/* Put the remaining descriptors back in the wait list. */
spin_lock_irq(&chan->lock);
spin_lock_irqsave(&chan->lock, flags);
list_splice(&list, &chan->desc.wait);
spin_unlock_irq(&chan->lock);
spin_unlock_irqrestore(&chan->lock, flags);
}
/*
@ -556,12 +558,13 @@ static void rcar_dmac_desc_recycle_acked(struct rcar_dmac_chan *chan)
static struct rcar_dmac_desc *rcar_dmac_desc_get(struct rcar_dmac_chan *chan)
{
struct rcar_dmac_desc *desc;
unsigned long flags;
int ret;
/* Recycle acked descriptors before attempting allocation. */
rcar_dmac_desc_recycle_acked(chan);
spin_lock_irq(&chan->lock);
spin_lock_irqsave(&chan->lock, flags);
while (list_empty(&chan->desc.free)) {
/*
@ -570,17 +573,17 @@ static struct rcar_dmac_desc *rcar_dmac_desc_get(struct rcar_dmac_chan *chan)
* allocated descriptors. If the allocation fails return an
* error.
*/
spin_unlock_irq(&chan->lock);
spin_unlock_irqrestore(&chan->lock, flags);
ret = rcar_dmac_desc_alloc(chan, GFP_NOWAIT);
if (ret < 0)
return NULL;
spin_lock_irq(&chan->lock);
spin_lock_irqsave(&chan->lock, flags);
}
desc = list_first_entry(&chan->desc.free, struct rcar_dmac_desc, node);
list_del(&desc->node);
spin_unlock_irq(&chan->lock);
spin_unlock_irqrestore(&chan->lock, flags);
return desc;
}
@ -593,6 +596,7 @@ static struct rcar_dmac_desc *rcar_dmac_desc_get(struct rcar_dmac_chan *chan)
static int rcar_dmac_xfer_chunk_alloc(struct rcar_dmac_chan *chan, gfp_t gfp)
{
struct rcar_dmac_desc_page *page;
unsigned long flags;
LIST_HEAD(list);
unsigned int i;
@ -606,10 +610,10 @@ static int rcar_dmac_xfer_chunk_alloc(struct rcar_dmac_chan *chan, gfp_t gfp)
list_add_tail(&chunk->node, &list);
}
spin_lock_irq(&chan->lock);
spin_lock_irqsave(&chan->lock, flags);
list_splice_tail(&list, &chan->desc.chunks_free);
list_add_tail(&page->node, &chan->desc.pages);
spin_unlock_irq(&chan->lock);
spin_unlock_irqrestore(&chan->lock, flags);
return 0;
}
@ -627,9 +631,10 @@ static struct rcar_dmac_xfer_chunk *
rcar_dmac_xfer_chunk_get(struct rcar_dmac_chan *chan)
{
struct rcar_dmac_xfer_chunk *chunk;
unsigned long flags;
int ret;
spin_lock_irq(&chan->lock);
spin_lock_irqsave(&chan->lock, flags);
while (list_empty(&chan->desc.chunks_free)) {
/*
@ -638,18 +643,18 @@ rcar_dmac_xfer_chunk_get(struct rcar_dmac_chan *chan)
* allocated descriptors. If the allocation fails return an
* error.
*/
spin_unlock_irq(&chan->lock);
spin_unlock_irqrestore(&chan->lock, flags);
ret = rcar_dmac_xfer_chunk_alloc(chan, GFP_NOWAIT);
if (ret < 0)
return NULL;
spin_lock_irq(&chan->lock);
spin_lock_irqsave(&chan->lock, flags);
}
chunk = list_first_entry(&chan->desc.chunks_free,
struct rcar_dmac_xfer_chunk, node);
list_del(&chunk->node);
spin_unlock_irq(&chan->lock);
spin_unlock_irqrestore(&chan->lock, flags);
return chunk;
}

View File

@ -0,0 +1,9 @@
#ifndef __DT_APQ8016_LPASS_H
#define __DT_APQ8016_LPASS_H
#define MI2S_PRIMARY 0
#define MI2S_SECONDARY 1
#define MI2S_TERTIARY 2
#define MI2S_QUATERNARY 3
#endif /* __DT_APQ8016_LPASS_H */

View File

@ -2301,8 +2301,8 @@ static int max98095_probe(struct snd_soc_codec *codec)
/* register an audio interrupt */
ret = request_threaded_irq(client->irq, NULL,
max98095_report_jack,
IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
"max98095", codec);
IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING |
IRQF_ONESHOT, "max98095", codec);
if (ret) {
dev_err(codec->dev, "Failed to request IRQ: %d\n", ret);
goto err_access;

View File

@ -100,12 +100,13 @@ config SND_OMAP_SOC_OMAP_TWL4030
config SND_OMAP_SOC_OMAP_ABE_TWL6040
tristate "SoC Audio support for OMAP boards using ABE and twl6040 codec"
depends on TWL6040_CORE && SND_OMAP_SOC && (ARCH_OMAP4 || SOC_OMAP5 || COMPILE_TEST)
depends on TWL6040_CORE && SND_OMAP_SOC
depends on ARCH_OMAP4 || (SOC_OMAP5 && MFD_PALMAS) || COMPILE_TEST
select SND_OMAP_SOC_DMIC
select SND_OMAP_SOC_MCPDM
select SND_SOC_TWL6040
select SND_SOC_DMIC
select COMMON_CLK_PALMAS if MFD_PALMAS
select COMMON_CLK_PALMAS if (SOC_OMAP5 && MFD_PALMAS)
help
Say Y if you want to add support for SoC audio on OMAP boards using
ABE and twl6040 codec. This driver currently supports:

View File

@ -159,9 +159,8 @@ static inline void twl4030_disconnect_pin(struct snd_soc_dapm_context *dapm,
static int omap_twl4030_init(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_codec *codec = rtd->codec;
struct snd_soc_card *card = rtd->card;
struct snd_soc_dapm_context *dapm = &codec->dapm;
struct snd_soc_dapm_context *dapm = &card->dapm;
struct omap_tw4030_pdata *pdata = dev_get_platdata(card->dev);
struct omap_twl4030 *priv = snd_soc_card_get_drvdata(card);
int ret = 0;

View File

@ -45,29 +45,6 @@ static const struct snd_soc_dapm_route brownstone_audio_map[] = {
{"MICBIAS1", NULL, "Main Mic"},
};
static int brownstone_wm8994_init(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_codec *codec = rtd->codec;
struct snd_soc_dapm_context *dapm = &codec->dapm;
/* set endpoints to not connected */
snd_soc_dapm_nc_pin(dapm, "HPOUT2P");
snd_soc_dapm_nc_pin(dapm, "HPOUT2N");
snd_soc_dapm_nc_pin(dapm, "LINEOUT1N");
snd_soc_dapm_nc_pin(dapm, "LINEOUT1P");
snd_soc_dapm_nc_pin(dapm, "LINEOUT2N");
snd_soc_dapm_nc_pin(dapm, "LINEOUT2P");
snd_soc_dapm_nc_pin(dapm, "IN1LN");
snd_soc_dapm_nc_pin(dapm, "IN1LP");
snd_soc_dapm_nc_pin(dapm, "IN1RP");
snd_soc_dapm_nc_pin(dapm, "IN2LP:VXRN");
snd_soc_dapm_nc_pin(dapm, "IN2RN");
snd_soc_dapm_nc_pin(dapm, "IN2RP:VXRP");
snd_soc_dapm_nc_pin(dapm, "IN2LN");
return 0;
}
static int brownstone_wm8994_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
@ -115,7 +92,6 @@ static struct snd_soc_dai_link brownstone_wm8994_dai[] = {
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBS_CFS,
.ops = &brownstone_ops,
.init = brownstone_wm8994_init,
},
};
@ -132,6 +108,7 @@ static struct snd_soc_card brownstone = {
.num_dapm_widgets = ARRAY_SIZE(brownstone_dapm_widgets),
.dapm_routes = brownstone_audio_map,
.num_dapm_routes = ARRAY_SIZE(brownstone_audio_map),
.fully_routed = true,
};
static int brownstone_probe(struct platform_device *pdev)

View File

@ -192,6 +192,7 @@ static int poodle_amp_event(struct snd_soc_dapm_widget *w,
static const struct snd_soc_dapm_widget wm8731_dapm_widgets[] = {
SND_SOC_DAPM_HP("Headphone Jack", NULL),
SND_SOC_DAPM_SPK("Ext Spk", poodle_amp_event),
SND_SOC_DAPM_MIC("Microphone", NULL),
};
/* Corgi machine connections to the codec pins */
@ -204,6 +205,8 @@ static const struct snd_soc_dapm_route poodle_audio_map[] = {
/* speaker connected to LOUT, ROUT */
{"Ext Spk", NULL, "ROUT"},
{"Ext Spk", NULL, "LOUT"},
{"MICIN", NULL, "Microphone"},
};
static const char *jack_function[] = {"Off", "Headphone"};
@ -220,20 +223,6 @@ static const struct snd_kcontrol_new wm8731_poodle_controls[] = {
poodle_set_spk),
};
/*
* Logic for a wm8731 as connected on a Sharp SL-C7x0 Device
*/
static int poodle_wm8731_init(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_codec *codec = rtd->codec;
struct snd_soc_dapm_context *dapm = &codec->dapm;
snd_soc_dapm_nc_pin(dapm, "LLINEIN");
snd_soc_dapm_nc_pin(dapm, "RLINEIN");
return 0;
}
/* poodle digital audio interface glue - connects codec <--> CPU */
static struct snd_soc_dai_link poodle_dai = {
.name = "WM8731",
@ -242,7 +231,6 @@ static struct snd_soc_dai_link poodle_dai = {
.codec_dai_name = "wm8731-hifi",
.platform_name = "pxa-pcm-audio",
.codec_name = "wm8731.0-001b",
.init = poodle_wm8731_init,
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBS_CFS,
.ops = &poodle_ops,
@ -261,6 +249,7 @@ static struct snd_soc_card poodle = {
.num_dapm_widgets = ARRAY_SIZE(wm8731_dapm_widgets),
.dapm_routes = poodle_audio_map,
.num_dapm_routes = ARRAY_SIZE(poodle_audio_map),
.fully_routed = true,
};
static int poodle_probe(struct platform_device *pdev)

View File

@ -185,17 +185,6 @@ static const struct snd_kcontrol_new tosa_controls[] = {
tosa_set_spk),
};
static int tosa_ac97_init(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_codec *codec = rtd->codec;
struct snd_soc_dapm_context *dapm = &codec->dapm;
snd_soc_dapm_nc_pin(dapm, "OUT3");
snd_soc_dapm_nc_pin(dapm, "MONOOUT");
return 0;
}
static struct snd_soc_dai_link tosa_dai[] = {
{
.name = "AC97",
@ -204,7 +193,6 @@ static struct snd_soc_dai_link tosa_dai[] = {
.codec_dai_name = "wm9712-hifi",
.platform_name = "pxa-pcm-audio",
.codec_name = "wm9712-codec",
.init = tosa_ac97_init,
.ops = &tosa_ops,
},
{
@ -230,6 +218,7 @@ static struct snd_soc_card tosa = {
.num_dapm_widgets = ARRAY_SIZE(tosa_dapm_widgets),
.dapm_routes = audio_map,
.num_dapm_routes = ARRAY_SIZE(audio_map),
.fully_routed = true,
};
static int tosa_probe(struct platform_device *pdev)

View File

@ -132,16 +132,8 @@ static const struct snd_soc_dapm_route z2_audio_map[] = {
*/
static int z2_wm8750_init(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_codec *codec = rtd->codec;
struct snd_soc_dapm_context *dapm = &codec->dapm;
int ret;
/* NC codec pins */
snd_soc_dapm_disable_pin(dapm, "LINPUT3");
snd_soc_dapm_disable_pin(dapm, "RINPUT3");
snd_soc_dapm_disable_pin(dapm, "OUT3");
snd_soc_dapm_disable_pin(dapm, "MONO1");
/* Jack detection API stuff */
ret = snd_soc_card_jack_new(rtd->card, "Headset Jack", SND_JACK_HEADSET,
&hs_jack, hs_jack_pins,
@ -189,6 +181,7 @@ static struct snd_soc_card snd_soc_z2 = {
.num_dapm_widgets = ARRAY_SIZE(wm8750_dapm_widgets),
.dapm_routes = z2_audio_map,
.num_dapm_routes = ARRAY_SIZE(z2_audio_map),
.fully_routed = true,
};
static struct platform_device *z2_snd_device;

View File

@ -6,19 +6,28 @@ config SND_SOC_QCOM
config SND_SOC_LPASS_CPU
tristate
depends on SND_SOC_QCOM
select REGMAP_MMIO
config SND_SOC_LPASS_PLATFORM
tristate
depends on SND_SOC_QCOM
select REGMAP_MMIO
config SND_SOC_LPASS_IPQ806X
tristate
depends on SND_SOC_QCOM
select SND_SOC_LPASS_CPU
select SND_SOC_LPASS_PLATFORM
config SND_SOC_LPASS_APQ8016
tristate
depends on SND_SOC_QCOM
select SND_SOC_LPASS_CPU
select SND_SOC_LPASS_PLATFORM
config SND_SOC_STORM
tristate "ASoC I2S support for Storm boards"
depends on (ARCH_QCOM && SND_SOC_QCOM) || COMPILE_TEST
select SND_SOC_LPASS_CPU
select SND_SOC_LPASS_PLATFORM
depends on SND_SOC_QCOM && (ARCH_QCOM || COMPILE_TEST)
select SND_SOC_LPASS_IPQ806X
select SND_SOC_MAX98357A
help
Say Y or M if you want add support for SoC audio on the

View File

@ -1,9 +1,13 @@
# Platform
snd-soc-lpass-cpu-objs := lpass-cpu.o
snd-soc-lpass-platform-objs := lpass-platform.o
snd-soc-lpass-ipq806x-objs := lpass-ipq806x.o
snd-soc-lpass-apq8016-objs := lpass-apq8016.o
obj-$(CONFIG_SND_SOC_LPASS_CPU) += snd-soc-lpass-cpu.o
obj-$(CONFIG_SND_SOC_LPASS_PLATFORM) += snd-soc-lpass-platform.o
obj-$(CONFIG_SND_SOC_LPASS_IPQ806X) += snd-soc-lpass-ipq806x.o
obj-$(CONFIG_SND_SOC_LPASS_APQ8016) += snd-soc-lpass-apq8016.o
# Machine
snd-soc-storm-objs := storm.o

View File

@ -0,0 +1,242 @@
/*
* Copyright (c) 2010-2011,2013-2015 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* lpass-apq8016.c -- ALSA SoC CPU DAI driver for APQ8016 LPASS
*
*/
#include <linux/clk.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/soc-dai.h>
#include <dt-bindings/sound/apq8016-lpass.h>
#include "lpass-lpaif-reg.h"
#include "lpass.h"
static struct snd_soc_dai_driver apq8016_lpass_cpu_dai_driver[] = {
[MI2S_PRIMARY] = {
.id = MI2S_PRIMARY,
.name = "Primary MI2S",
.playback = {
.stream_name = "Primary Playback",
.formats = SNDRV_PCM_FMTBIT_S16 |
SNDRV_PCM_FMTBIT_S24 |
SNDRV_PCM_FMTBIT_S32,
.rates = SNDRV_PCM_RATE_8000 |
SNDRV_PCM_RATE_16000 |
SNDRV_PCM_RATE_32000 |
SNDRV_PCM_RATE_48000 |
SNDRV_PCM_RATE_96000,
.rate_min = 8000,
.rate_max = 96000,
.channels_min = 1,
.channels_max = 8,
},
.probe = &asoc_qcom_lpass_cpu_dai_probe,
.ops = &asoc_qcom_lpass_cpu_dai_ops,
},
[MI2S_SECONDARY] = {
.id = MI2S_SECONDARY,
.name = "Secondary MI2S",
.playback = {
.stream_name = "Secondary Playback",
.formats = SNDRV_PCM_FMTBIT_S16 |
SNDRV_PCM_FMTBIT_S24 |
SNDRV_PCM_FMTBIT_S32,
.rates = SNDRV_PCM_RATE_8000 |
SNDRV_PCM_RATE_16000 |
SNDRV_PCM_RATE_32000 |
SNDRV_PCM_RATE_48000 |
SNDRV_PCM_RATE_96000,
.rate_min = 8000,
.rate_max = 96000,
.channels_min = 1,
.channels_max = 8,
},
.probe = &asoc_qcom_lpass_cpu_dai_probe,
.ops = &asoc_qcom_lpass_cpu_dai_ops,
},
[MI2S_TERTIARY] = {
.id = MI2S_TERTIARY,
.name = "Tertiary MI2S",
.capture = {
.stream_name = "Tertiary Capture",
.formats = SNDRV_PCM_FMTBIT_S16 |
SNDRV_PCM_FMTBIT_S24 |
SNDRV_PCM_FMTBIT_S32,
.rates = SNDRV_PCM_RATE_8000 |
SNDRV_PCM_RATE_16000 |
SNDRV_PCM_RATE_32000 |
SNDRV_PCM_RATE_48000 |
SNDRV_PCM_RATE_96000,
.rate_min = 8000,
.rate_max = 96000,
.channels_min = 1,
.channels_max = 8,
},
.probe = &asoc_qcom_lpass_cpu_dai_probe,
.ops = &asoc_qcom_lpass_cpu_dai_ops,
},
[MI2S_QUATERNARY] = {
.id = MI2S_QUATERNARY,
.name = "Quatenary MI2S",
.playback = {
.stream_name = "Quatenary Playback",
.formats = SNDRV_PCM_FMTBIT_S16 |
SNDRV_PCM_FMTBIT_S24 |
SNDRV_PCM_FMTBIT_S32,
.rates = SNDRV_PCM_RATE_8000 |
SNDRV_PCM_RATE_16000 |
SNDRV_PCM_RATE_32000 |
SNDRV_PCM_RATE_48000 |
SNDRV_PCM_RATE_96000,
.rate_min = 8000,
.rate_max = 96000,
.channels_min = 1,
.channels_max = 8,
},
.capture = {
.stream_name = "Quatenary Capture",
.formats = SNDRV_PCM_FMTBIT_S16 |
SNDRV_PCM_FMTBIT_S24 |
SNDRV_PCM_FMTBIT_S32,
.rates = SNDRV_PCM_RATE_8000 |
SNDRV_PCM_RATE_16000 |
SNDRV_PCM_RATE_32000 |
SNDRV_PCM_RATE_48000 |
SNDRV_PCM_RATE_96000,
.rate_min = 8000,
.rate_max = 96000,
.channels_min = 1,
.channels_max = 8,
},
.probe = &asoc_qcom_lpass_cpu_dai_probe,
.ops = &asoc_qcom_lpass_cpu_dai_ops,
},
};
static int apq8016_lpass_alloc_dma_channel(struct lpass_data *drvdata)
{
struct lpass_variant *v = drvdata->variant;
int chan = find_first_zero_bit(&drvdata->rdma_ch_bit_map,
v->rdma_channels);
if (chan >= v->rdma_channels)
return -EBUSY;
set_bit(chan, &drvdata->rdma_ch_bit_map);
return chan;
}
static int apq8016_lpass_free_dma_channel(struct lpass_data *drvdata, int chan)
{
clear_bit(chan, &drvdata->rdma_ch_bit_map);
return 0;
}
static int apq8016_lpass_init(struct platform_device *pdev)
{
struct lpass_data *drvdata = platform_get_drvdata(pdev);
struct device *dev = &pdev->dev;
int ret;
drvdata->pcnoc_mport_clk = devm_clk_get(dev, "pcnoc-mport-clk");
if (IS_ERR(drvdata->pcnoc_mport_clk)) {
dev_err(&pdev->dev, "%s() error getting pcnoc-mport-clk: %ld\n",
__func__, PTR_ERR(drvdata->pcnoc_mport_clk));
return PTR_ERR(drvdata->pcnoc_mport_clk);
}
ret = clk_prepare_enable(drvdata->pcnoc_mport_clk);
if (ret) {
dev_err(&pdev->dev, "%s() Error enabling pcnoc-mport-clk: %d\n",
__func__, ret);
return ret;
}
drvdata->pcnoc_sway_clk = devm_clk_get(dev, "pcnoc-sway-clk");
if (IS_ERR(drvdata->pcnoc_sway_clk)) {
dev_err(&pdev->dev, "%s() error getting pcnoc-sway-clk: %ld\n",
__func__, PTR_ERR(drvdata->pcnoc_sway_clk));
return PTR_ERR(drvdata->pcnoc_sway_clk);
}
ret = clk_prepare_enable(drvdata->pcnoc_sway_clk);
if (ret) {
dev_err(&pdev->dev, "%s() Error enabling pcnoc_sway_clk: %d\n",
__func__, ret);
return ret;
}
return 0;
}
static int apq8016_lpass_exit(struct platform_device *pdev)
{
struct lpass_data *drvdata = platform_get_drvdata(pdev);
clk_disable_unprepare(drvdata->pcnoc_mport_clk);
clk_disable_unprepare(drvdata->pcnoc_sway_clk);
return 0;
}
static struct lpass_variant apq8016_data = {
.i2sctrl_reg_base = 0x1000,
.i2sctrl_reg_stride = 0x1000,
.i2s_ports = 4,
.irq_reg_base = 0x6000,
.irq_reg_stride = 0x1000,
.irq_ports = 3,
.rdma_reg_base = 0x8400,
.rdma_reg_stride = 0x1000,
.rdma_channels = 2,
.rdmactl_audif_start = 1,
.dai_driver = apq8016_lpass_cpu_dai_driver,
.num_dai = ARRAY_SIZE(apq8016_lpass_cpu_dai_driver),
.init = apq8016_lpass_init,
.exit = apq8016_lpass_exit,
.alloc_dma_channel = apq8016_lpass_alloc_dma_channel,
.free_dma_channel = apq8016_lpass_free_dma_channel,
};
static const struct of_device_id apq8016_lpass_cpu_device_id[] = {
{ .compatible = "qcom,lpass-cpu-apq8016", .data = &apq8016_data },
{}
};
MODULE_DEVICE_TABLE(of, apq8016_lpass_cpu_device_id);
static struct platform_driver apq8016_lpass_cpu_platform_driver = {
.driver = {
.name = "apq8016-lpass-cpu",
.of_match_table = of_match_ptr(apq8016_lpass_cpu_device_id),
},
.probe = asoc_qcom_lpass_cpu_platform_probe,
.remove = asoc_qcom_lpass_cpu_platform_remove,
};
module_platform_driver(apq8016_lpass_cpu_platform_driver);
MODULE_DESCRIPTION("APQ8016 LPASS CPU Driver");
MODULE_LICENSE("GPL v2");

View File

@ -14,21 +14,17 @@
*/
#include <linux/clk.h>
#include <linux/compiler.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/ioport.h>
#include <linux/kernel.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <linux/regmap.h>
#include <sound/soc.h>
#include <sound/soc-dai.h>
#include "lpass-lpaif-ipq806x.h"
#include "lpass-lpaif-reg.h"
#include "lpass.h"
static int lpass_cpu_daiops_set_sysclk(struct snd_soc_dai *dai, int clk_id,
@ -37,7 +33,10 @@ static int lpass_cpu_daiops_set_sysclk(struct snd_soc_dai *dai, int clk_id,
struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
int ret;
ret = clk_set_rate(drvdata->mi2s_osr_clk, freq);
if (IS_ERR(drvdata->mi2s_osr_clk[dai->driver->id]))
return 0;
ret = clk_set_rate(drvdata->mi2s_osr_clk[dai->driver->id], freq);
if (ret)
dev_err(dai->dev, "%s() error setting mi2s osrclk to %u: %d\n",
__func__, freq, ret);
@ -51,18 +50,23 @@ static int lpass_cpu_daiops_startup(struct snd_pcm_substream *substream,
struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
int ret;
ret = clk_prepare_enable(drvdata->mi2s_osr_clk);
if (ret) {
dev_err(dai->dev, "%s() error in enabling mi2s osr clk: %d\n",
__func__, ret);
return ret;
if (!IS_ERR(drvdata->mi2s_osr_clk[dai->driver->id])) {
ret = clk_prepare_enable(
drvdata->mi2s_osr_clk[dai->driver->id]);
if (ret) {
dev_err(dai->dev, "%s() error in enabling mi2s osr clk: %d\n",
__func__, ret);
return ret;
}
}
ret = clk_prepare_enable(drvdata->mi2s_bit_clk);
ret = clk_prepare_enable(drvdata->mi2s_bit_clk[dai->driver->id]);
if (ret) {
dev_err(dai->dev, "%s() error in enabling mi2s bit clk: %d\n",
__func__, ret);
clk_disable_unprepare(drvdata->mi2s_osr_clk);
if (!IS_ERR(drvdata->mi2s_osr_clk[dai->driver->id]))
clk_disable_unprepare(
drvdata->mi2s_osr_clk[dai->driver->id]);
return ret;
}
@ -74,8 +78,10 @@ static void lpass_cpu_daiops_shutdown(struct snd_pcm_substream *substream,
{
struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
clk_disable_unprepare(drvdata->mi2s_bit_clk);
clk_disable_unprepare(drvdata->mi2s_osr_clk);
clk_disable_unprepare(drvdata->mi2s_bit_clk[dai->driver->id]);
if (!IS_ERR(drvdata->mi2s_osr_clk[dai->driver->id]))
clk_disable_unprepare(drvdata->mi2s_osr_clk[dai->driver->id]);
}
static int lpass_cpu_daiops_hw_params(struct snd_pcm_substream *substream,
@ -142,14 +148,16 @@ static int lpass_cpu_daiops_hw_params(struct snd_pcm_substream *substream,
}
ret = regmap_write(drvdata->lpaif_map,
LPAIF_I2SCTL_REG(LPAIF_I2S_PORT_MI2S), regval);
LPAIF_I2SCTL_REG(drvdata->variant, dai->driver->id),
regval);
if (ret) {
dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n",
__func__, ret);
return ret;
}
ret = clk_set_rate(drvdata->mi2s_bit_clk, rate * bitwidth * 2);
ret = clk_set_rate(drvdata->mi2s_bit_clk[dai->driver->id],
rate * bitwidth * 2);
if (ret) {
dev_err(dai->dev, "%s() error setting mi2s bitclk to %u: %d\n",
__func__, rate * bitwidth * 2, ret);
@ -166,7 +174,8 @@ static int lpass_cpu_daiops_hw_free(struct snd_pcm_substream *substream,
int ret;
ret = regmap_write(drvdata->lpaif_map,
LPAIF_I2SCTL_REG(LPAIF_I2S_PORT_MI2S), 0);
LPAIF_I2SCTL_REG(drvdata->variant, dai->driver->id),
0);
if (ret)
dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n",
__func__, ret);
@ -181,7 +190,7 @@ static int lpass_cpu_daiops_prepare(struct snd_pcm_substream *substream,
int ret;
ret = regmap_update_bits(drvdata->lpaif_map,
LPAIF_I2SCTL_REG(LPAIF_I2S_PORT_MI2S),
LPAIF_I2SCTL_REG(drvdata->variant, dai->driver->id),
LPAIF_I2SCTL_SPKEN_MASK, LPAIF_I2SCTL_SPKEN_ENABLE);
if (ret)
dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n",
@ -201,7 +210,8 @@ static int lpass_cpu_daiops_trigger(struct snd_pcm_substream *substream,
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
ret = regmap_update_bits(drvdata->lpaif_map,
LPAIF_I2SCTL_REG(LPAIF_I2S_PORT_MI2S),
LPAIF_I2SCTL_REG(drvdata->variant,
dai->driver->id),
LPAIF_I2SCTL_SPKEN_MASK,
LPAIF_I2SCTL_SPKEN_ENABLE);
if (ret)
@ -212,7 +222,8 @@ static int lpass_cpu_daiops_trigger(struct snd_pcm_substream *substream,
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
ret = regmap_update_bits(drvdata->lpaif_map,
LPAIF_I2SCTL_REG(LPAIF_I2S_PORT_MI2S),
LPAIF_I2SCTL_REG(drvdata->variant,
dai->driver->id),
LPAIF_I2SCTL_SPKEN_MASK,
LPAIF_I2SCTL_SPKEN_DISABLE);
if (ret)
@ -224,7 +235,7 @@ static int lpass_cpu_daiops_trigger(struct snd_pcm_substream *substream,
return ret;
}
static struct snd_soc_dai_ops lpass_cpu_dai_ops = {
struct snd_soc_dai_ops asoc_qcom_lpass_cpu_dai_ops = {
.set_sysclk = lpass_cpu_daiops_set_sysclk,
.startup = lpass_cpu_daiops_startup,
.shutdown = lpass_cpu_daiops_shutdown,
@ -233,41 +244,23 @@ static struct snd_soc_dai_ops lpass_cpu_dai_ops = {
.prepare = lpass_cpu_daiops_prepare,
.trigger = lpass_cpu_daiops_trigger,
};
EXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_dai_ops);
static int lpass_cpu_dai_probe(struct snd_soc_dai *dai)
int asoc_qcom_lpass_cpu_dai_probe(struct snd_soc_dai *dai)
{
struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
int ret;
/* ensure audio hardware is disabled */
ret = regmap_write(drvdata->lpaif_map,
LPAIF_I2SCTL_REG(LPAIF_I2S_PORT_MI2S), 0);
LPAIF_I2SCTL_REG(drvdata->variant, dai->driver->id), 0);
if (ret)
dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n",
__func__, ret);
return ret;
}
static struct snd_soc_dai_driver lpass_cpu_dai_driver = {
.playback = {
.stream_name = "lpass-cpu-playback",
.formats = SNDRV_PCM_FMTBIT_S16 |
SNDRV_PCM_FMTBIT_S24 |
SNDRV_PCM_FMTBIT_S32,
.rates = SNDRV_PCM_RATE_8000 |
SNDRV_PCM_RATE_16000 |
SNDRV_PCM_RATE_32000 |
SNDRV_PCM_RATE_48000 |
SNDRV_PCM_RATE_96000,
.rate_min = 8000,
.rate_max = 96000,
.channels_min = 1,
.channels_max = 8,
},
.probe = &lpass_cpu_dai_probe,
.ops = &lpass_cpu_dai_ops,
};
EXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_dai_probe);
static const struct snd_soc_component_driver lpass_cpu_comp_driver = {
.name = "lpass-cpu",
@ -275,27 +268,29 @@ static const struct snd_soc_component_driver lpass_cpu_comp_driver = {
static bool lpass_cpu_regmap_writeable(struct device *dev, unsigned int reg)
{
struct lpass_data *drvdata = dev_get_drvdata(dev);
struct lpass_variant *v = drvdata->variant;
int i;
for (i = 0; i < LPAIF_I2S_PORT_NUM; ++i)
if (reg == LPAIF_I2SCTL_REG(i))
for (i = 0; i < v->i2s_ports; ++i)
if (reg == LPAIF_I2SCTL_REG(v, i))
return true;
for (i = 0; i < LPAIF_IRQ_PORT_NUM; ++i) {
if (reg == LPAIF_IRQEN_REG(i))
for (i = 0; i < v->irq_ports; ++i) {
if (reg == LPAIF_IRQEN_REG(v, i))
return true;
if (reg == LPAIF_IRQCLEAR_REG(i))
if (reg == LPAIF_IRQCLEAR_REG(v, i))
return true;
}
for (i = 0; i < LPAIF_RDMA_CHAN_NUM; ++i) {
if (reg == LPAIF_RDMACTL_REG(i))
for (i = 0; i < v->rdma_channels; ++i) {
if (reg == LPAIF_RDMACTL_REG(v, i))
return true;
if (reg == LPAIF_RDMABASE_REG(i))
if (reg == LPAIF_RDMABASE_REG(v, i))
return true;
if (reg == LPAIF_RDMABUFF_REG(i))
if (reg == LPAIF_RDMABUFF_REG(v, i))
return true;
if (reg == LPAIF_RDMAPER_REG(i))
if (reg == LPAIF_RDMAPER_REG(v, i))
return true;
}
@ -304,29 +299,31 @@ static bool lpass_cpu_regmap_writeable(struct device *dev, unsigned int reg)
static bool lpass_cpu_regmap_readable(struct device *dev, unsigned int reg)
{
struct lpass_data *drvdata = dev_get_drvdata(dev);
struct lpass_variant *v = drvdata->variant;
int i;
for (i = 0; i < LPAIF_I2S_PORT_NUM; ++i)
if (reg == LPAIF_I2SCTL_REG(i))
for (i = 0; i < v->i2s_ports; ++i)
if (reg == LPAIF_I2SCTL_REG(v, i))
return true;
for (i = 0; i < LPAIF_IRQ_PORT_NUM; ++i) {
if (reg == LPAIF_IRQEN_REG(i))
for (i = 0; i < v->irq_ports; ++i) {
if (reg == LPAIF_IRQEN_REG(v, i))
return true;
if (reg == LPAIF_IRQSTAT_REG(i))
if (reg == LPAIF_IRQSTAT_REG(v, i))
return true;
}
for (i = 0; i < LPAIF_RDMA_CHAN_NUM; ++i) {
if (reg == LPAIF_RDMACTL_REG(i))
for (i = 0; i < v->rdma_channels; ++i) {
if (reg == LPAIF_RDMACTL_REG(v, i))
return true;
if (reg == LPAIF_RDMABASE_REG(i))
if (reg == LPAIF_RDMABASE_REG(v, i))
return true;
if (reg == LPAIF_RDMABUFF_REG(i))
if (reg == LPAIF_RDMABUFF_REG(v, i))
return true;
if (reg == LPAIF_RDMACURR_REG(i))
if (reg == LPAIF_RDMACURR_REG(v, i))
return true;
if (reg == LPAIF_RDMAPER_REG(i))
if (reg == LPAIF_RDMAPER_REG(v, i))
return true;
}
@ -335,36 +332,41 @@ static bool lpass_cpu_regmap_readable(struct device *dev, unsigned int reg)
static bool lpass_cpu_regmap_volatile(struct device *dev, unsigned int reg)
{
struct lpass_data *drvdata = dev_get_drvdata(dev);
struct lpass_variant *v = drvdata->variant;
int i;
for (i = 0; i < LPAIF_IRQ_PORT_NUM; ++i)
if (reg == LPAIF_IRQSTAT_REG(i))
for (i = 0; i < v->irq_ports; ++i)
if (reg == LPAIF_IRQSTAT_REG(v, i))
return true;
for (i = 0; i < LPAIF_RDMA_CHAN_NUM; ++i)
if (reg == LPAIF_RDMACURR_REG(i))
for (i = 0; i < v->rdma_channels; ++i)
if (reg == LPAIF_RDMACURR_REG(v, i))
return true;
return false;
}
static const struct regmap_config lpass_cpu_regmap_config = {
static struct regmap_config lpass_cpu_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
.max_register = LPAIF_RDMAPER_REG(LPAIF_RDMA_CHAN_MAX),
.writeable_reg = lpass_cpu_regmap_writeable,
.readable_reg = lpass_cpu_regmap_readable,
.volatile_reg = lpass_cpu_regmap_volatile,
.cache_type = REGCACHE_FLAT,
};
static int lpass_cpu_platform_probe(struct platform_device *pdev)
int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev)
{
struct lpass_data *drvdata;
struct device_node *dsp_of_node;
struct resource *res;
int ret;
struct lpass_variant *variant;
struct device *dev = &pdev->dev;
const struct of_device_id *match;
char clk_name[16];
int ret, i, dai_id;
dsp_of_node = of_parse_phandle(pdev->dev.of_node, "qcom,adsp", 0);
if (dsp_of_node) {
@ -379,11 +381,14 @@ static int lpass_cpu_platform_probe(struct platform_device *pdev)
return -ENOMEM;
platform_set_drvdata(pdev, drvdata);
match = of_match_device(dev->driver->of_match_table, dev);
if (!match || !match->data)
return -EINVAL;
drvdata->variant = (struct lpass_variant *)match->data;
variant = drvdata->variant;
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "lpass-lpaif");
if (!res) {
dev_err(&pdev->dev, "%s() error getting resource\n", __func__);
return -ENODEV;
}
drvdata->lpaif = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR((void const __force *)drvdata->lpaif)) {
@ -393,6 +398,9 @@ static int lpass_cpu_platform_probe(struct platform_device *pdev)
return PTR_ERR((void const __force *)drvdata->lpaif);
}
lpass_cpu_regmap_config.max_register = LPAIF_RDMAPER_REG(variant,
variant->rdma_channels);
drvdata->lpaif_map = devm_regmap_init_mmio(&pdev->dev, drvdata->lpaif,
&lpass_cpu_regmap_config);
if (IS_ERR(drvdata->lpaif_map)) {
@ -401,18 +409,38 @@ static int lpass_cpu_platform_probe(struct platform_device *pdev)
return PTR_ERR(drvdata->lpaif_map);
}
drvdata->mi2s_osr_clk = devm_clk_get(&pdev->dev, "mi2s-osr-clk");
if (IS_ERR(drvdata->mi2s_osr_clk)) {
dev_err(&pdev->dev, "%s() error getting mi2s-osr-clk: %ld\n",
__func__, PTR_ERR(drvdata->mi2s_osr_clk));
return PTR_ERR(drvdata->mi2s_osr_clk);
}
if (variant->init)
variant->init(pdev);
drvdata->mi2s_bit_clk = devm_clk_get(&pdev->dev, "mi2s-bit-clk");
if (IS_ERR(drvdata->mi2s_bit_clk)) {
dev_err(&pdev->dev, "%s() error getting mi2s-bit-clk: %ld\n",
__func__, PTR_ERR(drvdata->mi2s_bit_clk));
return PTR_ERR(drvdata->mi2s_bit_clk);
for (i = 0; i < variant->num_dai; i++) {
dai_id = variant->dai_driver[i].id;
if (variant->num_dai > 1)
sprintf(clk_name, "mi2s-osr-clk%d", i);
else
sprintf(clk_name, "mi2s-osr-clk");
drvdata->mi2s_osr_clk[dai_id] = devm_clk_get(&pdev->dev,
clk_name);
if (IS_ERR(drvdata->mi2s_osr_clk[dai_id])) {
dev_warn(&pdev->dev,
"%s() error getting mi2s-osr-clk: %ld\n",
__func__,
PTR_ERR(drvdata->mi2s_osr_clk[dai_id]));
}
if (variant->num_dai > 1)
sprintf(clk_name, "mi2s-bit-clk%d", i);
else
sprintf(clk_name, "mi2s-bit-clk");
drvdata->mi2s_bit_clk[dai_id] = devm_clk_get(&pdev->dev,
clk_name);
if (IS_ERR(drvdata->mi2s_bit_clk[dai_id])) {
dev_err(&pdev->dev,
"%s() error getting mi2s-bit-clk: %ld\n",
__func__, PTR_ERR(drvdata->mi2s_bit_clk[i]));
return PTR_ERR(drvdata->mi2s_bit_clk[dai_id]);
}
}
drvdata->ahbix_clk = devm_clk_get(&pdev->dev, "ahbix-clk");
@ -439,7 +467,9 @@ static int lpass_cpu_platform_probe(struct platform_device *pdev)
}
ret = devm_snd_soc_register_component(&pdev->dev,
&lpass_cpu_comp_driver, &lpass_cpu_dai_driver, 1);
&lpass_cpu_comp_driver,
variant->dai_driver,
variant->num_dai);
if (ret) {
dev_err(&pdev->dev, "%s() error registering cpu driver: %d\n",
__func__, ret);
@ -459,33 +489,17 @@ err_clk:
clk_disable_unprepare(drvdata->ahbix_clk);
return ret;
}
EXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_platform_probe);
static int lpass_cpu_platform_remove(struct platform_device *pdev)
int asoc_qcom_lpass_cpu_platform_remove(struct platform_device *pdev)
{
struct lpass_data *drvdata = platform_get_drvdata(pdev);
if (drvdata->variant->exit)
drvdata->variant->exit(pdev);
clk_disable_unprepare(drvdata->ahbix_clk);
return 0;
}
#ifdef CONFIG_OF
static const struct of_device_id lpass_cpu_device_id[] = {
{ .compatible = "qcom,lpass-cpu" },
{}
};
MODULE_DEVICE_TABLE(of, lpass_cpu_device_id);
#endif
static struct platform_driver lpass_cpu_platform_driver = {
.driver = {
.name = "lpass-cpu",
.of_match_table = of_match_ptr(lpass_cpu_device_id),
},
.probe = lpass_cpu_platform_probe,
.remove = lpass_cpu_platform_remove,
};
module_platform_driver(lpass_cpu_platform_driver);
MODULE_DESCRIPTION("QTi LPASS CPU Driver");
MODULE_LICENSE("GPL v2");
EXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_platform_remove);

View File

@ -0,0 +1,109 @@
/*
* Copyright (c) 2010-2011,2013-2015 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* lpass-ipq806x.c -- ALSA SoC CPU DAI driver for QTi LPASS
* Splited out the IPQ8064 soc specific from lpass-cpu.c
*/
#include <linux/clk.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <sound/pcm.h>
#include <sound/soc.h>
#include <sound/soc-dai.h>
#include "lpass-lpaif-reg.h"
#include "lpass.h"
enum lpaif_i2s_ports {
IPQ806X_LPAIF_I2S_PORT_CODEC_SPK,
IPQ806X_LPAIF_I2S_PORT_CODEC_MIC,
IPQ806X_LPAIF_I2S_PORT_SEC_SPK,
IPQ806X_LPAIF_I2S_PORT_SEC_MIC,
IPQ806X_LPAIF_I2S_PORT_MI2S,
};
enum lpaif_dma_channels {
IPQ806X_LPAIF_RDMA_CHAN_MI2S,
IPQ806X_LPAIF_RDMA_CHAN_PCM0,
IPQ806X_LPAIF_RDMA_CHAN_PCM1,
};
static struct snd_soc_dai_driver ipq806x_lpass_cpu_dai_driver = {
.id = IPQ806X_LPAIF_I2S_PORT_MI2S,
.playback = {
.stream_name = "lpass-cpu-playback",
.formats = SNDRV_PCM_FMTBIT_S16 |
SNDRV_PCM_FMTBIT_S24 |
SNDRV_PCM_FMTBIT_S32,
.rates = SNDRV_PCM_RATE_8000 |
SNDRV_PCM_RATE_16000 |
SNDRV_PCM_RATE_32000 |
SNDRV_PCM_RATE_48000 |
SNDRV_PCM_RATE_96000,
.rate_min = 8000,
.rate_max = 96000,
.channels_min = 1,
.channels_max = 8,
},
.probe = &asoc_qcom_lpass_cpu_dai_probe,
.ops = &asoc_qcom_lpass_cpu_dai_ops,
};
static int ipq806x_lpass_alloc_dma_channel(struct lpass_data *drvdata)
{
return IPQ806X_LPAIF_RDMA_CHAN_MI2S;
}
static int ipq806x_lpass_free_dma_channel(struct lpass_data *drvdata, int chan)
{
return 0;
}
struct lpass_variant ipq806x_data = {
.i2sctrl_reg_base = 0x0010,
.i2sctrl_reg_stride = 0x04,
.i2s_ports = 5,
.irq_reg_base = 0x3000,
.irq_reg_stride = 0x1000,
.irq_ports = 3,
.rdma_reg_base = 0x6000,
.rdma_reg_stride = 0x1000,
.rdma_channels = 4,
.dai_driver = &ipq806x_lpass_cpu_dai_driver,
.num_dai = 1,
.alloc_dma_channel = ipq806x_lpass_alloc_dma_channel,
.free_dma_channel = ipq806x_lpass_free_dma_channel,
};
static const struct of_device_id ipq806x_lpass_cpu_device_id[] = {
{ .compatible = "qcom,lpass-cpu", .data = &ipq806x_data },
{}
};
MODULE_DEVICE_TABLE(of, ipq806x_lpass_cpu_device_id);
static struct platform_driver ipq806x_lpass_cpu_platform_driver = {
.driver = {
.name = "lpass-cpu",
.of_match_table = of_match_ptr(ipq806x_lpass_cpu_device_id),
},
.probe = asoc_qcom_lpass_cpu_platform_probe,
.remove = asoc_qcom_lpass_cpu_platform_remove,
};
module_platform_driver(ipq806x_lpass_cpu_platform_driver);
MODULE_DESCRIPTION("QTi LPASS CPU Driver");
MODULE_LICENSE("GPL v2");

View File

@ -9,37 +9,17 @@
* 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.
*
* lpass-lpaif-ipq806x.h -- Definitions for the QTi LPAIF in the ipq806x LPASS
*/
#ifndef __LPASS_LPAIF_H__
#define __LPASS_LPAIF_H__
#define LPAIF_BANK_OFFSET 0x1000
#ifndef __LPASS_LPAIF_REG_H__
#define __LPASS_LPAIF_REG_H__
/* LPAIF I2S */
#define LPAIF_I2SCTL_REG_BASE 0x0010
#define LPAIF_I2SCTL_REG_STRIDE 0x4
#define LPAIF_I2SCTL_REG_ADDR(addr, port) \
(LPAIF_I2SCTL_REG_BASE + (addr) + (LPAIF_I2SCTL_REG_STRIDE * (port)))
enum lpaif_i2s_ports {
LPAIF_I2S_PORT_MIN = 0,
LPAIF_I2S_PORT_CODEC_SPK = 0,
LPAIF_I2S_PORT_CODEC_MIC = 1,
LPAIF_I2S_PORT_SEC_SPK = 2,
LPAIF_I2S_PORT_SEC_MIC = 3,
LPAIF_I2S_PORT_MI2S = 4,
LPAIF_I2S_PORT_MAX = 4,
LPAIF_I2S_PORT_NUM = 5,
};
#define LPAIF_I2SCTL_REG(port) LPAIF_I2SCTL_REG_ADDR(0x0, (port))
#define LPAIF_I2SCTL_REG_ADDR(v, addr, port) \
(v->i2sctrl_reg_base + (addr) + v->i2sctrl_reg_stride * (port))
#define LPAIF_I2SCTL_REG(v, port) LPAIF_I2SCTL_REG_ADDR(v, 0x0, (port))
#define LPAIF_I2SCTL_LOOPBACK_MASK 0x8000
#define LPAIF_I2SCTL_LOOPBACK_SHIFT 15
#define LPAIF_I2SCTL_LOOPBACK_DISABLE (0 << LPAIF_I2SCTL_LOOPBACK_SHIFT)
@ -79,55 +59,36 @@ enum lpaif_i2s_ports {
#define LPAIF_I2SCTL_BITWIDTH_32 (2 << LPAIF_I2SCTL_BITWIDTH_SHIFT)
/* LPAIF IRQ */
#define LPAIF_IRQ_REG_ADDR(v, addr, port) \
(v->irq_reg_base + (addr) + v->irq_reg_stride * (port))
#define LPAIF_IRQ_REG_BASE 0x3000
#define LPAIF_IRQ_REG_STRIDE 0x1000
#define LPAIF_IRQ_REG_ADDR(addr, port) \
(LPAIF_IRQ_REG_BASE + (addr) + (LPAIF_IRQ_REG_STRIDE * (port)))
#define LPAIF_IRQ_PORT_HOST 0
enum lpaif_irq_ports {
LPAIF_IRQ_PORT_MIN = 0,
LPAIF_IRQ_PORT_HOST = 0,
LPAIF_IRQ_PORT_ADSP = 1,
LPAIF_IRQ_PORT_MAX = 2,
LPAIF_IRQ_PORT_NUM = 3,
};
#define LPAIF_IRQEN_REG(port) LPAIF_IRQ_REG_ADDR(0x0, (port))
#define LPAIF_IRQSTAT_REG(port) LPAIF_IRQ_REG_ADDR(0x4, (port))
#define LPAIF_IRQCLEAR_REG(port) LPAIF_IRQ_REG_ADDR(0xC, (port))
#define LPAIF_IRQEN_REG(v, port) LPAIF_IRQ_REG_ADDR(v, 0x0, (port))
#define LPAIF_IRQSTAT_REG(v, port) LPAIF_IRQ_REG_ADDR(v, 0x4, (port))
#define LPAIF_IRQCLEAR_REG(v, port) LPAIF_IRQ_REG_ADDR(v, 0xC, (port))
#define LPAIF_IRQ_BITSTRIDE 3
#define LPAIF_IRQ_PER(chan) (1 << (LPAIF_IRQ_BITSTRIDE * (chan)))
#define LPAIF_IRQ_XRUN(chan) (2 << (LPAIF_IRQ_BITSTRIDE * (chan)))
#define LPAIF_IRQ_ERR(chan) (4 << (LPAIF_IRQ_BITSTRIDE * (chan)))
#define LPAIF_IRQ_ALL(chan) (7 << (LPAIF_IRQ_BITSTRIDE * (chan)))
/* LPAIF DMA */
#define LPAIF_RDMA_REG_BASE 0x6000
#define LPAIF_RDMA_REG_STRIDE 0x1000
#define LPAIF_RDMA_REG_ADDR(addr, chan) \
(LPAIF_RDMA_REG_BASE + (addr) + (LPAIF_RDMA_REG_STRIDE * (chan)))
#define LPAIF_RDMA_REG_ADDR(v, addr, chan) \
(v->rdma_reg_base + (addr) + v->rdma_reg_stride * (chan))
enum lpaif_dma_channels {
LPAIF_RDMA_CHAN_MIN = 0,
#define LPAIF_RDMACTL_AUDINTF(id) (id << LPAIF_RDMACTL_AUDINTF_SHIFT)
LPAIF_RDMA_CHAN_MI2S = 0,
LPAIF_RDMA_CHAN_PCM0 = 1,
LPAIF_RDMA_CHAN_PCM1 = 2,
LPAIF_RDMA_CHAN_MAX = 4,
LPAIF_RDMA_CHAN_NUM = 5,
};
#define LPAIF_RDMACTL_REG(chan) LPAIF_RDMA_REG_ADDR(0x00, (chan))
#define LPAIF_RDMABASE_REG(chan) LPAIF_RDMA_REG_ADDR(0x04, (chan))
#define LPAIF_RDMABUFF_REG(chan) LPAIF_RDMA_REG_ADDR(0x08, (chan))
#define LPAIF_RDMACURR_REG(chan) LPAIF_RDMA_REG_ADDR(0x0C, (chan))
#define LPAIF_RDMAPER_REG(chan) LPAIF_RDMA_REG_ADDR(0x10, (chan))
#define LPAIF_RDMACTL_REG(v, chan) LPAIF_RDMA_REG_ADDR(v, 0x00, (chan))
#define LPAIF_RDMABASE_REG(v, chan) LPAIF_RDMA_REG_ADDR(v, 0x04, (chan))
#define LPAIF_RDMABUFF_REG(v, chan) LPAIF_RDMA_REG_ADDR(v, 0x08, (chan))
#define LPAIF_RDMACURR_REG(v, chan) LPAIF_RDMA_REG_ADDR(v, 0x0C, (chan))
#define LPAIF_RDMAPER_REG(v, chan) LPAIF_RDMA_REG_ADDR(v, 0x10, (chan))
#define LPAIF_RDMAPERCNT_REG(v, chan) LPAIF_RDMA_REG_ADDR(v, 0x14, (chan))
#define LPAIF_RDMACTL_BURSTEN_MASK 0x800
#define LPAIF_RDMACTL_BURSTEN_SHIFT 11
@ -145,13 +106,6 @@ enum lpaif_dma_channels {
#define LPAIF_RDMACTL_AUDINTF_MASK 0x0F0
#define LPAIF_RDMACTL_AUDINTF_SHIFT 4
#define LPAIF_RDMACTL_AUDINTF_NONE (0 << LPAIF_RDMACTL_AUDINTF_SHIFT)
#define LPAIF_RDMACTL_AUDINTF_CODEC (1 << LPAIF_RDMACTL_AUDINTF_SHIFT)
#define LPAIF_RDMACTL_AUDINTF_PCM (2 << LPAIF_RDMACTL_AUDINTF_SHIFT)
#define LPAIF_RDMACTL_AUDINTF_SEC_I2S (3 << LPAIF_RDMACTL_AUDINTF_SHIFT)
#define LPAIF_RDMACTL_AUDINTF_MI2S (4 << LPAIF_RDMACTL_AUDINTF_SHIFT)
#define LPAIF_RDMACTL_AUDINTF_HDMI (5 << LPAIF_RDMACTL_AUDINTF_SHIFT)
#define LPAIF_RDMACTL_AUDINTF_SEC_PCM (7 << LPAIF_RDMACTL_AUDINTF_SHIFT)
#define LPAIF_RDMACTL_FIFOWM_MASK 0x00E
#define LPAIF_RDMACTL_FIFOWM_SHIFT 1
@ -169,4 +123,4 @@ enum lpaif_dma_channels {
#define LPAIF_RDMACTL_ENABLE_OFF (0 << LPAIF_RDMACTL_ENABLE_SHIFT)
#define LPAIF_RDMACTL_ENABLE_ON (1 << LPAIF_RDMACTL_ENABLE_SHIFT)
#endif /* __LPASS_LPAIF_H__ */
#endif /* __LPASS_LPAIF_REG_H__ */

View File

@ -13,23 +13,22 @@
* lpass-platform.c -- ALSA SoC platform driver for QTi LPASS
*/
#include <linux/compiler.h>
#include <linux/device.h>
#include <linux/dma-mapping.h>
#include <linux/err.h>
#include <linux/export.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <sound/memalloc.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <linux/regmap.h>
#include <sound/soc.h>
#include "lpass-lpaif-ipq806x.h"
#include "lpass-lpaif-reg.h"
#include "lpass.h"
struct lpass_pcm_data {
int rdma_ch;
int i2s_port;
};
#define LPASS_PLATFORM_BUFFER_SIZE (16 * 1024)
#define LPASS_PLATFORM_PERIODS 2
@ -84,13 +83,15 @@ static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
struct lpass_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(soc_runtime);
struct lpass_data *drvdata =
snd_soc_platform_get_drvdata(soc_runtime->platform);
struct lpass_variant *v = drvdata->variant;
snd_pcm_format_t format = params_format(params);
unsigned int channels = params_channels(params);
unsigned int regval;
int bitwidth;
int ret;
int ret, rdma_port = pcm_data->i2s_port + v->rdmactl_audif_start;
bitwidth = snd_pcm_format_width(format);
if (bitwidth < 0) {
@ -100,7 +101,7 @@ static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream,
}
regval = LPAIF_RDMACTL_BURSTEN_INCR4 |
LPAIF_RDMACTL_AUDINTF_MI2S |
LPAIF_RDMACTL_AUDINTF(rdma_port) |
LPAIF_RDMACTL_FIFOWM_8;
switch (bitwidth) {
@ -156,7 +157,7 @@ static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream,
}
ret = regmap_write(drvdata->lpaif_map,
LPAIF_RDMACTL_REG(LPAIF_RDMA_CHAN_MI2S), regval);
LPAIF_RDMACTL_REG(v, pcm_data->rdma_ch), regval);
if (ret) {
dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n",
__func__, ret);
@ -169,12 +170,14 @@ static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream,
static int lpass_platform_pcmops_hw_free(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
struct lpass_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(soc_runtime);
struct lpass_data *drvdata =
snd_soc_platform_get_drvdata(soc_runtime->platform);
struct lpass_variant *v = drvdata->variant;
int ret;
ret = regmap_write(drvdata->lpaif_map,
LPAIF_RDMACTL_REG(LPAIF_RDMA_CHAN_MI2S), 0);
LPAIF_RDMACTL_REG(v, pcm_data->rdma_ch), 0);
if (ret)
dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n",
__func__, ret);
@ -186,12 +189,14 @@ static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
struct lpass_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(soc_runtime);
struct lpass_data *drvdata =
snd_soc_platform_get_drvdata(soc_runtime->platform);
int ret;
struct lpass_variant *v = drvdata->variant;
int ret, ch = pcm_data->rdma_ch;
ret = regmap_write(drvdata->lpaif_map,
LPAIF_RDMABASE_REG(LPAIF_RDMA_CHAN_MI2S),
LPAIF_RDMABASE_REG(v, ch),
runtime->dma_addr);
if (ret) {
dev_err(soc_runtime->dev, "%s() error writing to rdmabase reg: %d\n",
@ -200,7 +205,7 @@ static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream)
}
ret = regmap_write(drvdata->lpaif_map,
LPAIF_RDMABUFF_REG(LPAIF_RDMA_CHAN_MI2S),
LPAIF_RDMABUFF_REG(v, ch),
(snd_pcm_lib_buffer_bytes(substream) >> 2) - 1);
if (ret) {
dev_err(soc_runtime->dev, "%s() error writing to rdmabuff reg: %d\n",
@ -209,7 +214,7 @@ static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream)
}
ret = regmap_write(drvdata->lpaif_map,
LPAIF_RDMAPER_REG(LPAIF_RDMA_CHAN_MI2S),
LPAIF_RDMAPER_REG(v, ch),
(snd_pcm_lib_period_bytes(substream) >> 2) - 1);
if (ret) {
dev_err(soc_runtime->dev, "%s() error writing to rdmaper reg: %d\n",
@ -218,7 +223,7 @@ static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream)
}
ret = regmap_update_bits(drvdata->lpaif_map,
LPAIF_RDMACTL_REG(LPAIF_RDMA_CHAN_MI2S),
LPAIF_RDMACTL_REG(v, ch),
LPAIF_RDMACTL_ENABLE_MASK, LPAIF_RDMACTL_ENABLE_ON);
if (ret) {
dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n",
@ -233,9 +238,11 @@ static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream,
int cmd)
{
struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
struct lpass_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(soc_runtime);
struct lpass_data *drvdata =
snd_soc_platform_get_drvdata(soc_runtime->platform);
int ret;
struct lpass_variant *v = drvdata->variant;
int ret, ch = pcm_data->rdma_ch;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
@ -243,8 +250,8 @@ static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream,
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
/* clear status before enabling interrupts */
ret = regmap_write(drvdata->lpaif_map,
LPAIF_IRQCLEAR_REG(LPAIF_IRQ_PORT_HOST),
LPAIF_IRQ_ALL(LPAIF_RDMA_CHAN_MI2S));
LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST),
LPAIF_IRQ_ALL(ch));
if (ret) {
dev_err(soc_runtime->dev, "%s() error writing to irqclear reg: %d\n",
__func__, ret);
@ -252,9 +259,9 @@ static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream,
}
ret = regmap_update_bits(drvdata->lpaif_map,
LPAIF_IRQEN_REG(LPAIF_IRQ_PORT_HOST),
LPAIF_IRQ_ALL(LPAIF_RDMA_CHAN_MI2S),
LPAIF_IRQ_ALL(LPAIF_RDMA_CHAN_MI2S));
LPAIF_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST),
LPAIF_IRQ_ALL(ch),
LPAIF_IRQ_ALL(ch));
if (ret) {
dev_err(soc_runtime->dev, "%s() error writing to irqen reg: %d\n",
__func__, ret);
@ -262,7 +269,7 @@ static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream,
}
ret = regmap_update_bits(drvdata->lpaif_map,
LPAIF_RDMACTL_REG(LPAIF_RDMA_CHAN_MI2S),
LPAIF_RDMACTL_REG(v, ch),
LPAIF_RDMACTL_ENABLE_MASK,
LPAIF_RDMACTL_ENABLE_ON);
if (ret) {
@ -275,7 +282,7 @@ static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream,
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
ret = regmap_update_bits(drvdata->lpaif_map,
LPAIF_RDMACTL_REG(LPAIF_RDMA_CHAN_MI2S),
LPAIF_RDMACTL_REG(v, ch),
LPAIF_RDMACTL_ENABLE_MASK,
LPAIF_RDMACTL_ENABLE_OFF);
if (ret) {
@ -285,8 +292,8 @@ static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream,
}
ret = regmap_update_bits(drvdata->lpaif_map,
LPAIF_IRQEN_REG(LPAIF_IRQ_PORT_HOST),
LPAIF_IRQ_ALL(LPAIF_RDMA_CHAN_MI2S), 0);
LPAIF_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST),
LPAIF_IRQ_ALL(ch), 0);
if (ret) {
dev_err(soc_runtime->dev, "%s() error writing to irqen reg: %d\n",
__func__, ret);
@ -302,13 +309,15 @@ static snd_pcm_uframes_t lpass_platform_pcmops_pointer(
struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
struct lpass_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(soc_runtime);
struct lpass_data *drvdata =
snd_soc_platform_get_drvdata(soc_runtime->platform);
struct lpass_variant *v = drvdata->variant;
unsigned int base_addr, curr_addr;
int ret;
int ret, ch = pcm_data->rdma_ch;
ret = regmap_read(drvdata->lpaif_map,
LPAIF_RDMABASE_REG(LPAIF_RDMA_CHAN_MI2S), &base_addr);
LPAIF_RDMABASE_REG(v, ch), &base_addr);
if (ret) {
dev_err(soc_runtime->dev, "%s() error reading from rdmabase reg: %d\n",
__func__, ret);
@ -316,7 +325,7 @@ static snd_pcm_uframes_t lpass_platform_pcmops_pointer(
}
ret = regmap_read(drvdata->lpaif_map,
LPAIF_RDMACURR_REG(LPAIF_RDMA_CHAN_MI2S), &curr_addr);
LPAIF_RDMACURR_REG(v, ch), &curr_addr);
if (ret) {
dev_err(soc_runtime->dev, "%s() error reading from rdmacurr reg: %d\n",
__func__, ret);
@ -347,29 +356,20 @@ static struct snd_pcm_ops lpass_platform_pcm_ops = {
.mmap = lpass_platform_pcmops_mmap,
};
static irqreturn_t lpass_platform_lpaif_irq(int irq, void *data)
static irqreturn_t lpass_dma_interrupt_handler(
struct snd_pcm_substream *substream,
struct lpass_data *drvdata,
int chan, u32 interrupts)
{
struct snd_pcm_substream *substream = data;
struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
struct lpass_data *drvdata =
snd_soc_platform_get_drvdata(soc_runtime->platform);
unsigned int interrupts;
struct lpass_variant *v = drvdata->variant;
irqreturn_t ret = IRQ_NONE;
int rv;
rv = regmap_read(drvdata->lpaif_map,
LPAIF_IRQSTAT_REG(LPAIF_IRQ_PORT_HOST), &interrupts);
if (rv) {
dev_err(soc_runtime->dev, "%s() error reading from irqstat reg: %d\n",
__func__, rv);
return IRQ_NONE;
}
interrupts &= LPAIF_IRQ_ALL(LPAIF_RDMA_CHAN_MI2S);
if (interrupts & LPAIF_IRQ_PER(LPAIF_RDMA_CHAN_MI2S)) {
if (interrupts & LPAIF_IRQ_PER(chan)) {
rv = regmap_write(drvdata->lpaif_map,
LPAIF_IRQCLEAR_REG(LPAIF_IRQ_PORT_HOST),
LPAIF_IRQ_PER(LPAIF_RDMA_CHAN_MI2S));
LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST),
LPAIF_IRQ_PER(chan));
if (rv) {
dev_err(soc_runtime->dev, "%s() error writing to irqclear reg: %d\n",
__func__, rv);
@ -379,10 +379,10 @@ static irqreturn_t lpass_platform_lpaif_irq(int irq, void *data)
ret = IRQ_HANDLED;
}
if (interrupts & LPAIF_IRQ_XRUN(LPAIF_RDMA_CHAN_MI2S)) {
if (interrupts & LPAIF_IRQ_XRUN(chan)) {
rv = regmap_write(drvdata->lpaif_map,
LPAIF_IRQCLEAR_REG(LPAIF_IRQ_PORT_HOST),
LPAIF_IRQ_XRUN(LPAIF_RDMA_CHAN_MI2S));
LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST),
LPAIF_IRQ_XRUN(chan));
if (rv) {
dev_err(soc_runtime->dev, "%s() error writing to irqclear reg: %d\n",
__func__, rv);
@ -393,10 +393,10 @@ static irqreturn_t lpass_platform_lpaif_irq(int irq, void *data)
ret = IRQ_HANDLED;
}
if (interrupts & LPAIF_IRQ_ERR(LPAIF_RDMA_CHAN_MI2S)) {
if (interrupts & LPAIF_IRQ_ERR(chan)) {
rv = regmap_write(drvdata->lpaif_map,
LPAIF_IRQCLEAR_REG(LPAIF_IRQ_PORT_HOST),
LPAIF_IRQ_ERR(LPAIF_RDMA_CHAN_MI2S));
LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST),
LPAIF_IRQ_ERR(chan));
if (rv) {
dev_err(soc_runtime->dev, "%s() error writing to irqclear reg: %d\n",
__func__, rv);
@ -410,6 +410,35 @@ static irqreturn_t lpass_platform_lpaif_irq(int irq, void *data)
return ret;
}
static irqreturn_t lpass_platform_lpaif_irq(int irq, void *data)
{
struct lpass_data *drvdata = data;
struct lpass_variant *v = drvdata->variant;
unsigned int irqs;
int rv, chan;
rv = regmap_read(drvdata->lpaif_map,
LPAIF_IRQSTAT_REG(v, LPAIF_IRQ_PORT_HOST), &irqs);
if (rv) {
pr_err("%s() error reading from irqstat reg: %d\n",
__func__, rv);
return IRQ_NONE;
}
/* Handle per channel interrupts */
for (chan = 0; chan < LPASS_MAX_DMA_CHANNELS; chan++) {
if (irqs & LPAIF_IRQ_ALL(chan) && drvdata->substream[chan]) {
rv = lpass_dma_interrupt_handler(
drvdata->substream[chan],
drvdata, chan, irqs);
if (rv != IRQ_HANDLED)
return rv;
}
}
return IRQ_HANDLED;
}
static int lpass_platform_alloc_buffer(struct snd_pcm_substream *substream,
struct snd_soc_pcm_runtime *soc_runtime)
{
@ -448,9 +477,27 @@ static int lpass_platform_pcm_new(struct snd_soc_pcm_runtime *soc_runtime)
struct snd_pcm *pcm = soc_runtime->pcm;
struct snd_pcm_substream *substream =
pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
struct snd_soc_dai *cpu_dai = soc_runtime->cpu_dai;
struct lpass_data *drvdata =
snd_soc_platform_get_drvdata(soc_runtime->platform);
struct lpass_variant *v = drvdata->variant;
int ret;
struct lpass_pcm_data *data;
data = devm_kzalloc(soc_runtime->dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
if (v->alloc_dma_channel)
data->rdma_ch = v->alloc_dma_channel(drvdata);
if (IS_ERR_VALUE(data->rdma_ch))
return data->rdma_ch;
drvdata->substream[data->rdma_ch] = substream;
data->i2s_port = cpu_dai->driver->id;
snd_soc_pcm_set_drvdata(soc_runtime, data);
soc_runtime->dev->coherent_dma_mask = DMA_BIT_MASK(32);
soc_runtime->dev->dma_mask = &soc_runtime->dev->coherent_dma_mask;
@ -459,29 +506,12 @@ static int lpass_platform_pcm_new(struct snd_soc_pcm_runtime *soc_runtime)
if (ret)
return ret;
ret = devm_request_irq(soc_runtime->dev, drvdata->lpaif_irq,
lpass_platform_lpaif_irq, IRQF_TRIGGER_RISING,
"lpass-irq-lpaif", substream);
if (ret) {
dev_err(soc_runtime->dev, "%s() irq request failed: %d\n",
__func__, ret);
goto err_buf;
}
/* ensure audio hardware is disabled */
ret = regmap_write(drvdata->lpaif_map,
LPAIF_IRQEN_REG(LPAIF_IRQ_PORT_HOST), 0);
if (ret) {
dev_err(soc_runtime->dev, "%s() error writing to irqen reg: %d\n",
__func__, ret);
return ret;
}
ret = regmap_write(drvdata->lpaif_map,
LPAIF_RDMACTL_REG(LPAIF_RDMA_CHAN_MI2S), 0);
LPAIF_RDMACTL_REG(v, data->rdma_ch), 0);
if (ret) {
dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n",
__func__, ret);
return ret;
goto err_buf;
}
return 0;
@ -496,6 +526,15 @@ static void lpass_platform_pcm_free(struct snd_pcm *pcm)
struct snd_pcm_substream *substream =
pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
struct lpass_data *drvdata =
snd_soc_platform_get_drvdata(soc_runtime->platform);
struct lpass_pcm_data *data = snd_soc_pcm_get_drvdata(soc_runtime);
struct lpass_variant *v = drvdata->variant;
drvdata->substream[data->rdma_ch] = NULL;
if (v->free_dma_channel)
v->free_dma_channel(drvdata, data->rdma_ch);
lpass_platform_free_buffer(substream, soc_runtime);
}
@ -509,6 +548,8 @@ static struct snd_soc_platform_driver lpass_platform_driver = {
int asoc_qcom_lpass_platform_register(struct platform_device *pdev)
{
struct lpass_data *drvdata = platform_get_drvdata(pdev);
struct lpass_variant *v = drvdata->variant;
int ret;
drvdata->lpaif_irq = platform_get_irq_byname(pdev, "lpass-irq-lpaif");
if (drvdata->lpaif_irq < 0) {
@ -517,6 +558,25 @@ int asoc_qcom_lpass_platform_register(struct platform_device *pdev)
return -ENODEV;
}
/* ensure audio hardware is disabled */
ret = regmap_write(drvdata->lpaif_map,
LPAIF_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST), 0);
if (ret) {
dev_err(&pdev->dev, "%s() error writing to irqen reg: %d\n",
__func__, ret);
return ret;
}
ret = devm_request_irq(&pdev->dev, drvdata->lpaif_irq,
lpass_platform_lpaif_irq, IRQF_TRIGGER_RISING,
"lpass-irq-lpaif", drvdata);
if (ret) {
dev_err(&pdev->dev, "%s() irq request failed: %d\n",
__func__, ret);
return ret;
}
return devm_snd_soc_register_platform(&pdev->dev,
&lpass_platform_driver);
}

View File

@ -22,6 +22,8 @@
#include <linux/regmap.h>
#define LPASS_AHBIX_CLOCK_FREQUENCY 131072000
#define LPASS_MAX_MI2S_PORTS (8)
#define LPASS_MAX_DMA_CHANNELS (8)
/* Both the CPU DAI and platform drivers will access this data */
struct lpass_data {
@ -30,10 +32,10 @@ struct lpass_data {
struct clk *ahbix_clk;
/* MI2S system clock */
struct clk *mi2s_osr_clk;
struct clk *mi2s_osr_clk[LPASS_MAX_MI2S_PORTS];
/* MI2S bit clock (derived from system clock by a divider */
struct clk *mi2s_bit_clk;
struct clk *mi2s_bit_clk[LPASS_MAX_MI2S_PORTS];
/* low-power audio interface (LPAIF) registers */
void __iomem *lpaif;
@ -43,9 +45,54 @@ struct lpass_data {
/* interrupts from the low-power audio interface (LPAIF) */
int lpaif_irq;
/* SOC specific variations in the LPASS IP integration */
struct lpass_variant *variant;
/* bit map to keep track of static channel allocations */
unsigned long rdma_ch_bit_map;
/* used it for handling interrupt per dma channel */
struct snd_pcm_substream *substream[LPASS_MAX_DMA_CHANNELS];
/* 8016 specific */
struct clk *pcnoc_mport_clk;
struct clk *pcnoc_sway_clk;
};
/* Vairant data per each SOC */
struct lpass_variant {
u32 i2sctrl_reg_base;
u32 i2sctrl_reg_stride;
u32 i2s_ports;
u32 irq_reg_base;
u32 irq_reg_stride;
u32 irq_ports;
u32 rdma_reg_base;
u32 rdma_reg_stride;
u32 rdma_channels;
/**
* on SOCs like APQ8016 the channel control bits start
* at different offset to ipq806x
**/
u32 rdmactl_audif_start;
/* SOC specific intialization like clocks */
int (*init)(struct platform_device *pdev);
int (*exit)(struct platform_device *pdev);
int (*alloc_dma_channel)(struct lpass_data *data);
int (*free_dma_channel)(struct lpass_data *data, int ch);
/* SOC specific dais */
struct snd_soc_dai_driver *dai_driver;
int num_dai;
};
/* register the platform driver from the CPU DAI driver */
int asoc_qcom_lpass_platform_register(struct platform_device *);
int asoc_qcom_lpass_cpu_platform_remove(struct platform_device *pdev);
int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev);
int asoc_qcom_lpass_cpu_dai_probe(struct snd_soc_dai *dai);
extern struct snd_soc_dai_ops asoc_qcom_lpass_cpu_dai_ops;
#endif /* __LPASS_H__ */

View File

@ -170,6 +170,14 @@ void rsnd_mod_quit(struct rsnd_mod *mod)
clk_unprepare(mod->clk);
}
int rsnd_mod_is_working(struct rsnd_mod *mod)
{
struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
/* see rsnd_dai_stream_init/quit() */
return !!io->substream;
}
/*
* settting function
*/
@ -272,9 +280,10 @@ struct rsnd_dai *rsnd_rdai_get(struct rsnd_priv *priv, int id)
return priv->rdai + id;
}
#define rsnd_dai_to_priv(dai) snd_soc_dai_get_drvdata(dai)
static struct rsnd_dai *rsnd_dai_to_rdai(struct snd_soc_dai *dai)
{
struct rsnd_priv *priv = snd_soc_dai_get_drvdata(dai);
struct rsnd_priv *priv = rsnd_dai_to_priv(dai);
return rsnd_rdai_get(priv, dai->id);
}
@ -314,7 +323,7 @@ void rsnd_dai_pointer_update(struct rsnd_dai_stream *io, int byte)
}
}
static int rsnd_dai_stream_init(struct rsnd_dai_stream *io,
static void rsnd_dai_stream_init(struct rsnd_dai_stream *io,
struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
@ -326,8 +335,11 @@ static int rsnd_dai_stream_init(struct rsnd_dai_stream *io,
runtime->channels *
samples_to_bytes(runtime, 1);
io->next_period_byte = io->byte_per_period;
}
return 0;
static void rsnd_dai_stream_quit(struct rsnd_dai_stream *io)
{
io->substream = NULL;
}
static
@ -351,20 +363,18 @@ struct rsnd_dai_stream *rsnd_rdai_to_io(struct rsnd_dai *rdai,
static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *dai)
{
struct rsnd_priv *priv = snd_soc_dai_get_drvdata(dai);
struct rsnd_priv *priv = rsnd_dai_to_priv(dai);
struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai);
struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream);
int ssi_id = rsnd_mod_id(rsnd_io_to_mod_ssi(io));
int ret;
unsigned long flags;
rsnd_lock(priv, flags);
spin_lock_irqsave(&priv->lock, flags);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
ret = rsnd_dai_stream_init(io, substream);
if (ret < 0)
goto dai_trigger_end;
rsnd_dai_stream_init(io, substream);
ret = rsnd_platform_call(priv, dai, start, ssi_id);
if (ret < 0)
@ -390,13 +400,15 @@ static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd,
ret = rsnd_platform_call(priv, dai, stop, ssi_id);
if (ret < 0)
goto dai_trigger_end;
rsnd_dai_stream_quit(io);
break;
default:
ret = -EINVAL;
}
dai_trigger_end:
rsnd_unlock(priv, flags);
spin_unlock_irqrestore(&priv->lock, flags);
return ret;
}
@ -833,12 +845,14 @@ static int __rsnd_kctrl_new(struct rsnd_mod *mod,
struct rsnd_kctrl_cfg *cfg,
void (*update)(struct rsnd_mod *mod))
{
struct snd_soc_card *soc_card = rtd->card;
struct snd_card *card = rtd->card->snd_card;
struct snd_kcontrol *kctrl;
struct snd_kcontrol_new knew = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = name,
.info = rsnd_kctrl_info,
.index = rtd - soc_card->rtd,
.get = rsnd_kctrl_get,
.put = rsnd_kctrl_put,
.private_value = (unsigned long)cfg,

View File

@ -303,6 +303,7 @@ int rsnd_mod_init(struct rsnd_mod *mod,
int id);
void rsnd_mod_quit(struct rsnd_mod *mod);
char *rsnd_mod_name(struct rsnd_mod *mod);
int rsnd_mod_is_working(struct rsnd_mod *mod);
struct dma_chan *rsnd_mod_dma_req(struct rsnd_mod *mod);
/*
@ -449,8 +450,6 @@ struct rsnd_priv {
#define rsnd_priv_to_pdev(priv) ((priv)->pdev)
#define rsnd_priv_to_dev(priv) (&(rsnd_priv_to_pdev(priv)->dev))
#define rsnd_priv_to_info(priv) ((priv)->info)
#define rsnd_lock(priv, flags) spin_lock_irqsave(&priv->lock, flags)
#define rsnd_unlock(priv, flags) spin_unlock_irqrestore(&priv->lock, flags)
/*
* rsnd_kctrl

View File

@ -232,6 +232,7 @@ rsrc_card_sub_parse_of(struct rsrc_card_priv *priv,
if (args_count) {
*args_count = args.args_count;
dai_link->dynamic = 1;
dai_link->dpcm_merged_format = 1;
} else {
dai_link->no_pcm = 1;
priv->codec_conf.of_node = (*p_node);

View File

@ -673,10 +673,13 @@ static int _rsnd_src_stop_gen2(struct rsnd_mod *mod)
static irqreturn_t rsnd_src_interrupt_gen2(int irq, void *data)
{
struct rsnd_mod *mod = data;
struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
if (!io)
return IRQ_NONE;
spin_lock(&priv->lock);
/* ignore all cases if not working */
if (!rsnd_mod_is_working(mod))
goto rsnd_src_interrupt_gen2_out;
if (rsnd_src_error_record_gen2(mod)) {
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
@ -692,6 +695,8 @@ static irqreturn_t rsnd_src_interrupt_gen2(int irq, void *data)
else
dev_warn(dev, "no more SRC restart\n");
}
rsnd_src_interrupt_gen2_out:
spin_unlock(&priv->lock);
return IRQ_HANDLED;
}

View File

@ -66,6 +66,7 @@ struct rsnd_ssi {
u32 cr_own;
u32 cr_clk;
int chan;
int err;
unsigned int usrcnt;
};
@ -80,7 +81,7 @@ struct rsnd_ssi {
#define rsnd_mod_to_ssi(_mod) container_of((_mod), struct rsnd_ssi, mod)
#define rsnd_dma_to_ssi(dma) rsnd_mod_to_ssi(rsnd_dma_to_mod(dma))
#define rsnd_ssi_pio_available(ssi) ((ssi)->info->irq > 0)
#define rsnd_ssi_clk_from_parent(ssi) ((ssi)->parent)
#define rsnd_ssi_parent(ssi) ((ssi)->parent)
#define rsnd_ssi_mode_flags(p) ((p)->info->flags)
#define rsnd_ssi_dai_id(ssi) ((ssi)->info->dai_id)
#define rsnd_ssi_of_node(priv) \
@ -189,8 +190,10 @@ static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi,
rsnd_mod_hw_start(&ssi->mod);
if (rsnd_rdai_is_clk_master(rdai)) {
if (rsnd_ssi_clk_from_parent(ssi))
rsnd_ssi_hw_start(ssi->parent, io);
struct rsnd_ssi *ssi_parent = rsnd_ssi_parent(ssi);
if (ssi_parent)
rsnd_ssi_hw_start(ssi_parent, io);
else
rsnd_ssi_master_clk_start(ssi, io);
}
@ -229,8 +232,10 @@ static void rsnd_ssi_hw_stop(struct rsnd_ssi *ssi)
struct device *dev = rsnd_priv_to_dev(priv);
u32 cr;
if (0 == ssi->usrcnt) /* stop might be called without start */
if (0 == ssi->usrcnt) {
dev_err(dev, "%s called without starting\n", __func__);
return;
}
ssi->usrcnt--;
@ -253,13 +258,17 @@ static void rsnd_ssi_hw_stop(struct rsnd_ssi *ssi)
rsnd_ssi_status_check(&ssi->mod, IIRQ);
if (rsnd_rdai_is_clk_master(rdai)) {
if (rsnd_ssi_clk_from_parent(ssi))
rsnd_ssi_hw_stop(ssi->parent);
struct rsnd_ssi *ssi_parent = rsnd_ssi_parent(ssi);
if (ssi_parent)
rsnd_ssi_hw_stop(ssi_parent);
else
rsnd_ssi_master_clk_stop(ssi);
}
rsnd_mod_hw_stop(&ssi->mod);
ssi->chan = 0;
}
dev_dbg(dev, "%s[%d] hw stopped\n",
@ -336,6 +345,35 @@ static int rsnd_ssi_quit(struct rsnd_mod *mod,
return 0;
}
static int rsnd_ssi_hw_params(struct rsnd_mod *mod,
struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
struct rsnd_ssi *ssi_parent = rsnd_ssi_parent(ssi);
int chan = params_channels(params);
/*
* Already working.
* It will happen if SSI has parent/child connection.
*/
if (ssi->usrcnt) {
/*
* it is error if child <-> parent SSI uses
* different channels.
*/
if (ssi->chan != chan)
return -EIO;
}
/* It will be removed on rsnd_ssi_hw_stop */
ssi->chan = chan;
if (ssi_parent)
return rsnd_ssi_hw_params(&ssi_parent->mod, substream, params);
return 0;
}
static void rsnd_ssi_record_error(struct rsnd_ssi *ssi, u32 status)
{
/* under/over flow error */
@ -385,10 +423,15 @@ static irqreturn_t rsnd_ssi_interrupt(int irq, void *data)
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
int is_dma = rsnd_ssi_is_dma_mode(mod);
u32 status = rsnd_mod_read(mod, SSISR);
u32 status;
if (!io)
return IRQ_NONE;
spin_lock(&priv->lock);
/* ignore all cases if not working */
if (!rsnd_mod_is_working(mod))
goto rsnd_ssi_interrupt_out;
status = rsnd_mod_read(mod, SSISR);
/* PIO only */
if (!is_dma && (status & DIRQ)) {
@ -428,6 +471,9 @@ static irqreturn_t rsnd_ssi_interrupt(int irq, void *data)
rsnd_ssi_record_error(ssi, status);
rsnd_ssi_interrupt_out:
spin_unlock(&priv->lock);
return IRQ_HANDLED;
}
@ -456,6 +502,7 @@ static struct rsnd_mod_ops rsnd_ssi_pio_ops = {
.quit = rsnd_ssi_quit,
.start = rsnd_ssi_start,
.stop = rsnd_ssi_stop,
.hw_params = rsnd_ssi_hw_params,
};
static int rsnd_ssi_dma_probe(struct rsnd_mod *mod,
@ -565,6 +612,7 @@ static struct rsnd_mod_ops rsnd_ssi_dma_ops = {
.start = rsnd_ssi_dma_start,
.stop = rsnd_ssi_dma_stop,
.fallback = rsnd_ssi_fallback,
.hw_params = rsnd_ssi_hw_params,
};
int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod)
@ -598,7 +646,7 @@ int rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod)
return !!(rsnd_ssi_mode_flags(ssi) & RSND_SSI_CLK_PIN_SHARE);
}
static void rsnd_ssi_parent_clk_setup(struct rsnd_priv *priv, struct rsnd_ssi *ssi)
static void rsnd_ssi_parent_setup(struct rsnd_priv *priv, struct rsnd_ssi *ssi)
{
if (!rsnd_ssi_is_pin_sharing(&ssi->mod))
return;
@ -732,7 +780,7 @@ int rsnd_ssi_probe(struct platform_device *pdev,
if (ret)
return ret;
rsnd_ssi_parent_clk_setup(priv, ssi);
rsnd_ssi_parent_setup(priv, ssi);
}
return 0;