Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,32 @@ properties:
'#sound-dai-cells':
const: 1

'#address-cells':
const: 1

'#size-cells':
const: 0

# Digital Audio Interfaces
patternProperties:
'^dai@[0-9]+$':
type: object
description:
Q6DSP Digital Audio Interfaces.

properties:
reg:
description:
Digital Audio Interface ID

clocks:
minItems: 1
maxItems: 3

clock-names:
minItems: 1
maxItems: 3

required:
- compatible
- '#sound-dai-cells'
Expand All @@ -29,7 +55,16 @@ unevaluatedProperties: false

examples:
- |
dais {
bedais {
compatible = "qcom,q6apm-lpass-dais";
#sound-dai-cells = <1>;
#address-cells = <1>;
#size-cells = <0>;

dai@16 {
reg = <PRIMARY_MI2S_RX>;
clocks = <&q6prmcc LPASS_CLK_ID_MCLK_1
LPASS_CLK_ATTRIBUTE_COUPLE_NO>;
clock-names = "mclk";
};
};
137 changes: 135 additions & 2 deletions sound/soc/qcom/qdsp6/q6apm-lpass-dais.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@
// Copyright (c) 2021, Linaro Limited

#include <dt-bindings/sound/qcom,q6dsp-lpass-ports.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <sound/pcm.h>
Expand All @@ -15,13 +17,22 @@
#include "q6dsp-common.h"
#include "audioreach.h"
#include "q6apm.h"
#include "q6prm.h"

#define AUDIOREACH_BE_PCM_BASE 16

struct q6apm_dai_priv_data {
struct clk *mclk;
struct clk *bclk;
struct clk *eclk;
bool mclk_enabled, bclk_enabled, eclk_enabled;
};

struct q6apm_lpass_dai_data {
struct q6apm_graph *graph[APM_PORT_MAX];
bool is_port_started[APM_PORT_MAX];
struct audioreach_module_config module_config[APM_PORT_MAX];
struct q6apm_dai_priv_data priv[APM_PORT_MAX];
};

static int q6dma_set_channel_map(struct snd_soc_dai *dai,
Expand Down Expand Up @@ -238,6 +249,70 @@ static int q6apm_lpass_dai_startup(struct snd_pcm_substream *substream, struct s
return 0;
}

static int q6i2s_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
{
return q6apm_lpass_dai_startup(substream, dai);
}

static void q6i2s_lpass_dai_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
{
struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev);

if (dai_data->priv[dai->id].mclk_enabled) {
clk_disable_unprepare(dai_data->priv[dai->id].mclk);
dai_data->priv[dai->id].mclk_enabled = false;
}

if (dai_data->priv[dai->id].bclk_enabled) {
clk_disable_unprepare(dai_data->priv[dai->id].bclk);
dai_data->priv[dai->id].bclk_enabled = false;
}

if (dai_data->priv[dai->id].eclk_enabled) {
clk_disable_unprepare(dai_data->priv[dai->id].eclk);
dai_data->priv[dai->id].eclk_enabled = false;
}
q6apm_lpass_dai_shutdown(substream, dai);
}

static int q6i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int freq, int dir)
{
struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev);
struct clk *sysclk;
bool *enabled;
int ret = 0;

switch (clk_id) {
case LPAIF_MI2S_MCLK:
sysclk = dai_data->priv[dai->id].mclk;
enabled = &dai_data->priv[dai->id].mclk_enabled;
break;
case LPAIF_MI2S_BCLK:
sysclk = dai_data->priv[dai->id].bclk;
enabled = &dai_data->priv[dai->id].bclk_enabled;
break;
case LPAIF_MI2S_ECLK:
sysclk = dai_data->priv[dai->id].eclk;
enabled = &dai_data->priv[dai->id].eclk_enabled;
break;
default:
break;
}

if (sysclk) {
clk_set_rate(sysclk, freq);
ret = clk_prepare_enable(sysclk);
if (ret) {
dev_err(dai->dev, "Error, Unable to prepare (%d) sysclk\n", clk_id);
return ret;
}

*enabled = true;
}

return ret;
}

