ASoC: qdsp6: q6afe: Add support to MI2S ports
This patch adds support to 4 MI2S ports on LPASS. Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org> Reviewed-and-tested-by: Rohit kumar <rohitkr@codeaurora.org> Reviewed-by: Banajit Goswami <bgoswami@codeaurora.org> Signed-off-by: Mark Brown <broonie@kernel.org>
This commit is contained in:
parent
4d430d5a0e
commit
d383914572
|
@ -15,6 +15,10 @@
|
|||
#include <linux/spinlock.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/soc/qcom/apr.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/soc-dai.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include "q6dsp-errno.h"
|
||||
#include "q6core.h"
|
||||
#include "q6afe.h"
|
||||
|
@ -31,6 +35,32 @@
|
|||
#define AFE_PARAM_ID_CDC_SLIMBUS_SLAVE_CFG 0x00010235
|
||||
|
||||
#define AFE_PARAM_ID_SLIMBUS_CONFIG 0x00010212
|
||||
#define AFE_PARAM_ID_I2S_CONFIG 0x0001020D
|
||||
|
||||
/* I2S config specific */
|
||||
#define AFE_API_VERSION_I2S_CONFIG 0x1
|
||||
#define AFE_PORT_I2S_SD0 0x1
|
||||
#define AFE_PORT_I2S_SD1 0x2
|
||||
#define AFE_PORT_I2S_SD2 0x3
|
||||
#define AFE_PORT_I2S_SD3 0x4
|
||||
#define AFE_PORT_I2S_SD0_MASK BIT(0x1)
|
||||
#define AFE_PORT_I2S_SD1_MASK BIT(0x2)
|
||||
#define AFE_PORT_I2S_SD2_MASK BIT(0x3)
|
||||
#define AFE_PORT_I2S_SD3_MASK BIT(0x4)
|
||||
#define AFE_PORT_I2S_SD0_1_MASK GENMASK(2, 1)
|
||||
#define AFE_PORT_I2S_SD2_3_MASK GENMASK(4, 3)
|
||||
#define AFE_PORT_I2S_SD0_1_2_MASK GENMASK(3, 1)
|
||||
#define AFE_PORT_I2S_SD0_1_2_3_MASK GENMASK(4, 1)
|
||||
#define AFE_PORT_I2S_QUAD01 0x5
|
||||
#define AFE_PORT_I2S_QUAD23 0x6
|
||||
#define AFE_PORT_I2S_6CHS 0x7
|
||||
#define AFE_PORT_I2S_8CHS 0x8
|
||||
#define AFE_PORT_I2S_MONO 0x0
|
||||
#define AFE_PORT_I2S_STEREO 0x1
|
||||
#define AFE_PORT_CONFIG_I2S_WS_SRC_EXTERNAL 0x0
|
||||
#define AFE_PORT_CONFIG_I2S_WS_SRC_INTERNAL 0x1
|
||||
#define AFE_LINEAR_PCM_DATA 0x0
|
||||
|
||||
|
||||
/* Port IDs */
|
||||
#define AFE_API_VERSION_HDMI_CONFIG 0x1
|
||||
|
@ -66,6 +96,19 @@
|
|||
#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_6_RX 0x400c
|
||||
/* SLIMbus Tx port on channel 6. */
|
||||
#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_6_TX 0x400d
|
||||
#define AFE_PORT_ID_PRIMARY_MI2S_RX 0x1000
|
||||
#define AFE_PORT_ID_PRIMARY_MI2S_TX 0x1001
|
||||
#define AFE_PORT_ID_SECONDARY_MI2S_RX 0x1002
|
||||
#define AFE_PORT_ID_SECONDARY_MI2S_TX 0x1003
|
||||
#define AFE_PORT_ID_TERTIARY_MI2S_RX 0x1004
|
||||
#define AFE_PORT_ID_TERTIARY_MI2S_TX 0x1005
|
||||
#define AFE_PORT_ID_QUATERNARY_MI2S_RX 0x1006
|
||||
#define AFE_PORT_ID_QUATERNARY_MI2S_TX 0x1007
|
||||
|
||||
#define Q6AFE_LPASS_MODE_CLK1_VALID 1
|
||||
#define Q6AFE_LPASS_MODE_CLK2_VALID 2
|
||||
#define Q6AFE_LPASS_CLK_SRC_INTERNAL 1
|
||||
#define Q6AFE_LPASS_CLK_ROOT_DEFAULT 0
|
||||
|
||||
#define TIMEOUT_MS 1000
|
||||
#define AFE_CMD_RESP_AVAIL 0
|
||||
|
@ -159,10 +202,21 @@ struct afe_param_id_slimbus_cfg {
|
|||
*/
|
||||
} __packed;
|
||||
|
||||
struct afe_param_id_i2s_cfg {
|
||||
u32 i2s_cfg_minor_version;
|
||||
u16 bit_width;
|
||||
u16 channel_mode;
|
||||
u16 mono_stereo;
|
||||
u16 ws_src;
|
||||
u32 sample_rate;
|
||||
u16 data_format;
|
||||
u16 reserved;
|
||||
} __packed;
|
||||
|
||||
union afe_port_config {
|
||||
struct afe_param_id_hdmi_multi_chan_audio_cfg hdmi_multi_ch;
|
||||
struct afe_param_id_slimbus_cfg slim_cfg;
|
||||
struct afe_param_id_i2s_cfg i2s_cfg;
|
||||
} __packed;
|
||||
|
||||
struct q6afe_port {
|
||||
|
@ -206,6 +260,22 @@ static struct afe_port_map port_maps[AFE_PORT_MAX] = {
|
|||
SLIMBUS_5_RX, 1, 1},
|
||||
[SLIMBUS_6_RX] = { AFE_PORT_ID_SLIMBUS_MULTI_CHAN_6_RX,
|
||||
SLIMBUS_6_RX, 1, 1},
|
||||
[PRIMARY_MI2S_RX] = { AFE_PORT_ID_PRIMARY_MI2S_RX,
|
||||
PRIMARY_MI2S_RX, 1, 1},
|
||||
[PRIMARY_MI2S_TX] = { AFE_PORT_ID_PRIMARY_MI2S_TX,
|
||||
PRIMARY_MI2S_RX, 0, 1},
|
||||
[SECONDARY_MI2S_RX] = { AFE_PORT_ID_SECONDARY_MI2S_RX,
|
||||
SECONDARY_MI2S_RX, 1, 1},
|
||||
[SECONDARY_MI2S_TX] = { AFE_PORT_ID_SECONDARY_MI2S_TX,
|
||||
SECONDARY_MI2S_TX, 0, 1},
|
||||
[TERTIARY_MI2S_RX] = { AFE_PORT_ID_TERTIARY_MI2S_RX,
|
||||
TERTIARY_MI2S_RX, 1, 1},
|
||||
[TERTIARY_MI2S_TX] = { AFE_PORT_ID_TERTIARY_MI2S_TX,
|
||||
TERTIARY_MI2S_TX, 0, 1},
|
||||
[QUATERNARY_MI2S_RX] = { AFE_PORT_ID_QUATERNARY_MI2S_RX,
|
||||
QUATERNARY_MI2S_RX, 1, 1},
|
||||
[QUATERNARY_MI2S_TX] = { AFE_PORT_ID_QUATERNARY_MI2S_TX,
|
||||
QUATERNARY_MI2S_TX, 0, 1},
|
||||
};
|
||||
|
||||
static void q6afe_port_free(struct kref *ref)
|
||||
|
@ -481,6 +551,149 @@ void q6afe_hdmi_port_prepare(struct q6afe_port *port,
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(q6afe_hdmi_port_prepare);
|
||||
|
||||
/**
|
||||
* q6afe_i2s_port_prepare() - Prepare i2s afe port.
|
||||
*
|
||||
* @port: Instance of afe port
|
||||
* @cfg: I2S configuration for the afe port
|
||||
* Return: Will be an negative on error and zero on success.
|
||||
*/
|
||||
int q6afe_i2s_port_prepare(struct q6afe_port *port, struct q6afe_i2s_cfg *cfg)
|
||||
{
|
||||
union afe_port_config *pcfg = &port->port_cfg;
|
||||
struct device *dev = port->afe->dev;
|
||||
int num_sd_lines;
|
||||
|
||||
pcfg->i2s_cfg.i2s_cfg_minor_version = AFE_API_VERSION_I2S_CONFIG;
|
||||
pcfg->i2s_cfg.sample_rate = cfg->sample_rate;
|
||||
pcfg->i2s_cfg.bit_width = cfg->bit_width;
|
||||
pcfg->i2s_cfg.data_format = AFE_LINEAR_PCM_DATA;
|
||||
|
||||
switch (cfg->fmt & SND_SOC_DAIFMT_MASTER_MASK) {
|
||||
case SND_SOC_DAIFMT_CBS_CFS:
|
||||
pcfg->i2s_cfg.ws_src = AFE_PORT_CONFIG_I2S_WS_SRC_INTERNAL;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_CBM_CFM:
|
||||
/* CPU is slave */
|
||||
pcfg->i2s_cfg.ws_src = AFE_PORT_CONFIG_I2S_WS_SRC_EXTERNAL;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
num_sd_lines = hweight_long(cfg->sd_line_mask);
|
||||
|
||||
switch (num_sd_lines) {
|
||||
case 0:
|
||||
dev_err(dev, "no line is assigned\n");
|
||||
return -EINVAL;
|
||||
case 1:
|
||||
switch (cfg->sd_line_mask) {
|
||||
case AFE_PORT_I2S_SD0_MASK:
|
||||
pcfg->i2s_cfg.channel_mode = AFE_PORT_I2S_SD0;
|
||||
break;
|
||||
case AFE_PORT_I2S_SD1_MASK:
|
||||
pcfg->i2s_cfg.channel_mode = AFE_PORT_I2S_SD1;
|
||||
break;
|
||||
case AFE_PORT_I2S_SD2_MASK:
|
||||
pcfg->i2s_cfg.channel_mode = AFE_PORT_I2S_SD2;
|
||||
break;
|
||||
case AFE_PORT_I2S_SD3_MASK:
|
||||
pcfg->i2s_cfg.channel_mode = AFE_PORT_I2S_SD3;
|
||||
break;
|
||||
default:
|
||||
dev_err(dev, "Invalid SD lines\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
switch (cfg->sd_line_mask) {
|
||||
case AFE_PORT_I2S_SD0_1_MASK:
|
||||
pcfg->i2s_cfg.channel_mode = AFE_PORT_I2S_QUAD01;
|
||||
break;
|
||||
case AFE_PORT_I2S_SD2_3_MASK:
|
||||
pcfg->i2s_cfg.channel_mode = AFE_PORT_I2S_QUAD23;
|
||||
break;
|
||||
default:
|
||||
dev_err(dev, "Invalid SD lines\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
switch (cfg->sd_line_mask) {
|
||||
case AFE_PORT_I2S_SD0_1_2_MASK:
|
||||
pcfg->i2s_cfg.channel_mode = AFE_PORT_I2S_6CHS;
|
||||
break;
|
||||
default:
|
||||
dev_err(dev, "Invalid SD lines\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
switch (cfg->sd_line_mask) {
|
||||
case AFE_PORT_I2S_SD0_1_2_3_MASK:
|
||||
pcfg->i2s_cfg.channel_mode = AFE_PORT_I2S_8CHS;
|
||||
|
||||
break;
|
||||
default:
|
||||
dev_err(dev, "Invalid SD lines\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
dev_err(dev, "Invalid SD lines\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (cfg->num_channels) {
|
||||
case 1:
|
||||
case 2:
|
||||
switch (pcfg->i2s_cfg.channel_mode) {
|
||||
case AFE_PORT_I2S_QUAD01:
|
||||
case AFE_PORT_I2S_6CHS:
|
||||
case AFE_PORT_I2S_8CHS:
|
||||
pcfg->i2s_cfg.channel_mode = AFE_PORT_I2S_SD0;
|
||||
break;
|
||||
case AFE_PORT_I2S_QUAD23:
|
||||
pcfg->i2s_cfg.channel_mode = AFE_PORT_I2S_SD2;
|
||||
break;
|
||||
}
|
||||
|
||||
if (cfg->num_channels == 2)
|
||||
pcfg->i2s_cfg.mono_stereo = AFE_PORT_I2S_STEREO;
|
||||
else
|
||||
pcfg->i2s_cfg.mono_stereo = AFE_PORT_I2S_MONO;
|
||||
|
||||
break;
|
||||
case 3:
|
||||
case 4:
|
||||
if (pcfg->i2s_cfg.channel_mode < AFE_PORT_I2S_QUAD01) {
|
||||
dev_err(dev, "Invalid Channel mode\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
case 5:
|
||||
case 6:
|
||||
if (pcfg->i2s_cfg.channel_mode < AFE_PORT_I2S_6CHS) {
|
||||
dev_err(dev, "Invalid Channel mode\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
case 7:
|
||||
case 8:
|
||||
if (pcfg->i2s_cfg.channel_mode < AFE_PORT_I2S_8CHS) {
|
||||
dev_err(dev, "Invalid Channel mode\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(q6afe_i2s_port_prepare);
|
||||
|
||||
/**
|
||||
* q6afe_port_start() - Start a afe port
|
||||
*
|
||||
|
@ -580,6 +793,17 @@ struct q6afe_port *q6afe_port_get_from_id(struct device *dev, int id)
|
|||
case AFE_PORT_ID_SLIMBUS_MULTI_CHAN_6_RX:
|
||||
cfg_type = AFE_PARAM_ID_SLIMBUS_CONFIG;
|
||||
break;
|
||||
|
||||
case AFE_PORT_ID_PRIMARY_MI2S_RX:
|
||||
case AFE_PORT_ID_PRIMARY_MI2S_TX:
|
||||
case AFE_PORT_ID_SECONDARY_MI2S_RX:
|
||||
case AFE_PORT_ID_SECONDARY_MI2S_TX:
|
||||
case AFE_PORT_ID_TERTIARY_MI2S_RX:
|
||||
case AFE_PORT_ID_TERTIARY_MI2S_TX:
|
||||
case AFE_PORT_ID_QUATERNARY_MI2S_RX:
|
||||
case AFE_PORT_ID_QUATERNARY_MI2S_TX:
|
||||
cfg_type = AFE_PARAM_ID_I2S_CONFIG;
|
||||
break;
|
||||
default:
|
||||
dev_err(dev, "Invalid port id 0x%x\n", port_id);
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
#define MSM_AFE_PORT_TYPE_TX 1
|
||||
#define AFE_MAX_PORTS AFE_PORT_MAX
|
||||
|
||||
#define Q6AFE_MAX_MI2S_LINES 4
|
||||
|
||||
#define AFE_MAX_CHAN_COUNT 8
|
||||
#define AFE_PORT_MAX_AUDIO_CHAN_CNT 0x8
|
||||
|
||||
|
@ -29,9 +31,19 @@ struct q6afe_slim_cfg {
|
|||
u8 ch_mapping[AFE_MAX_CHAN_COUNT];
|
||||
};
|
||||
|
||||
struct q6afe_i2s_cfg {
|
||||
u32 sample_rate;
|
||||
u16 bit_width;
|
||||
u16 data_format;
|
||||
u16 num_channels;
|
||||
u32 sd_line_mask;
|
||||
int fmt;
|
||||
};
|
||||
|
||||
struct q6afe_port_config {
|
||||
struct q6afe_hdmi_cfg hdmi;
|
||||
struct q6afe_slim_cfg slim;
|
||||
struct q6afe_i2s_cfg i2s_cfg;
|
||||
};
|
||||
|
||||
struct q6afe_port;
|
||||
|
@ -45,5 +57,6 @@ void q6afe_hdmi_port_prepare(struct q6afe_port *port,
|
|||
struct q6afe_hdmi_cfg *cfg);
|
||||
void q6afe_slim_port_prepare(struct q6afe_port *port,
|
||||
struct q6afe_slim_cfg *cfg);
|
||||
int q6afe_i2s_port_prepare(struct q6afe_port *port, struct q6afe_i2s_cfg *cfg);
|
||||
|
||||
#endif /* __Q6AFE_H__ */
|
||||
|
|
Loading…
Reference in New Issue