| /* 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> |
| #include <linux/mfd/wcd9xxx/wcd9xxx_registers.h> |
| |
| #define WCD9XXX_CHIP_ID_TAIKO 0x00000201 |
| |
| 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]; |
| |
| u16 rx_port_start_offset; |
| u16 num_rx_slave_port; |
| u16 port_ch_0_start_port_id; |
| u16 port_ch_0_end_port_id; |
| u16 pgd_tx_port_ch_1_end_port_id; |
| u16 rx_port_ch_reg_base; |
| u16 port_tx_cfg_reg_base; |
| u16 port_rx_cfg_reg_base; |
| int number_of_tx_slave_dev_ports; |
| int number_of_rx_slave_dev_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); |
| |
| static int wcd9xxx_configure_ports(struct wcd9xxx *wcd9xxx) |
| { |
| int i; |
| u32 id; |
| for (i = 0; i < 4; i++) |
| ((u8 *)&id)[i] = wcd9xxx_reg_read(wcd9xxx, |
| WCD9XXX_A_CHIP_ID_BYTE_0 + i); |
| id = cpu_to_be32(id); |
| pr_debug("%s: chip id 0x%08x\n", __func__, id); |
| if (id != WCD9XXX_CHIP_ID_TAIKO) { |
| sh_ch.rx_port_start_offset = |
| TABLA_SB_PGD_OFFSET_OF_RX_SLAVE_DEV_PORTS; |
| sh_ch.num_rx_slave_port = |
| TABLA_SB_PGD_MAX_NUMBER_OF_RX_SLAVE_DEV_PORTS; |
| sh_ch.port_ch_0_start_port_id = |
| TABLA_SB_PGD_RX_PORT_MULTI_CHANNEL_0_START_PORT_ID; |
| sh_ch.port_ch_0_end_port_id = |
| TABLA_SB_PGD_RX_PORT_MULTI_CHANNEL_0_END_PORT_ID; |
| sh_ch.pgd_tx_port_ch_1_end_port_id = |
| TABLA_SB_PGD_TX_PORT_MULTI_CHANNEL_1_END_PORT_ID; |
| |
| sh_ch.rx_port_ch_reg_base = |
| 0x180 + (TABLA_SB_PGD_OFFSET_OF_RX_SLAVE_DEV_PORTS * 4); |
| sh_ch.port_rx_cfg_reg_base = |
| 0x040 + (TABLA_SB_PGD_OFFSET_OF_RX_SLAVE_DEV_PORTS); |
| sh_ch.port_tx_cfg_reg_base = 0x040; |
| |
| sh_ch.number_of_tx_slave_dev_ports = |
| TABLA_SB_PGD_MAX_NUMBER_OF_TX_SLAVE_DEV_PORTS; |
| sh_ch.number_of_rx_slave_dev_ports = |
| TABLA_SB_PGD_MAX_NUMBER_OF_RX_SLAVE_DEV_PORTS; |
| } else { |
| sh_ch.rx_port_start_offset = |
| TAIKO_SB_PGD_OFFSET_OF_RX_SLAVE_DEV_PORTS; |
| sh_ch.num_rx_slave_port = |
| TAIKO_SB_PGD_MAX_NUMBER_OF_RX_SLAVE_DEV_PORTS; |
| sh_ch.port_ch_0_start_port_id = |
| TAIKO_SB_PGD_RX_PORT_MULTI_CHANNEL_0_START_PORT_ID; |
| sh_ch.port_ch_0_end_port_id = |
| TAIKO_SB_PGD_RX_PORT_MULTI_CHANNEL_0_END_PORT_ID; |
| sh_ch.pgd_tx_port_ch_1_end_port_id = |
| TAIKO_SB_PGD_TX_PORT_MULTI_CHANNEL_1_END_PORT_ID; |
| |
| sh_ch.rx_port_ch_reg_base = 0x180; |
| sh_ch.port_rx_cfg_reg_base = 0x040; |
| sh_ch.port_tx_cfg_reg_base = 0x050; |
| |
| sh_ch.number_of_tx_slave_dev_ports = |
| TAIKO_SB_PGD_MAX_NUMBER_OF_TX_SLAVE_DEV_PORTS; |
| sh_ch.number_of_rx_slave_dev_ports = |
| TAIKO_SB_PGD_MAX_NUMBER_OF_RX_SLAVE_DEV_PORTS; |
| } |
| |
| return 0; |
| } |
| |
| int wcd9xxx_init_slimslave(struct wcd9xxx *wcd9xxx, u8 wcd9xxx_pgd_la) |
| { |
| int ret = 0; |
| |
| ret = wcd9xxx_configure_ports(wcd9xxx); |
| if (ret) { |
| pr_err("%s: Failed to configure register address offset\n", |
| __func__); |
| goto err; |
| } |
| |
| 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 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); |
| 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 < sh_ch.number_of_rx_slave_dev_ports; ch_idx++) |
| rx_ch[ch_idx] = rx[ch_idx].ch_num; |
| for (ch_idx = 0; ch_idx < sh_ch.number_of_tx_slave_dev_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 < sh_ch.number_of_rx_slave_dev_ports; |
| ch_idx++) { |
| slave_port_id = (ch_idx + sh_ch.rx_port_start_offset); |
| 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; |
| } |
| pr_debug("%s:ch_num=%d ch_h=%d sph=%d la=%d slave_port_id %d\n", |
| __func__, rx[ch_idx].ch_num, rx[ch_idx].ch_h, |
| rx[ch_idx].sph, wcd9xxx_pgd_la, slave_port_id); |
| } |
| 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_debug("%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 < sh_ch.number_of_tx_slave_dev_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 < sh_ch.number_of_rx_slave_dev_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 < sh_ch.number_of_tx_slave_dev_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; |
| 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_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 - sh_ch.rx_port_start_offset); |
| ch_h[i] = rx[idx].ch_h; |
| sph[i] = rx[idx].sph; |
| slave_port_id = idx; |
| pr_debug("%s: idx %d, ch_h %d, sph %d\n", |
| __func__, idx, ch_h[i], sph[i]); |
| if ((slave_port_id > sh_ch.num_rx_slave_port)) { |
| pr_err("Slimbus: invalid slave port id: %d", |
| slave_port_id); |
| ret = -EINVAL; |
| goto err; |
| } |
| slave_port_id += sh_ch.rx_port_start_offset; |
| pr_debug("%s: slave_port_id %d\n", __func__, slave_port_id); |
| /* look for the valid port range and chose the |
| * payload accordingly |
| */ |
| if ((slave_port_id > sh_ch.pgd_tx_port_ch_1_end_port_id) && |
| (slave_port_id <= sh_ch.port_ch_0_end_port_id)) { |
| payload_rx = payload_rx | |
| (1 << (slave_port_id - |
| sh_ch.port_ch_0_start_port_id)); |
| } else { |
| ret = -EINVAL; |
| goto err; |
| } |
| |
| multi_chan_cfg_reg_addr = |
| SB_PGD_RX_PORT_MULTI_CHANNEL_0(sh_ch.rx_port_ch_reg_base, |
| idx); |
| pr_debug("%s: multi_chan_cfg_reg_addr 0x%x\n", __func__, |
| multi_chan_cfg_reg_addr); |
| |
| /* 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( |
| sh_ch.port_rx_cfg_reg_base, idx), |
| 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 - sh_ch.rx_port_start_offset); |
| 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; |
| pr_debug("%s: idx %d, ch_h %d, sph %d, slave_port_id %d\n", |
| __func__, idx, ch_h[i], sph[i], slave_port_id); |
| if (slave_port_id > sh_ch.number_of_tx_slave_dev_ports) { |
| 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 <= |
| sh_ch.pgd_tx_port_ch_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 { |
| pr_err("%s: slave port id %d error\n", __func__, |
| slave_port_id); |
| ret = -EINVAL; |
| goto err; |
| } |
| multi_chan_cfg_reg_addr = |
| SB_PGD_TX_PORT_MULTI_CHANNEL_0(slave_port_id); |
| pr_debug("%s: multi_chan_cfg_reg_addr 0x%x\n", __func__, |
| multi_chan_cfg_reg_addr); |
| /* 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; |
| pr_debug("%s: tx_cfg_reg 0x%x wm 0x%x\n", __func__, |
| SB_PGD_PORT_CFG_BYTE_ADDR(sh_ch.port_tx_cfg_reg_base, |
| slave_port_id), wm_payload); |
| ret = wcd9xxx_interface_reg_write( |
| wcd9xxx, |
| SB_PGD_PORT_CFG_BYTE_ADDR( |
| sh_ch.port_tx_cfg_reg_base, |
| 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_debug("%s: ch_cnt[%d]\n", __func__, ch_cnt); |
| for (i = 0; i < ch_cnt; i++) { |
| idx = (ch_num[i] - BASE_CH_NUM - sh_ch.rx_port_start_offset); |
| if (idx < 0) { |
| pr_err("%s: Error:-Invalid index found = %d\n", |
| __func__, idx); |
| ret = -EINVAL; |
| goto err; |
| } |
| sph[i] = rx[idx].sph; |
| grph = rx[idx].grph; |
| pr_debug("%s: ch_num[%d] %d, idx %d, sph[%d] %x, grph %x\n", |
| __func__, i, ch_num[i], idx, i, sph[i], grph); |
| } |
| |
| /* 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; |
| } |
| /* 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); |
| } |
| for (i = 0; i < ch_cnt; i++) { |
| idx = (ch_num[i] - BASE_CH_NUM - sh_ch.rx_port_start_offset); |
| 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_debug("%s: ch_cnt[%d]\n", __func__, ch_cnt); |
| for (i = 0; i < ch_cnt; i++) { |
| idx = (ch_num[i] - BASE_CH_NUM); |
| if (idx < 0) { |
| pr_err("%s: Error:- Invalid index found = %d\n", |
| __func__, idx); |
| ret = -EINVAL; |
| goto err; |
| } |
| 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); |
| |
| int wcd9xxx_get_slave_port(unsigned int ch_num) |
| { |
| int ret = 0; |
| |
| pr_debug("%s: ch_num[%d]\n", __func__, ch_num); |
| ret = (ch_num - BASE_CH_NUM); |
| if (ret < 0) { |
| pr_err("%s: Error:- Invalid slave port found = %d\n", |
| __func__, ret); |
| return -EINVAL; |
| } |
| return ret; |
| } |
| EXPORT_SYMBOL_GPL(wcd9xxx_get_slave_port); |