diff options
-rw-r--r-- | sound/soc/qcom/qdsp6/q6afe.c | 248 |
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); |