aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sound/soc/qcom/qdsp6/q6afe.c248
1 files changed, 248 insertions, 0 deletions
diff --git a/sound/soc/qcom/qdsp6/q6afe.c b/sound/soc/qcom/qdsp6/q6afe.c
index 01f43218984b9..22d543b229b78 100644
--- a/sound/soc/qcom/qdsp6/q6afe.c
+++ b/sound/soc/qcom/qdsp6/q6afe.c
@@ -32,6 +32,7 @@
#define AFE_PARAM_ID_HDMI_CONFIG 0x00010210
#define AFE_MODULE_AUDIO_DEV_INTERFACE 0x0001020C
#define AFE_MODULE_TDM 0x0001028A
+#define AFE_MODULE_GROUP_DEVICE 0x00010254
#define AFE_PARAM_ID_CDC_SLIMBUS_SLAVE_CFG 0x00010235
@@ -41,6 +42,8 @@
#define AFE_PARAM_ID_SLIMBUS_CONFIG 0x00010212
#define AFE_PARAM_ID_I2S_CONFIG 0x0001020D
#define AFE_PARAM_ID_TDM_CONFIG 0x0001029D
+#define AFE_PARAM_ID_GROUP_DEVICE_ENABLE 0x00010256
+#define AFE_PARAM_ID_GROUP_DEVICE_TDM_CONFIG 0x0001029E
#define AFE_PARAM_ID_PORT_SLOT_MAPPING_CONFIG 0x00010297
/* I2S config specific */
@@ -298,16 +301,40 @@
#define AFE_PORT_ID_QUINARY_TDM_TX_7 \
(AFE_PORT_ID_QUINARY_TDM_TX + 0x0E)
+#define AFE_GROUP_DEVICE_ID_PRIMARY_TDM_RX \
+ (AFE_PORT_ID_PRIMARY_TDM_RX + 0x100)
+#define AFE_GROUP_DEVICE_ID_PRIMARY_TDM_TX \
+ (AFE_PORT_ID_PRIMARY_TDM_TX + 0x100)
+#define AFE_GROUP_DEVICE_ID_SECONDARY_TDM_RX \
+ (AFE_PORT_ID_SECONDARY_TDM_RX + 0x100)
+#define AFE_GROUP_DEVICE_ID_SECONDARY_TDM_TX \
+ (AFE_PORT_ID_SECONDARY_TDM_TX + 0x100)
+#define AFE_GROUP_DEVICE_ID_TERTIARY_TDM_RX \
+ (AFE_PORT_ID_TERTIARY_TDM_RX + 0x100)
+#define AFE_GROUP_DEVICE_ID_TERTIARY_TDM_TX \
+ (AFE_PORT_ID_TERTIARY_TDM_TX + 0x100)
+#define AFE_GROUP_DEVICE_ID_QUATERNARY_TDM_RX \
+ (AFE_PORT_ID_QUATERNARY_TDM_RX + 0x100)
+#define AFE_GROUP_DEVICE_ID_QUATERNARY_TDM_TX \
+ (AFE_PORT_ID_QUATERNARY_TDM_TX + 0x100)
+#define AFE_GROUP_DEVICE_ID_QUINARY_TDM_RX \
+ (AFE_PORT_ID_QUINARY_TDM_RX + 0x100)
+#define AFE_GROUP_DEVICE_ID_QUINARY_TDM_TX \
+ (AFE_PORT_ID_QUINARY_TDM_TX + 0x100)
+
#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 AFE_API_VERSION_TDM_CONFIG 1
#define AFE_API_VERSION_SLOT_MAPPING_CONFIG 1
+#define AFE_API_VERSION_GROUP_DEVICE_TDM_CONFIG 0x1
+#define AFE_GROUP_DEVICE_NUM_PORTS 8
#define TIMEOUT_MS 1000
#define AFE_CMD_RESP_AVAIL 0
#define AFE_CMD_RESP_NONE 1
+#define AFE_PORT_INVALID 0xFFFF
struct q6afe {
struct apr_device *apr;
@@ -315,7 +342,9 @@ struct q6afe {
struct q6core_svc_api_info ainfo;
struct mutex lock;
struct list_head port_list;
+ struct list_head group_list;
spinlock_t port_list_lock;
+ spinlock_t group_list_lock;
struct platform_device *pdev_dais;
};
@@ -465,6 +494,44 @@ struct afe_clk_set {
uint32_t enable;
};
+struct afe_group_device_group_cfg {
+ u32 minor_version;
+ u16 group_id;
+ u16 num_channels;
+ u16 port_id[8];
+} __packed;
+
+struct afe_group_device_enable {
+ u16 group_id;
+ u16 enable;
+} __packed;
+
+struct afe_param_id_group_device_tdm_cfg {
+ u32 group_device_cfg_minor_version;
+ u16 group_id;
+ u16 reserved;
+ u16 port_id[AFE_GROUP_DEVICE_NUM_PORTS];
+ u32 num_channels;
+ u32 sample_rate;
+ u32 bit_width;
+ u16 nslots_per_frame;
+ u16 slot_width;
+ u32 slot_mask;
+} __packed;
+
+struct q6afe_port_group {
+ int token;
+ int id;
+ int next_index;
+ struct kref refcount;
+ struct q6afe *afe;
+ struct afe_group_device_group_cfg group_cfg;
+ struct afe_group_device_enable group_enable;
+ struct afe_param_id_group_device_tdm_cfg tdm_cfg;
+ struct list_head node;
+
+};
+
struct afe_param_id_slot_mapping_cfg {
u32 minor_version;
u16 num_channels;
@@ -480,6 +547,7 @@ struct q6afe_port {
struct aprv2_ibasic_rsp_result_t result;
int token;
int id;
+ struct q6afe_port_group *grp;
int cfg_type;
struct q6afe *afe;
struct kref refcount;
@@ -995,6 +1063,63 @@ int q6afe_port_set_sysclk(struct q6afe_port *port, int clk_id,
}
EXPORT_SYMBOL_GPL(q6afe_port_set_sysclk);
+static int q6afe_port_group_set_param(struct q6afe_port *port)
+
+{
+ struct q6afe_port_group *grp = port->grp;
+
+ return q6afe_port_set_param(port, &grp->tdm_cfg,
+ AFE_PARAM_ID_GROUP_DEVICE_TDM_CONFIG,
+ AFE_MODULE_GROUP_DEVICE,
+ sizeof(grp->tdm_cfg));
+
+}
+
+/**
+ * q6afe_group_enable() - Enable afe port group
+ *
+ * @port: Instance of port to start
+ * @enable: enable/disable boollean flag.
+ *
+ * Return: Will be an negative on packet size on success.
+ */
+static int q6afe_port_group_enable(struct q6afe_port *port, bool enable)
+{
+ struct q6afe_port_group *grp = port->grp;
+ struct afe_group_device_enable group_enable;
+ int ret;
+
+ if (enable) {
+ ret = q6afe_port_group_set_param(port);
+ if (ret < 0) {
+ pr_err("%s: afe send failed %d\n", __func__, ret);
+ return ret;
+ }
+ }
+
+ group_enable.group_id = grp->id;
+ group_enable.enable = enable;
+
+ return q6afe_port_set_param(port, &group_enable,
+ AFE_PARAM_ID_GROUP_DEVICE_ENABLE,
+ AFE_MODULE_GROUP_DEVICE,
+ sizeof(group_enable));
+}
+
+static void q6afe_port_group_free(struct kref *ref)
+{
+ struct q6afe_port_group *g;
+ struct q6afe *afe;
+ unsigned long flags;
+
+ g = container_of(ref, struct q6afe_port_group, refcount);
+ afe = g->afe;
+
+ spin_lock_irqsave(&afe->group_list_lock, flags);
+ list_del(&g->node);
+ spin_unlock_irqrestore(&afe->group_list_lock, flags);
+ kfree(g);
+}
/**
* q6afe_port_stop() - Stop a afe port
*
@@ -1042,6 +1167,13 @@ int q6afe_port_stop(struct q6afe_port *port)
if (ret)
dev_err(afe->dev, "AFE close failed %d\n", ret);
+ if (port->grp) {
+ if (kref_read(&port->grp->refcount) == 1)
+ q6afe_port_group_enable(port, false);
+
+ kref_put(&port->grp->refcount, q6afe_port_group_free);
+ }
+
kfree(pkt);
return ret;
}
@@ -1083,6 +1215,7 @@ void q6afe_tdm_port_prepare(struct q6afe_port *port,
struct q6afe_tdm_cfg *cfg)
{
union afe_port_config *pcfg = &port->port_cfg;
+ struct q6afe_port_group *g;
pcfg->tdm_cfg.tdm_cfg_minor_version = AFE_API_VERSION_TDM_CONFIG;
pcfg->tdm_cfg.num_channels = cfg->num_channels;
@@ -1105,6 +1238,18 @@ void q6afe_tdm_port_prepare(struct q6afe_port *port,
port->scfg->data_align_type = cfg->data_align_type;
memcpy(port->scfg->ch_mapping, cfg->ch_mapping,
sizeof(u16) * AFE_PORT_MAX_AUDIO_CHAN_CNT);
+ g = port->grp;
+ if (!g) {
+ dev_err(port->afe->dev, "Unable to find Group\n");
+ return;
+ }
+
+ g->tdm_cfg.bit_width = cfg->slot_width;
+ g->tdm_cfg.num_channels = cfg->nslots_per_frame;
+ g->tdm_cfg.sample_rate = cfg->sample_rate;
+ g->tdm_cfg.nslots_per_frame = cfg->nslots_per_frame;
+ g->tdm_cfg.slot_width = cfg->slot_width;
+ g->tdm_cfg.slot_mask = cfg->slot_mask;
}
EXPORT_SYMBOL_GPL(q6afe_tdm_port_prepare);
@@ -1282,6 +1427,7 @@ EXPORT_SYMBOL_GPL(q6afe_i2s_port_prepare);
int q6afe_port_start(struct q6afe_port *port)
{
struct afe_port_cmd_device_start *start;
+ struct q6afe_port_group *grp = port->grp;
struct q6afe *afe = port->afe;
int port_id = port->id;
int ret, param_id = port->cfg_type;
@@ -1289,6 +1435,11 @@ int q6afe_port_start(struct q6afe_port *port)
int pkt_size;
void *p;
+ if (port->grp) {
+ if (kref_read(&grp->refcount) == 1)
+ q6afe_port_group_enable(port, true);
+ }
+
ret = q6afe_port_set_param_v2(port, &port->port_cfg, param_id,
AFE_MODULE_AUDIO_DEV_INTERFACE,
sizeof(port->port_cfg));
@@ -1338,6 +1489,88 @@ int q6afe_port_start(struct q6afe_port *port)
}
EXPORT_SYMBOL_GPL(q6afe_port_start);
+
+static int q6afe_get_port_group_id(int id)
+{
+ int gid;
+
+ switch (id) {
+ case AFE_PORT_ID_PRIMARY_TDM_RX ... AFE_PORT_ID_PRIMARY_TDM_TX_7:
+ gid = (id & 0x2) ? AFE_GROUP_DEVICE_ID_PRIMARY_TDM_TX :
+ AFE_GROUP_DEVICE_ID_PRIMARY_TDM_RX;
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_RX ... AFE_PORT_ID_SECONDARY_TDM_TX_7:
+ gid = (id & 0x2) ? AFE_GROUP_DEVICE_ID_SECONDARY_TDM_TX :
+ AFE_GROUP_DEVICE_ID_SECONDARY_TDM_RX;
+
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_RX ... AFE_PORT_ID_TERTIARY_TDM_TX_7:
+ gid = (id & 0x2) ? AFE_GROUP_DEVICE_ID_TERTIARY_TDM_TX :
+ AFE_GROUP_DEVICE_ID_TERTIARY_TDM_RX;
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX ... AFE_PORT_ID_QUATERNARY_TDM_TX_7:
+ gid = (id & 0x2) ? AFE_GROUP_DEVICE_ID_QUATERNARY_TDM_TX :
+ AFE_GROUP_DEVICE_ID_QUATERNARY_TDM_RX;
+ break;
+ case AFE_PORT_ID_QUINARY_TDM_RX ... AFE_PORT_ID_QUINARY_TDM_TX_7:
+ gid = (id & 0x2) ? AFE_GROUP_DEVICE_ID_QUINARY_TDM_TX :
+ AFE_GROUP_DEVICE_ID_QUINARY_TDM_RX;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return gid;
+}
+
+static struct q6afe_port_group *q6afe_add_port_group(struct q6afe_port *port)
+{
+ struct q6afe_port_group *g = NULL;
+ struct q6afe_port_group *grp = NULL;
+ struct q6afe *afe = port->afe;
+ unsigned long flags;
+ int i, gid = q6afe_get_port_group_id(port->id);
+
+ spin_lock_irqsave(&afe->group_list_lock, flags);
+
+ list_for_each_entry(grp, &afe->group_list, node) {
+ if (gid == grp->id) {
+ g = grp;
+ break;
+ }
+ }
+
+ if (!g) {
+ g = kzalloc(sizeof(*g), GFP_ATOMIC);
+ if (!g) {
+ spin_unlock_irqrestore(&afe->group_list_lock, flags);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ kref_init(&g->refcount);
+ g->afe = afe;
+ g->id = gid;
+
+ /* set all the ports to invalid state */
+ for (i = 0; i < AFE_GROUP_DEVICE_NUM_PORTS; i++)
+ g->tdm_cfg.port_id[i] = AFE_PORT_INVALID;
+
+ g->tdm_cfg.group_device_cfg_minor_version =
+ AFE_API_VERSION_GROUP_DEVICE_TDM_CONFIG;
+ g->tdm_cfg.group_id = gid;
+ g->group_enable.group_id = gid;
+
+ list_add_tail(&g->node, &afe->group_list);
+ } else {
+ kref_get(&g->refcount);
+ }
+
+ g->tdm_cfg.port_id[g->next_index++] = port->id;
+
+ spin_unlock_irqrestore(&afe->group_list_lock, flags);
+
+ return g;
+}
/**
* q6afe_port_get_from_id() - Get port instance from a port id
*
@@ -1354,6 +1587,7 @@ struct q6afe_port *q6afe_port_get_from_id(struct device *dev, int id)
struct q6afe_port *port;
unsigned long flags;
int cfg_type;
+ bool has_group = false;
if (id < 0 || id > AFE_PORT_MAX) {
dev_err(dev, "AFE port token[%d] invalid!\n", id);
@@ -1395,6 +1629,7 @@ struct q6afe_port *q6afe_port_get_from_id(struct device *dev, int id)
break;
case AFE_PORT_ID_PRIMARY_TDM_RX ... AFE_PORT_ID_QUINARY_TDM_TX_7:
cfg_type = AFE_PARAM_ID_TDM_CONFIG;
+ has_group = true;
break;
default:
@@ -1418,6 +1653,17 @@ struct q6afe_port *q6afe_port_get_from_id(struct device *dev, int id)
list_add_tail(&port->node, &afe->port_list);
spin_unlock_irqrestore(&afe->port_list_lock, flags);
+ if (has_group) {
+ struct q6afe_port_group *g;
+
+ g = q6afe_add_port_group(port);
+ if (IS_ERR(g)) {
+ kref_put(&port->refcount, q6afe_port_free);
+ return ERR_CAST(g);
+ }
+ port->grp = g;
+ }
+
return port;
}
@@ -1449,7 +1695,9 @@ static int q6afe_probe(struct apr_device *adev)
mutex_init(&afe->lock);
afe->dev = dev;
INIT_LIST_HEAD(&afe->port_list);
+ INIT_LIST_HEAD(&afe->group_list);
spin_lock_init(&afe->port_list_lock);
+ spin_lock_init(&afe->group_list_lock);
dev_set_drvdata(dev, afe);