blob: 3aff7f17ecb6edd03d304a444eb7d2c768dc54ec [file] [log] [blame]
/* 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_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 < 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_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 -
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) {
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_debug("%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_debug("%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);