ASoC: wcd9xxx: Move to a generic framework for codec core driver.

The current core assumes TABLA as the only codec driver registering.
To support single binary for multiple targets its essential that
we remove this restriction and move to a generic framework
to support multiple codec. This can be done by moving all codec
specific code to dedicated codec driver and use core driver to probe
the codec based on slimbus device id and do generic setup for the
codec. This also helps to have same boards with different flavours
of codec variants.

The WCD9XXX family of codecs share the initial codec register
mapping which holds the Slimbus device id to identify the
codec existing on the target.Core driver now registers the
codec device based on this check.

Change-Id: I4c43d5f04c20696f4f5138411460681ec7879d34
Signed-off-by: Asish Bhattacharya <asishb@codeaurora.org>
diff --git a/drivers/mfd/wcd9xxx-slimslave.c b/drivers/mfd/wcd9xxx-slimslave.c
new file mode 100644
index 0000000..acef55e
--- /dev/null
+++ b/drivers/mfd/wcd9xxx-slimslave.c
@@ -0,0 +1,528 @@
+/* Copyright (c) 2012, Code Aurora Forum. 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.
+ */
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/mfd/wcd9xxx/wcd9xxx-slimslave.h>
+
+struct wcd9xxx_slim_sch_rx {
+	u32 sph;
+	u32 ch_num;
+	u16 ch_h;
+	u16 grph;
+};
+
+struct wcd9xxx_slim_sch_tx {
+	u32 sph;
+	u32 ch_num;
+	u16 ch_h;
+	u16 grph;
+};
+
+struct wcd9xxx_slim_sch {
+	struct wcd9xxx_slim_sch_rx rx[SLIM_MAX_RX_PORTS];
+	struct wcd9xxx_slim_sch_tx tx[SLIM_MAX_TX_PORTS];
+};
+
+static struct wcd9xxx_slim_sch sh_ch;
+
+static int wcd9xxx_alloc_slim_sh_ch_rx(struct wcd9xxx *wcd9xxx,
+					u8 wcd9xxx_pgd_la);
+static int wcd9xxx_alloc_slim_sh_ch_tx(struct wcd9xxx *wcd9xxx,
+					u8 wcd9xxx_pgd_la);
+static int wcd9xxx_dealloc_slim_sh_ch_rx(struct wcd9xxx *wcd9xxx);
+static int wcd9xxx_dealloc_slim_sh_ch_tx(struct wcd9xxx *wcd9xxx);
+
+int wcd9xxx_init_slimslave(struct wcd9xxx *wcd9xxx, u8 wcd9xxx_pgd_la)
+{
+	int ret = 0;
+
+	ret = wcd9xxx_alloc_slim_sh_ch_rx(wcd9xxx, wcd9xxx_pgd_la);
+	if (ret) {
+		pr_err("%s: Failed to alloc rx slimbus shared channels\n",
+								__func__);
+		goto rx_err;
+	}
+	ret = wcd9xxx_alloc_slim_sh_ch_tx(wcd9xxx, wcd9xxx_pgd_la);
+	if (ret) {
+		pr_err("%s: Failed to alloc tx slimbus shared channels\n",
+								__func__);
+		goto tx_err;
+	}
+	return 0;
+tx_err:
+	wcd9xxx_dealloc_slim_sh_ch_rx(wcd9xxx);
+rx_err:
+	return ret;
+}
+
+
+int wcd9xxx_deinit_slimslave(struct wcd9xxx *wcd9xxx)
+{
+	int ret = 0;
+	ret = wcd9xxx_dealloc_slim_sh_ch_rx(wcd9xxx);
+	if (ret < 0) {
+		pr_err("%s: fail to dealloc rx slim ports\n", __func__);
+		goto err;
+	}
+	ret = wcd9xxx_dealloc_slim_sh_ch_tx(wcd9xxx);
+	if (ret < 0) {
+		pr_err("%s: fail to dealloc tx slim ports\n", __func__);
+		goto err;
+	}
+err:
+	return ret;
+}
+
+int wcd9xxx_get_channel(struct wcd9xxx *wcd9xxx,
+		unsigned int *rx_ch,
+		unsigned int *tx_ch)
+{
+	int ch_idx = 0;
+	struct wcd9xxx_slim_sch_rx *rx = sh_ch.rx;
+	struct wcd9xxx_slim_sch_tx *tx = sh_ch.tx;
+
+	for (ch_idx = 0; ch_idx < SLIM_MAX_RX_PORTS; ch_idx++)
+		rx_ch[ch_idx] = rx[ch_idx].ch_num;
+	for (ch_idx = 0; ch_idx < SLIM_MAX_TX_PORTS; ch_idx++)
+		tx_ch[ch_idx] = tx[ch_idx].ch_num;
+	return 0;
+}
+
+static int wcd9xxx_alloc_slim_sh_ch_rx(struct wcd9xxx *wcd9xxx,
+			u8 wcd9xxx_pgd_la)
+{
+	int ret = 0;
+	u8 ch_idx ;
+	u16 slave_port_id = 0;
+	struct wcd9xxx_slim_sch_rx *rx = sh_ch.rx;
+
+	/*
+	 * DSP requires channel number to be between 128 and 255.
+	 */
+	pr_debug("%s: pgd_la[%d]\n", __func__, wcd9xxx_pgd_la);
+	for (ch_idx = 0; ch_idx < SLIM_MAX_RX_PORTS; ch_idx++) {
+		slave_port_id = (ch_idx + 1 +
+				SB_PGD_OFFSET_OF_RX_SLAVE_DEV_PORTS);
+		rx[ch_idx].ch_num = slave_port_id + BASE_CH_NUM;
+		ret = slim_get_slaveport(wcd9xxx_pgd_la, slave_port_id,
+					&rx[ch_idx].sph, SLIM_SINK);
+		if (ret < 0) {
+			pr_err("%s: slave port failure id[%d] ret[%d]\n",
+					__func__, slave_port_id, ret);
+			goto err;
+		}
+
+		ret = slim_query_ch(wcd9xxx->slim, rx[ch_idx].ch_num,
+							&rx[ch_idx].ch_h);
+		if (ret < 0) {
+			pr_err("%s: slim_query_ch failed ch-num[%d] ret[%d]\n",
+					__func__, rx[ch_idx].ch_num, ret);
+			goto err;
+		}
+	}
+err:
+	return ret;
+}
+
+static int wcd9xxx_alloc_slim_sh_ch_tx(struct wcd9xxx *wcd9xxx,
+			u8 wcd9xxx_pgd_la)
+{
+	int ret = 0;
+	u8 ch_idx ;
+	struct wcd9xxx_slim_sch_tx *tx = sh_ch.tx;
+	u16 slave_port_id = 0;
+
+	pr_err("%s: pgd_la[%d]\n", __func__, wcd9xxx_pgd_la);
+	/* DSP requires channel number to be between 128 and 255. For RX port
+	 * use channel numbers from 138 to 144, for TX port
+	 * use channel numbers from 128 to 137
+	 */
+	for (ch_idx = 0; ch_idx < SLIM_MAX_TX_PORTS; ch_idx++) {
+		slave_port_id = ch_idx;
+		tx[ch_idx].ch_num = slave_port_id + BASE_CH_NUM;
+		ret = slim_get_slaveport(wcd9xxx_pgd_la, slave_port_id,
+					&tx[ch_idx].sph, SLIM_SRC);
+		if (ret < 0) {
+			pr_err("%s: slave port failure id[%d] ret[%d]\n",
+					__func__, slave_port_id, ret);
+			goto err;
+		}
+		ret = slim_query_ch(wcd9xxx->slim, tx[ch_idx].ch_num,
+							&tx[ch_idx].ch_h);
+		if (ret < 0) {
+			pr_err("%s: slim_query_ch failed ch-num[%d] ret[%d]\n",
+					__func__, tx[ch_idx].ch_num, ret);
+			goto err;
+		}
+	}
+err:
+	return ret;
+}
+
+static int wcd9xxx_dealloc_slim_sh_ch_rx(struct wcd9xxx *wcd9xxx)
+{
+	int idx = 0;
+	int ret = 0;
+	struct wcd9xxx_slim_sch_rx *rx = sh_ch.rx;
+	/* slim_dealloc_ch */
+	for (idx = 0; idx < SLIM_MAX_RX_PORTS; idx++) {
+		ret = slim_dealloc_ch(wcd9xxx->slim, rx[idx].ch_h);
+		if (ret < 0) {
+			pr_err("%s: slim_dealloc_ch fail ret[%d] ch_h[%d]\n",
+				__func__, ret, rx[idx].ch_h);
+		}
+	}
+	memset(sh_ch.rx, 0, sizeof(sh_ch.rx));
+	return ret;
+}
+
+static int wcd9xxx_dealloc_slim_sh_ch_tx(struct wcd9xxx *wcd9xxx)
+{
+	int idx = 0;
+	int ret = 0;
+	struct wcd9xxx_slim_sch_tx *tx = sh_ch.tx;
+	/* slim_dealloc_ch */
+	for (idx = 0; idx < SLIM_MAX_TX_PORTS; idx++) {
+		ret = slim_dealloc_ch(wcd9xxx->slim, tx[idx].ch_h);
+		if (ret < 0) {
+			pr_err("%s: slim_dealloc_ch fail ret[%d] ch_h[%d]\n",
+				__func__, ret, tx[idx].ch_h);
+		}
+	}
+	memset(sh_ch.tx, 0, sizeof(sh_ch.tx));
+	return ret;
+}
+
+/* Enable slimbus slave device for RX path */
+int wcd9xxx_cfg_slim_sch_rx(struct wcd9xxx *wcd9xxx, unsigned int *ch_num,
+				unsigned int ch_cnt, unsigned int rate)
+{
+	u8 i = 0;
+	u16 grph;
+	u32 sph[SLIM_MAX_RX_PORTS] = {0};
+	u16 ch_h[SLIM_MAX_RX_PORTS] = {0};
+	u16 slave_port_id;
+	u8  payload_rx = 0, wm_payload = 0;
+	int ret, idx = 0;
+	unsigned short  multi_chan_cfg_reg_addr;
+	struct wcd9xxx_slim_sch_rx *rx = sh_ch.rx;
+	struct slim_ch prop;
+
+	/* Configure slave interface device */
+	pr_err("%s: ch_cnt[%d] rate=%d\n", __func__, ch_cnt, rate);
+
+	for (i = 0; i < ch_cnt; i++) {
+		idx = (ch_num[i] - BASE_CH_NUM -
+			SB_PGD_OFFSET_OF_RX_SLAVE_DEV_PORTS - 1);
+		ch_h[i] = rx[idx].ch_h;
+		sph[i] = rx[idx].sph;
+		slave_port_id = idx + 1;
+		if ((slave_port_id > SB_PGD_MAX_NUMBER_OF_RX_SLAVE_DEV_PORTS) ||
+			(slave_port_id == 0)) {
+			pr_err("Slimbus: invalid slave port id: %d",
+							slave_port_id);
+			ret = -EINVAL;
+			goto err;
+		}
+		slave_port_id += SB_PGD_OFFSET_OF_RX_SLAVE_DEV_PORTS;
+		/* look for the valid port range and chose the
+		 * payload accordingly
+		 */
+		if ((slave_port_id >
+				SB_PGD_TX_PORT_MULTI_CHANNEL_1_END_PORT_ID) &&
+			(slave_port_id <=
+			 SB_PGD_RX_PORT_MULTI_CHANNEL_0_END_PORT_ID)) {
+				payload_rx = payload_rx  |
+				(1 <<
+				(slave_port_id -
+				SB_PGD_RX_PORT_MULTI_CHANNEL_0_START_PORT_ID));
+		} else {
+			ret = -EINVAL;
+			goto err;
+		}
+		multi_chan_cfg_reg_addr =
+				SB_PGD_RX_PORT_MULTI_CHANNEL_0(slave_port_id);
+		/* write to interface device */
+		ret = wcd9xxx_interface_reg_write(wcd9xxx,
+				multi_chan_cfg_reg_addr,
+				payload_rx);
+		if (ret < 0) {
+			pr_err("%s:Intf-dev fail reg[%d] payload[%d] ret[%d]\n",
+							__func__,
+							multi_chan_cfg_reg_addr,
+							payload_rx, ret);
+			goto err;
+		}
+		/* configure the slave port for water mark and enable*/
+		wm_payload = (SLAVE_PORT_WATER_MARK_VALUE <<
+				SLAVE_PORT_WATER_MARK_SHIFT) +
+				SLAVE_PORT_ENABLE;
+		ret = wcd9xxx_interface_reg_write(wcd9xxx,
+				SB_PGD_PORT_CFG_BYTE_ADDR(slave_port_id),
+				wm_payload);
+		if (ret < 0) {
+			pr_err("%s:watermark set failure for port[%d] ret[%d]",
+						__func__, slave_port_id, ret);
+		}
+	}
+
+	/* slim_define_ch api */
+	prop.prot = SLIM_AUTO_ISO;
+	prop.baser = SLIM_RATE_4000HZ;
+	prop.dataf = SLIM_CH_DATAF_NOT_DEFINED;
+	prop.auxf = SLIM_CH_AUXF_NOT_APPLICABLE;
+	prop.ratem = (rate/4000);
+	prop.sampleszbits = 16;
+
+	ret = slim_define_ch(wcd9xxx->slim, &prop, ch_h, ch_cnt,
+					true, &grph);
+	if (ret < 0) {
+		pr_err("%s: slim_define_ch failed ret[%d]\n",
+					__func__, ret);
+		goto err;
+	}
+	for (i = 0; i < ch_cnt; i++) {
+		ret = slim_connect_sink(wcd9xxx->slim, &sph[i],
+							1, ch_h[i]);
+		if (ret < 0) {
+			pr_err("%s: slim_connect_sink failed ret[%d]\n",
+						__func__, ret);
+			goto err_close_slim_sch;
+		}
+	}
+	/* slim_control_ch */
+	ret = slim_control_ch(wcd9xxx->slim, grph, SLIM_CH_ACTIVATE,
+					true);
+	if (ret < 0) {
+		pr_err("%s: slim_control_ch failed ret[%d]\n",
+				__func__, ret);
+		goto err_close_slim_sch;
+	}
+	for (i = 0; i < ch_cnt; i++) {
+		idx = (ch_num[i] - BASE_CH_NUM -
+				SB_PGD_OFFSET_OF_RX_SLAVE_DEV_PORTS - 1);
+		rx[idx].grph = grph;
+	}
+	return 0;
+
+err_close_slim_sch:
+	/*  release all acquired handles */
+	wcd9xxx_close_slim_sch_rx(wcd9xxx, ch_num, ch_cnt);
+err:
+	return ret;
+}
+EXPORT_SYMBOL_GPL(wcd9xxx_cfg_slim_sch_rx);
+
+/* Enable slimbus slave device for RX path */
+int wcd9xxx_cfg_slim_sch_tx(struct wcd9xxx *wcd9xxx, unsigned int *ch_num,
+				unsigned int ch_cnt, unsigned int rate)
+{
+	u8 i = 0;
+	u8  payload_tx_0 = 0, payload_tx_1 = 0, wm_payload = 0;
+	u16 grph;
+	u32 sph[SLIM_MAX_TX_PORTS] = {0};
+	u16 ch_h[SLIM_MAX_TX_PORTS] = {0};
+	u16 idx = 0, slave_port_id;
+	int ret = 0;
+	unsigned short  multi_chan_cfg_reg_addr;
+
+	struct wcd9xxx_slim_sch_tx *tx = sh_ch.tx;
+	struct slim_ch prop;
+
+	pr_debug("%s: ch_cnt[%d] rate[%d]\n", __func__, ch_cnt, rate);
+	for (i = 0; i < ch_cnt; i++) {
+		idx = (ch_num[i] - BASE_CH_NUM);
+		ch_h[i] = tx[idx].ch_h;
+		sph[i] = tx[idx].sph;
+		slave_port_id = idx ;
+		if ((slave_port_id > SB_PGD_MAX_NUMBER_OF_TX_SLAVE_DEV_PORTS) ||
+		(slave_port_id == 0)) {
+			pr_err("SLIMbus: invalid slave port id: %d",
+							slave_port_id);
+			ret = -EINVAL;
+			goto err;
+		}
+		/* look for the valid port range and chose the
+		 *  payload accordingly
+		 */
+		if (slave_port_id <=
+			SB_PGD_TX_PORT_MULTI_CHANNEL_0_END_PORT_ID) {
+			payload_tx_0 = payload_tx_0 | (1 << slave_port_id);
+		} else if (slave_port_id <
+				SB_PGD_TX_PORT_MULTI_CHANNEL_1_END_PORT_ID) {
+				payload_tx_1 = payload_tx_1 |
+				(1 <<
+				(slave_port_id -
+				SB_PGD_TX_PORT_MULTI_CHANNEL_1_START_PORT_ID));
+		} else {
+			ret = -EINVAL;
+			goto err;
+		}
+		multi_chan_cfg_reg_addr =
+				SB_PGD_TX_PORT_MULTI_CHANNEL_0(slave_port_id);
+		/* write to interface device */
+		ret = wcd9xxx_interface_reg_write(wcd9xxx,
+				multi_chan_cfg_reg_addr,
+				payload_tx_0);
+		if (ret < 0) {
+			pr_err("%s:Intf-dev fail reg[%d] payload[%d] ret[%d]\n",
+								__func__,
+						multi_chan_cfg_reg_addr,
+						payload_tx_0, ret);
+			goto err;
+		}
+		multi_chan_cfg_reg_addr =
+				SB_PGD_TX_PORT_MULTI_CHANNEL_1(slave_port_id);
+		/* ports 8,9 */
+		ret = wcd9xxx_interface_reg_write(wcd9xxx,
+				multi_chan_cfg_reg_addr,
+				payload_tx_1);
+		if (ret < 0) {
+			pr_err("%s:Intf-dev fail reg[%d] payload[%d] ret[%d]\n",
+								__func__,
+						multi_chan_cfg_reg_addr,
+						payload_tx_1, ret);
+			goto err;
+		}
+		/* configure the slave port for water mark and enable*/
+		wm_payload = (SLAVE_PORT_WATER_MARK_VALUE <<
+				SLAVE_PORT_WATER_MARK_SHIFT) +
+				SLAVE_PORT_ENABLE;
+		ret = wcd9xxx_interface_reg_write(wcd9xxx,
+				SB_PGD_PORT_CFG_BYTE_ADDR(slave_port_id),
+				wm_payload);
+		if (ret < 0) {
+			pr_err("%s:watermark set failure for port[%d] ret[%d]",
+						__func__,
+						slave_port_id, ret);
+		}
+	}
+
+	/* slim_define_ch api */
+	prop.prot = SLIM_AUTO_ISO;
+	prop.baser = SLIM_RATE_4000HZ;
+	prop.dataf = SLIM_CH_DATAF_NOT_DEFINED;
+	prop.auxf = SLIM_CH_AUXF_NOT_APPLICABLE;
+	prop.ratem = (rate/4000);
+	prop.sampleszbits = 16;
+	ret = slim_define_ch(wcd9xxx->slim, &prop, ch_h, ch_cnt,
+					true, &grph);
+	if (ret < 0) {
+		pr_err("%s: slim_define_ch failed ret[%d]\n",
+					__func__, ret);
+		goto err;
+	}
+	for (i = 0; i < ch_cnt; i++) {
+		ret = slim_connect_src(wcd9xxx->slim, sph[i],
+							ch_h[i]);
+		if (ret < 0) {
+			pr_err("%s: slim_connect_src failed ret[%d]\n",
+						__func__, ret);
+			goto err;
+		}
+	}
+	/* slim_control_ch */
+	ret = slim_control_ch(wcd9xxx->slim, grph, SLIM_CH_ACTIVATE,
+					true);
+	if (ret < 0) {
+		pr_err("%s: slim_control_ch failed ret[%d]\n",
+				__func__, ret);
+		goto err;
+	}
+	for (i = 0; i < ch_cnt; i++) {
+		idx = (ch_num[i] - BASE_CH_NUM);
+		tx[idx].grph = grph;
+	}
+	return 0;
+err:
+	/* release all acquired handles */
+	wcd9xxx_close_slim_sch_tx(wcd9xxx, ch_num, ch_cnt);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(wcd9xxx_cfg_slim_sch_tx);
+
+int wcd9xxx_close_slim_sch_rx(struct wcd9xxx *wcd9xxx, unsigned int *ch_num,
+				unsigned int ch_cnt)
+{
+	u16 grph = 0;
+	u32 sph[SLIM_MAX_RX_PORTS] = {0};
+	int i = 0 , idx = 0;
+	int ret = 0;
+	struct wcd9xxx_slim_sch_rx *rx = sh_ch.rx;
+
+	pr_err("%s: ch_cnt[%d]\n", __func__, ch_cnt);
+	for (i = 0; i < ch_cnt; i++) {
+		idx = (ch_num[i] - BASE_CH_NUM -
+				SB_PGD_OFFSET_OF_RX_SLAVE_DEV_PORTS - 1);
+		sph[i] = rx[idx].sph;
+		grph = rx[idx].grph;
+	}
+
+	/* slim_disconnect_port */
+	ret = slim_disconnect_ports(wcd9xxx->slim, sph, ch_cnt);
+	if (ret < 0) {
+		pr_err("%s: slim_disconnect_ports failed ret[%d]\n",
+				__func__, ret);
+	}
+	/* slim_control_ch (REMOVE) */
+	ret = slim_control_ch(wcd9xxx->slim, grph, SLIM_CH_REMOVE, true);
+	if (ret < 0) {
+		pr_err("%s: slim_control_ch failed ret[%d]\n",
+				__func__, ret);
+		goto err;
+	}
+	for (i = 0; i < ch_cnt; i++) {
+		idx = (ch_num[i] - BASE_CH_NUM -
+				SB_PGD_OFFSET_OF_RX_SLAVE_DEV_PORTS - 1);
+		rx[idx].grph = 0;
+	}
+err:
+	return ret;
+}
+EXPORT_SYMBOL_GPL(wcd9xxx_close_slim_sch_rx);
+
+int wcd9xxx_close_slim_sch_tx(struct wcd9xxx *wcd9xxx, unsigned int *ch_num,
+				unsigned int ch_cnt)
+{
+	u16 grph = 0;
+	u32 sph[SLIM_MAX_TX_PORTS] = {0};
+	int ret = 0;
+	int i = 0 , idx = 0;
+	struct wcd9xxx_slim_sch_tx *tx = sh_ch.tx;
+
+	pr_err("%s: ch_cnt[%d]\n", __func__, ch_cnt);
+	for (i = 0; i < ch_cnt; i++) {
+		idx = (ch_num[i] - BASE_CH_NUM);
+		sph[i] = tx[idx].sph;
+		grph = tx[idx].grph;
+	}
+	/* slim_disconnect_port */
+	ret = slim_disconnect_ports(wcd9xxx->slim, sph, ch_cnt);
+	if (ret < 0) {
+		pr_err("%s: slim_disconnect_ports failed ret[%d]\n",
+				__func__, ret);
+	}
+	/* slim_control_ch (REMOVE) */
+	ret = slim_control_ch(wcd9xxx->slim, grph, SLIM_CH_REMOVE, true);
+	if (ret < 0) {
+		pr_err("%s: slim_control_ch failed ret[%d]\n",
+				__func__, ret);
+		goto err;
+	}
+	for (i = 0; i < ch_cnt; i++) {
+		idx = (ch_num[i] - BASE_CH_NUM);
+		tx[idx].grph = 0;
+	}
+err:
+	return ret;
+}
+EXPORT_SYMBOL_GPL(wcd9xxx_close_slim_sch_tx);