static int q6i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev);
Expand All @@ -258,11 +333,12 @@ static const struct snd_soc_dai_ops q6dma_ops = {

static const struct snd_soc_dai_ops q6i2s_ops = {
.prepare = q6apm_lpass_dai_prepare,
.startup = q6apm_lpass_dai_startup,
.shutdown = q6apm_lpass_dai_shutdown,
.startup = q6i2s_dai_startup,
.shutdown = q6i2s_lpass_dai_shutdown,
.set_channel_map = q6dma_set_channel_map,
.hw_params = q6dma_hw_params,
.set_fmt = q6i2s_set_fmt,
.set_sysclk = q6i2s_set_sysclk,
};

static const struct snd_soc_dai_ops q6hdmi_ops = {
Expand All @@ -280,19 +356,76 @@ static const struct snd_soc_component_driver q6apm_lpass_dai_component = {
.use_dai_pcm_id = true,
};

static int of_q6apm_parse_dai_data(struct device *dev,
struct q6apm_lpass_dai_data *data)
{
struct device_node *node;
int ret;

for_each_child_of_node(dev->of_node, node) {
struct q6apm_dai_priv_data *priv;
int id;

ret = of_property_read_u32(node, "reg", &id);
if (ret || id < 0 || id >= APM_PORT_MAX) {
dev_err(dev, "valid dai id not found:%d\n", ret);
continue;
}

switch (id) {
/* MI2S specific properties */
case PRIMARY_MI2S_RX ... QUATERNARY_MI2S_TX:
case QUINARY_MI2S_RX ... QUINARY_MI2S_TX:
priv = &data->priv[id];
priv->mclk = of_clk_get_by_name(node, "mclk");
if (IS_ERR(priv->mclk)) {
if (PTR_ERR(priv->mclk) == -EPROBE_DEFER)
return dev_err_probe(dev, PTR_ERR(priv->mclk),
"unable to get mi2s mclk\n");
priv->mclk = NULL;
}

priv->bclk = of_clk_get_by_name(node, "bclk");
if (IS_ERR(priv->bclk)) {
if (PTR_ERR(priv->bclk) == -EPROBE_DEFER)
return dev_err_probe(dev, PTR_ERR(priv->bclk),
"unable to get mi2s bclk\n");
priv->bclk = NULL;
}

priv->eclk = of_clk_get_by_name(node, "eclk");
if (IS_ERR(priv->eclk)) {
if (PTR_ERR(priv->eclk) == -EPROBE_DEFER)
return dev_err_probe(dev, PTR_ERR(priv->eclk),
"unable to get mi2s eclk\n");
priv->eclk = NULL;
}
break;
default:
break;
}
}

return 0;
}

static int q6apm_lpass_dai_dev_probe(struct platform_device *pdev)
{
struct q6dsp_audio_port_dai_driver_config cfg;
struct q6apm_lpass_dai_data *dai_data;
struct snd_soc_dai_driver *dais;
struct device *dev = &pdev->dev;
int num_dais;
int ret;

dai_data = devm_kzalloc(dev, sizeof(*dai_data), GFP_KERNEL);
if (!dai_data)
return -ENOMEM;

dev_set_drvdata(dev, dai_data);
ret = of_q6apm_parse_dai_data(dev, dai_data);
if (ret)
return ret;

memset(&cfg, 0, sizeof(cfg));
cfg.q6i2s_ops = &q6i2s_ops;
Expand Down
5 changes: 5 additions & 0 deletions sound/soc/qcom/qdsp6/q6prm-clocks.c
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ static const struct q6dsp_clk_init q6prm_clks[] = {
Q6PRM_CLK(LPASS_CLK_ID_INT5_MI2S_IBIT),
Q6PRM_CLK(LPASS_CLK_ID_INT6_MI2S_IBIT),
Q6PRM_CLK(LPASS_CLK_ID_QUI_MI2S_OSR),
Q6PRM_CLK(LPASS_CLK_ID_MCLK_1),
Q6PRM_CLK(LPASS_CLK_ID_MCLK_2),
Q6PRM_CLK(LPASS_CLK_ID_MCLK_3),
Q6PRM_CLK(LPASS_CLK_ID_MCLK_4),
Q6PRM_CLK(LPASS_CLK_ID_MCLK_5),
Q6PRM_CLK(LPASS_CLK_ID_WSA_CORE_MCLK),
Q6PRM_CLK(LPASS_CLK_ID_WSA_CORE_NPL_MCLK),
Q6PRM_CLK(LPASS_CLK_ID_VA_CORE_MCLK),
Expand Down
15 changes: 15 additions & 0 deletions sound/soc/qcom/qdsp6/q6prm.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
#ifndef __Q6PRM_H__
#define __Q6PRM_H__

#define LPAIF_MI2S_MCLK 1
#define LPAIF_MI2S_BCLK 2
#define LPAIF_MI2S_ECLK 3

/* Clock ID for Primary I2S IBIT */
#define Q6PRM_LPASS_CLK_ID_PRI_MI2S_IBIT 0x100
/* Clock ID for Primary I2S EBIT */
Expand Down Expand Up @@ -52,6 +56,17 @@
/* Clock ID for QUINARY MI2S OSR CLK */
#define Q6PRM_LPASS_CLK_ID_QUI_MI2S_OSR 0x116

/* Clock ID for MCLK1 */
#define Q6PRM_LPASS_CLK_ID_MCLK_1 0x300
/* Clock ID for MCLK2 */
#define Q6PRM_LPASS_CLK_ID_MCLK_2 0x301
/* Clock ID for MCLK3 */
#define Q6PRM_LPASS_CLK_ID_MCLK_3 0x302
/* Clock ID for MCLK4 */
#define Q6PRM_LPASS_CLK_ID_MCLK_4 0x303
/* Clock ID for MCLK5 */
#define Q6PRM_LPASS_CLK_ID_MCLK_5 0x304

#define Q6PRM_LPASS_CLK_ID_WSA_CORE_MCLK 0x305
#define Q6PRM_LPASS_CLK_ID_WSA_CORE_NPL_MCLK 0x306

Expand Down
Loading