Files
android_kernel_samsung_sm86…/slimbus/btfm_slim.c
Balakrishna Godavarthi d22c687077 btfmcodec: Create btfmcodec driver
This changes handles below.

1. Create btfm codec driver.
2. add support to enumerate char device.
3. create hardware endpoint abstract layers.
4. Register with ALSA driver

CRs-Fixed: 3298745
Change-Id: Id75dad16de8414b3b6a24d265c8acb54eca4d5dc
2023-01-15 19:16:43 +05:30

634 rivejä
18 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2016-2021, The Linux Foundation. All rights reserved.
* Copyright (c) 2021 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of_gpio.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/debugfs.h>
#include <linux/ratelimit.h>
#include <linux/slab.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <sound/tlv.h>
#include "btpower.h"
#include "btfm_slim.h"
#include "btfm_slim_slave.h"
#include "btfm_slim_hw_interface.h"
#define DELAY_FOR_PORT_OPEN_MS (200)
#define SLIM_MANF_ID_QCOM 0x217
#define SLIM_PROD_CODE 0x221
static bool btfm_is_port_opening_delayed = true;
static int btfm_num_ports_open;
int btfm_slim_write(struct btfmslim *btfmslim,
uint16_t reg, uint8_t reg_val, uint8_t pgd)
{
int ret = -1;
uint32_t reg_addr;
int slim_write_tries = SLIM_SLAVE_RW_MAX_TRIES;
BTFMSLIM_INFO("Write to %s", pgd?"PGD":"IFD");
reg_addr = SLIM_SLAVE_REG_OFFSET + reg;
for ( ; slim_write_tries != 0; slim_write_tries--) {
mutex_lock(&btfmslim->xfer_lock);
ret = slim_writeb(pgd ? btfmslim->slim_pgd :
&btfmslim->slim_ifd, reg_addr, reg_val);
mutex_unlock(&btfmslim->xfer_lock);
if (ret) {
BTFMSLIM_DBG("retrying to Write 0x%02x to reg 0x%x ret %d",
reg_val, reg_addr, ret);
} else {
BTFMSLIM_DBG("Written 0x%02x to reg 0x%x ret %d", reg_val, reg_addr, ret);
break;
}
usleep_range(5000, 5100);
}
if (ret) {
BTFMSLIM_DBG("retrying to Write 0x%02x to reg 0x%x ret %d",
reg_val, reg_addr, ret);
}
return ret;
}
int btfm_slim_read(struct btfmslim *btfmslim, uint32_t reg, uint8_t pgd)
{
int ret = -1;
int slim_read_tries = SLIM_SLAVE_RW_MAX_TRIES;
uint32_t reg_addr;
BTFMSLIM_DBG("Read from %s", pgd?"PGD":"IFD");
reg_addr = SLIM_SLAVE_REG_OFFSET + reg;
for ( ; slim_read_tries != 0; slim_read_tries--) {
mutex_lock(&btfmslim->xfer_lock);
ret = slim_readb(pgd ? btfmslim->slim_pgd :
&btfmslim->slim_ifd, reg_addr);
BTFMSLIM_DBG("Read 0x%02x from reg 0x%x", ret, reg_addr);
mutex_unlock(&btfmslim->xfer_lock);
if (ret > 0)
break;
usleep_range(5000, 5100);
}
return ret;
}
static bool btfm_slim_is_sb_reset_needed(int chip_ver)
{
switch (chip_ver) {
case QCA_APACHE_SOC_ID_0100:
case QCA_APACHE_SOC_ID_0110:
case QCA_APACHE_SOC_ID_0120:
case QCA_APACHE_SOC_ID_0121:
return true;
default:
return false;
}
}
int btfm_slim_enable_ch(struct btfmslim *btfmslim, struct btfmslim_ch *ch,
uint8_t rxport, uint32_t rates, uint8_t nchan)
{
int ret = -1;
int i = 0;
struct btfmslim_ch *chan = ch;
int chipset_ver;
if (!btfmslim || !ch)
return -EINVAL;
BTFMSLIM_DBG("port: %d ch: %d", ch->port, ch->ch);
chan->dai.sruntime = slim_stream_allocate(btfmslim->slim_pgd, "BTFM_SLIM");
if (chan->dai.sruntime == NULL) {
BTFMSLIM_ERR("slim_stream_allocate failed");
return -EINVAL;
}
chan->dai.sconfig.bps = btfmslim->bps;
chan->dai.sconfig.direction = btfmslim->direction;
chan->dai.sconfig.rate = rates;
chan->dai.sconfig.ch_count = nchan;
chan->dai.sconfig.chs = kcalloc(nchan, sizeof(unsigned int), GFP_KERNEL);
if (!chan->dai.sconfig.chs)
return -ENOMEM;
for (i = 0; i < nchan; i++, ch++) {
/* Enable port through registration setting */
if (btfmslim->vendor_port_en) {
ret = btfmslim->vendor_port_en(btfmslim, ch->port,
rxport, 1);
if (ret < 0) {
BTFMSLIM_ERR("vendor_port_en failed ret[%d]",
ret);
goto error;
}
}
chan->dai.sconfig.chs[i] = ch->ch;
chan->dai.sconfig.port_mask |= BIT(ch->port);
}
/* Activate the channel immediately */
BTFMSLIM_INFO("port: %d, ch: %d", chan->port, chan->ch);
chipset_ver = btpower_get_chipset_version();
BTFMSLIM_INFO("chipset soc version:%x", chipset_ver);
/* Delay port opening for few chipsets if:
1. for 8k, feedback channel
2. 44.1k, 88.2k rxports
*/
if (((rates == 8000 && btfm_feedback_ch_setting && rxport == 0) ||
(rxport == 1 && (rates == 44100 || rates == 88200))) &&
btfm_slim_is_sb_reset_needed(chipset_ver)) {
BTFMSLIM_INFO("btfm_is_port_opening_delayed %d",
btfm_is_port_opening_delayed);
if (!btfm_is_port_opening_delayed) {
BTFMSLIM_INFO("SB reset needed, sleeping");
btfm_is_port_opening_delayed = true;
msleep(DELAY_FOR_PORT_OPEN_MS);
}
}
/* for feedback channel, PCM bit should not be set */
if (btfm_feedback_ch_setting) {
BTFMSLIM_DBG("port open for feedback ch, not setting PCM bit");
//prop.dataf = SLIM_CH_DATAF_NOT_DEFINED;
/* reset so that next port open sets the data format properly */
btfm_feedback_ch_setting = 0;
}
ret = slim_stream_prepare(chan->dai.sruntime, &chan->dai.sconfig);
if (ret) {
BTFMSLIM_ERR("slim_stream_prepare failed = %d", ret);
goto error;
}
ret = slim_stream_enable(chan->dai.sruntime);
if (ret) {
BTFMSLIM_ERR("slim_stream_enable failed = %d", ret);
goto error;
}
if (ret == 0)
btfm_num_ports_open++;
BTFMSLIM_INFO("btfm_num_ports_open: %d", btfm_num_ports_open);
return ret;
error:
BTFMSLIM_INFO("error %d while opening port, btfm_num_ports_open: %d",
ret, btfm_num_ports_open);
kfree(chan->dai.sconfig.chs);
chan->dai.sconfig.chs = NULL;
return ret;
}
int btfm_slim_disable_ch(struct btfmslim *btfmslim, struct btfmslim_ch *ch,
uint8_t rxport, uint8_t nchan)
{
int ret = -1;
int i = 0;
int chipset_ver = 0;
if (!btfmslim || !ch)
return -EINVAL;
BTFMSLIM_INFO("port:%d ", ch->port);
if (ch->dai.sruntime == NULL) {
BTFMSLIM_ERR("Channel not enabled yet. returning");
return -EINVAL;
}
btfm_is_port_opening_delayed = false;
if (rxport && (btfmslim->sample_rate == 44100 ||
btfmslim->sample_rate == 88200)) {
BTFMSLIM_INFO("disconnecting the ports, removing the channel");
/* disconnect the ports of the stream */
ret = slim_stream_unprepare_disconnect_port(ch->dai.sruntime,
true, false);
if (ret != 0)
BTFMSLIM_ERR("slim_stream_unprepare failed %d", ret);
}
ret = slim_stream_disable(ch->dai.sruntime);
if (ret != 0) {
BTFMSLIM_ERR("slim_stream_disable failed returned val = %d", ret);
if ((btfmslim->sample_rate != 44100) && (btfmslim->sample_rate != 88200)) {
/* disconnect the ports of the stream */
ret = slim_stream_unprepare_disconnect_port(ch->dai.sruntime,
true, false);
if (ret != 0)
BTFMSLIM_ERR("slim_stream_unprepare failed %d", ret);
}
}
/* free the ports allocated to the stream */
ret = slim_stream_unprepare_disconnect_port(ch->dai.sruntime, false, true);
if (ret != 0)
BTFMSLIM_ERR("slim_stream_unprepare failed returned val = %d", ret);
/* Disable port through registration setting */
for (i = 0; i < nchan; i++, ch++) {
if (btfmslim->vendor_port_en) {
ret = btfmslim->vendor_port_en(btfmslim, ch->port,
rxport, 0);
if (ret < 0) {
BTFMSLIM_ERR("vendor_port_en failed [%d]", ret);
break;
}
}
}
ch->dai.sconfig.port_mask = 0;
if (ch->dai.sconfig.chs != NULL)
kfree(ch->dai.sconfig.chs);
if (btfm_num_ports_open > 0)
btfm_num_ports_open--;
ch->dai.sruntime = NULL;
BTFMSLIM_INFO("btfm_num_ports_open: %d", btfm_num_ports_open);
chipset_ver = btpower_get_chipset_version();
if (btfm_num_ports_open == 0 && (chipset_ver == QCA_HSP_SOC_ID_0200 ||
chipset_ver == QCA_HSP_SOC_ID_0210 ||
chipset_ver == QCA_HSP_SOC_ID_1201 ||
chipset_ver == QCA_HSP_SOC_ID_1211 ||
chipset_ver == QCA_HAMILTON_SOC_ID_0100 ||
chipset_ver == QCA_HAMILTON_SOC_ID_0101 ||
chipset_ver == QCA_HAMILTON_SOC_ID_0200 )) {
BTFMSLIM_INFO("SB reset needed after all ports disabled, sleeping");
msleep(DELAY_FOR_PORT_OPEN_MS);
}
return ret;
}
static int btfm_slim_alloc_port(struct btfmslim *btfmslim)
{
int ret = -EINVAL, i;
int chipset_ver;
struct btfmslim_ch *rx_chs;
struct btfmslim_ch *tx_chs;
if (!btfmslim)
return ret;
chipset_ver = btpower_get_chipset_version();
BTFMSLIM_INFO("chipset soc version:%x", chipset_ver);
rx_chs = btfmslim->rx_chs;
tx_chs = btfmslim->tx_chs;
if ((chipset_ver >= QCA_CHEROKEE_SOC_ID_0310) &&
(chipset_ver <= QCA_CHEROKEE_SOC_ID_0320_UMC)) {
for (i = 0; (tx_chs->port != BTFM_SLIM_PGD_PORT_LAST) &&
(i < BTFM_SLIM_NUM_CODEC_DAIS); i++, tx_chs++) {
if (tx_chs->port == SLAVE_SB_PGD_PORT_TX1_FM)
tx_chs->port = CHRKVER3_SB_PGD_PORT_TX1_FM;
else if (tx_chs->port == SLAVE_SB_PGD_PORT_TX2_FM)
tx_chs->port = CHRKVER3_SB_PGD_PORT_TX2_FM;
BTFMSLIM_INFO("Tx port:%d", tx_chs->port);
}
tx_chs = btfmslim->tx_chs;
}
if (!rx_chs || !tx_chs)
return ret;
return 0;
}
static int btfm_slim_get_logical_addr(struct slim_device *slim)
{
int ret = 0;
const unsigned long timeout = jiffies +
msecs_to_jiffies(SLIM_SLAVE_PRESENT_TIMEOUT);
BTFMSLIM_INFO("");
do {
ret = slim_get_logical_addr(slim);
if (!ret) {
BTFMSLIM_DBG("Assigned l-addr: 0x%x", slim->laddr);
break;
}
/* Give SLIMBUS time to report present and be ready. */
usleep_range(1000, 1100);
BTFMSLIM_DBG("retyring get logical addr");
} while (time_before(jiffies, timeout));
return ret;
}
int btfm_slim_hw_init(struct btfmslim *btfmslim)
{
int ret = -1;
int chipset_ver;
struct slim_device *slim;
struct slim_device *slim_ifd;
BTFMSLIM_DBG("");
if (!btfmslim)
return -EINVAL;
if (btfmslim->enabled) {
BTFMSLIM_DBG("Already enabled");
return 0;
}
slim = btfmslim->slim_pgd;
slim_ifd = &btfmslim->slim_ifd;
mutex_lock(&btfmslim->io_lock);
BTFMSLIM_INFO(
"PGD Enum Addr: mfr id:%.02x prod code:%.02x dev ind:%.02x ins:%.02x",
slim->e_addr.manf_id, slim->e_addr.prod_code,
slim->e_addr.dev_index, slim->e_addr.instance);
chipset_ver = btpower_get_chipset_version();
BTFMSLIM_INFO("chipset soc version:%x", chipset_ver);
if (chipset_ver == QCA_HSP_SOC_ID_0100 ||
chipset_ver == QCA_HSP_SOC_ID_0110 ||
chipset_ver == QCA_HSP_SOC_ID_0200 ||
chipset_ver == QCA_HSP_SOC_ID_0210 ||
chipset_ver == QCA_HSP_SOC_ID_1201 ||
chipset_ver == QCA_HSP_SOC_ID_1211) {
BTFMSLIM_INFO("chipset is hastings prime, overwriting EA");
slim->is_laddr_valid = false;
slim->e_addr.manf_id = SLIM_MANF_ID_QCOM;
slim->e_addr.prod_code = SLIM_PROD_CODE;
slim->e_addr.dev_index = 0x01;
slim->e_addr.instance = 0x0;
/* we are doing this to indicate that this is not a child node
* (doesn't have call back functions). Needed only for querying
* logical address.
*/
slim_ifd->dev.driver = NULL;
slim_ifd->ctrl = btfmslim->slim_pgd->ctrl; //slimbus controller structure.
slim_ifd->is_laddr_valid = false;
slim_ifd->e_addr.manf_id = SLIM_MANF_ID_QCOM;
slim_ifd->e_addr.prod_code = SLIM_PROD_CODE;
slim_ifd->e_addr.dev_index = 0x0;
slim_ifd->e_addr.instance = 0x0;
slim_ifd->laddr = 0x0;
} else if (chipset_ver == QCA_MOSELLE_SOC_ID_0100 ||
chipset_ver == QCA_MOSELLE_SOC_ID_0110) {
BTFMSLIM_INFO("chipset is Moselle, overwriting EA");
slim->is_laddr_valid = false;
slim->e_addr.manf_id = SLIM_MANF_ID_QCOM;
slim->e_addr.prod_code = 0x222;
slim->e_addr.dev_index = 0x01;
slim->e_addr.instance = 0x0;
/* we are doing this to indicate that this is not a child node
* (doesn't have call back functions). Needed only for querying
* logical address.
*/
slim_ifd->dev.driver = NULL;
slim_ifd->ctrl = btfmslim->slim_pgd->ctrl; //slimbus controller structure.
slim_ifd->is_laddr_valid = false;
slim_ifd->e_addr.manf_id = SLIM_MANF_ID_QCOM;
slim_ifd->e_addr.prod_code = 0x222;
slim_ifd->e_addr.dev_index = 0x0;
slim_ifd->e_addr.instance = 0x0;
slim_ifd->laddr = 0x0;
} else if (chipset_ver == QCA_HAMILTON_SOC_ID_0100 ||
chipset_ver == QCA_HAMILTON_SOC_ID_0101 ||
chipset_ver == QCA_HAMILTON_SOC_ID_0200) {
BTFMSLIM_INFO("chipset is Hamliton, overwriting EA");
slim->is_laddr_valid = false;
slim->e_addr.manf_id = SLIM_MANF_ID_QCOM;
slim->e_addr.prod_code = 0x220;
slim->e_addr.dev_index = 0x01;
slim->e_addr.instance = 0x0;
/* we are doing this to indicate that this is not a child node
* (doesn't have call back functions). Needed only for querying
* logical address.
*/
slim_ifd->dev.driver = NULL;
slim_ifd->ctrl = btfmslim->slim_pgd->ctrl; //slimbus controller structure.
slim_ifd->is_laddr_valid = false;
slim_ifd->e_addr.manf_id = SLIM_MANF_ID_QCOM;
slim_ifd->e_addr.prod_code = 0x220;
slim_ifd->e_addr.dev_index = 0x0;
slim_ifd->e_addr.instance = 0x0;
slim_ifd->laddr = 0x0;
} else if (chipset_ver == QCA_CHEROKEE_SOC_ID_0200 ||
chipset_ver == QCA_CHEROKEE_SOC_ID_0201 ||
chipset_ver == QCA_CHEROKEE_SOC_ID_0210 ||
chipset_ver == QCA_CHEROKEE_SOC_ID_0211 ||
chipset_ver == QCA_CHEROKEE_SOC_ID_0310 ||
chipset_ver == QCA_CHEROKEE_SOC_ID_0320 ||
chipset_ver == QCA_CHEROKEE_SOC_ID_0320_UMC ||
chipset_ver == QCA_APACHE_SOC_ID_0100 ||
chipset_ver == QCA_APACHE_SOC_ID_0110 ||
chipset_ver == QCA_APACHE_SOC_ID_0120 ||
chipset_ver == QCA_APACHE_SOC_ID_0121) {
BTFMSLIM_INFO("chipset is Chk/Apache, overwriting EA");
slim->is_laddr_valid = false;
slim->e_addr.manf_id = SLIM_MANF_ID_QCOM;
slim->e_addr.prod_code = 0x220;
slim->e_addr.dev_index = 0x01;
slim->e_addr.instance = 0x0;
/* we are doing this to indicate that this is not a child node
* (doesn't have call back functions). Needed only for querying
* logical address.
*/
slim_ifd->dev.driver = NULL;
slim_ifd->ctrl = btfmslim->slim_pgd->ctrl; //slimbus controller structure.
slim_ifd->is_laddr_valid = false;
slim_ifd->e_addr.manf_id = SLIM_MANF_ID_QCOM;
slim_ifd->e_addr.prod_code = 0x220;
slim_ifd->e_addr.dev_index = 0x0;
slim_ifd->e_addr.instance = 0x0;
slim_ifd->laddr = 0x0;
}
BTFMSLIM_INFO(
"PGD Enum Addr: manu id:%.02x prod code:%.02x dev idx:%.02x instance:%.02x",
slim->e_addr.manf_id, slim->e_addr.prod_code,
slim->e_addr.dev_index, slim->e_addr.instance);
BTFMSLIM_INFO(
"IFD Enum Addr: manu id:%.02x prod code:%.02x dev idx:%.02x instance:%.02x",
slim_ifd->e_addr.manf_id, slim_ifd->e_addr.prod_code,
slim_ifd->e_addr.dev_index, slim_ifd->e_addr.instance);
/* Assign Logical Address for PGD (Ported Generic Device)
* enumeration address
*/
ret = btfm_slim_get_logical_addr(btfmslim->slim_pgd);
if (ret) {
BTFMSLIM_ERR("failed to get slimbus logical address: %d", ret);
goto error;
}
/* Assign Logical Address for Ported Generic Device
* enumeration address
*/
ret = btfm_slim_get_logical_addr(&btfmslim->slim_ifd);
if (ret) {
BTFMSLIM_ERR("failed to get slimbus logical address: %d", ret);
goto error;
}
ret = btfm_slim_alloc_port(btfmslim);
if (ret != 0)
goto error;
/* Start vendor specific initialization and get port information */
if (btfmslim->vendor_init)
ret = btfmslim->vendor_init(btfmslim);
/* Only when all registers read/write successfully, it set to
* enabled status
*/
btfmslim->enabled = 1;
error:
mutex_unlock(&btfmslim->io_lock);
return ret;
}
int btfm_slim_hw_deinit(struct btfmslim *btfmslim)
{
int ret = 0;
BTFMSLIM_INFO("");
if (!btfmslim)
return -EINVAL;
if (!btfmslim->enabled) {
BTFMSLIM_DBG("Already disabled");
return 0;
}
mutex_lock(&btfmslim->io_lock);
btfmslim->enabled = 0;
mutex_unlock(&btfmslim->io_lock);
return ret;
}
static int btfm_slim_status(struct slim_device *sdev,
enum slim_device_status status)
{
int ret = 0;
struct device *dev = &sdev->dev;
struct btfmslim *btfm_slim;
btfm_slim = dev_get_drvdata(dev);
#if IS_ENABLED(CONFIG_BTFM_SLIM)
ret = btfm_slim_register_codec(btfm_slim);
#else
ret = btfm_slim_register_hw_ep(btfm_slim);
#endif
if (ret)
BTFMSLIM_ERR("error, registering slimbus codec failed");
return ret;
}
static int btfm_slim_probe(struct slim_device *slim)
{
int ret = 0;
struct btfmslim *btfm_slim;
pr_info("%s: name = %s\n", __func__, dev_name(&slim->dev));
/*this as true during the probe then slimbus won't check for logical address*/
slim->is_laddr_valid = true;
dev_set_name(&slim->dev, "%s", BTFMSLIM_DEV_NAME);
pr_info("%s: name = %s\n", __func__, dev_name(&slim->dev));
BTFMSLIM_DBG("");
BTFMSLIM_ERR("is_laddr_valid is true");
if (!slim->ctrl)
return -EINVAL;
/* Allocation btfmslim data pointer */
btfm_slim = kzalloc(sizeof(struct btfmslim), GFP_KERNEL);
if (btfm_slim == NULL) {
BTFMSLIM_ERR("error, allocation failed");
return -ENOMEM;
}
/* BTFM Slimbus driver control data configuration */
btfm_slim->slim_pgd = slim;
/* Assign vendor specific function */
btfm_slim->rx_chs = SLIM_SLAVE_RXPORT;
btfm_slim->tx_chs = SLIM_SLAVE_TXPORT;
btfm_slim->vendor_init = SLIM_SLAVE_INIT;
btfm_slim->vendor_port_en = SLIM_SLAVE_PORT_EN;
/* Created Mutex for slimbus data transfer */
mutex_init(&btfm_slim->io_lock);
mutex_init(&btfm_slim->xfer_lock);
dev_set_drvdata(&slim->dev, btfm_slim);
/* Driver specific data allocation */
btfm_slim->dev = &slim->dev;
ret = btpower_register_slimdev(&slim->dev);
if (ret < 0) {
#if IS_ENABLED(CONFIG_BTFM_SLIM)
btfm_slim_unregister_codec(&slim->dev);
#else
btfm_slim_unregister_hwep();
#endif
ret = -EPROBE_DEFER;
goto dealloc;
}
return ret;
dealloc:
mutex_destroy(&btfm_slim->io_lock);
mutex_destroy(&btfm_slim->xfer_lock);
kfree(btfm_slim);
return ret;
}
static void btfm_slim_remove(struct slim_device *slim)
{
struct device *dev = &slim->dev;
struct btfmslim *btfm_slim = dev_get_drvdata(dev);
BTFMSLIM_DBG("");
mutex_destroy(&btfm_slim->io_lock);
mutex_destroy(&btfm_slim->xfer_lock);
snd_soc_unregister_component(&slim->dev);
kfree(btfm_slim);
}
static const struct slim_device_id btfm_slim_id[] = {
{
.manf_id = SLIM_MANF_ID_QCOM,
.prod_code = SLIM_PROD_CODE,
.dev_index = 0x1,
.instance = 0x0,
},
{
.manf_id = SLIM_MANF_ID_QCOM,
.prod_code = 0x220,
.dev_index = 0x1,
.instance = 0x0,
}
};
MODULE_DEVICE_TABLE(slim, btfm_slim_id);
static struct slim_driver btfm_slim_driver = {
.driver = {
.name = "btfmslim-driver",
.owner = THIS_MODULE,
},
.probe = btfm_slim_probe,
.device_status = btfm_slim_status,
.remove = btfm_slim_remove,
.id_table = btfm_slim_id
};
module_slim_driver(btfm_slim_driver);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("BTFM Slimbus Slave driver");