mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2025-01-10 15:10:38 +00:00
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:
commit
a178831a63
@ -4,12 +4,21 @@ This node models the Qualcomm Technologies Low-Power Audio SubSystem (LPASS).
|
|||||||
|
|
||||||
Required properties:
|
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.
|
- clocks : Must contain an entry for each entry in clock-names.
|
||||||
- clock-names : A list which must include the following entries:
|
- clock-names : A list which must include the following entries:
|
||||||
* "ahbix-clk"
|
* "ahbix-clk"
|
||||||
* "mi2s-osr-clk"
|
* "mi2s-osr-clk"
|
||||||
* "mi2s-bit-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
|
- interrupts : Must contain an entry for each entry in
|
||||||
interrupt-names.
|
interrupt-names.
|
||||||
- interrupt-names : A list which must include the following entries:
|
- 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:
|
- reg-names : A list which must include the following entries:
|
||||||
* "lpass-lpaif"
|
* "lpass-lpaif"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Optional properties:
|
Optional properties:
|
||||||
|
|
||||||
- qcom,adsp : Phandle for the audio DSP node
|
- qcom,adsp : Phandle for the audio DSP node
|
||||||
|
@ -48,7 +48,7 @@ DAI subnode properties:
|
|||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
rcar_sound: rcar_sound@ec500000 {
|
rcar_sound: sound@ec500000 {
|
||||||
#sound-dai-cells = <1>;
|
#sound-dai-cells = <1>;
|
||||||
compatible = "renesas,rcar_sound-r8a7791", "renesas,rcar_sound-gen2";
|
compatible = "renesas,rcar_sound-r8a7791", "renesas,rcar_sound-gen2";
|
||||||
reg = <0 0xec500000 0 0x1000>, /* SCU */
|
reg = <0 0xec500000 0 0x1000>, /* SCU */
|
||||||
|
@ -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)
|
static int rcar_dmac_desc_alloc(struct rcar_dmac_chan *chan, gfp_t gfp)
|
||||||
{
|
{
|
||||||
struct rcar_dmac_desc_page *page;
|
struct rcar_dmac_desc_page *page;
|
||||||
|
unsigned long flags;
|
||||||
LIST_HEAD(list);
|
LIST_HEAD(list);
|
||||||
unsigned int i;
|
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);
|
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_splice_tail(&list, &chan->desc.free);
|
||||||
list_add_tail(&page->node, &chan->desc.pages);
|
list_add_tail(&page->node, &chan->desc.pages);
|
||||||
spin_unlock_irq(&chan->lock);
|
spin_unlock_irqrestore(&chan->lock, flags);
|
||||||
|
|
||||||
return 0;
|
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)
|
static void rcar_dmac_desc_recycle_acked(struct rcar_dmac_chan *chan)
|
||||||
{
|
{
|
||||||
struct rcar_dmac_desc *desc, *_desc;
|
struct rcar_dmac_desc *desc, *_desc;
|
||||||
|
unsigned long flags;
|
||||||
LIST_HEAD(list);
|
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
|
* list_for_each_entry_safe, isn't safe if we release the channel lock
|
||||||
* around the rcar_dmac_desc_put() call.
|
* 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);
|
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) {
|
list_for_each_entry_safe(desc, _desc, &list, node) {
|
||||||
if (async_tx_test_ack(&desc->async_tx)) {
|
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;
|
return;
|
||||||
|
|
||||||
/* Put the remaining descriptors back in the wait list. */
|
/* 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);
|
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)
|
static struct rcar_dmac_desc *rcar_dmac_desc_get(struct rcar_dmac_chan *chan)
|
||||||
{
|
{
|
||||||
struct rcar_dmac_desc *desc;
|
struct rcar_dmac_desc *desc;
|
||||||
|
unsigned long flags;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
/* Recycle acked descriptors before attempting allocation. */
|
/* Recycle acked descriptors before attempting allocation. */
|
||||||
rcar_dmac_desc_recycle_acked(chan);
|
rcar_dmac_desc_recycle_acked(chan);
|
||||||
|
|
||||||
spin_lock_irq(&chan->lock);
|
spin_lock_irqsave(&chan->lock, flags);
|
||||||
|
|
||||||
while (list_empty(&chan->desc.free)) {
|
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
|
* allocated descriptors. If the allocation fails return an
|
||||||
* error.
|
* error.
|
||||||
*/
|
*/
|
||||||
spin_unlock_irq(&chan->lock);
|
spin_unlock_irqrestore(&chan->lock, flags);
|
||||||
ret = rcar_dmac_desc_alloc(chan, GFP_NOWAIT);
|
ret = rcar_dmac_desc_alloc(chan, GFP_NOWAIT);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return NULL;
|
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);
|
desc = list_first_entry(&chan->desc.free, struct rcar_dmac_desc, node);
|
||||||
list_del(&desc->node);
|
list_del(&desc->node);
|
||||||
|
|
||||||
spin_unlock_irq(&chan->lock);
|
spin_unlock_irqrestore(&chan->lock, flags);
|
||||||
|
|
||||||
return desc;
|
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)
|
static int rcar_dmac_xfer_chunk_alloc(struct rcar_dmac_chan *chan, gfp_t gfp)
|
||||||
{
|
{
|
||||||
struct rcar_dmac_desc_page *page;
|
struct rcar_dmac_desc_page *page;
|
||||||
|
unsigned long flags;
|
||||||
LIST_HEAD(list);
|
LIST_HEAD(list);
|
||||||
unsigned int i;
|
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);
|
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_splice_tail(&list, &chan->desc.chunks_free);
|
||||||
list_add_tail(&page->node, &chan->desc.pages);
|
list_add_tail(&page->node, &chan->desc.pages);
|
||||||
spin_unlock_irq(&chan->lock);
|
spin_unlock_irqrestore(&chan->lock, flags);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -627,9 +631,10 @@ static struct rcar_dmac_xfer_chunk *
|
|||||||
rcar_dmac_xfer_chunk_get(struct rcar_dmac_chan *chan)
|
rcar_dmac_xfer_chunk_get(struct rcar_dmac_chan *chan)
|
||||||
{
|
{
|
||||||
struct rcar_dmac_xfer_chunk *chunk;
|
struct rcar_dmac_xfer_chunk *chunk;
|
||||||
|
unsigned long flags;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
spin_lock_irq(&chan->lock);
|
spin_lock_irqsave(&chan->lock, flags);
|
||||||
|
|
||||||
while (list_empty(&chan->desc.chunks_free)) {
|
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
|
* allocated descriptors. If the allocation fails return an
|
||||||
* error.
|
* error.
|
||||||
*/
|
*/
|
||||||
spin_unlock_irq(&chan->lock);
|
spin_unlock_irqrestore(&chan->lock, flags);
|
||||||
ret = rcar_dmac_xfer_chunk_alloc(chan, GFP_NOWAIT);
|
ret = rcar_dmac_xfer_chunk_alloc(chan, GFP_NOWAIT);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return NULL;
|
return NULL;
|
||||||
spin_lock_irq(&chan->lock);
|
spin_lock_irqsave(&chan->lock, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
chunk = list_first_entry(&chan->desc.chunks_free,
|
chunk = list_first_entry(&chan->desc.chunks_free,
|
||||||
struct rcar_dmac_xfer_chunk, node);
|
struct rcar_dmac_xfer_chunk, node);
|
||||||
list_del(&chunk->node);
|
list_del(&chunk->node);
|
||||||
|
|
||||||
spin_unlock_irq(&chan->lock);
|
spin_unlock_irqrestore(&chan->lock, flags);
|
||||||
|
|
||||||
return chunk;
|
return chunk;
|
||||||
}
|
}
|
||||||
|
9
include/dt-bindings/sound/apq8016-lpass.h
Normal file
9
include/dt-bindings/sound/apq8016-lpass.h
Normal 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 */
|
@ -2301,8 +2301,8 @@ static int max98095_probe(struct snd_soc_codec *codec)
|
|||||||
/* register an audio interrupt */
|
/* register an audio interrupt */
|
||||||
ret = request_threaded_irq(client->irq, NULL,
|
ret = request_threaded_irq(client->irq, NULL,
|
||||||
max98095_report_jack,
|
max98095_report_jack,
|
||||||
IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
|
IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING |
|
||||||
"max98095", codec);
|
IRQF_ONESHOT, "max98095", codec);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(codec->dev, "Failed to request IRQ: %d\n", ret);
|
dev_err(codec->dev, "Failed to request IRQ: %d\n", ret);
|
||||||
goto err_access;
|
goto err_access;
|
||||||
|
@ -100,12 +100,13 @@ config SND_OMAP_SOC_OMAP_TWL4030
|
|||||||
|
|
||||||
config SND_OMAP_SOC_OMAP_ABE_TWL6040
|
config SND_OMAP_SOC_OMAP_ABE_TWL6040
|
||||||
tristate "SoC Audio support for OMAP boards using ABE and twl6040 codec"
|
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_DMIC
|
||||||
select SND_OMAP_SOC_MCPDM
|
select SND_OMAP_SOC_MCPDM
|
||||||
select SND_SOC_TWL6040
|
select SND_SOC_TWL6040
|
||||||
select SND_SOC_DMIC
|
select SND_SOC_DMIC
|
||||||
select COMMON_CLK_PALMAS if MFD_PALMAS
|
select COMMON_CLK_PALMAS if (SOC_OMAP5 && MFD_PALMAS)
|
||||||
help
|
help
|
||||||
Say Y if you want to add support for SoC audio on OMAP boards using
|
Say Y if you want to add support for SoC audio on OMAP boards using
|
||||||
ABE and twl6040 codec. This driver currently supports:
|
ABE and twl6040 codec. This driver currently supports:
|
||||||
|
@ -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)
|
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_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_tw4030_pdata *pdata = dev_get_platdata(card->dev);
|
||||||
struct omap_twl4030 *priv = snd_soc_card_get_drvdata(card);
|
struct omap_twl4030 *priv = snd_soc_card_get_drvdata(card);
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
@ -45,29 +45,6 @@ static const struct snd_soc_dapm_route brownstone_audio_map[] = {
|
|||||||
{"MICBIAS1", NULL, "Main Mic"},
|
{"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,
|
static int brownstone_wm8994_hw_params(struct snd_pcm_substream *substream,
|
||||||
struct snd_pcm_hw_params *params)
|
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 |
|
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
|
||||||
SND_SOC_DAIFMT_CBS_CFS,
|
SND_SOC_DAIFMT_CBS_CFS,
|
||||||
.ops = &brownstone_ops,
|
.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),
|
.num_dapm_widgets = ARRAY_SIZE(brownstone_dapm_widgets),
|
||||||
.dapm_routes = brownstone_audio_map,
|
.dapm_routes = brownstone_audio_map,
|
||||||
.num_dapm_routes = ARRAY_SIZE(brownstone_audio_map),
|
.num_dapm_routes = ARRAY_SIZE(brownstone_audio_map),
|
||||||
|
.fully_routed = true,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int brownstone_probe(struct platform_device *pdev)
|
static int brownstone_probe(struct platform_device *pdev)
|
||||||
|
@ -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[] = {
|
static const struct snd_soc_dapm_widget wm8731_dapm_widgets[] = {
|
||||||
SND_SOC_DAPM_HP("Headphone Jack", NULL),
|
SND_SOC_DAPM_HP("Headphone Jack", NULL),
|
||||||
SND_SOC_DAPM_SPK("Ext Spk", poodle_amp_event),
|
SND_SOC_DAPM_SPK("Ext Spk", poodle_amp_event),
|
||||||
|
SND_SOC_DAPM_MIC("Microphone", NULL),
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Corgi machine connections to the codec pins */
|
/* 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 */
|
/* speaker connected to LOUT, ROUT */
|
||||||
{"Ext Spk", NULL, "ROUT"},
|
{"Ext Spk", NULL, "ROUT"},
|
||||||
{"Ext Spk", NULL, "LOUT"},
|
{"Ext Spk", NULL, "LOUT"},
|
||||||
|
|
||||||
|
{"MICIN", NULL, "Microphone"},
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char *jack_function[] = {"Off", "Headphone"};
|
static const char *jack_function[] = {"Off", "Headphone"};
|
||||||
@ -220,20 +223,6 @@ static const struct snd_kcontrol_new wm8731_poodle_controls[] = {
|
|||||||
poodle_set_spk),
|
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 */
|
/* poodle digital audio interface glue - connects codec <--> CPU */
|
||||||
static struct snd_soc_dai_link poodle_dai = {
|
static struct snd_soc_dai_link poodle_dai = {
|
||||||
.name = "WM8731",
|
.name = "WM8731",
|
||||||
@ -242,7 +231,6 @@ static struct snd_soc_dai_link poodle_dai = {
|
|||||||
.codec_dai_name = "wm8731-hifi",
|
.codec_dai_name = "wm8731-hifi",
|
||||||
.platform_name = "pxa-pcm-audio",
|
.platform_name = "pxa-pcm-audio",
|
||||||
.codec_name = "wm8731.0-001b",
|
.codec_name = "wm8731.0-001b",
|
||||||
.init = poodle_wm8731_init,
|
|
||||||
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
|
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
|
||||||
SND_SOC_DAIFMT_CBS_CFS,
|
SND_SOC_DAIFMT_CBS_CFS,
|
||||||
.ops = &poodle_ops,
|
.ops = &poodle_ops,
|
||||||
@ -261,6 +249,7 @@ static struct snd_soc_card poodle = {
|
|||||||
.num_dapm_widgets = ARRAY_SIZE(wm8731_dapm_widgets),
|
.num_dapm_widgets = ARRAY_SIZE(wm8731_dapm_widgets),
|
||||||
.dapm_routes = poodle_audio_map,
|
.dapm_routes = poodle_audio_map,
|
||||||
.num_dapm_routes = ARRAY_SIZE(poodle_audio_map),
|
.num_dapm_routes = ARRAY_SIZE(poodle_audio_map),
|
||||||
|
.fully_routed = true,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int poodle_probe(struct platform_device *pdev)
|
static int poodle_probe(struct platform_device *pdev)
|
||||||
|
@ -185,17 +185,6 @@ static const struct snd_kcontrol_new tosa_controls[] = {
|
|||||||
tosa_set_spk),
|
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[] = {
|
static struct snd_soc_dai_link tosa_dai[] = {
|
||||||
{
|
{
|
||||||
.name = "AC97",
|
.name = "AC97",
|
||||||
@ -204,7 +193,6 @@ static struct snd_soc_dai_link tosa_dai[] = {
|
|||||||
.codec_dai_name = "wm9712-hifi",
|
.codec_dai_name = "wm9712-hifi",
|
||||||
.platform_name = "pxa-pcm-audio",
|
.platform_name = "pxa-pcm-audio",
|
||||||
.codec_name = "wm9712-codec",
|
.codec_name = "wm9712-codec",
|
||||||
.init = tosa_ac97_init,
|
|
||||||
.ops = &tosa_ops,
|
.ops = &tosa_ops,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -230,6 +218,7 @@ static struct snd_soc_card tosa = {
|
|||||||
.num_dapm_widgets = ARRAY_SIZE(tosa_dapm_widgets),
|
.num_dapm_widgets = ARRAY_SIZE(tosa_dapm_widgets),
|
||||||
.dapm_routes = audio_map,
|
.dapm_routes = audio_map,
|
||||||
.num_dapm_routes = ARRAY_SIZE(audio_map),
|
.num_dapm_routes = ARRAY_SIZE(audio_map),
|
||||||
|
.fully_routed = true,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int tosa_probe(struct platform_device *pdev)
|
static int tosa_probe(struct platform_device *pdev)
|
||||||
|
@ -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)
|
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;
|
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 */
|
/* Jack detection API stuff */
|
||||||
ret = snd_soc_card_jack_new(rtd->card, "Headset Jack", SND_JACK_HEADSET,
|
ret = snd_soc_card_jack_new(rtd->card, "Headset Jack", SND_JACK_HEADSET,
|
||||||
&hs_jack, hs_jack_pins,
|
&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),
|
.num_dapm_widgets = ARRAY_SIZE(wm8750_dapm_widgets),
|
||||||
.dapm_routes = z2_audio_map,
|
.dapm_routes = z2_audio_map,
|
||||||
.num_dapm_routes = ARRAY_SIZE(z2_audio_map),
|
.num_dapm_routes = ARRAY_SIZE(z2_audio_map),
|
||||||
|
.fully_routed = true,
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct platform_device *z2_snd_device;
|
static struct platform_device *z2_snd_device;
|
||||||
|
@ -6,19 +6,28 @@ config SND_SOC_QCOM
|
|||||||
|
|
||||||
config SND_SOC_LPASS_CPU
|
config SND_SOC_LPASS_CPU
|
||||||
tristate
|
tristate
|
||||||
depends on SND_SOC_QCOM
|
|
||||||
select REGMAP_MMIO
|
select REGMAP_MMIO
|
||||||
|
|
||||||
config SND_SOC_LPASS_PLATFORM
|
config SND_SOC_LPASS_PLATFORM
|
||||||
tristate
|
tristate
|
||||||
depends on SND_SOC_QCOM
|
|
||||||
select REGMAP_MMIO
|
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
|
config SND_SOC_STORM
|
||||||
tristate "ASoC I2S support for Storm boards"
|
tristate "ASoC I2S support for Storm boards"
|
||||||
depends on (ARCH_QCOM && SND_SOC_QCOM) || COMPILE_TEST
|
depends on SND_SOC_QCOM && (ARCH_QCOM || COMPILE_TEST)
|
||||||
select SND_SOC_LPASS_CPU
|
select SND_SOC_LPASS_IPQ806X
|
||||||
select SND_SOC_LPASS_PLATFORM
|
|
||||||
select SND_SOC_MAX98357A
|
select SND_SOC_MAX98357A
|
||||||
help
|
help
|
||||||
Say Y or M if you want add support for SoC audio on the
|
Say Y or M if you want add support for SoC audio on the
|
||||||
|
@ -1,9 +1,13 @@
|
|||||||
# Platform
|
# Platform
|
||||||
snd-soc-lpass-cpu-objs := lpass-cpu.o
|
snd-soc-lpass-cpu-objs := lpass-cpu.o
|
||||||
snd-soc-lpass-platform-objs := lpass-platform.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_CPU) += snd-soc-lpass-cpu.o
|
||||||
obj-$(CONFIG_SND_SOC_LPASS_PLATFORM) += snd-soc-lpass-platform.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
|
# Machine
|
||||||
snd-soc-storm-objs := storm.o
|
snd-soc-storm-objs := storm.o
|
||||||
|
242
sound/soc/qcom/lpass-apq8016.c
Normal file
242
sound/soc/qcom/lpass-apq8016.c
Normal 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");
|
||||||
|
|
@ -14,21 +14,17 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/clk.h>
|
#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/kernel.h>
|
||||||
#include <linux/mod_devicetable.h>
|
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
|
#include <linux/of_device.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <sound/pcm.h>
|
#include <sound/pcm.h>
|
||||||
#include <sound/pcm_params.h>
|
#include <sound/pcm_params.h>
|
||||||
#include <linux/regmap.h>
|
#include <linux/regmap.h>
|
||||||
#include <sound/soc.h>
|
#include <sound/soc.h>
|
||||||
#include <sound/soc-dai.h>
|
#include <sound/soc-dai.h>
|
||||||
#include "lpass-lpaif-ipq806x.h"
|
#include "lpass-lpaif-reg.h"
|
||||||
#include "lpass.h"
|
#include "lpass.h"
|
||||||
|
|
||||||
static int lpass_cpu_daiops_set_sysclk(struct snd_soc_dai *dai, int clk_id,
|
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);
|
struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
|
||||||
int ret;
|
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)
|
if (ret)
|
||||||
dev_err(dai->dev, "%s() error setting mi2s osrclk to %u: %d\n",
|
dev_err(dai->dev, "%s() error setting mi2s osrclk to %u: %d\n",
|
||||||
__func__, freq, ret);
|
__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);
|
struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = clk_prepare_enable(drvdata->mi2s_osr_clk);
|
if (!IS_ERR(drvdata->mi2s_osr_clk[dai->driver->id])) {
|
||||||
if (ret) {
|
ret = clk_prepare_enable(
|
||||||
dev_err(dai->dev, "%s() error in enabling mi2s osr clk: %d\n",
|
drvdata->mi2s_osr_clk[dai->driver->id]);
|
||||||
__func__, ret);
|
if (ret) {
|
||||||
return 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) {
|
if (ret) {
|
||||||
dev_err(dai->dev, "%s() error in enabling mi2s bit clk: %d\n",
|
dev_err(dai->dev, "%s() error in enabling mi2s bit clk: %d\n",
|
||||||
__func__, ret);
|
__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;
|
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);
|
struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
|
||||||
|
|
||||||
clk_disable_unprepare(drvdata->mi2s_bit_clk);
|
clk_disable_unprepare(drvdata->mi2s_bit_clk[dai->driver->id]);
|
||||||
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]);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int lpass_cpu_daiops_hw_params(struct snd_pcm_substream *substream,
|
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,
|
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) {
|
if (ret) {
|
||||||
dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n",
|
dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n",
|
||||||
__func__, ret);
|
__func__, ret);
|
||||||
return 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) {
|
if (ret) {
|
||||||
dev_err(dai->dev, "%s() error setting mi2s bitclk to %u: %d\n",
|
dev_err(dai->dev, "%s() error setting mi2s bitclk to %u: %d\n",
|
||||||
__func__, rate * bitwidth * 2, ret);
|
__func__, rate * bitwidth * 2, ret);
|
||||||
@ -166,7 +174,8 @@ static int lpass_cpu_daiops_hw_free(struct snd_pcm_substream *substream,
|
|||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = regmap_write(drvdata->lpaif_map,
|
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)
|
if (ret)
|
||||||
dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n",
|
dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n",
|
||||||
__func__, ret);
|
__func__, ret);
|
||||||
@ -181,7 +190,7 @@ static int lpass_cpu_daiops_prepare(struct snd_pcm_substream *substream,
|
|||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = regmap_update_bits(drvdata->lpaif_map,
|
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);
|
LPAIF_I2SCTL_SPKEN_MASK, LPAIF_I2SCTL_SPKEN_ENABLE);
|
||||||
if (ret)
|
if (ret)
|
||||||
dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n",
|
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_RESUME:
|
||||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||||
ret = regmap_update_bits(drvdata->lpaif_map,
|
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_MASK,
|
||||||
LPAIF_I2SCTL_SPKEN_ENABLE);
|
LPAIF_I2SCTL_SPKEN_ENABLE);
|
||||||
if (ret)
|
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_SUSPEND:
|
||||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||||
ret = regmap_update_bits(drvdata->lpaif_map,
|
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_MASK,
|
||||||
LPAIF_I2SCTL_SPKEN_DISABLE);
|
LPAIF_I2SCTL_SPKEN_DISABLE);
|
||||||
if (ret)
|
if (ret)
|
||||||
@ -224,7 +235,7 @@ static int lpass_cpu_daiops_trigger(struct snd_pcm_substream *substream,
|
|||||||
return ret;
|
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,
|
.set_sysclk = lpass_cpu_daiops_set_sysclk,
|
||||||
.startup = lpass_cpu_daiops_startup,
|
.startup = lpass_cpu_daiops_startup,
|
||||||
.shutdown = lpass_cpu_daiops_shutdown,
|
.shutdown = lpass_cpu_daiops_shutdown,
|
||||||
@ -233,41 +244,23 @@ static struct snd_soc_dai_ops lpass_cpu_dai_ops = {
|
|||||||
.prepare = lpass_cpu_daiops_prepare,
|
.prepare = lpass_cpu_daiops_prepare,
|
||||||
.trigger = lpass_cpu_daiops_trigger,
|
.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);
|
struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
/* ensure audio hardware is disabled */
|
/* ensure audio hardware is disabled */
|
||||||
ret = regmap_write(drvdata->lpaif_map,
|
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)
|
if (ret)
|
||||||
dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n",
|
dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n",
|
||||||
__func__, ret);
|
__func__, ret);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_dai_probe);
|
||||||
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,
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct snd_soc_component_driver lpass_cpu_comp_driver = {
|
static const struct snd_soc_component_driver lpass_cpu_comp_driver = {
|
||||||
.name = "lpass-cpu",
|
.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)
|
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;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < LPAIF_I2S_PORT_NUM; ++i)
|
for (i = 0; i < v->i2s_ports; ++i)
|
||||||
if (reg == LPAIF_I2SCTL_REG(i))
|
if (reg == LPAIF_I2SCTL_REG(v, i))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
for (i = 0; i < LPAIF_IRQ_PORT_NUM; ++i) {
|
for (i = 0; i < v->irq_ports; ++i) {
|
||||||
if (reg == LPAIF_IRQEN_REG(i))
|
if (reg == LPAIF_IRQEN_REG(v, i))
|
||||||
return true;
|
return true;
|
||||||
if (reg == LPAIF_IRQCLEAR_REG(i))
|
if (reg == LPAIF_IRQCLEAR_REG(v, i))
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < LPAIF_RDMA_CHAN_NUM; ++i) {
|
for (i = 0; i < v->rdma_channels; ++i) {
|
||||||
if (reg == LPAIF_RDMACTL_REG(i))
|
if (reg == LPAIF_RDMACTL_REG(v, i))
|
||||||
return true;
|
return true;
|
||||||
if (reg == LPAIF_RDMABASE_REG(i))
|
if (reg == LPAIF_RDMABASE_REG(v, i))
|
||||||
return true;
|
return true;
|
||||||
if (reg == LPAIF_RDMABUFF_REG(i))
|
if (reg == LPAIF_RDMABUFF_REG(v, i))
|
||||||
return true;
|
return true;
|
||||||
if (reg == LPAIF_RDMAPER_REG(i))
|
if (reg == LPAIF_RDMAPER_REG(v, i))
|
||||||
return true;
|
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)
|
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;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < LPAIF_I2S_PORT_NUM; ++i)
|
for (i = 0; i < v->i2s_ports; ++i)
|
||||||
if (reg == LPAIF_I2SCTL_REG(i))
|
if (reg == LPAIF_I2SCTL_REG(v, i))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
for (i = 0; i < LPAIF_IRQ_PORT_NUM; ++i) {
|
for (i = 0; i < v->irq_ports; ++i) {
|
||||||
if (reg == LPAIF_IRQEN_REG(i))
|
if (reg == LPAIF_IRQEN_REG(v, i))
|
||||||
return true;
|
return true;
|
||||||
if (reg == LPAIF_IRQSTAT_REG(i))
|
if (reg == LPAIF_IRQSTAT_REG(v, i))
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < LPAIF_RDMA_CHAN_NUM; ++i) {
|
for (i = 0; i < v->rdma_channels; ++i) {
|
||||||
if (reg == LPAIF_RDMACTL_REG(i))
|
if (reg == LPAIF_RDMACTL_REG(v, i))
|
||||||
return true;
|
return true;
|
||||||
if (reg == LPAIF_RDMABASE_REG(i))
|
if (reg == LPAIF_RDMABASE_REG(v, i))
|
||||||
return true;
|
return true;
|
||||||
if (reg == LPAIF_RDMABUFF_REG(i))
|
if (reg == LPAIF_RDMABUFF_REG(v, i))
|
||||||
return true;
|
return true;
|
||||||
if (reg == LPAIF_RDMACURR_REG(i))
|
if (reg == LPAIF_RDMACURR_REG(v, i))
|
||||||
return true;
|
return true;
|
||||||
if (reg == LPAIF_RDMAPER_REG(i))
|
if (reg == LPAIF_RDMAPER_REG(v, i))
|
||||||
return true;
|
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)
|
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;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < LPAIF_IRQ_PORT_NUM; ++i)
|
for (i = 0; i < v->irq_ports; ++i)
|
||||||
if (reg == LPAIF_IRQSTAT_REG(i))
|
if (reg == LPAIF_IRQSTAT_REG(v, i))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
for (i = 0; i < LPAIF_RDMA_CHAN_NUM; ++i)
|
for (i = 0; i < v->rdma_channels; ++i)
|
||||||
if (reg == LPAIF_RDMACURR_REG(i))
|
if (reg == LPAIF_RDMACURR_REG(v, i))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct regmap_config lpass_cpu_regmap_config = {
|
static struct regmap_config lpass_cpu_regmap_config = {
|
||||||
.reg_bits = 32,
|
.reg_bits = 32,
|
||||||
.reg_stride = 4,
|
.reg_stride = 4,
|
||||||
.val_bits = 32,
|
.val_bits = 32,
|
||||||
.max_register = LPAIF_RDMAPER_REG(LPAIF_RDMA_CHAN_MAX),
|
|
||||||
.writeable_reg = lpass_cpu_regmap_writeable,
|
.writeable_reg = lpass_cpu_regmap_writeable,
|
||||||
.readable_reg = lpass_cpu_regmap_readable,
|
.readable_reg = lpass_cpu_regmap_readable,
|
||||||
.volatile_reg = lpass_cpu_regmap_volatile,
|
.volatile_reg = lpass_cpu_regmap_volatile,
|
||||||
.cache_type = REGCACHE_FLAT,
|
.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 lpass_data *drvdata;
|
||||||
struct device_node *dsp_of_node;
|
struct device_node *dsp_of_node;
|
||||||
struct resource *res;
|
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);
|
dsp_of_node = of_parse_phandle(pdev->dev.of_node, "qcom,adsp", 0);
|
||||||
if (dsp_of_node) {
|
if (dsp_of_node) {
|
||||||
@ -379,11 +381,14 @@ static int lpass_cpu_platform_probe(struct platform_device *pdev)
|
|||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
platform_set_drvdata(pdev, drvdata);
|
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");
|
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);
|
drvdata->lpaif = devm_ioremap_resource(&pdev->dev, res);
|
||||||
if (IS_ERR((void const __force *)drvdata->lpaif)) {
|
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);
|
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,
|
drvdata->lpaif_map = devm_regmap_init_mmio(&pdev->dev, drvdata->lpaif,
|
||||||
&lpass_cpu_regmap_config);
|
&lpass_cpu_regmap_config);
|
||||||
if (IS_ERR(drvdata->lpaif_map)) {
|
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);
|
return PTR_ERR(drvdata->lpaif_map);
|
||||||
}
|
}
|
||||||
|
|
||||||
drvdata->mi2s_osr_clk = devm_clk_get(&pdev->dev, "mi2s-osr-clk");
|
if (variant->init)
|
||||||
if (IS_ERR(drvdata->mi2s_osr_clk)) {
|
variant->init(pdev);
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
drvdata->mi2s_bit_clk = devm_clk_get(&pdev->dev, "mi2s-bit-clk");
|
for (i = 0; i < variant->num_dai; i++) {
|
||||||
if (IS_ERR(drvdata->mi2s_bit_clk)) {
|
dai_id = variant->dai_driver[i].id;
|
||||||
dev_err(&pdev->dev, "%s() error getting mi2s-bit-clk: %ld\n",
|
if (variant->num_dai > 1)
|
||||||
__func__, PTR_ERR(drvdata->mi2s_bit_clk));
|
sprintf(clk_name, "mi2s-osr-clk%d", i);
|
||||||
return PTR_ERR(drvdata->mi2s_bit_clk);
|
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");
|
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,
|
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) {
|
if (ret) {
|
||||||
dev_err(&pdev->dev, "%s() error registering cpu driver: %d\n",
|
dev_err(&pdev->dev, "%s() error registering cpu driver: %d\n",
|
||||||
__func__, ret);
|
__func__, ret);
|
||||||
@ -459,33 +489,17 @@ err_clk:
|
|||||||
clk_disable_unprepare(drvdata->ahbix_clk);
|
clk_disable_unprepare(drvdata->ahbix_clk);
|
||||||
return ret;
|
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);
|
struct lpass_data *drvdata = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
|
if (drvdata->variant->exit)
|
||||||
|
drvdata->variant->exit(pdev);
|
||||||
|
|
||||||
clk_disable_unprepare(drvdata->ahbix_clk);
|
clk_disable_unprepare(drvdata->ahbix_clk);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_platform_remove);
|
||||||
#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");
|
|
||||||
|
109
sound/soc/qcom/lpass-ipq806x.c
Normal file
109
sound/soc/qcom/lpass-ipq806x.c
Normal 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");
|
@ -9,37 +9,17 @@
|
|||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
* GNU General Public License for more details.
|
* GNU General Public License for more details.
|
||||||
*
|
|
||||||
* lpass-lpaif-ipq806x.h -- Definitions for the QTi LPAIF in the ipq806x LPASS
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef __LPASS_LPAIF_H__
|
#ifndef __LPASS_LPAIF_REG_H__
|
||||||
#define __LPASS_LPAIF_H__
|
#define __LPASS_LPAIF_REG_H__
|
||||||
|
|
||||||
#define LPAIF_BANK_OFFSET 0x1000
|
|
||||||
|
|
||||||
/* LPAIF I2S */
|
/* LPAIF I2S */
|
||||||
|
|
||||||
#define LPAIF_I2SCTL_REG_BASE 0x0010
|
#define LPAIF_I2SCTL_REG_ADDR(v, addr, port) \
|
||||||
#define LPAIF_I2SCTL_REG_STRIDE 0x4
|
(v->i2sctrl_reg_base + (addr) + v->i2sctrl_reg_stride * (port))
|
||||||
#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(v, port) LPAIF_I2SCTL_REG_ADDR(v, 0x0, (port))
|
||||||
#define LPAIF_I2SCTL_LOOPBACK_MASK 0x8000
|
#define LPAIF_I2SCTL_LOOPBACK_MASK 0x8000
|
||||||
#define LPAIF_I2SCTL_LOOPBACK_SHIFT 15
|
#define LPAIF_I2SCTL_LOOPBACK_SHIFT 15
|
||||||
#define LPAIF_I2SCTL_LOOPBACK_DISABLE (0 << LPAIF_I2SCTL_LOOPBACK_SHIFT)
|
#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)
|
#define LPAIF_I2SCTL_BITWIDTH_32 (2 << LPAIF_I2SCTL_BITWIDTH_SHIFT)
|
||||||
|
|
||||||
/* LPAIF IRQ */
|
/* 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_PORT_HOST 0
|
||||||
#define LPAIF_IRQ_REG_STRIDE 0x1000
|
|
||||||
#define LPAIF_IRQ_REG_ADDR(addr, port) \
|
|
||||||
(LPAIF_IRQ_REG_BASE + (addr) + (LPAIF_IRQ_REG_STRIDE * (port)))
|
|
||||||
|
|
||||||
enum lpaif_irq_ports {
|
#define LPAIF_IRQEN_REG(v, port) LPAIF_IRQ_REG_ADDR(v, 0x0, (port))
|
||||||
LPAIF_IRQ_PORT_MIN = 0,
|
#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))
|
||||||
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_IRQ_BITSTRIDE 3
|
#define LPAIF_IRQ_BITSTRIDE 3
|
||||||
|
|
||||||
#define LPAIF_IRQ_PER(chan) (1 << (LPAIF_IRQ_BITSTRIDE * (chan)))
|
#define LPAIF_IRQ_PER(chan) (1 << (LPAIF_IRQ_BITSTRIDE * (chan)))
|
||||||
#define LPAIF_IRQ_XRUN(chan) (2 << (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_ERR(chan) (4 << (LPAIF_IRQ_BITSTRIDE * (chan)))
|
||||||
|
|
||||||
#define LPAIF_IRQ_ALL(chan) (7 << (LPAIF_IRQ_BITSTRIDE * (chan)))
|
#define LPAIF_IRQ_ALL(chan) (7 << (LPAIF_IRQ_BITSTRIDE * (chan)))
|
||||||
|
|
||||||
/* LPAIF DMA */
|
/* LPAIF DMA */
|
||||||
|
|
||||||
#define LPAIF_RDMA_REG_BASE 0x6000
|
#define LPAIF_RDMA_REG_ADDR(v, addr, chan) \
|
||||||
#define LPAIF_RDMA_REG_STRIDE 0x1000
|
(v->rdma_reg_base + (addr) + v->rdma_reg_stride * (chan))
|
||||||
#define LPAIF_RDMA_REG_ADDR(addr, chan) \
|
|
||||||
(LPAIF_RDMA_REG_BASE + (addr) + (LPAIF_RDMA_REG_STRIDE * (chan)))
|
|
||||||
|
|
||||||
enum lpaif_dma_channels {
|
#define LPAIF_RDMACTL_AUDINTF(id) (id << LPAIF_RDMACTL_AUDINTF_SHIFT)
|
||||||
LPAIF_RDMA_CHAN_MIN = 0,
|
|
||||||
|
|
||||||
LPAIF_RDMA_CHAN_MI2S = 0,
|
#define LPAIF_RDMACTL_REG(v, chan) LPAIF_RDMA_REG_ADDR(v, 0x00, (chan))
|
||||||
LPAIF_RDMA_CHAN_PCM0 = 1,
|
#define LPAIF_RDMABASE_REG(v, chan) LPAIF_RDMA_REG_ADDR(v, 0x04, (chan))
|
||||||
LPAIF_RDMA_CHAN_PCM1 = 2,
|
#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))
|
||||||
LPAIF_RDMA_CHAN_MAX = 4,
|
#define LPAIF_RDMAPER_REG(v, chan) LPAIF_RDMA_REG_ADDR(v, 0x10, (chan))
|
||||||
LPAIF_RDMA_CHAN_NUM = 5,
|
#define LPAIF_RDMAPERCNT_REG(v, chan) LPAIF_RDMA_REG_ADDR(v, 0x14, (chan))
|
||||||
};
|
|
||||||
|
|
||||||
#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_BURSTEN_MASK 0x800
|
#define LPAIF_RDMACTL_BURSTEN_MASK 0x800
|
||||||
#define LPAIF_RDMACTL_BURSTEN_SHIFT 11
|
#define LPAIF_RDMACTL_BURSTEN_SHIFT 11
|
||||||
@ -145,13 +106,6 @@ enum lpaif_dma_channels {
|
|||||||
|
|
||||||
#define LPAIF_RDMACTL_AUDINTF_MASK 0x0F0
|
#define LPAIF_RDMACTL_AUDINTF_MASK 0x0F0
|
||||||
#define LPAIF_RDMACTL_AUDINTF_SHIFT 4
|
#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_MASK 0x00E
|
||||||
#define LPAIF_RDMACTL_FIFOWM_SHIFT 1
|
#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_OFF (0 << LPAIF_RDMACTL_ENABLE_SHIFT)
|
||||||
#define LPAIF_RDMACTL_ENABLE_ON (1 << LPAIF_RDMACTL_ENABLE_SHIFT)
|
#define LPAIF_RDMACTL_ENABLE_ON (1 << LPAIF_RDMACTL_ENABLE_SHIFT)
|
||||||
|
|
||||||
#endif /* __LPASS_LPAIF_H__ */
|
#endif /* __LPASS_LPAIF_REG_H__ */
|
@ -13,23 +13,22 @@
|
|||||||
* lpass-platform.c -- ALSA SoC platform driver for QTi LPASS
|
* 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/dma-mapping.h>
|
||||||
#include <linux/err.h>
|
|
||||||
#include <linux/export.h>
|
#include <linux/export.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/io.h>
|
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <sound/memalloc.h>
|
|
||||||
#include <sound/pcm.h>
|
|
||||||
#include <sound/pcm_params.h>
|
#include <sound/pcm_params.h>
|
||||||
#include <linux/regmap.h>
|
#include <linux/regmap.h>
|
||||||
#include <sound/soc.h>
|
#include <sound/soc.h>
|
||||||
#include "lpass-lpaif-ipq806x.h"
|
#include "lpass-lpaif-reg.h"
|
||||||
#include "lpass.h"
|
#include "lpass.h"
|
||||||
|
|
||||||
|
struct lpass_pcm_data {
|
||||||
|
int rdma_ch;
|
||||||
|
int i2s_port;
|
||||||
|
};
|
||||||
|
|
||||||
#define LPASS_PLATFORM_BUFFER_SIZE (16 * 1024)
|
#define LPASS_PLATFORM_BUFFER_SIZE (16 * 1024)
|
||||||
#define LPASS_PLATFORM_PERIODS 2
|
#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_pcm_hw_params *params)
|
||||||
{
|
{
|
||||||
struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
|
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 =
|
struct lpass_data *drvdata =
|
||||||
snd_soc_platform_get_drvdata(soc_runtime->platform);
|
snd_soc_platform_get_drvdata(soc_runtime->platform);
|
||||||
|
struct lpass_variant *v = drvdata->variant;
|
||||||
snd_pcm_format_t format = params_format(params);
|
snd_pcm_format_t format = params_format(params);
|
||||||
unsigned int channels = params_channels(params);
|
unsigned int channels = params_channels(params);
|
||||||
unsigned int regval;
|
unsigned int regval;
|
||||||
int bitwidth;
|
int bitwidth;
|
||||||
int ret;
|
int ret, rdma_port = pcm_data->i2s_port + v->rdmactl_audif_start;
|
||||||
|
|
||||||
bitwidth = snd_pcm_format_width(format);
|
bitwidth = snd_pcm_format_width(format);
|
||||||
if (bitwidth < 0) {
|
if (bitwidth < 0) {
|
||||||
@ -100,7 +101,7 @@ static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream,
|
|||||||
}
|
}
|
||||||
|
|
||||||
regval = LPAIF_RDMACTL_BURSTEN_INCR4 |
|
regval = LPAIF_RDMACTL_BURSTEN_INCR4 |
|
||||||
LPAIF_RDMACTL_AUDINTF_MI2S |
|
LPAIF_RDMACTL_AUDINTF(rdma_port) |
|
||||||
LPAIF_RDMACTL_FIFOWM_8;
|
LPAIF_RDMACTL_FIFOWM_8;
|
||||||
|
|
||||||
switch (bitwidth) {
|
switch (bitwidth) {
|
||||||
@ -156,7 +157,7 @@ static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream,
|
|||||||
}
|
}
|
||||||
|
|
||||||
ret = regmap_write(drvdata->lpaif_map,
|
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) {
|
if (ret) {
|
||||||
dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n",
|
dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n",
|
||||||
__func__, ret);
|
__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)
|
static int lpass_platform_pcmops_hw_free(struct snd_pcm_substream *substream)
|
||||||
{
|
{
|
||||||
struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
|
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 =
|
struct lpass_data *drvdata =
|
||||||
snd_soc_platform_get_drvdata(soc_runtime->platform);
|
snd_soc_platform_get_drvdata(soc_runtime->platform);
|
||||||
|
struct lpass_variant *v = drvdata->variant;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = regmap_write(drvdata->lpaif_map,
|
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)
|
if (ret)
|
||||||
dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n",
|
dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n",
|
||||||
__func__, ret);
|
__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_pcm_runtime *runtime = substream->runtime;
|
||||||
struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
|
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 =
|
struct lpass_data *drvdata =
|
||||||
snd_soc_platform_get_drvdata(soc_runtime->platform);
|
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,
|
ret = regmap_write(drvdata->lpaif_map,
|
||||||
LPAIF_RDMABASE_REG(LPAIF_RDMA_CHAN_MI2S),
|
LPAIF_RDMABASE_REG(v, ch),
|
||||||
runtime->dma_addr);
|
runtime->dma_addr);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(soc_runtime->dev, "%s() error writing to rdmabase reg: %d\n",
|
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,
|
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);
|
(snd_pcm_lib_buffer_bytes(substream) >> 2) - 1);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(soc_runtime->dev, "%s() error writing to rdmabuff reg: %d\n",
|
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,
|
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);
|
(snd_pcm_lib_period_bytes(substream) >> 2) - 1);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(soc_runtime->dev, "%s() error writing to rdmaper reg: %d\n",
|
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,
|
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);
|
LPAIF_RDMACTL_ENABLE_MASK, LPAIF_RDMACTL_ENABLE_ON);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n",
|
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)
|
int cmd)
|
||||||
{
|
{
|
||||||
struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
|
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 =
|
struct lpass_data *drvdata =
|
||||||
snd_soc_platform_get_drvdata(soc_runtime->platform);
|
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) {
|
switch (cmd) {
|
||||||
case SNDRV_PCM_TRIGGER_START:
|
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:
|
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||||
/* clear status before enabling interrupts */
|
/* clear status before enabling interrupts */
|
||||||
ret = regmap_write(drvdata->lpaif_map,
|
ret = regmap_write(drvdata->lpaif_map,
|
||||||
LPAIF_IRQCLEAR_REG(LPAIF_IRQ_PORT_HOST),
|
LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST),
|
||||||
LPAIF_IRQ_ALL(LPAIF_RDMA_CHAN_MI2S));
|
LPAIF_IRQ_ALL(ch));
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(soc_runtime->dev, "%s() error writing to irqclear reg: %d\n",
|
dev_err(soc_runtime->dev, "%s() error writing to irqclear reg: %d\n",
|
||||||
__func__, ret);
|
__func__, ret);
|
||||||
@ -252,9 +259,9 @@ static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream,
|
|||||||
}
|
}
|
||||||
|
|
||||||
ret = regmap_update_bits(drvdata->lpaif_map,
|
ret = regmap_update_bits(drvdata->lpaif_map,
|
||||||
LPAIF_IRQEN_REG(LPAIF_IRQ_PORT_HOST),
|
LPAIF_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST),
|
||||||
LPAIF_IRQ_ALL(LPAIF_RDMA_CHAN_MI2S),
|
LPAIF_IRQ_ALL(ch),
|
||||||
LPAIF_IRQ_ALL(LPAIF_RDMA_CHAN_MI2S));
|
LPAIF_IRQ_ALL(ch));
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(soc_runtime->dev, "%s() error writing to irqen reg: %d\n",
|
dev_err(soc_runtime->dev, "%s() error writing to irqen reg: %d\n",
|
||||||
__func__, ret);
|
__func__, ret);
|
||||||
@ -262,7 +269,7 @@ static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream,
|
|||||||
}
|
}
|
||||||
|
|
||||||
ret = regmap_update_bits(drvdata->lpaif_map,
|
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_MASK,
|
||||||
LPAIF_RDMACTL_ENABLE_ON);
|
LPAIF_RDMACTL_ENABLE_ON);
|
||||||
if (ret) {
|
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_SUSPEND:
|
||||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||||
ret = regmap_update_bits(drvdata->lpaif_map,
|
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_MASK,
|
||||||
LPAIF_RDMACTL_ENABLE_OFF);
|
LPAIF_RDMACTL_ENABLE_OFF);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
@ -285,8 +292,8 @@ static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream,
|
|||||||
}
|
}
|
||||||
|
|
||||||
ret = regmap_update_bits(drvdata->lpaif_map,
|
ret = regmap_update_bits(drvdata->lpaif_map,
|
||||||
LPAIF_IRQEN_REG(LPAIF_IRQ_PORT_HOST),
|
LPAIF_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST),
|
||||||
LPAIF_IRQ_ALL(LPAIF_RDMA_CHAN_MI2S), 0);
|
LPAIF_IRQ_ALL(ch), 0);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(soc_runtime->dev, "%s() error writing to irqen reg: %d\n",
|
dev_err(soc_runtime->dev, "%s() error writing to irqen reg: %d\n",
|
||||||
__func__, ret);
|
__func__, ret);
|
||||||
@ -302,13 +309,15 @@ static snd_pcm_uframes_t lpass_platform_pcmops_pointer(
|
|||||||
struct snd_pcm_substream *substream)
|
struct snd_pcm_substream *substream)
|
||||||
{
|
{
|
||||||
struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
|
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 =
|
struct lpass_data *drvdata =
|
||||||
snd_soc_platform_get_drvdata(soc_runtime->platform);
|
snd_soc_platform_get_drvdata(soc_runtime->platform);
|
||||||
|
struct lpass_variant *v = drvdata->variant;
|
||||||
unsigned int base_addr, curr_addr;
|
unsigned int base_addr, curr_addr;
|
||||||
int ret;
|
int ret, ch = pcm_data->rdma_ch;
|
||||||
|
|
||||||
ret = regmap_read(drvdata->lpaif_map,
|
ret = regmap_read(drvdata->lpaif_map,
|
||||||
LPAIF_RDMABASE_REG(LPAIF_RDMA_CHAN_MI2S), &base_addr);
|
LPAIF_RDMABASE_REG(v, ch), &base_addr);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(soc_runtime->dev, "%s() error reading from rdmabase reg: %d\n",
|
dev_err(soc_runtime->dev, "%s() error reading from rdmabase reg: %d\n",
|
||||||
__func__, ret);
|
__func__, ret);
|
||||||
@ -316,7 +325,7 @@ static snd_pcm_uframes_t lpass_platform_pcmops_pointer(
|
|||||||
}
|
}
|
||||||
|
|
||||||
ret = regmap_read(drvdata->lpaif_map,
|
ret = regmap_read(drvdata->lpaif_map,
|
||||||
LPAIF_RDMACURR_REG(LPAIF_RDMA_CHAN_MI2S), &curr_addr);
|
LPAIF_RDMACURR_REG(v, ch), &curr_addr);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(soc_runtime->dev, "%s() error reading from rdmacurr reg: %d\n",
|
dev_err(soc_runtime->dev, "%s() error reading from rdmacurr reg: %d\n",
|
||||||
__func__, ret);
|
__func__, ret);
|
||||||
@ -347,29 +356,20 @@ static struct snd_pcm_ops lpass_platform_pcm_ops = {
|
|||||||
.mmap = lpass_platform_pcmops_mmap,
|
.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 snd_soc_pcm_runtime *soc_runtime = substream->private_data;
|
||||||
struct lpass_data *drvdata =
|
struct lpass_variant *v = drvdata->variant;
|
||||||
snd_soc_platform_get_drvdata(soc_runtime->platform);
|
|
||||||
unsigned int interrupts;
|
|
||||||
irqreturn_t ret = IRQ_NONE;
|
irqreturn_t ret = IRQ_NONE;
|
||||||
int rv;
|
int rv;
|
||||||
|
|
||||||
rv = regmap_read(drvdata->lpaif_map,
|
if (interrupts & LPAIF_IRQ_PER(chan)) {
|
||||||
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)) {
|
|
||||||
rv = regmap_write(drvdata->lpaif_map,
|
rv = regmap_write(drvdata->lpaif_map,
|
||||||
LPAIF_IRQCLEAR_REG(LPAIF_IRQ_PORT_HOST),
|
LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST),
|
||||||
LPAIF_IRQ_PER(LPAIF_RDMA_CHAN_MI2S));
|
LPAIF_IRQ_PER(chan));
|
||||||
if (rv) {
|
if (rv) {
|
||||||
dev_err(soc_runtime->dev, "%s() error writing to irqclear reg: %d\n",
|
dev_err(soc_runtime->dev, "%s() error writing to irqclear reg: %d\n",
|
||||||
__func__, rv);
|
__func__, rv);
|
||||||
@ -379,10 +379,10 @@ static irqreturn_t lpass_platform_lpaif_irq(int irq, void *data)
|
|||||||
ret = IRQ_HANDLED;
|
ret = IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (interrupts & LPAIF_IRQ_XRUN(LPAIF_RDMA_CHAN_MI2S)) {
|
if (interrupts & LPAIF_IRQ_XRUN(chan)) {
|
||||||
rv = regmap_write(drvdata->lpaif_map,
|
rv = regmap_write(drvdata->lpaif_map,
|
||||||
LPAIF_IRQCLEAR_REG(LPAIF_IRQ_PORT_HOST),
|
LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST),
|
||||||
LPAIF_IRQ_XRUN(LPAIF_RDMA_CHAN_MI2S));
|
LPAIF_IRQ_XRUN(chan));
|
||||||
if (rv) {
|
if (rv) {
|
||||||
dev_err(soc_runtime->dev, "%s() error writing to irqclear reg: %d\n",
|
dev_err(soc_runtime->dev, "%s() error writing to irqclear reg: %d\n",
|
||||||
__func__, rv);
|
__func__, rv);
|
||||||
@ -393,10 +393,10 @@ static irqreturn_t lpass_platform_lpaif_irq(int irq, void *data)
|
|||||||
ret = IRQ_HANDLED;
|
ret = IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (interrupts & LPAIF_IRQ_ERR(LPAIF_RDMA_CHAN_MI2S)) {
|
if (interrupts & LPAIF_IRQ_ERR(chan)) {
|
||||||
rv = regmap_write(drvdata->lpaif_map,
|
rv = regmap_write(drvdata->lpaif_map,
|
||||||
LPAIF_IRQCLEAR_REG(LPAIF_IRQ_PORT_HOST),
|
LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST),
|
||||||
LPAIF_IRQ_ERR(LPAIF_RDMA_CHAN_MI2S));
|
LPAIF_IRQ_ERR(chan));
|
||||||
if (rv) {
|
if (rv) {
|
||||||
dev_err(soc_runtime->dev, "%s() error writing to irqclear reg: %d\n",
|
dev_err(soc_runtime->dev, "%s() error writing to irqclear reg: %d\n",
|
||||||
__func__, rv);
|
__func__, rv);
|
||||||
@ -410,6 +410,35 @@ static irqreturn_t lpass_platform_lpaif_irq(int irq, void *data)
|
|||||||
return ret;
|
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,
|
static int lpass_platform_alloc_buffer(struct snd_pcm_substream *substream,
|
||||||
struct snd_soc_pcm_runtime *soc_runtime)
|
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 *pcm = soc_runtime->pcm;
|
||||||
struct snd_pcm_substream *substream =
|
struct snd_pcm_substream *substream =
|
||||||
pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
|
pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
|
||||||
|
struct snd_soc_dai *cpu_dai = soc_runtime->cpu_dai;
|
||||||
struct lpass_data *drvdata =
|
struct lpass_data *drvdata =
|
||||||
snd_soc_platform_get_drvdata(soc_runtime->platform);
|
snd_soc_platform_get_drvdata(soc_runtime->platform);
|
||||||
|
struct lpass_variant *v = drvdata->variant;
|
||||||
int ret;
|
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->coherent_dma_mask = DMA_BIT_MASK(32);
|
||||||
soc_runtime->dev->dma_mask = &soc_runtime->dev->coherent_dma_mask;
|
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)
|
if (ret)
|
||||||
return 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,
|
ret = regmap_write(drvdata->lpaif_map,
|
||||||
LPAIF_IRQEN_REG(LPAIF_IRQ_PORT_HOST), 0);
|
LPAIF_RDMACTL_REG(v, data->rdma_ch), 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);
|
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n",
|
dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n",
|
||||||
__func__, ret);
|
__func__, ret);
|
||||||
return ret;
|
goto err_buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -496,6 +526,15 @@ static void lpass_platform_pcm_free(struct snd_pcm *pcm)
|
|||||||
struct snd_pcm_substream *substream =
|
struct snd_pcm_substream *substream =
|
||||||
pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
|
pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
|
||||||
struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
|
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);
|
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)
|
int asoc_qcom_lpass_platform_register(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct lpass_data *drvdata = platform_get_drvdata(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");
|
drvdata->lpaif_irq = platform_get_irq_byname(pdev, "lpass-irq-lpaif");
|
||||||
if (drvdata->lpaif_irq < 0) {
|
if (drvdata->lpaif_irq < 0) {
|
||||||
@ -517,6 +558,25 @@ int asoc_qcom_lpass_platform_register(struct platform_device *pdev)
|
|||||||
return -ENODEV;
|
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,
|
return devm_snd_soc_register_platform(&pdev->dev,
|
||||||
&lpass_platform_driver);
|
&lpass_platform_driver);
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,8 @@
|
|||||||
#include <linux/regmap.h>
|
#include <linux/regmap.h>
|
||||||
|
|
||||||
#define LPASS_AHBIX_CLOCK_FREQUENCY 131072000
|
#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 */
|
/* Both the CPU DAI and platform drivers will access this data */
|
||||||
struct lpass_data {
|
struct lpass_data {
|
||||||
@ -30,10 +32,10 @@ struct lpass_data {
|
|||||||
struct clk *ahbix_clk;
|
struct clk *ahbix_clk;
|
||||||
|
|
||||||
/* MI2S system clock */
|
/* 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 */
|
/* 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 */
|
/* low-power audio interface (LPAIF) registers */
|
||||||
void __iomem *lpaif;
|
void __iomem *lpaif;
|
||||||
@ -43,9 +45,54 @@ struct lpass_data {
|
|||||||
|
|
||||||
/* interrupts from the low-power audio interface (LPAIF) */
|
/* interrupts from the low-power audio interface (LPAIF) */
|
||||||
int lpaif_irq;
|
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 */
|
/* register the platform driver from the CPU DAI driver */
|
||||||
int asoc_qcom_lpass_platform_register(struct platform_device *);
|
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__ */
|
#endif /* __LPASS_H__ */
|
||||||
|
@ -170,6 +170,14 @@ void rsnd_mod_quit(struct rsnd_mod *mod)
|
|||||||
clk_unprepare(mod->clk);
|
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
|
* settting function
|
||||||
*/
|
*/
|
||||||
@ -272,9 +280,10 @@ struct rsnd_dai *rsnd_rdai_get(struct rsnd_priv *priv, int id)
|
|||||||
return priv->rdai + 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)
|
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);
|
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_substream *substream)
|
||||||
{
|
{
|
||||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||||
@ -326,8 +335,11 @@ static int rsnd_dai_stream_init(struct rsnd_dai_stream *io,
|
|||||||
runtime->channels *
|
runtime->channels *
|
||||||
samples_to_bytes(runtime, 1);
|
samples_to_bytes(runtime, 1);
|
||||||
io->next_period_byte = io->byte_per_period;
|
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
|
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,
|
static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd,
|
||||||
struct snd_soc_dai *dai)
|
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 *rdai = rsnd_dai_to_rdai(dai);
|
||||||
struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream);
|
struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream);
|
||||||
int ssi_id = rsnd_mod_id(rsnd_io_to_mod_ssi(io));
|
int ssi_id = rsnd_mod_id(rsnd_io_to_mod_ssi(io));
|
||||||
int ret;
|
int ret;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
|
||||||
rsnd_lock(priv, flags);
|
spin_lock_irqsave(&priv->lock, flags);
|
||||||
|
|
||||||
switch (cmd) {
|
switch (cmd) {
|
||||||
case SNDRV_PCM_TRIGGER_START:
|
case SNDRV_PCM_TRIGGER_START:
|
||||||
ret = rsnd_dai_stream_init(io, substream);
|
rsnd_dai_stream_init(io, substream);
|
||||||
if (ret < 0)
|
|
||||||
goto dai_trigger_end;
|
|
||||||
|
|
||||||
ret = rsnd_platform_call(priv, dai, start, ssi_id);
|
ret = rsnd_platform_call(priv, dai, start, ssi_id);
|
||||||
if (ret < 0)
|
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);
|
ret = rsnd_platform_call(priv, dai, stop, ssi_id);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto dai_trigger_end;
|
goto dai_trigger_end;
|
||||||
|
|
||||||
|
rsnd_dai_stream_quit(io);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
dai_trigger_end:
|
dai_trigger_end:
|
||||||
rsnd_unlock(priv, flags);
|
spin_unlock_irqrestore(&priv->lock, flags);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -833,12 +845,14 @@ static int __rsnd_kctrl_new(struct rsnd_mod *mod,
|
|||||||
struct rsnd_kctrl_cfg *cfg,
|
struct rsnd_kctrl_cfg *cfg,
|
||||||
void (*update)(struct rsnd_mod *mod))
|
void (*update)(struct rsnd_mod *mod))
|
||||||
{
|
{
|
||||||
|
struct snd_soc_card *soc_card = rtd->card;
|
||||||
struct snd_card *card = rtd->card->snd_card;
|
struct snd_card *card = rtd->card->snd_card;
|
||||||
struct snd_kcontrol *kctrl;
|
struct snd_kcontrol *kctrl;
|
||||||
struct snd_kcontrol_new knew = {
|
struct snd_kcontrol_new knew = {
|
||||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||||
.name = name,
|
.name = name,
|
||||||
.info = rsnd_kctrl_info,
|
.info = rsnd_kctrl_info,
|
||||||
|
.index = rtd - soc_card->rtd,
|
||||||
.get = rsnd_kctrl_get,
|
.get = rsnd_kctrl_get,
|
||||||
.put = rsnd_kctrl_put,
|
.put = rsnd_kctrl_put,
|
||||||
.private_value = (unsigned long)cfg,
|
.private_value = (unsigned long)cfg,
|
||||||
|
@ -303,6 +303,7 @@ int rsnd_mod_init(struct rsnd_mod *mod,
|
|||||||
int id);
|
int id);
|
||||||
void rsnd_mod_quit(struct rsnd_mod *mod);
|
void rsnd_mod_quit(struct rsnd_mod *mod);
|
||||||
char *rsnd_mod_name(struct rsnd_mod *mod);
|
char *rsnd_mod_name(struct rsnd_mod *mod);
|
||||||
|
int rsnd_mod_is_working(struct rsnd_mod *mod);
|
||||||
struct dma_chan *rsnd_mod_dma_req(struct rsnd_mod *mod);
|
struct dma_chan *rsnd_mod_dma_req(struct rsnd_mod *mod);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -449,8 +450,6 @@ struct rsnd_priv {
|
|||||||
#define rsnd_priv_to_pdev(priv) ((priv)->pdev)
|
#define rsnd_priv_to_pdev(priv) ((priv)->pdev)
|
||||||
#define rsnd_priv_to_dev(priv) (&(rsnd_priv_to_pdev(priv)->dev))
|
#define rsnd_priv_to_dev(priv) (&(rsnd_priv_to_pdev(priv)->dev))
|
||||||
#define rsnd_priv_to_info(priv) ((priv)->info)
|
#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
|
* rsnd_kctrl
|
||||||
|
@ -232,6 +232,7 @@ rsrc_card_sub_parse_of(struct rsrc_card_priv *priv,
|
|||||||
if (args_count) {
|
if (args_count) {
|
||||||
*args_count = args.args_count;
|
*args_count = args.args_count;
|
||||||
dai_link->dynamic = 1;
|
dai_link->dynamic = 1;
|
||||||
|
dai_link->dpcm_merged_format = 1;
|
||||||
} else {
|
} else {
|
||||||
dai_link->no_pcm = 1;
|
dai_link->no_pcm = 1;
|
||||||
priv->codec_conf.of_node = (*p_node);
|
priv->codec_conf.of_node = (*p_node);
|
||||||
|
@ -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)
|
static irqreturn_t rsnd_src_interrupt_gen2(int irq, void *data)
|
||||||
{
|
{
|
||||||
struct rsnd_mod *mod = 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)
|
spin_lock(&priv->lock);
|
||||||
return IRQ_NONE;
|
|
||||||
|
/* 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)) {
|
if (rsnd_src_error_record_gen2(mod)) {
|
||||||
struct rsnd_priv *priv = rsnd_mod_to_priv(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
|
else
|
||||||
dev_warn(dev, "no more SRC restart\n");
|
dev_warn(dev, "no more SRC restart\n");
|
||||||
}
|
}
|
||||||
|
rsnd_src_interrupt_gen2_out:
|
||||||
|
spin_unlock(&priv->lock);
|
||||||
|
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
@ -66,6 +66,7 @@ struct rsnd_ssi {
|
|||||||
|
|
||||||
u32 cr_own;
|
u32 cr_own;
|
||||||
u32 cr_clk;
|
u32 cr_clk;
|
||||||
|
int chan;
|
||||||
int err;
|
int err;
|
||||||
unsigned int usrcnt;
|
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_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_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_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_mode_flags(p) ((p)->info->flags)
|
||||||
#define rsnd_ssi_dai_id(ssi) ((ssi)->info->dai_id)
|
#define rsnd_ssi_dai_id(ssi) ((ssi)->info->dai_id)
|
||||||
#define rsnd_ssi_of_node(priv) \
|
#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);
|
rsnd_mod_hw_start(&ssi->mod);
|
||||||
|
|
||||||
if (rsnd_rdai_is_clk_master(rdai)) {
|
if (rsnd_rdai_is_clk_master(rdai)) {
|
||||||
if (rsnd_ssi_clk_from_parent(ssi))
|
struct rsnd_ssi *ssi_parent = rsnd_ssi_parent(ssi);
|
||||||
rsnd_ssi_hw_start(ssi->parent, io);
|
|
||||||
|
if (ssi_parent)
|
||||||
|
rsnd_ssi_hw_start(ssi_parent, io);
|
||||||
else
|
else
|
||||||
rsnd_ssi_master_clk_start(ssi, io);
|
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);
|
struct device *dev = rsnd_priv_to_dev(priv);
|
||||||
u32 cr;
|
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;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
ssi->usrcnt--;
|
ssi->usrcnt--;
|
||||||
|
|
||||||
@ -253,13 +258,17 @@ static void rsnd_ssi_hw_stop(struct rsnd_ssi *ssi)
|
|||||||
rsnd_ssi_status_check(&ssi->mod, IIRQ);
|
rsnd_ssi_status_check(&ssi->mod, IIRQ);
|
||||||
|
|
||||||
if (rsnd_rdai_is_clk_master(rdai)) {
|
if (rsnd_rdai_is_clk_master(rdai)) {
|
||||||
if (rsnd_ssi_clk_from_parent(ssi))
|
struct rsnd_ssi *ssi_parent = rsnd_ssi_parent(ssi);
|
||||||
rsnd_ssi_hw_stop(ssi->parent);
|
|
||||||
|
if (ssi_parent)
|
||||||
|
rsnd_ssi_hw_stop(ssi_parent);
|
||||||
else
|
else
|
||||||
rsnd_ssi_master_clk_stop(ssi);
|
rsnd_ssi_master_clk_stop(ssi);
|
||||||
}
|
}
|
||||||
|
|
||||||
rsnd_mod_hw_stop(&ssi->mod);
|
rsnd_mod_hw_stop(&ssi->mod);
|
||||||
|
|
||||||
|
ssi->chan = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
dev_dbg(dev, "%s[%d] hw stopped\n",
|
dev_dbg(dev, "%s[%d] hw stopped\n",
|
||||||
@ -336,6 +345,35 @@ static int rsnd_ssi_quit(struct rsnd_mod *mod,
|
|||||||
return 0;
|
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)
|
static void rsnd_ssi_record_error(struct rsnd_ssi *ssi, u32 status)
|
||||||
{
|
{
|
||||||
/* under/over flow error */
|
/* 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_priv *priv = rsnd_mod_to_priv(mod);
|
||||||
struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
|
struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
|
||||||
int is_dma = rsnd_ssi_is_dma_mode(mod);
|
int is_dma = rsnd_ssi_is_dma_mode(mod);
|
||||||
u32 status = rsnd_mod_read(mod, SSISR);
|
u32 status;
|
||||||
|
|
||||||
if (!io)
|
spin_lock(&priv->lock);
|
||||||
return IRQ_NONE;
|
|
||||||
|
/* 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 */
|
/* PIO only */
|
||||||
if (!is_dma && (status & DIRQ)) {
|
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_record_error(ssi, status);
|
||||||
|
|
||||||
|
rsnd_ssi_interrupt_out:
|
||||||
|
spin_unlock(&priv->lock);
|
||||||
|
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -456,6 +502,7 @@ static struct rsnd_mod_ops rsnd_ssi_pio_ops = {
|
|||||||
.quit = rsnd_ssi_quit,
|
.quit = rsnd_ssi_quit,
|
||||||
.start = rsnd_ssi_start,
|
.start = rsnd_ssi_start,
|
||||||
.stop = rsnd_ssi_stop,
|
.stop = rsnd_ssi_stop,
|
||||||
|
.hw_params = rsnd_ssi_hw_params,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int rsnd_ssi_dma_probe(struct rsnd_mod *mod,
|
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,
|
.start = rsnd_ssi_dma_start,
|
||||||
.stop = rsnd_ssi_dma_stop,
|
.stop = rsnd_ssi_dma_stop,
|
||||||
.fallback = rsnd_ssi_fallback,
|
.fallback = rsnd_ssi_fallback,
|
||||||
|
.hw_params = rsnd_ssi_hw_params,
|
||||||
};
|
};
|
||||||
|
|
||||||
int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod)
|
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);
|
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))
|
if (!rsnd_ssi_is_pin_sharing(&ssi->mod))
|
||||||
return;
|
return;
|
||||||
@ -732,7 +780,7 @@ int rsnd_ssi_probe(struct platform_device *pdev,
|
|||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
rsnd_ssi_parent_clk_setup(priv, ssi);
|
rsnd_ssi_parent_setup(priv, ssi);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user