mirror of
https://github.com/torvalds/linux.git
synced 2024-11-11 22:51:42 +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:
|
||||
|
||||
- compatible : "qcom,lpass-cpu"
|
||||
- compatible : "qcom,lpass-cpu" or "qcom,apq8016-lpass-cpu"
|
||||
- clocks : Must contain an entry for each entry in clock-names.
|
||||
- clock-names : A list which must include the following entries:
|
||||
* "ahbix-clk"
|
||||
* "mi2s-osr-clk"
|
||||
* "mi2s-bit-clk"
|
||||
: required clocks for "qcom,lpass-cpu-apq8016"
|
||||
* "ahbix-clk"
|
||||
* "mi2s-bit-clk0"
|
||||
* "mi2s-bit-clk1"
|
||||
* "mi2s-bit-clk2"
|
||||
* "mi2s-bit-clk3"
|
||||
* "pcnoc-mport-clk"
|
||||
* "pcnoc-sway-clk"
|
||||
|
||||
- interrupts : Must contain an entry for each entry in
|
||||
interrupt-names.
|
||||
- interrupt-names : A list which must include the following entries:
|
||||
@ -22,6 +31,8 @@ Required properties:
|
||||
- reg-names : A list which must include the following entries:
|
||||
* "lpass-lpaif"
|
||||
|
||||
|
||||
|
||||
Optional properties:
|
||||
|
||||
- qcom,adsp : Phandle for the audio DSP node
|
||||
|
@ -48,7 +48,7 @@ DAI subnode properties:
|
||||
|
||||
Example:
|
||||
|
||||
rcar_sound: rcar_sound@ec500000 {
|
||||
rcar_sound: sound@ec500000 {
|
||||
#sound-dai-cells = <1>;
|
||||
compatible = "renesas,rcar_sound-r8a7791", "renesas,rcar_sound-gen2";
|
||||
reg = <0 0xec500000 0 0x1000>, /* SCU */
|
||||
|
@ -465,6 +465,7 @@ static dma_cookie_t rcar_dmac_tx_submit(struct dma_async_tx_descriptor *tx)
|
||||
static int rcar_dmac_desc_alloc(struct rcar_dmac_chan *chan, gfp_t gfp)
|
||||
{
|
||||
struct rcar_dmac_desc_page *page;
|
||||
unsigned long flags;
|
||||
LIST_HEAD(list);
|
||||
unsigned int i;
|
||||
|
||||
@ -482,10 +483,10 @@ static int rcar_dmac_desc_alloc(struct rcar_dmac_chan *chan, gfp_t gfp)
|
||||
list_add_tail(&desc->node, &list);
|
||||
}
|
||||
|
||||
spin_lock_irq(&chan->lock);
|
||||
spin_lock_irqsave(&chan->lock, flags);
|
||||
list_splice_tail(&list, &chan->desc.free);
|
||||
list_add_tail(&page->node, &chan->desc.pages);
|
||||
spin_unlock_irq(&chan->lock);
|
||||
spin_unlock_irqrestore(&chan->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -516,6 +517,7 @@ static void rcar_dmac_desc_put(struct rcar_dmac_chan *chan,
|
||||
static void rcar_dmac_desc_recycle_acked(struct rcar_dmac_chan *chan)
|
||||
{
|
||||
struct rcar_dmac_desc *desc, *_desc;
|
||||
unsigned long flags;
|
||||
LIST_HEAD(list);
|
||||
|
||||
/*
|
||||
@ -524,9 +526,9 @@ static void rcar_dmac_desc_recycle_acked(struct rcar_dmac_chan *chan)
|
||||
* list_for_each_entry_safe, isn't safe if we release the channel lock
|
||||
* around the rcar_dmac_desc_put() call.
|
||||
*/
|
||||
spin_lock_irq(&chan->lock);
|
||||
spin_lock_irqsave(&chan->lock, flags);
|
||||
list_splice_init(&chan->desc.wait, &list);
|
||||
spin_unlock_irq(&chan->lock);
|
||||
spin_unlock_irqrestore(&chan->lock, flags);
|
||||
|
||||
list_for_each_entry_safe(desc, _desc, &list, node) {
|
||||
if (async_tx_test_ack(&desc->async_tx)) {
|
||||
@ -539,9 +541,9 @@ static void rcar_dmac_desc_recycle_acked(struct rcar_dmac_chan *chan)
|
||||
return;
|
||||
|
||||
/* Put the remaining descriptors back in the wait list. */
|
||||
spin_lock_irq(&chan->lock);
|
||||
spin_lock_irqsave(&chan->lock, flags);
|
||||
list_splice(&list, &chan->desc.wait);
|
||||
spin_unlock_irq(&chan->lock);
|
||||
spin_unlock_irqrestore(&chan->lock, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -556,12 +558,13 @@ static void rcar_dmac_desc_recycle_acked(struct rcar_dmac_chan *chan)
|
||||
static struct rcar_dmac_desc *rcar_dmac_desc_get(struct rcar_dmac_chan *chan)
|
||||
{
|
||||
struct rcar_dmac_desc *desc;
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
/* Recycle acked descriptors before attempting allocation. */
|
||||
rcar_dmac_desc_recycle_acked(chan);
|
||||
|
||||
spin_lock_irq(&chan->lock);
|
||||
spin_lock_irqsave(&chan->lock, flags);
|
||||
|
||||
while (list_empty(&chan->desc.free)) {
|
||||
/*
|
||||
@ -570,17 +573,17 @@ static struct rcar_dmac_desc *rcar_dmac_desc_get(struct rcar_dmac_chan *chan)
|
||||
* allocated descriptors. If the allocation fails return an
|
||||
* error.
|
||||
*/
|
||||
spin_unlock_irq(&chan->lock);
|
||||
spin_unlock_irqrestore(&chan->lock, flags);
|
||||
ret = rcar_dmac_desc_alloc(chan, GFP_NOWAIT);
|
||||
if (ret < 0)
|
||||
return NULL;
|
||||
spin_lock_irq(&chan->lock);
|
||||
spin_lock_irqsave(&chan->lock, flags);
|
||||
}
|
||||
|
||||
desc = list_first_entry(&chan->desc.free, struct rcar_dmac_desc, node);
|
||||
list_del(&desc->node);
|
||||
|
||||
spin_unlock_irq(&chan->lock);
|
||||
spin_unlock_irqrestore(&chan->lock, flags);
|
||||
|
||||
return desc;
|
||||
}
|
||||
@ -593,6 +596,7 @@ static struct rcar_dmac_desc *rcar_dmac_desc_get(struct rcar_dmac_chan *chan)
|
||||
static int rcar_dmac_xfer_chunk_alloc(struct rcar_dmac_chan *chan, gfp_t gfp)
|
||||
{
|
||||
struct rcar_dmac_desc_page *page;
|
||||
unsigned long flags;
|
||||
LIST_HEAD(list);
|
||||
unsigned int i;
|
||||
|
||||
@ -606,10 +610,10 @@ static int rcar_dmac_xfer_chunk_alloc(struct rcar_dmac_chan *chan, gfp_t gfp)
|
||||
list_add_tail(&chunk->node, &list);
|
||||
}
|
||||
|
||||
spin_lock_irq(&chan->lock);
|
||||
spin_lock_irqsave(&chan->lock, flags);
|
||||
list_splice_tail(&list, &chan->desc.chunks_free);
|
||||
list_add_tail(&page->node, &chan->desc.pages);
|
||||
spin_unlock_irq(&chan->lock);
|
||||
spin_unlock_irqrestore(&chan->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -627,9 +631,10 @@ static struct rcar_dmac_xfer_chunk *
|
||||
rcar_dmac_xfer_chunk_get(struct rcar_dmac_chan *chan)
|
||||
{
|
||||
struct rcar_dmac_xfer_chunk *chunk;
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
spin_lock_irq(&chan->lock);
|
||||
spin_lock_irqsave(&chan->lock, flags);
|
||||
|
||||
while (list_empty(&chan->desc.chunks_free)) {
|
||||
/*
|
||||
@ -638,18 +643,18 @@ rcar_dmac_xfer_chunk_get(struct rcar_dmac_chan *chan)
|
||||
* allocated descriptors. If the allocation fails return an
|
||||
* error.
|
||||
*/
|
||||
spin_unlock_irq(&chan->lock);
|
||||
spin_unlock_irqrestore(&chan->lock, flags);
|
||||
ret = rcar_dmac_xfer_chunk_alloc(chan, GFP_NOWAIT);
|
||||
if (ret < 0)
|
||||
return NULL;
|
||||
spin_lock_irq(&chan->lock);
|
||||
spin_lock_irqsave(&chan->lock, flags);
|
||||
}
|
||||
|
||||
chunk = list_first_entry(&chan->desc.chunks_free,
|
||||
struct rcar_dmac_xfer_chunk, node);
|
||||
list_del(&chunk->node);
|
||||
|
||||
spin_unlock_irq(&chan->lock);
|
||||
spin_unlock_irqrestore(&chan->lock, flags);
|
||||
|
||||
return chunk;
|
||||
}
|
||||
|
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 */
|
||||
ret = request_threaded_irq(client->irq, NULL,
|
||||
max98095_report_jack,
|
||||
IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
|
||||
"max98095", codec);
|
||||
IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING |
|
||||
IRQF_ONESHOT, "max98095", codec);
|
||||
if (ret) {
|
||||
dev_err(codec->dev, "Failed to request IRQ: %d\n", ret);
|
||||
goto err_access;
|
||||
|
@ -100,12 +100,13 @@ config SND_OMAP_SOC_OMAP_TWL4030
|
||||
|
||||
config SND_OMAP_SOC_OMAP_ABE_TWL6040
|
||||
tristate "SoC Audio support for OMAP boards using ABE and twl6040 codec"
|
||||
depends on TWL6040_CORE && SND_OMAP_SOC && (ARCH_OMAP4 || SOC_OMAP5 || COMPILE_TEST)
|
||||
depends on TWL6040_CORE && SND_OMAP_SOC
|
||||
depends on ARCH_OMAP4 || (SOC_OMAP5 && MFD_PALMAS) || COMPILE_TEST
|
||||
select SND_OMAP_SOC_DMIC
|
||||
select SND_OMAP_SOC_MCPDM
|
||||
select SND_SOC_TWL6040
|
||||
select SND_SOC_DMIC
|
||||
select COMMON_CLK_PALMAS if MFD_PALMAS
|
||||
select COMMON_CLK_PALMAS if (SOC_OMAP5 && MFD_PALMAS)
|
||||
help
|
||||
Say Y if you want to add support for SoC audio on OMAP boards using
|
||||
ABE and twl6040 codec. This driver currently supports:
|
||||
|
@ -159,9 +159,8 @@ static inline void twl4030_disconnect_pin(struct snd_soc_dapm_context *dapm,
|
||||
|
||||
static int omap_twl4030_init(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
struct snd_soc_codec *codec = rtd->codec;
|
||||
struct snd_soc_card *card = rtd->card;
|
||||
struct snd_soc_dapm_context *dapm = &codec->dapm;
|
||||
struct snd_soc_dapm_context *dapm = &card->dapm;
|
||||
struct omap_tw4030_pdata *pdata = dev_get_platdata(card->dev);
|
||||
struct omap_twl4030 *priv = snd_soc_card_get_drvdata(card);
|
||||
int ret = 0;
|
||||
|
@ -45,29 +45,6 @@ static const struct snd_soc_dapm_route brownstone_audio_map[] = {
|
||||
{"MICBIAS1", NULL, "Main Mic"},
|
||||
};
|
||||
|
||||
static int brownstone_wm8994_init(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
struct snd_soc_codec *codec = rtd->codec;
|
||||
struct snd_soc_dapm_context *dapm = &codec->dapm;
|
||||
|
||||
/* set endpoints to not connected */
|
||||
snd_soc_dapm_nc_pin(dapm, "HPOUT2P");
|
||||
snd_soc_dapm_nc_pin(dapm, "HPOUT2N");
|
||||
snd_soc_dapm_nc_pin(dapm, "LINEOUT1N");
|
||||
snd_soc_dapm_nc_pin(dapm, "LINEOUT1P");
|
||||
snd_soc_dapm_nc_pin(dapm, "LINEOUT2N");
|
||||
snd_soc_dapm_nc_pin(dapm, "LINEOUT2P");
|
||||
snd_soc_dapm_nc_pin(dapm, "IN1LN");
|
||||
snd_soc_dapm_nc_pin(dapm, "IN1LP");
|
||||
snd_soc_dapm_nc_pin(dapm, "IN1RP");
|
||||
snd_soc_dapm_nc_pin(dapm, "IN2LP:VXRN");
|
||||
snd_soc_dapm_nc_pin(dapm, "IN2RN");
|
||||
snd_soc_dapm_nc_pin(dapm, "IN2RP:VXRP");
|
||||
snd_soc_dapm_nc_pin(dapm, "IN2LN");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int brownstone_wm8994_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
@ -115,7 +92,6 @@ static struct snd_soc_dai_link brownstone_wm8994_dai[] = {
|
||||
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
|
||||
SND_SOC_DAIFMT_CBS_CFS,
|
||||
.ops = &brownstone_ops,
|
||||
.init = brownstone_wm8994_init,
|
||||
},
|
||||
};
|
||||
|
||||
@ -132,6 +108,7 @@ static struct snd_soc_card brownstone = {
|
||||
.num_dapm_widgets = ARRAY_SIZE(brownstone_dapm_widgets),
|
||||
.dapm_routes = brownstone_audio_map,
|
||||
.num_dapm_routes = ARRAY_SIZE(brownstone_audio_map),
|
||||
.fully_routed = true,
|
||||
};
|
||||
|
||||
static int brownstone_probe(struct platform_device *pdev)
|
||||
|
@ -192,6 +192,7 @@ static int poodle_amp_event(struct snd_soc_dapm_widget *w,
|
||||
static const struct snd_soc_dapm_widget wm8731_dapm_widgets[] = {
|
||||
SND_SOC_DAPM_HP("Headphone Jack", NULL),
|
||||
SND_SOC_DAPM_SPK("Ext Spk", poodle_amp_event),
|
||||
SND_SOC_DAPM_MIC("Microphone", NULL),
|
||||
};
|
||||
|
||||
/* Corgi machine connections to the codec pins */
|
||||
@ -204,6 +205,8 @@ static const struct snd_soc_dapm_route poodle_audio_map[] = {
|
||||
/* speaker connected to LOUT, ROUT */
|
||||
{"Ext Spk", NULL, "ROUT"},
|
||||
{"Ext Spk", NULL, "LOUT"},
|
||||
|
||||
{"MICIN", NULL, "Microphone"},
|
||||
};
|
||||
|
||||
static const char *jack_function[] = {"Off", "Headphone"};
|
||||
@ -220,20 +223,6 @@ static const struct snd_kcontrol_new wm8731_poodle_controls[] = {
|
||||
poodle_set_spk),
|
||||
};
|
||||
|
||||
/*
|
||||
* Logic for a wm8731 as connected on a Sharp SL-C7x0 Device
|
||||
*/
|
||||
static int poodle_wm8731_init(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
struct snd_soc_codec *codec = rtd->codec;
|
||||
struct snd_soc_dapm_context *dapm = &codec->dapm;
|
||||
|
||||
snd_soc_dapm_nc_pin(dapm, "LLINEIN");
|
||||
snd_soc_dapm_nc_pin(dapm, "RLINEIN");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* poodle digital audio interface glue - connects codec <--> CPU */
|
||||
static struct snd_soc_dai_link poodle_dai = {
|
||||
.name = "WM8731",
|
||||
@ -242,7 +231,6 @@ static struct snd_soc_dai_link poodle_dai = {
|
||||
.codec_dai_name = "wm8731-hifi",
|
||||
.platform_name = "pxa-pcm-audio",
|
||||
.codec_name = "wm8731.0-001b",
|
||||
.init = poodle_wm8731_init,
|
||||
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
|
||||
SND_SOC_DAIFMT_CBS_CFS,
|
||||
.ops = &poodle_ops,
|
||||
@ -261,6 +249,7 @@ static struct snd_soc_card poodle = {
|
||||
.num_dapm_widgets = ARRAY_SIZE(wm8731_dapm_widgets),
|
||||
.dapm_routes = poodle_audio_map,
|
||||
.num_dapm_routes = ARRAY_SIZE(poodle_audio_map),
|
||||
.fully_routed = true,
|
||||
};
|
||||
|
||||
static int poodle_probe(struct platform_device *pdev)
|
||||
|
@ -185,17 +185,6 @@ static const struct snd_kcontrol_new tosa_controls[] = {
|
||||
tosa_set_spk),
|
||||
};
|
||||
|
||||
static int tosa_ac97_init(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
struct snd_soc_codec *codec = rtd->codec;
|
||||
struct snd_soc_dapm_context *dapm = &codec->dapm;
|
||||
|
||||
snd_soc_dapm_nc_pin(dapm, "OUT3");
|
||||
snd_soc_dapm_nc_pin(dapm, "MONOOUT");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_soc_dai_link tosa_dai[] = {
|
||||
{
|
||||
.name = "AC97",
|
||||
@ -204,7 +193,6 @@ static struct snd_soc_dai_link tosa_dai[] = {
|
||||
.codec_dai_name = "wm9712-hifi",
|
||||
.platform_name = "pxa-pcm-audio",
|
||||
.codec_name = "wm9712-codec",
|
||||
.init = tosa_ac97_init,
|
||||
.ops = &tosa_ops,
|
||||
},
|
||||
{
|
||||
@ -230,6 +218,7 @@ static struct snd_soc_card tosa = {
|
||||
.num_dapm_widgets = ARRAY_SIZE(tosa_dapm_widgets),
|
||||
.dapm_routes = audio_map,
|
||||
.num_dapm_routes = ARRAY_SIZE(audio_map),
|
||||
.fully_routed = true,
|
||||
};
|
||||
|
||||
static int tosa_probe(struct platform_device *pdev)
|
||||
|
@ -132,16 +132,8 @@ static const struct snd_soc_dapm_route z2_audio_map[] = {
|
||||
*/
|
||||
static int z2_wm8750_init(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
struct snd_soc_codec *codec = rtd->codec;
|
||||
struct snd_soc_dapm_context *dapm = &codec->dapm;
|
||||
int ret;
|
||||
|
||||
/* NC codec pins */
|
||||
snd_soc_dapm_disable_pin(dapm, "LINPUT3");
|
||||
snd_soc_dapm_disable_pin(dapm, "RINPUT3");
|
||||
snd_soc_dapm_disable_pin(dapm, "OUT3");
|
||||
snd_soc_dapm_disable_pin(dapm, "MONO1");
|
||||
|
||||
/* Jack detection API stuff */
|
||||
ret = snd_soc_card_jack_new(rtd->card, "Headset Jack", SND_JACK_HEADSET,
|
||||
&hs_jack, hs_jack_pins,
|
||||
@ -189,6 +181,7 @@ static struct snd_soc_card snd_soc_z2 = {
|
||||
.num_dapm_widgets = ARRAY_SIZE(wm8750_dapm_widgets),
|
||||
.dapm_routes = z2_audio_map,
|
||||
.num_dapm_routes = ARRAY_SIZE(z2_audio_map),
|
||||
.fully_routed = true,
|
||||
};
|
||||
|
||||
static struct platform_device *z2_snd_device;
|
||||
|
@ -6,19 +6,28 @@ config SND_SOC_QCOM
|
||||
|
||||
config SND_SOC_LPASS_CPU
|
||||
tristate
|
||||
depends on SND_SOC_QCOM
|
||||
select REGMAP_MMIO
|
||||
|
||||
config SND_SOC_LPASS_PLATFORM
|
||||
tristate
|
||||
depends on SND_SOC_QCOM
|
||||
select REGMAP_MMIO
|
||||
|
||||
config SND_SOC_LPASS_IPQ806X
|
||||
tristate
|
||||
depends on SND_SOC_QCOM
|
||||
select SND_SOC_LPASS_CPU
|
||||
select SND_SOC_LPASS_PLATFORM
|
||||
|
||||
config SND_SOC_LPASS_APQ8016
|
||||
tristate
|
||||
depends on SND_SOC_QCOM
|
||||
select SND_SOC_LPASS_CPU
|
||||
select SND_SOC_LPASS_PLATFORM
|
||||
|
||||
config SND_SOC_STORM
|
||||
tristate "ASoC I2S support for Storm boards"
|
||||
depends on (ARCH_QCOM && SND_SOC_QCOM) || COMPILE_TEST
|
||||
select SND_SOC_LPASS_CPU
|
||||
select SND_SOC_LPASS_PLATFORM
|
||||
depends on SND_SOC_QCOM && (ARCH_QCOM || COMPILE_TEST)
|
||||
select SND_SOC_LPASS_IPQ806X
|
||||
select SND_SOC_MAX98357A
|
||||
help
|
||||
Say Y or M if you want add support for SoC audio on the
|
||||
|
@ -1,9 +1,13 @@
|
||||
# Platform
|
||||
snd-soc-lpass-cpu-objs := lpass-cpu.o
|
||||
snd-soc-lpass-platform-objs := lpass-platform.o
|
||||
snd-soc-lpass-ipq806x-objs := lpass-ipq806x.o
|
||||
snd-soc-lpass-apq8016-objs := lpass-apq8016.o
|
||||
|
||||
obj-$(CONFIG_SND_SOC_LPASS_CPU) += snd-soc-lpass-cpu.o
|
||||
obj-$(CONFIG_SND_SOC_LPASS_PLATFORM) += snd-soc-lpass-platform.o
|
||||
obj-$(CONFIG_SND_SOC_LPASS_IPQ806X) += snd-soc-lpass-ipq806x.o
|
||||
obj-$(CONFIG_SND_SOC_LPASS_APQ8016) += snd-soc-lpass-apq8016.o
|
||||
|
||||
# Machine
|
||||
snd-soc-storm-objs := storm.o
|
||||
|
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/compiler.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/soc-dai.h>
|
||||
#include "lpass-lpaif-ipq806x.h"
|
||||
#include "lpass-lpaif-reg.h"
|
||||
#include "lpass.h"
|
||||
|
||||
static int lpass_cpu_daiops_set_sysclk(struct snd_soc_dai *dai, int clk_id,
|
||||
@ -37,7 +33,10 @@ static int lpass_cpu_daiops_set_sysclk(struct snd_soc_dai *dai, int clk_id,
|
||||
struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
|
||||
int ret;
|
||||
|
||||
ret = clk_set_rate(drvdata->mi2s_osr_clk, freq);
|
||||
if (IS_ERR(drvdata->mi2s_osr_clk[dai->driver->id]))
|
||||
return 0;
|
||||
|
||||
ret = clk_set_rate(drvdata->mi2s_osr_clk[dai->driver->id], freq);
|
||||
if (ret)
|
||||
dev_err(dai->dev, "%s() error setting mi2s osrclk to %u: %d\n",
|
||||
__func__, freq, ret);
|
||||
@ -51,18 +50,23 @@ static int lpass_cpu_daiops_startup(struct snd_pcm_substream *substream,
|
||||
struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
|
||||
int ret;
|
||||
|
||||
ret = clk_prepare_enable(drvdata->mi2s_osr_clk);
|
||||
if (ret) {
|
||||
dev_err(dai->dev, "%s() error in enabling mi2s osr clk: %d\n",
|
||||
__func__, ret);
|
||||
return ret;
|
||||
if (!IS_ERR(drvdata->mi2s_osr_clk[dai->driver->id])) {
|
||||
ret = clk_prepare_enable(
|
||||
drvdata->mi2s_osr_clk[dai->driver->id]);
|
||||
if (ret) {
|
||||
dev_err(dai->dev, "%s() error in enabling mi2s osr clk: %d\n",
|
||||
__func__, ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(drvdata->mi2s_bit_clk);
|
||||
ret = clk_prepare_enable(drvdata->mi2s_bit_clk[dai->driver->id]);
|
||||
if (ret) {
|
||||
dev_err(dai->dev, "%s() error in enabling mi2s bit clk: %d\n",
|
||||
__func__, ret);
|
||||
clk_disable_unprepare(drvdata->mi2s_osr_clk);
|
||||
if (!IS_ERR(drvdata->mi2s_osr_clk[dai->driver->id]))
|
||||
clk_disable_unprepare(
|
||||
drvdata->mi2s_osr_clk[dai->driver->id]);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -74,8 +78,10 @@ static void lpass_cpu_daiops_shutdown(struct snd_pcm_substream *substream,
|
||||
{
|
||||
struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
|
||||
|
||||
clk_disable_unprepare(drvdata->mi2s_bit_clk);
|
||||
clk_disable_unprepare(drvdata->mi2s_osr_clk);
|
||||
clk_disable_unprepare(drvdata->mi2s_bit_clk[dai->driver->id]);
|
||||
|
||||
if (!IS_ERR(drvdata->mi2s_osr_clk[dai->driver->id]))
|
||||
clk_disable_unprepare(drvdata->mi2s_osr_clk[dai->driver->id]);
|
||||
}
|
||||
|
||||
static int lpass_cpu_daiops_hw_params(struct snd_pcm_substream *substream,
|
||||
@ -142,14 +148,16 @@ static int lpass_cpu_daiops_hw_params(struct snd_pcm_substream *substream,
|
||||
}
|
||||
|
||||
ret = regmap_write(drvdata->lpaif_map,
|
||||
LPAIF_I2SCTL_REG(LPAIF_I2S_PORT_MI2S), regval);
|
||||
LPAIF_I2SCTL_REG(drvdata->variant, dai->driver->id),
|
||||
regval);
|
||||
if (ret) {
|
||||
dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n",
|
||||
__func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = clk_set_rate(drvdata->mi2s_bit_clk, rate * bitwidth * 2);
|
||||
ret = clk_set_rate(drvdata->mi2s_bit_clk[dai->driver->id],
|
||||
rate * bitwidth * 2);
|
||||
if (ret) {
|
||||
dev_err(dai->dev, "%s() error setting mi2s bitclk to %u: %d\n",
|
||||
__func__, rate * bitwidth * 2, ret);
|
||||
@ -166,7 +174,8 @@ static int lpass_cpu_daiops_hw_free(struct snd_pcm_substream *substream,
|
||||
int ret;
|
||||
|
||||
ret = regmap_write(drvdata->lpaif_map,
|
||||
LPAIF_I2SCTL_REG(LPAIF_I2S_PORT_MI2S), 0);
|
||||
LPAIF_I2SCTL_REG(drvdata->variant, dai->driver->id),
|
||||
0);
|
||||
if (ret)
|
||||
dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n",
|
||||
__func__, ret);
|
||||
@ -181,7 +190,7 @@ static int lpass_cpu_daiops_prepare(struct snd_pcm_substream *substream,
|
||||
int ret;
|
||||
|
||||
ret = regmap_update_bits(drvdata->lpaif_map,
|
||||
LPAIF_I2SCTL_REG(LPAIF_I2S_PORT_MI2S),
|
||||
LPAIF_I2SCTL_REG(drvdata->variant, dai->driver->id),
|
||||
LPAIF_I2SCTL_SPKEN_MASK, LPAIF_I2SCTL_SPKEN_ENABLE);
|
||||
if (ret)
|
||||
dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n",
|
||||
@ -201,7 +210,8 @@ static int lpass_cpu_daiops_trigger(struct snd_pcm_substream *substream,
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
ret = regmap_update_bits(drvdata->lpaif_map,
|
||||
LPAIF_I2SCTL_REG(LPAIF_I2S_PORT_MI2S),
|
||||
LPAIF_I2SCTL_REG(drvdata->variant,
|
||||
dai->driver->id),
|
||||
LPAIF_I2SCTL_SPKEN_MASK,
|
||||
LPAIF_I2SCTL_SPKEN_ENABLE);
|
||||
if (ret)
|
||||
@ -212,7 +222,8 @@ static int lpass_cpu_daiops_trigger(struct snd_pcm_substream *substream,
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
ret = regmap_update_bits(drvdata->lpaif_map,
|
||||
LPAIF_I2SCTL_REG(LPAIF_I2S_PORT_MI2S),
|
||||
LPAIF_I2SCTL_REG(drvdata->variant,
|
||||
dai->driver->id),
|
||||
LPAIF_I2SCTL_SPKEN_MASK,
|
||||
LPAIF_I2SCTL_SPKEN_DISABLE);
|
||||
if (ret)
|
||||
@ -224,7 +235,7 @@ static int lpass_cpu_daiops_trigger(struct snd_pcm_substream *substream,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct snd_soc_dai_ops lpass_cpu_dai_ops = {
|
||||
struct snd_soc_dai_ops asoc_qcom_lpass_cpu_dai_ops = {
|
||||
.set_sysclk = lpass_cpu_daiops_set_sysclk,
|
||||
.startup = lpass_cpu_daiops_startup,
|
||||
.shutdown = lpass_cpu_daiops_shutdown,
|
||||
@ -233,41 +244,23 @@ static struct snd_soc_dai_ops lpass_cpu_dai_ops = {
|
||||
.prepare = lpass_cpu_daiops_prepare,
|
||||
.trigger = lpass_cpu_daiops_trigger,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_dai_ops);
|
||||
|
||||
static int lpass_cpu_dai_probe(struct snd_soc_dai *dai)
|
||||
int asoc_qcom_lpass_cpu_dai_probe(struct snd_soc_dai *dai)
|
||||
{
|
||||
struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
|
||||
int ret;
|
||||
|
||||
/* ensure audio hardware is disabled */
|
||||
ret = regmap_write(drvdata->lpaif_map,
|
||||
LPAIF_I2SCTL_REG(LPAIF_I2S_PORT_MI2S), 0);
|
||||
LPAIF_I2SCTL_REG(drvdata->variant, dai->driver->id), 0);
|
||||
if (ret)
|
||||
dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n",
|
||||
__func__, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct snd_soc_dai_driver lpass_cpu_dai_driver = {
|
||||
.playback = {
|
||||
.stream_name = "lpass-cpu-playback",
|
||||
.formats = SNDRV_PCM_FMTBIT_S16 |
|
||||
SNDRV_PCM_FMTBIT_S24 |
|
||||
SNDRV_PCM_FMTBIT_S32,
|
||||
.rates = SNDRV_PCM_RATE_8000 |
|
||||
SNDRV_PCM_RATE_16000 |
|
||||
SNDRV_PCM_RATE_32000 |
|
||||
SNDRV_PCM_RATE_48000 |
|
||||
SNDRV_PCM_RATE_96000,
|
||||
.rate_min = 8000,
|
||||
.rate_max = 96000,
|
||||
.channels_min = 1,
|
||||
.channels_max = 8,
|
||||
},
|
||||
.probe = &lpass_cpu_dai_probe,
|
||||
.ops = &lpass_cpu_dai_ops,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_dai_probe);
|
||||
|
||||
static const struct snd_soc_component_driver lpass_cpu_comp_driver = {
|
||||
.name = "lpass-cpu",
|
||||
@ -275,27 +268,29 @@ static const struct snd_soc_component_driver lpass_cpu_comp_driver = {
|
||||
|
||||
static bool lpass_cpu_regmap_writeable(struct device *dev, unsigned int reg)
|
||||
{
|
||||
struct lpass_data *drvdata = dev_get_drvdata(dev);
|
||||
struct lpass_variant *v = drvdata->variant;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < LPAIF_I2S_PORT_NUM; ++i)
|
||||
if (reg == LPAIF_I2SCTL_REG(i))
|
||||
for (i = 0; i < v->i2s_ports; ++i)
|
||||
if (reg == LPAIF_I2SCTL_REG(v, i))
|
||||
return true;
|
||||
|
||||
for (i = 0; i < LPAIF_IRQ_PORT_NUM; ++i) {
|
||||
if (reg == LPAIF_IRQEN_REG(i))
|
||||
for (i = 0; i < v->irq_ports; ++i) {
|
||||
if (reg == LPAIF_IRQEN_REG(v, i))
|
||||
return true;
|
||||
if (reg == LPAIF_IRQCLEAR_REG(i))
|
||||
if (reg == LPAIF_IRQCLEAR_REG(v, i))
|
||||
return true;
|
||||
}
|
||||
|
||||
for (i = 0; i < LPAIF_RDMA_CHAN_NUM; ++i) {
|
||||
if (reg == LPAIF_RDMACTL_REG(i))
|
||||
for (i = 0; i < v->rdma_channels; ++i) {
|
||||
if (reg == LPAIF_RDMACTL_REG(v, i))
|
||||
return true;
|
||||
if (reg == LPAIF_RDMABASE_REG(i))
|
||||
if (reg == LPAIF_RDMABASE_REG(v, i))
|
||||
return true;
|
||||
if (reg == LPAIF_RDMABUFF_REG(i))
|
||||
if (reg == LPAIF_RDMABUFF_REG(v, i))
|
||||
return true;
|
||||
if (reg == LPAIF_RDMAPER_REG(i))
|
||||
if (reg == LPAIF_RDMAPER_REG(v, i))
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -304,29 +299,31 @@ static bool lpass_cpu_regmap_writeable(struct device *dev, unsigned int reg)
|
||||
|
||||
static bool lpass_cpu_regmap_readable(struct device *dev, unsigned int reg)
|
||||
{
|
||||
struct lpass_data *drvdata = dev_get_drvdata(dev);
|
||||
struct lpass_variant *v = drvdata->variant;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < LPAIF_I2S_PORT_NUM; ++i)
|
||||
if (reg == LPAIF_I2SCTL_REG(i))
|
||||
for (i = 0; i < v->i2s_ports; ++i)
|
||||
if (reg == LPAIF_I2SCTL_REG(v, i))
|
||||
return true;
|
||||
|
||||
for (i = 0; i < LPAIF_IRQ_PORT_NUM; ++i) {
|
||||
if (reg == LPAIF_IRQEN_REG(i))
|
||||
for (i = 0; i < v->irq_ports; ++i) {
|
||||
if (reg == LPAIF_IRQEN_REG(v, i))
|
||||
return true;
|
||||
if (reg == LPAIF_IRQSTAT_REG(i))
|
||||
if (reg == LPAIF_IRQSTAT_REG(v, i))
|
||||
return true;
|
||||
}
|
||||
|
||||
for (i = 0; i < LPAIF_RDMA_CHAN_NUM; ++i) {
|
||||
if (reg == LPAIF_RDMACTL_REG(i))
|
||||
for (i = 0; i < v->rdma_channels; ++i) {
|
||||
if (reg == LPAIF_RDMACTL_REG(v, i))
|
||||
return true;
|
||||
if (reg == LPAIF_RDMABASE_REG(i))
|
||||
if (reg == LPAIF_RDMABASE_REG(v, i))
|
||||
return true;
|
||||
if (reg == LPAIF_RDMABUFF_REG(i))
|
||||
if (reg == LPAIF_RDMABUFF_REG(v, i))
|
||||
return true;
|
||||
if (reg == LPAIF_RDMACURR_REG(i))
|
||||
if (reg == LPAIF_RDMACURR_REG(v, i))
|
||||
return true;
|
||||
if (reg == LPAIF_RDMAPER_REG(i))
|
||||
if (reg == LPAIF_RDMAPER_REG(v, i))
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -335,36 +332,41 @@ static bool lpass_cpu_regmap_readable(struct device *dev, unsigned int reg)
|
||||
|
||||
static bool lpass_cpu_regmap_volatile(struct device *dev, unsigned int reg)
|
||||
{
|
||||
struct lpass_data *drvdata = dev_get_drvdata(dev);
|
||||
struct lpass_variant *v = drvdata->variant;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < LPAIF_IRQ_PORT_NUM; ++i)
|
||||
if (reg == LPAIF_IRQSTAT_REG(i))
|
||||
for (i = 0; i < v->irq_ports; ++i)
|
||||
if (reg == LPAIF_IRQSTAT_REG(v, i))
|
||||
return true;
|
||||
|
||||
for (i = 0; i < LPAIF_RDMA_CHAN_NUM; ++i)
|
||||
if (reg == LPAIF_RDMACURR_REG(i))
|
||||
for (i = 0; i < v->rdma_channels; ++i)
|
||||
if (reg == LPAIF_RDMACURR_REG(v, i))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static const struct regmap_config lpass_cpu_regmap_config = {
|
||||
static struct regmap_config lpass_cpu_regmap_config = {
|
||||
.reg_bits = 32,
|
||||
.reg_stride = 4,
|
||||
.val_bits = 32,
|
||||
.max_register = LPAIF_RDMAPER_REG(LPAIF_RDMA_CHAN_MAX),
|
||||
.writeable_reg = lpass_cpu_regmap_writeable,
|
||||
.readable_reg = lpass_cpu_regmap_readable,
|
||||
.volatile_reg = lpass_cpu_regmap_volatile,
|
||||
.cache_type = REGCACHE_FLAT,
|
||||
};
|
||||
|
||||
static int lpass_cpu_platform_probe(struct platform_device *pdev)
|
||||
int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct lpass_data *drvdata;
|
||||
struct device_node *dsp_of_node;
|
||||
struct resource *res;
|
||||
int ret;
|
||||
struct lpass_variant *variant;
|
||||
struct device *dev = &pdev->dev;
|
||||
const struct of_device_id *match;
|
||||
char clk_name[16];
|
||||
int ret, i, dai_id;
|
||||
|
||||
dsp_of_node = of_parse_phandle(pdev->dev.of_node, "qcom,adsp", 0);
|
||||
if (dsp_of_node) {
|
||||
@ -379,11 +381,14 @@ static int lpass_cpu_platform_probe(struct platform_device *pdev)
|
||||
return -ENOMEM;
|
||||
platform_set_drvdata(pdev, drvdata);
|
||||
|
||||
match = of_match_device(dev->driver->of_match_table, dev);
|
||||
if (!match || !match->data)
|
||||
return -EINVAL;
|
||||
|
||||
drvdata->variant = (struct lpass_variant *)match->data;
|
||||
variant = drvdata->variant;
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "lpass-lpaif");
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "%s() error getting resource\n", __func__);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
drvdata->lpaif = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR((void const __force *)drvdata->lpaif)) {
|
||||
@ -393,6 +398,9 @@ static int lpass_cpu_platform_probe(struct platform_device *pdev)
|
||||
return PTR_ERR((void const __force *)drvdata->lpaif);
|
||||
}
|
||||
|
||||
lpass_cpu_regmap_config.max_register = LPAIF_RDMAPER_REG(variant,
|
||||
variant->rdma_channels);
|
||||
|
||||
drvdata->lpaif_map = devm_regmap_init_mmio(&pdev->dev, drvdata->lpaif,
|
||||
&lpass_cpu_regmap_config);
|
||||
if (IS_ERR(drvdata->lpaif_map)) {
|
||||
@ -401,18 +409,38 @@ static int lpass_cpu_platform_probe(struct platform_device *pdev)
|
||||
return PTR_ERR(drvdata->lpaif_map);
|
||||
}
|
||||
|
||||
drvdata->mi2s_osr_clk = devm_clk_get(&pdev->dev, "mi2s-osr-clk");
|
||||
if (IS_ERR(drvdata->mi2s_osr_clk)) {
|
||||
dev_err(&pdev->dev, "%s() error getting mi2s-osr-clk: %ld\n",
|
||||
__func__, PTR_ERR(drvdata->mi2s_osr_clk));
|
||||
return PTR_ERR(drvdata->mi2s_osr_clk);
|
||||
}
|
||||
if (variant->init)
|
||||
variant->init(pdev);
|
||||
|
||||
drvdata->mi2s_bit_clk = devm_clk_get(&pdev->dev, "mi2s-bit-clk");
|
||||
if (IS_ERR(drvdata->mi2s_bit_clk)) {
|
||||
dev_err(&pdev->dev, "%s() error getting mi2s-bit-clk: %ld\n",
|
||||
__func__, PTR_ERR(drvdata->mi2s_bit_clk));
|
||||
return PTR_ERR(drvdata->mi2s_bit_clk);
|
||||
for (i = 0; i < variant->num_dai; i++) {
|
||||
dai_id = variant->dai_driver[i].id;
|
||||
if (variant->num_dai > 1)
|
||||
sprintf(clk_name, "mi2s-osr-clk%d", i);
|
||||
else
|
||||
sprintf(clk_name, "mi2s-osr-clk");
|
||||
|
||||
drvdata->mi2s_osr_clk[dai_id] = devm_clk_get(&pdev->dev,
|
||||
clk_name);
|
||||
if (IS_ERR(drvdata->mi2s_osr_clk[dai_id])) {
|
||||
dev_warn(&pdev->dev,
|
||||
"%s() error getting mi2s-osr-clk: %ld\n",
|
||||
__func__,
|
||||
PTR_ERR(drvdata->mi2s_osr_clk[dai_id]));
|
||||
}
|
||||
|
||||
if (variant->num_dai > 1)
|
||||
sprintf(clk_name, "mi2s-bit-clk%d", i);
|
||||
else
|
||||
sprintf(clk_name, "mi2s-bit-clk");
|
||||
|
||||
drvdata->mi2s_bit_clk[dai_id] = devm_clk_get(&pdev->dev,
|
||||
clk_name);
|
||||
if (IS_ERR(drvdata->mi2s_bit_clk[dai_id])) {
|
||||
dev_err(&pdev->dev,
|
||||
"%s() error getting mi2s-bit-clk: %ld\n",
|
||||
__func__, PTR_ERR(drvdata->mi2s_bit_clk[i]));
|
||||
return PTR_ERR(drvdata->mi2s_bit_clk[dai_id]);
|
||||
}
|
||||
}
|
||||
|
||||
drvdata->ahbix_clk = devm_clk_get(&pdev->dev, "ahbix-clk");
|
||||
@ -439,7 +467,9 @@ static int lpass_cpu_platform_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
ret = devm_snd_soc_register_component(&pdev->dev,
|
||||
&lpass_cpu_comp_driver, &lpass_cpu_dai_driver, 1);
|
||||
&lpass_cpu_comp_driver,
|
||||
variant->dai_driver,
|
||||
variant->num_dai);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "%s() error registering cpu driver: %d\n",
|
||||
__func__, ret);
|
||||
@ -459,33 +489,17 @@ err_clk:
|
||||
clk_disable_unprepare(drvdata->ahbix_clk);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_platform_probe);
|
||||
|
||||
static int lpass_cpu_platform_remove(struct platform_device *pdev)
|
||||
int asoc_qcom_lpass_cpu_platform_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct lpass_data *drvdata = platform_get_drvdata(pdev);
|
||||
|
||||
if (drvdata->variant->exit)
|
||||
drvdata->variant->exit(pdev);
|
||||
|
||||
clk_disable_unprepare(drvdata->ahbix_clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id lpass_cpu_device_id[] = {
|
||||
{ .compatible = "qcom,lpass-cpu" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, lpass_cpu_device_id);
|
||||
#endif
|
||||
|
||||
static struct platform_driver lpass_cpu_platform_driver = {
|
||||
.driver = {
|
||||
.name = "lpass-cpu",
|
||||
.of_match_table = of_match_ptr(lpass_cpu_device_id),
|
||||
},
|
||||
.probe = lpass_cpu_platform_probe,
|
||||
.remove = lpass_cpu_platform_remove,
|
||||
};
|
||||
module_platform_driver(lpass_cpu_platform_driver);
|
||||
|
||||
MODULE_DESCRIPTION("QTi LPASS CPU Driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
EXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_platform_remove);
|
||||
|
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
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* lpass-lpaif-ipq806x.h -- Definitions for the QTi LPAIF in the ipq806x LPASS
|
||||
*/
|
||||
|
||||
#ifndef __LPASS_LPAIF_H__
|
||||
#define __LPASS_LPAIF_H__
|
||||
|
||||
#define LPAIF_BANK_OFFSET 0x1000
|
||||
#ifndef __LPASS_LPAIF_REG_H__
|
||||
#define __LPASS_LPAIF_REG_H__
|
||||
|
||||
/* LPAIF I2S */
|
||||
|
||||
#define LPAIF_I2SCTL_REG_BASE 0x0010
|
||||
#define LPAIF_I2SCTL_REG_STRIDE 0x4
|
||||
#define LPAIF_I2SCTL_REG_ADDR(addr, port) \
|
||||
(LPAIF_I2SCTL_REG_BASE + (addr) + (LPAIF_I2SCTL_REG_STRIDE * (port)))
|
||||
|
||||
enum lpaif_i2s_ports {
|
||||
LPAIF_I2S_PORT_MIN = 0,
|
||||
|
||||
LPAIF_I2S_PORT_CODEC_SPK = 0,
|
||||
LPAIF_I2S_PORT_CODEC_MIC = 1,
|
||||
LPAIF_I2S_PORT_SEC_SPK = 2,
|
||||
LPAIF_I2S_PORT_SEC_MIC = 3,
|
||||
LPAIF_I2S_PORT_MI2S = 4,
|
||||
|
||||
LPAIF_I2S_PORT_MAX = 4,
|
||||
LPAIF_I2S_PORT_NUM = 5,
|
||||
};
|
||||
|
||||
#define LPAIF_I2SCTL_REG(port) LPAIF_I2SCTL_REG_ADDR(0x0, (port))
|
||||
#define LPAIF_I2SCTL_REG_ADDR(v, addr, port) \
|
||||
(v->i2sctrl_reg_base + (addr) + v->i2sctrl_reg_stride * (port))
|
||||
|
||||
#define LPAIF_I2SCTL_REG(v, port) LPAIF_I2SCTL_REG_ADDR(v, 0x0, (port))
|
||||
#define LPAIF_I2SCTL_LOOPBACK_MASK 0x8000
|
||||
#define LPAIF_I2SCTL_LOOPBACK_SHIFT 15
|
||||
#define LPAIF_I2SCTL_LOOPBACK_DISABLE (0 << LPAIF_I2SCTL_LOOPBACK_SHIFT)
|
||||
@ -79,55 +59,36 @@ enum lpaif_i2s_ports {
|
||||
#define LPAIF_I2SCTL_BITWIDTH_32 (2 << LPAIF_I2SCTL_BITWIDTH_SHIFT)
|
||||
|
||||
/* LPAIF IRQ */
|
||||
#define LPAIF_IRQ_REG_ADDR(v, addr, port) \
|
||||
(v->irq_reg_base + (addr) + v->irq_reg_stride * (port))
|
||||
|
||||
#define LPAIF_IRQ_REG_BASE 0x3000
|
||||
#define LPAIF_IRQ_REG_STRIDE 0x1000
|
||||
#define LPAIF_IRQ_REG_ADDR(addr, port) \
|
||||
(LPAIF_IRQ_REG_BASE + (addr) + (LPAIF_IRQ_REG_STRIDE * (port)))
|
||||
#define LPAIF_IRQ_PORT_HOST 0
|
||||
|
||||
enum lpaif_irq_ports {
|
||||
LPAIF_IRQ_PORT_MIN = 0,
|
||||
|
||||
LPAIF_IRQ_PORT_HOST = 0,
|
||||
LPAIF_IRQ_PORT_ADSP = 1,
|
||||
|
||||
LPAIF_IRQ_PORT_MAX = 2,
|
||||
LPAIF_IRQ_PORT_NUM = 3,
|
||||
};
|
||||
|
||||
#define LPAIF_IRQEN_REG(port) LPAIF_IRQ_REG_ADDR(0x0, (port))
|
||||
#define LPAIF_IRQSTAT_REG(port) LPAIF_IRQ_REG_ADDR(0x4, (port))
|
||||
#define LPAIF_IRQCLEAR_REG(port) LPAIF_IRQ_REG_ADDR(0xC, (port))
|
||||
#define LPAIF_IRQEN_REG(v, port) LPAIF_IRQ_REG_ADDR(v, 0x0, (port))
|
||||
#define LPAIF_IRQSTAT_REG(v, port) LPAIF_IRQ_REG_ADDR(v, 0x4, (port))
|
||||
#define LPAIF_IRQCLEAR_REG(v, port) LPAIF_IRQ_REG_ADDR(v, 0xC, (port))
|
||||
|
||||
#define LPAIF_IRQ_BITSTRIDE 3
|
||||
|
||||
#define LPAIF_IRQ_PER(chan) (1 << (LPAIF_IRQ_BITSTRIDE * (chan)))
|
||||
#define LPAIF_IRQ_XRUN(chan) (2 << (LPAIF_IRQ_BITSTRIDE * (chan)))
|
||||
#define LPAIF_IRQ_ERR(chan) (4 << (LPAIF_IRQ_BITSTRIDE * (chan)))
|
||||
|
||||
#define LPAIF_IRQ_ALL(chan) (7 << (LPAIF_IRQ_BITSTRIDE * (chan)))
|
||||
|
||||
/* LPAIF DMA */
|
||||
|
||||
#define LPAIF_RDMA_REG_BASE 0x6000
|
||||
#define LPAIF_RDMA_REG_STRIDE 0x1000
|
||||
#define LPAIF_RDMA_REG_ADDR(addr, chan) \
|
||||
(LPAIF_RDMA_REG_BASE + (addr) + (LPAIF_RDMA_REG_STRIDE * (chan)))
|
||||
#define LPAIF_RDMA_REG_ADDR(v, addr, chan) \
|
||||
(v->rdma_reg_base + (addr) + v->rdma_reg_stride * (chan))
|
||||
|
||||
enum lpaif_dma_channels {
|
||||
LPAIF_RDMA_CHAN_MIN = 0,
|
||||
#define LPAIF_RDMACTL_AUDINTF(id) (id << LPAIF_RDMACTL_AUDINTF_SHIFT)
|
||||
|
||||
LPAIF_RDMA_CHAN_MI2S = 0,
|
||||
LPAIF_RDMA_CHAN_PCM0 = 1,
|
||||
LPAIF_RDMA_CHAN_PCM1 = 2,
|
||||
|
||||
LPAIF_RDMA_CHAN_MAX = 4,
|
||||
LPAIF_RDMA_CHAN_NUM = 5,
|
||||
};
|
||||
|
||||
#define LPAIF_RDMACTL_REG(chan) LPAIF_RDMA_REG_ADDR(0x00, (chan))
|
||||
#define LPAIF_RDMABASE_REG(chan) LPAIF_RDMA_REG_ADDR(0x04, (chan))
|
||||
#define LPAIF_RDMABUFF_REG(chan) LPAIF_RDMA_REG_ADDR(0x08, (chan))
|
||||
#define LPAIF_RDMACURR_REG(chan) LPAIF_RDMA_REG_ADDR(0x0C, (chan))
|
||||
#define LPAIF_RDMAPER_REG(chan) LPAIF_RDMA_REG_ADDR(0x10, (chan))
|
||||
#define LPAIF_RDMACTL_REG(v, chan) LPAIF_RDMA_REG_ADDR(v, 0x00, (chan))
|
||||
#define LPAIF_RDMABASE_REG(v, chan) LPAIF_RDMA_REG_ADDR(v, 0x04, (chan))
|
||||
#define LPAIF_RDMABUFF_REG(v, chan) LPAIF_RDMA_REG_ADDR(v, 0x08, (chan))
|
||||
#define LPAIF_RDMACURR_REG(v, chan) LPAIF_RDMA_REG_ADDR(v, 0x0C, (chan))
|
||||
#define LPAIF_RDMAPER_REG(v, chan) LPAIF_RDMA_REG_ADDR(v, 0x10, (chan))
|
||||
#define LPAIF_RDMAPERCNT_REG(v, chan) LPAIF_RDMA_REG_ADDR(v, 0x14, (chan))
|
||||
|
||||
#define LPAIF_RDMACTL_BURSTEN_MASK 0x800
|
||||
#define LPAIF_RDMACTL_BURSTEN_SHIFT 11
|
||||
@ -145,13 +106,6 @@ enum lpaif_dma_channels {
|
||||
|
||||
#define LPAIF_RDMACTL_AUDINTF_MASK 0x0F0
|
||||
#define LPAIF_RDMACTL_AUDINTF_SHIFT 4
|
||||
#define LPAIF_RDMACTL_AUDINTF_NONE (0 << LPAIF_RDMACTL_AUDINTF_SHIFT)
|
||||
#define LPAIF_RDMACTL_AUDINTF_CODEC (1 << LPAIF_RDMACTL_AUDINTF_SHIFT)
|
||||
#define LPAIF_RDMACTL_AUDINTF_PCM (2 << LPAIF_RDMACTL_AUDINTF_SHIFT)
|
||||
#define LPAIF_RDMACTL_AUDINTF_SEC_I2S (3 << LPAIF_RDMACTL_AUDINTF_SHIFT)
|
||||
#define LPAIF_RDMACTL_AUDINTF_MI2S (4 << LPAIF_RDMACTL_AUDINTF_SHIFT)
|
||||
#define LPAIF_RDMACTL_AUDINTF_HDMI (5 << LPAIF_RDMACTL_AUDINTF_SHIFT)
|
||||
#define LPAIF_RDMACTL_AUDINTF_SEC_PCM (7 << LPAIF_RDMACTL_AUDINTF_SHIFT)
|
||||
|
||||
#define LPAIF_RDMACTL_FIFOWM_MASK 0x00E
|
||||
#define LPAIF_RDMACTL_FIFOWM_SHIFT 1
|
||||
@ -169,4 +123,4 @@ enum lpaif_dma_channels {
|
||||
#define LPAIF_RDMACTL_ENABLE_OFF (0 << LPAIF_RDMACTL_ENABLE_SHIFT)
|
||||
#define LPAIF_RDMACTL_ENABLE_ON (1 << LPAIF_RDMACTL_ENABLE_SHIFT)
|
||||
|
||||
#endif /* __LPASS_LPAIF_H__ */
|
||||
#endif /* __LPASS_LPAIF_REG_H__ */
|
@ -13,23 +13,22 @@
|
||||
* lpass-platform.c -- ALSA SoC platform driver for QTi LPASS
|
||||
*/
|
||||
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <sound/memalloc.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <sound/soc.h>
|
||||
#include "lpass-lpaif-ipq806x.h"
|
||||
#include "lpass-lpaif-reg.h"
|
||||
#include "lpass.h"
|
||||
|
||||
struct lpass_pcm_data {
|
||||
int rdma_ch;
|
||||
int i2s_port;
|
||||
};
|
||||
|
||||
#define LPASS_PLATFORM_BUFFER_SIZE (16 * 1024)
|
||||
#define LPASS_PLATFORM_PERIODS 2
|
||||
|
||||
@ -84,13 +83,15 @@ static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
|
||||
struct lpass_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(soc_runtime);
|
||||
struct lpass_data *drvdata =
|
||||
snd_soc_platform_get_drvdata(soc_runtime->platform);
|
||||
struct lpass_variant *v = drvdata->variant;
|
||||
snd_pcm_format_t format = params_format(params);
|
||||
unsigned int channels = params_channels(params);
|
||||
unsigned int regval;
|
||||
int bitwidth;
|
||||
int ret;
|
||||
int ret, rdma_port = pcm_data->i2s_port + v->rdmactl_audif_start;
|
||||
|
||||
bitwidth = snd_pcm_format_width(format);
|
||||
if (bitwidth < 0) {
|
||||
@ -100,7 +101,7 @@ static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream,
|
||||
}
|
||||
|
||||
regval = LPAIF_RDMACTL_BURSTEN_INCR4 |
|
||||
LPAIF_RDMACTL_AUDINTF_MI2S |
|
||||
LPAIF_RDMACTL_AUDINTF(rdma_port) |
|
||||
LPAIF_RDMACTL_FIFOWM_8;
|
||||
|
||||
switch (bitwidth) {
|
||||
@ -156,7 +157,7 @@ static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream,
|
||||
}
|
||||
|
||||
ret = regmap_write(drvdata->lpaif_map,
|
||||
LPAIF_RDMACTL_REG(LPAIF_RDMA_CHAN_MI2S), regval);
|
||||
LPAIF_RDMACTL_REG(v, pcm_data->rdma_ch), regval);
|
||||
if (ret) {
|
||||
dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n",
|
||||
__func__, ret);
|
||||
@ -169,12 +170,14 @@ static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream,
|
||||
static int lpass_platform_pcmops_hw_free(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
|
||||
struct lpass_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(soc_runtime);
|
||||
struct lpass_data *drvdata =
|
||||
snd_soc_platform_get_drvdata(soc_runtime->platform);
|
||||
struct lpass_variant *v = drvdata->variant;
|
||||
int ret;
|
||||
|
||||
ret = regmap_write(drvdata->lpaif_map,
|
||||
LPAIF_RDMACTL_REG(LPAIF_RDMA_CHAN_MI2S), 0);
|
||||
LPAIF_RDMACTL_REG(v, pcm_data->rdma_ch), 0);
|
||||
if (ret)
|
||||
dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n",
|
||||
__func__, ret);
|
||||
@ -186,12 +189,14 @@ static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
|
||||
struct lpass_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(soc_runtime);
|
||||
struct lpass_data *drvdata =
|
||||
snd_soc_platform_get_drvdata(soc_runtime->platform);
|
||||
int ret;
|
||||
struct lpass_variant *v = drvdata->variant;
|
||||
int ret, ch = pcm_data->rdma_ch;
|
||||
|
||||
ret = regmap_write(drvdata->lpaif_map,
|
||||
LPAIF_RDMABASE_REG(LPAIF_RDMA_CHAN_MI2S),
|
||||
LPAIF_RDMABASE_REG(v, ch),
|
||||
runtime->dma_addr);
|
||||
if (ret) {
|
||||
dev_err(soc_runtime->dev, "%s() error writing to rdmabase reg: %d\n",
|
||||
@ -200,7 +205,7 @@ static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream)
|
||||
}
|
||||
|
||||
ret = regmap_write(drvdata->lpaif_map,
|
||||
LPAIF_RDMABUFF_REG(LPAIF_RDMA_CHAN_MI2S),
|
||||
LPAIF_RDMABUFF_REG(v, ch),
|
||||
(snd_pcm_lib_buffer_bytes(substream) >> 2) - 1);
|
||||
if (ret) {
|
||||
dev_err(soc_runtime->dev, "%s() error writing to rdmabuff reg: %d\n",
|
||||
@ -209,7 +214,7 @@ static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream)
|
||||
}
|
||||
|
||||
ret = regmap_write(drvdata->lpaif_map,
|
||||
LPAIF_RDMAPER_REG(LPAIF_RDMA_CHAN_MI2S),
|
||||
LPAIF_RDMAPER_REG(v, ch),
|
||||
(snd_pcm_lib_period_bytes(substream) >> 2) - 1);
|
||||
if (ret) {
|
||||
dev_err(soc_runtime->dev, "%s() error writing to rdmaper reg: %d\n",
|
||||
@ -218,7 +223,7 @@ static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream)
|
||||
}
|
||||
|
||||
ret = regmap_update_bits(drvdata->lpaif_map,
|
||||
LPAIF_RDMACTL_REG(LPAIF_RDMA_CHAN_MI2S),
|
||||
LPAIF_RDMACTL_REG(v, ch),
|
||||
LPAIF_RDMACTL_ENABLE_MASK, LPAIF_RDMACTL_ENABLE_ON);
|
||||
if (ret) {
|
||||
dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n",
|
||||
@ -233,9 +238,11 @@ static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream,
|
||||
int cmd)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
|
||||
struct lpass_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(soc_runtime);
|
||||
struct lpass_data *drvdata =
|
||||
snd_soc_platform_get_drvdata(soc_runtime->platform);
|
||||
int ret;
|
||||
struct lpass_variant *v = drvdata->variant;
|
||||
int ret, ch = pcm_data->rdma_ch;
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
@ -243,8 +250,8 @@ static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream,
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
/* clear status before enabling interrupts */
|
||||
ret = regmap_write(drvdata->lpaif_map,
|
||||
LPAIF_IRQCLEAR_REG(LPAIF_IRQ_PORT_HOST),
|
||||
LPAIF_IRQ_ALL(LPAIF_RDMA_CHAN_MI2S));
|
||||
LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST),
|
||||
LPAIF_IRQ_ALL(ch));
|
||||
if (ret) {
|
||||
dev_err(soc_runtime->dev, "%s() error writing to irqclear reg: %d\n",
|
||||
__func__, ret);
|
||||
@ -252,9 +259,9 @@ static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream,
|
||||
}
|
||||
|
||||
ret = regmap_update_bits(drvdata->lpaif_map,
|
||||
LPAIF_IRQEN_REG(LPAIF_IRQ_PORT_HOST),
|
||||
LPAIF_IRQ_ALL(LPAIF_RDMA_CHAN_MI2S),
|
||||
LPAIF_IRQ_ALL(LPAIF_RDMA_CHAN_MI2S));
|
||||
LPAIF_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST),
|
||||
LPAIF_IRQ_ALL(ch),
|
||||
LPAIF_IRQ_ALL(ch));
|
||||
if (ret) {
|
||||
dev_err(soc_runtime->dev, "%s() error writing to irqen reg: %d\n",
|
||||
__func__, ret);
|
||||
@ -262,7 +269,7 @@ static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream,
|
||||
}
|
||||
|
||||
ret = regmap_update_bits(drvdata->lpaif_map,
|
||||
LPAIF_RDMACTL_REG(LPAIF_RDMA_CHAN_MI2S),
|
||||
LPAIF_RDMACTL_REG(v, ch),
|
||||
LPAIF_RDMACTL_ENABLE_MASK,
|
||||
LPAIF_RDMACTL_ENABLE_ON);
|
||||
if (ret) {
|
||||
@ -275,7 +282,7 @@ static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream,
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
ret = regmap_update_bits(drvdata->lpaif_map,
|
||||
LPAIF_RDMACTL_REG(LPAIF_RDMA_CHAN_MI2S),
|
||||
LPAIF_RDMACTL_REG(v, ch),
|
||||
LPAIF_RDMACTL_ENABLE_MASK,
|
||||
LPAIF_RDMACTL_ENABLE_OFF);
|
||||
if (ret) {
|
||||
@ -285,8 +292,8 @@ static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream,
|
||||
}
|
||||
|
||||
ret = regmap_update_bits(drvdata->lpaif_map,
|
||||
LPAIF_IRQEN_REG(LPAIF_IRQ_PORT_HOST),
|
||||
LPAIF_IRQ_ALL(LPAIF_RDMA_CHAN_MI2S), 0);
|
||||
LPAIF_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST),
|
||||
LPAIF_IRQ_ALL(ch), 0);
|
||||
if (ret) {
|
||||
dev_err(soc_runtime->dev, "%s() error writing to irqen reg: %d\n",
|
||||
__func__, ret);
|
||||
@ -302,13 +309,15 @@ static snd_pcm_uframes_t lpass_platform_pcmops_pointer(
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
|
||||
struct lpass_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(soc_runtime);
|
||||
struct lpass_data *drvdata =
|
||||
snd_soc_platform_get_drvdata(soc_runtime->platform);
|
||||
struct lpass_variant *v = drvdata->variant;
|
||||
unsigned int base_addr, curr_addr;
|
||||
int ret;
|
||||
int ret, ch = pcm_data->rdma_ch;
|
||||
|
||||
ret = regmap_read(drvdata->lpaif_map,
|
||||
LPAIF_RDMABASE_REG(LPAIF_RDMA_CHAN_MI2S), &base_addr);
|
||||
LPAIF_RDMABASE_REG(v, ch), &base_addr);
|
||||
if (ret) {
|
||||
dev_err(soc_runtime->dev, "%s() error reading from rdmabase reg: %d\n",
|
||||
__func__, ret);
|
||||
@ -316,7 +325,7 @@ static snd_pcm_uframes_t lpass_platform_pcmops_pointer(
|
||||
}
|
||||
|
||||
ret = regmap_read(drvdata->lpaif_map,
|
||||
LPAIF_RDMACURR_REG(LPAIF_RDMA_CHAN_MI2S), &curr_addr);
|
||||
LPAIF_RDMACURR_REG(v, ch), &curr_addr);
|
||||
if (ret) {
|
||||
dev_err(soc_runtime->dev, "%s() error reading from rdmacurr reg: %d\n",
|
||||
__func__, ret);
|
||||
@ -347,29 +356,20 @@ static struct snd_pcm_ops lpass_platform_pcm_ops = {
|
||||
.mmap = lpass_platform_pcmops_mmap,
|
||||
};
|
||||
|
||||
static irqreturn_t lpass_platform_lpaif_irq(int irq, void *data)
|
||||
static irqreturn_t lpass_dma_interrupt_handler(
|
||||
struct snd_pcm_substream *substream,
|
||||
struct lpass_data *drvdata,
|
||||
int chan, u32 interrupts)
|
||||
{
|
||||
struct snd_pcm_substream *substream = data;
|
||||
struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
|
||||
struct lpass_data *drvdata =
|
||||
snd_soc_platform_get_drvdata(soc_runtime->platform);
|
||||
unsigned int interrupts;
|
||||
struct lpass_variant *v = drvdata->variant;
|
||||
irqreturn_t ret = IRQ_NONE;
|
||||
int rv;
|
||||
|
||||
rv = regmap_read(drvdata->lpaif_map,
|
||||
LPAIF_IRQSTAT_REG(LPAIF_IRQ_PORT_HOST), &interrupts);
|
||||
if (rv) {
|
||||
dev_err(soc_runtime->dev, "%s() error reading from irqstat reg: %d\n",
|
||||
__func__, rv);
|
||||
return IRQ_NONE;
|
||||
}
|
||||
interrupts &= LPAIF_IRQ_ALL(LPAIF_RDMA_CHAN_MI2S);
|
||||
|
||||
if (interrupts & LPAIF_IRQ_PER(LPAIF_RDMA_CHAN_MI2S)) {
|
||||
if (interrupts & LPAIF_IRQ_PER(chan)) {
|
||||
rv = regmap_write(drvdata->lpaif_map,
|
||||
LPAIF_IRQCLEAR_REG(LPAIF_IRQ_PORT_HOST),
|
||||
LPAIF_IRQ_PER(LPAIF_RDMA_CHAN_MI2S));
|
||||
LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST),
|
||||
LPAIF_IRQ_PER(chan));
|
||||
if (rv) {
|
||||
dev_err(soc_runtime->dev, "%s() error writing to irqclear reg: %d\n",
|
||||
__func__, rv);
|
||||
@ -379,10 +379,10 @@ static irqreturn_t lpass_platform_lpaif_irq(int irq, void *data)
|
||||
ret = IRQ_HANDLED;
|
||||
}
|
||||
|
||||
if (interrupts & LPAIF_IRQ_XRUN(LPAIF_RDMA_CHAN_MI2S)) {
|
||||
if (interrupts & LPAIF_IRQ_XRUN(chan)) {
|
||||
rv = regmap_write(drvdata->lpaif_map,
|
||||
LPAIF_IRQCLEAR_REG(LPAIF_IRQ_PORT_HOST),
|
||||
LPAIF_IRQ_XRUN(LPAIF_RDMA_CHAN_MI2S));
|
||||
LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST),
|
||||
LPAIF_IRQ_XRUN(chan));
|
||||
if (rv) {
|
||||
dev_err(soc_runtime->dev, "%s() error writing to irqclear reg: %d\n",
|
||||
__func__, rv);
|
||||
@ -393,10 +393,10 @@ static irqreturn_t lpass_platform_lpaif_irq(int irq, void *data)
|
||||
ret = IRQ_HANDLED;
|
||||
}
|
||||
|
||||
if (interrupts & LPAIF_IRQ_ERR(LPAIF_RDMA_CHAN_MI2S)) {
|
||||
if (interrupts & LPAIF_IRQ_ERR(chan)) {
|
||||
rv = regmap_write(drvdata->lpaif_map,
|
||||
LPAIF_IRQCLEAR_REG(LPAIF_IRQ_PORT_HOST),
|
||||
LPAIF_IRQ_ERR(LPAIF_RDMA_CHAN_MI2S));
|
||||
LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST),
|
||||
LPAIF_IRQ_ERR(chan));
|
||||
if (rv) {
|
||||
dev_err(soc_runtime->dev, "%s() error writing to irqclear reg: %d\n",
|
||||
__func__, rv);
|
||||
@ -410,6 +410,35 @@ static irqreturn_t lpass_platform_lpaif_irq(int irq, void *data)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static irqreturn_t lpass_platform_lpaif_irq(int irq, void *data)
|
||||
{
|
||||
struct lpass_data *drvdata = data;
|
||||
struct lpass_variant *v = drvdata->variant;
|
||||
unsigned int irqs;
|
||||
int rv, chan;
|
||||
|
||||
rv = regmap_read(drvdata->lpaif_map,
|
||||
LPAIF_IRQSTAT_REG(v, LPAIF_IRQ_PORT_HOST), &irqs);
|
||||
if (rv) {
|
||||
pr_err("%s() error reading from irqstat reg: %d\n",
|
||||
__func__, rv);
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
/* Handle per channel interrupts */
|
||||
for (chan = 0; chan < LPASS_MAX_DMA_CHANNELS; chan++) {
|
||||
if (irqs & LPAIF_IRQ_ALL(chan) && drvdata->substream[chan]) {
|
||||
rv = lpass_dma_interrupt_handler(
|
||||
drvdata->substream[chan],
|
||||
drvdata, chan, irqs);
|
||||
if (rv != IRQ_HANDLED)
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int lpass_platform_alloc_buffer(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_pcm_runtime *soc_runtime)
|
||||
{
|
||||
@ -448,9 +477,27 @@ static int lpass_platform_pcm_new(struct snd_soc_pcm_runtime *soc_runtime)
|
||||
struct snd_pcm *pcm = soc_runtime->pcm;
|
||||
struct snd_pcm_substream *substream =
|
||||
pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
|
||||
struct snd_soc_dai *cpu_dai = soc_runtime->cpu_dai;
|
||||
struct lpass_data *drvdata =
|
||||
snd_soc_platform_get_drvdata(soc_runtime->platform);
|
||||
struct lpass_variant *v = drvdata->variant;
|
||||
int ret;
|
||||
struct lpass_pcm_data *data;
|
||||
|
||||
data = devm_kzalloc(soc_runtime->dev, sizeof(*data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
if (v->alloc_dma_channel)
|
||||
data->rdma_ch = v->alloc_dma_channel(drvdata);
|
||||
|
||||
if (IS_ERR_VALUE(data->rdma_ch))
|
||||
return data->rdma_ch;
|
||||
|
||||
drvdata->substream[data->rdma_ch] = substream;
|
||||
data->i2s_port = cpu_dai->driver->id;
|
||||
|
||||
snd_soc_pcm_set_drvdata(soc_runtime, data);
|
||||
|
||||
soc_runtime->dev->coherent_dma_mask = DMA_BIT_MASK(32);
|
||||
soc_runtime->dev->dma_mask = &soc_runtime->dev->coherent_dma_mask;
|
||||
@ -459,29 +506,12 @@ static int lpass_platform_pcm_new(struct snd_soc_pcm_runtime *soc_runtime)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = devm_request_irq(soc_runtime->dev, drvdata->lpaif_irq,
|
||||
lpass_platform_lpaif_irq, IRQF_TRIGGER_RISING,
|
||||
"lpass-irq-lpaif", substream);
|
||||
if (ret) {
|
||||
dev_err(soc_runtime->dev, "%s() irq request failed: %d\n",
|
||||
__func__, ret);
|
||||
goto err_buf;
|
||||
}
|
||||
|
||||
/* ensure audio hardware is disabled */
|
||||
ret = regmap_write(drvdata->lpaif_map,
|
||||
LPAIF_IRQEN_REG(LPAIF_IRQ_PORT_HOST), 0);
|
||||
if (ret) {
|
||||
dev_err(soc_runtime->dev, "%s() error writing to irqen reg: %d\n",
|
||||
__func__, ret);
|
||||
return ret;
|
||||
}
|
||||
ret = regmap_write(drvdata->lpaif_map,
|
||||
LPAIF_RDMACTL_REG(LPAIF_RDMA_CHAN_MI2S), 0);
|
||||
LPAIF_RDMACTL_REG(v, data->rdma_ch), 0);
|
||||
if (ret) {
|
||||
dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n",
|
||||
__func__, ret);
|
||||
return ret;
|
||||
goto err_buf;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -496,6 +526,15 @@ static void lpass_platform_pcm_free(struct snd_pcm *pcm)
|
||||
struct snd_pcm_substream *substream =
|
||||
pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
|
||||
struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
|
||||
struct lpass_data *drvdata =
|
||||
snd_soc_platform_get_drvdata(soc_runtime->platform);
|
||||
struct lpass_pcm_data *data = snd_soc_pcm_get_drvdata(soc_runtime);
|
||||
struct lpass_variant *v = drvdata->variant;
|
||||
|
||||
drvdata->substream[data->rdma_ch] = NULL;
|
||||
|
||||
if (v->free_dma_channel)
|
||||
v->free_dma_channel(drvdata, data->rdma_ch);
|
||||
|
||||
lpass_platform_free_buffer(substream, soc_runtime);
|
||||
}
|
||||
@ -509,6 +548,8 @@ static struct snd_soc_platform_driver lpass_platform_driver = {
|
||||
int asoc_qcom_lpass_platform_register(struct platform_device *pdev)
|
||||
{
|
||||
struct lpass_data *drvdata = platform_get_drvdata(pdev);
|
||||
struct lpass_variant *v = drvdata->variant;
|
||||
int ret;
|
||||
|
||||
drvdata->lpaif_irq = platform_get_irq_byname(pdev, "lpass-irq-lpaif");
|
||||
if (drvdata->lpaif_irq < 0) {
|
||||
@ -517,6 +558,25 @@ int asoc_qcom_lpass_platform_register(struct platform_device *pdev)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* ensure audio hardware is disabled */
|
||||
ret = regmap_write(drvdata->lpaif_map,
|
||||
LPAIF_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST), 0);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "%s() error writing to irqen reg: %d\n",
|
||||
__func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = devm_request_irq(&pdev->dev, drvdata->lpaif_irq,
|
||||
lpass_platform_lpaif_irq, IRQF_TRIGGER_RISING,
|
||||
"lpass-irq-lpaif", drvdata);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "%s() irq request failed: %d\n",
|
||||
__func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
return devm_snd_soc_register_platform(&pdev->dev,
|
||||
&lpass_platform_driver);
|
||||
}
|
||||
|
@ -22,6 +22,8 @@
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#define LPASS_AHBIX_CLOCK_FREQUENCY 131072000
|
||||
#define LPASS_MAX_MI2S_PORTS (8)
|
||||
#define LPASS_MAX_DMA_CHANNELS (8)
|
||||
|
||||
/* Both the CPU DAI and platform drivers will access this data */
|
||||
struct lpass_data {
|
||||
@ -30,10 +32,10 @@ struct lpass_data {
|
||||
struct clk *ahbix_clk;
|
||||
|
||||
/* MI2S system clock */
|
||||
struct clk *mi2s_osr_clk;
|
||||
struct clk *mi2s_osr_clk[LPASS_MAX_MI2S_PORTS];
|
||||
|
||||
/* MI2S bit clock (derived from system clock by a divider */
|
||||
struct clk *mi2s_bit_clk;
|
||||
struct clk *mi2s_bit_clk[LPASS_MAX_MI2S_PORTS];
|
||||
|
||||
/* low-power audio interface (LPAIF) registers */
|
||||
void __iomem *lpaif;
|
||||
@ -43,9 +45,54 @@ struct lpass_data {
|
||||
|
||||
/* interrupts from the low-power audio interface (LPAIF) */
|
||||
int lpaif_irq;
|
||||
|
||||
/* SOC specific variations in the LPASS IP integration */
|
||||
struct lpass_variant *variant;
|
||||
|
||||
/* bit map to keep track of static channel allocations */
|
||||
unsigned long rdma_ch_bit_map;
|
||||
|
||||
/* used it for handling interrupt per dma channel */
|
||||
struct snd_pcm_substream *substream[LPASS_MAX_DMA_CHANNELS];
|
||||
|
||||
/* 8016 specific */
|
||||
struct clk *pcnoc_mport_clk;
|
||||
struct clk *pcnoc_sway_clk;
|
||||
};
|
||||
|
||||
/* Vairant data per each SOC */
|
||||
struct lpass_variant {
|
||||
u32 i2sctrl_reg_base;
|
||||
u32 i2sctrl_reg_stride;
|
||||
u32 i2s_ports;
|
||||
u32 irq_reg_base;
|
||||
u32 irq_reg_stride;
|
||||
u32 irq_ports;
|
||||
u32 rdma_reg_base;
|
||||
u32 rdma_reg_stride;
|
||||
u32 rdma_channels;
|
||||
|
||||
/**
|
||||
* on SOCs like APQ8016 the channel control bits start
|
||||
* at different offset to ipq806x
|
||||
**/
|
||||
u32 rdmactl_audif_start;
|
||||
/* SOC specific intialization like clocks */
|
||||
int (*init)(struct platform_device *pdev);
|
||||
int (*exit)(struct platform_device *pdev);
|
||||
int (*alloc_dma_channel)(struct lpass_data *data);
|
||||
int (*free_dma_channel)(struct lpass_data *data, int ch);
|
||||
|
||||
/* SOC specific dais */
|
||||
struct snd_soc_dai_driver *dai_driver;
|
||||
int num_dai;
|
||||
};
|
||||
|
||||
/* register the platform driver from the CPU DAI driver */
|
||||
int asoc_qcom_lpass_platform_register(struct platform_device *);
|
||||
int asoc_qcom_lpass_cpu_platform_remove(struct platform_device *pdev);
|
||||
int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev);
|
||||
int asoc_qcom_lpass_cpu_dai_probe(struct snd_soc_dai *dai);
|
||||
extern struct snd_soc_dai_ops asoc_qcom_lpass_cpu_dai_ops;
|
||||
|
||||
#endif /* __LPASS_H__ */
|
||||
|
@ -170,6 +170,14 @@ void rsnd_mod_quit(struct rsnd_mod *mod)
|
||||
clk_unprepare(mod->clk);
|
||||
}
|
||||
|
||||
int rsnd_mod_is_working(struct rsnd_mod *mod)
|
||||
{
|
||||
struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
|
||||
|
||||
/* see rsnd_dai_stream_init/quit() */
|
||||
return !!io->substream;
|
||||
}
|
||||
|
||||
/*
|
||||
* settting function
|
||||
*/
|
||||
@ -272,9 +280,10 @@ struct rsnd_dai *rsnd_rdai_get(struct rsnd_priv *priv, int id)
|
||||
return priv->rdai + id;
|
||||
}
|
||||
|
||||
#define rsnd_dai_to_priv(dai) snd_soc_dai_get_drvdata(dai)
|
||||
static struct rsnd_dai *rsnd_dai_to_rdai(struct snd_soc_dai *dai)
|
||||
{
|
||||
struct rsnd_priv *priv = snd_soc_dai_get_drvdata(dai);
|
||||
struct rsnd_priv *priv = rsnd_dai_to_priv(dai);
|
||||
|
||||
return rsnd_rdai_get(priv, dai->id);
|
||||
}
|
||||
@ -314,7 +323,7 @@ void rsnd_dai_pointer_update(struct rsnd_dai_stream *io, int byte)
|
||||
}
|
||||
}
|
||||
|
||||
static int rsnd_dai_stream_init(struct rsnd_dai_stream *io,
|
||||
static void rsnd_dai_stream_init(struct rsnd_dai_stream *io,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
@ -326,8 +335,11 @@ static int rsnd_dai_stream_init(struct rsnd_dai_stream *io,
|
||||
runtime->channels *
|
||||
samples_to_bytes(runtime, 1);
|
||||
io->next_period_byte = io->byte_per_period;
|
||||
}
|
||||
|
||||
return 0;
|
||||
static void rsnd_dai_stream_quit(struct rsnd_dai_stream *io)
|
||||
{
|
||||
io->substream = NULL;
|
||||
}
|
||||
|
||||
static
|
||||
@ -351,20 +363,18 @@ struct rsnd_dai_stream *rsnd_rdai_to_io(struct rsnd_dai *rdai,
|
||||
static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct rsnd_priv *priv = snd_soc_dai_get_drvdata(dai);
|
||||
struct rsnd_priv *priv = rsnd_dai_to_priv(dai);
|
||||
struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai);
|
||||
struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream);
|
||||
int ssi_id = rsnd_mod_id(rsnd_io_to_mod_ssi(io));
|
||||
int ret;
|
||||
unsigned long flags;
|
||||
|
||||
rsnd_lock(priv, flags);
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
ret = rsnd_dai_stream_init(io, substream);
|
||||
if (ret < 0)
|
||||
goto dai_trigger_end;
|
||||
rsnd_dai_stream_init(io, substream);
|
||||
|
||||
ret = rsnd_platform_call(priv, dai, start, ssi_id);
|
||||
if (ret < 0)
|
||||
@ -390,13 +400,15 @@ static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd,
|
||||
ret = rsnd_platform_call(priv, dai, stop, ssi_id);
|
||||
if (ret < 0)
|
||||
goto dai_trigger_end;
|
||||
|
||||
rsnd_dai_stream_quit(io);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
dai_trigger_end:
|
||||
rsnd_unlock(priv, flags);
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -833,12 +845,14 @@ static int __rsnd_kctrl_new(struct rsnd_mod *mod,
|
||||
struct rsnd_kctrl_cfg *cfg,
|
||||
void (*update)(struct rsnd_mod *mod))
|
||||
{
|
||||
struct snd_soc_card *soc_card = rtd->card;
|
||||
struct snd_card *card = rtd->card->snd_card;
|
||||
struct snd_kcontrol *kctrl;
|
||||
struct snd_kcontrol_new knew = {
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = name,
|
||||
.info = rsnd_kctrl_info,
|
||||
.index = rtd - soc_card->rtd,
|
||||
.get = rsnd_kctrl_get,
|
||||
.put = rsnd_kctrl_put,
|
||||
.private_value = (unsigned long)cfg,
|
||||
|
@ -303,6 +303,7 @@ int rsnd_mod_init(struct rsnd_mod *mod,
|
||||
int id);
|
||||
void rsnd_mod_quit(struct rsnd_mod *mod);
|
||||
char *rsnd_mod_name(struct rsnd_mod *mod);
|
||||
int rsnd_mod_is_working(struct rsnd_mod *mod);
|
||||
struct dma_chan *rsnd_mod_dma_req(struct rsnd_mod *mod);
|
||||
|
||||
/*
|
||||
@ -449,8 +450,6 @@ struct rsnd_priv {
|
||||
#define rsnd_priv_to_pdev(priv) ((priv)->pdev)
|
||||
#define rsnd_priv_to_dev(priv) (&(rsnd_priv_to_pdev(priv)->dev))
|
||||
#define rsnd_priv_to_info(priv) ((priv)->info)
|
||||
#define rsnd_lock(priv, flags) spin_lock_irqsave(&priv->lock, flags)
|
||||
#define rsnd_unlock(priv, flags) spin_unlock_irqrestore(&priv->lock, flags)
|
||||
|
||||
/*
|
||||
* rsnd_kctrl
|
||||
|
@ -232,6 +232,7 @@ rsrc_card_sub_parse_of(struct rsrc_card_priv *priv,
|
||||
if (args_count) {
|
||||
*args_count = args.args_count;
|
||||
dai_link->dynamic = 1;
|
||||
dai_link->dpcm_merged_format = 1;
|
||||
} else {
|
||||
dai_link->no_pcm = 1;
|
||||
priv->codec_conf.of_node = (*p_node);
|
||||
|
@ -673,10 +673,13 @@ static int _rsnd_src_stop_gen2(struct rsnd_mod *mod)
|
||||
static irqreturn_t rsnd_src_interrupt_gen2(int irq, void *data)
|
||||
{
|
||||
struct rsnd_mod *mod = data;
|
||||
struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
|
||||
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
|
||||
|
||||
if (!io)
|
||||
return IRQ_NONE;
|
||||
spin_lock(&priv->lock);
|
||||
|
||||
/* ignore all cases if not working */
|
||||
if (!rsnd_mod_is_working(mod))
|
||||
goto rsnd_src_interrupt_gen2_out;
|
||||
|
||||
if (rsnd_src_error_record_gen2(mod)) {
|
||||
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
|
||||
@ -692,6 +695,8 @@ static irqreturn_t rsnd_src_interrupt_gen2(int irq, void *data)
|
||||
else
|
||||
dev_warn(dev, "no more SRC restart\n");
|
||||
}
|
||||
rsnd_src_interrupt_gen2_out:
|
||||
spin_unlock(&priv->lock);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
@ -66,6 +66,7 @@ struct rsnd_ssi {
|
||||
|
||||
u32 cr_own;
|
||||
u32 cr_clk;
|
||||
int chan;
|
||||
int err;
|
||||
unsigned int usrcnt;
|
||||
};
|
||||
@ -80,7 +81,7 @@ struct rsnd_ssi {
|
||||
#define rsnd_mod_to_ssi(_mod) container_of((_mod), struct rsnd_ssi, mod)
|
||||
#define rsnd_dma_to_ssi(dma) rsnd_mod_to_ssi(rsnd_dma_to_mod(dma))
|
||||
#define rsnd_ssi_pio_available(ssi) ((ssi)->info->irq > 0)
|
||||
#define rsnd_ssi_clk_from_parent(ssi) ((ssi)->parent)
|
||||
#define rsnd_ssi_parent(ssi) ((ssi)->parent)
|
||||
#define rsnd_ssi_mode_flags(p) ((p)->info->flags)
|
||||
#define rsnd_ssi_dai_id(ssi) ((ssi)->info->dai_id)
|
||||
#define rsnd_ssi_of_node(priv) \
|
||||
@ -189,8 +190,10 @@ static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi,
|
||||
rsnd_mod_hw_start(&ssi->mod);
|
||||
|
||||
if (rsnd_rdai_is_clk_master(rdai)) {
|
||||
if (rsnd_ssi_clk_from_parent(ssi))
|
||||
rsnd_ssi_hw_start(ssi->parent, io);
|
||||
struct rsnd_ssi *ssi_parent = rsnd_ssi_parent(ssi);
|
||||
|
||||
if (ssi_parent)
|
||||
rsnd_ssi_hw_start(ssi_parent, io);
|
||||
else
|
||||
rsnd_ssi_master_clk_start(ssi, io);
|
||||
}
|
||||
@ -229,8 +232,10 @@ static void rsnd_ssi_hw_stop(struct rsnd_ssi *ssi)
|
||||
struct device *dev = rsnd_priv_to_dev(priv);
|
||||
u32 cr;
|
||||
|
||||
if (0 == ssi->usrcnt) /* stop might be called without start */
|
||||
if (0 == ssi->usrcnt) {
|
||||
dev_err(dev, "%s called without starting\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
ssi->usrcnt--;
|
||||
|
||||
@ -253,13 +258,17 @@ static void rsnd_ssi_hw_stop(struct rsnd_ssi *ssi)
|
||||
rsnd_ssi_status_check(&ssi->mod, IIRQ);
|
||||
|
||||
if (rsnd_rdai_is_clk_master(rdai)) {
|
||||
if (rsnd_ssi_clk_from_parent(ssi))
|
||||
rsnd_ssi_hw_stop(ssi->parent);
|
||||
struct rsnd_ssi *ssi_parent = rsnd_ssi_parent(ssi);
|
||||
|
||||
if (ssi_parent)
|
||||
rsnd_ssi_hw_stop(ssi_parent);
|
||||
else
|
||||
rsnd_ssi_master_clk_stop(ssi);
|
||||
}
|
||||
|
||||
rsnd_mod_hw_stop(&ssi->mod);
|
||||
|
||||
ssi->chan = 0;
|
||||
}
|
||||
|
||||
dev_dbg(dev, "%s[%d] hw stopped\n",
|
||||
@ -336,6 +345,35 @@ static int rsnd_ssi_quit(struct rsnd_mod *mod,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rsnd_ssi_hw_params(struct rsnd_mod *mod,
|
||||
struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
|
||||
struct rsnd_ssi *ssi_parent = rsnd_ssi_parent(ssi);
|
||||
int chan = params_channels(params);
|
||||
|
||||
/*
|
||||
* Already working.
|
||||
* It will happen if SSI has parent/child connection.
|
||||
*/
|
||||
if (ssi->usrcnt) {
|
||||
/*
|
||||
* it is error if child <-> parent SSI uses
|
||||
* different channels.
|
||||
*/
|
||||
if (ssi->chan != chan)
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* It will be removed on rsnd_ssi_hw_stop */
|
||||
ssi->chan = chan;
|
||||
if (ssi_parent)
|
||||
return rsnd_ssi_hw_params(&ssi_parent->mod, substream, params);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rsnd_ssi_record_error(struct rsnd_ssi *ssi, u32 status)
|
||||
{
|
||||
/* under/over flow error */
|
||||
@ -385,10 +423,15 @@ static irqreturn_t rsnd_ssi_interrupt(int irq, void *data)
|
||||
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
|
||||
struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
|
||||
int is_dma = rsnd_ssi_is_dma_mode(mod);
|
||||
u32 status = rsnd_mod_read(mod, SSISR);
|
||||
u32 status;
|
||||
|
||||
if (!io)
|
||||
return IRQ_NONE;
|
||||
spin_lock(&priv->lock);
|
||||
|
||||
/* ignore all cases if not working */
|
||||
if (!rsnd_mod_is_working(mod))
|
||||
goto rsnd_ssi_interrupt_out;
|
||||
|
||||
status = rsnd_mod_read(mod, SSISR);
|
||||
|
||||
/* PIO only */
|
||||
if (!is_dma && (status & DIRQ)) {
|
||||
@ -428,6 +471,9 @@ static irqreturn_t rsnd_ssi_interrupt(int irq, void *data)
|
||||
|
||||
rsnd_ssi_record_error(ssi, status);
|
||||
|
||||
rsnd_ssi_interrupt_out:
|
||||
spin_unlock(&priv->lock);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
@ -456,6 +502,7 @@ static struct rsnd_mod_ops rsnd_ssi_pio_ops = {
|
||||
.quit = rsnd_ssi_quit,
|
||||
.start = rsnd_ssi_start,
|
||||
.stop = rsnd_ssi_stop,
|
||||
.hw_params = rsnd_ssi_hw_params,
|
||||
};
|
||||
|
||||
static int rsnd_ssi_dma_probe(struct rsnd_mod *mod,
|
||||
@ -565,6 +612,7 @@ static struct rsnd_mod_ops rsnd_ssi_dma_ops = {
|
||||
.start = rsnd_ssi_dma_start,
|
||||
.stop = rsnd_ssi_dma_stop,
|
||||
.fallback = rsnd_ssi_fallback,
|
||||
.hw_params = rsnd_ssi_hw_params,
|
||||
};
|
||||
|
||||
int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod)
|
||||
@ -598,7 +646,7 @@ int rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod)
|
||||
return !!(rsnd_ssi_mode_flags(ssi) & RSND_SSI_CLK_PIN_SHARE);
|
||||
}
|
||||
|
||||
static void rsnd_ssi_parent_clk_setup(struct rsnd_priv *priv, struct rsnd_ssi *ssi)
|
||||
static void rsnd_ssi_parent_setup(struct rsnd_priv *priv, struct rsnd_ssi *ssi)
|
||||
{
|
||||
if (!rsnd_ssi_is_pin_sharing(&ssi->mod))
|
||||
return;
|
||||
@ -732,7 +780,7 @@ int rsnd_ssi_probe(struct platform_device *pdev,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
rsnd_ssi_parent_clk_setup(priv, ssi);
|
||||
rsnd_ssi_parent_setup(priv, ssi);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
Loading…
Reference in New Issue
Block a user