You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
621 lines
16 KiB
621 lines
16 KiB
/* Copyright (c) 2016-2019, The Linux Foundation. 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/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 "btfm_slim.h"
|
|
#include "btfm_slim_wcn3990.h"
|
|
#include <linux/bluetooth-power.h>
|
|
|
|
int btfm_slim_write(struct btfmslim *btfmslim,
|
|
uint16_t reg, int bytes, void *src, uint8_t pgd)
|
|
{
|
|
int ret, i;
|
|
struct slim_ele_access msg;
|
|
int slim_write_tries = SLIM_SLAVE_RW_MAX_TRIES;
|
|
|
|
BTFMSLIM_DBG("Write to %s", pgd?"PGD":"IFD");
|
|
msg.start_offset = SLIM_SLAVE_REG_OFFSET + reg;
|
|
msg.num_bytes = bytes;
|
|
msg.comp = NULL;
|
|
|
|
for ( ; slim_write_tries != 0; slim_write_tries--) {
|
|
mutex_lock(&btfmslim->xfer_lock);
|
|
ret = slim_change_val_element(pgd ? btfmslim->slim_pgd :
|
|
&btfmslim->slim_ifd, &msg, src, bytes);
|
|
mutex_unlock(&btfmslim->xfer_lock);
|
|
if (ret == 0)
|
|
break;
|
|
usleep_range(5000, 5100);
|
|
}
|
|
|
|
if (ret) {
|
|
BTFMSLIM_ERR("failed (%d)", ret);
|
|
return ret;
|
|
}
|
|
|
|
for (i = 0; i < bytes; i++)
|
|
BTFMSLIM_DBG("Write 0x%02x to reg 0x%x", ((uint8_t *)src)[i],
|
|
reg + i);
|
|
return 0;
|
|
}
|
|
|
|
int btfm_slim_write_pgd(struct btfmslim *btfmslim,
|
|
uint16_t reg, int bytes, void *src)
|
|
{
|
|
return btfm_slim_write(btfmslim, reg, bytes, src, PGD);
|
|
}
|
|
|
|
int btfm_slim_write_inf(struct btfmslim *btfmslim,
|
|
uint16_t reg, int bytes, void *src)
|
|
{
|
|
return btfm_slim_write(btfmslim, reg, bytes, src, IFD);
|
|
}
|
|
|
|
int btfm_slim_read(struct btfmslim *btfmslim, unsigned short reg,
|
|
int bytes, void *dest, uint8_t pgd)
|
|
{
|
|
int ret, i;
|
|
struct slim_ele_access msg;
|
|
int slim_read_tries = SLIM_SLAVE_RW_MAX_TRIES;
|
|
|
|
BTFMSLIM_DBG("Read from %s", pgd?"PGD":"IFD");
|
|
msg.start_offset = SLIM_SLAVE_REG_OFFSET + reg;
|
|
msg.num_bytes = bytes;
|
|
msg.comp = NULL;
|
|
|
|
for ( ; slim_read_tries != 0; slim_read_tries--) {
|
|
mutex_lock(&btfmslim->xfer_lock);
|
|
ret = slim_request_val_element(pgd ? btfmslim->slim_pgd :
|
|
&btfmslim->slim_ifd, &msg, dest, bytes);
|
|
mutex_unlock(&btfmslim->xfer_lock);
|
|
if (ret == 0)
|
|
break;
|
|
usleep_range(5000, 5100);
|
|
}
|
|
|
|
if (ret)
|
|
BTFMSLIM_ERR("failed (%d)", ret);
|
|
|
|
for (i = 0; i < bytes; i++)
|
|
BTFMSLIM_DBG("Read 0x%02x from reg 0x%x", ((uint8_t *)dest)[i],
|
|
reg + i);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int btfm_slim_read_pgd(struct btfmslim *btfmslim,
|
|
uint16_t reg, int bytes, void *dest)
|
|
{
|
|
return btfm_slim_read(btfmslim, reg, bytes, dest, PGD);
|
|
}
|
|
|
|
int btfm_slim_read_inf(struct btfmslim *btfmslim,
|
|
uint16_t reg, int bytes, void *dest)
|
|
{
|
|
return btfm_slim_read(btfmslim, reg, bytes, dest, IFD);
|
|
}
|
|
|
|
int btfm_slim_enable_ch(struct btfmslim *btfmslim, struct btfmslim_ch *ch,
|
|
uint8_t rxport, uint32_t rates, uint8_t grp, uint8_t nchan)
|
|
{
|
|
int ret, i;
|
|
struct slim_ch prop;
|
|
struct btfmslim_ch *chan = ch;
|
|
uint16_t ch_h[2];
|
|
|
|
if (!btfmslim || !ch)
|
|
return -EINVAL;
|
|
|
|
BTFMSLIM_DBG("port: %d ch: %d", ch->port, ch->ch);
|
|
|
|
/* Define the channel with below parameters */
|
|
prop.prot = ((rates == 44100) || (rates == 88200)) ?
|
|
SLIM_PUSH : SLIM_AUTO_ISO;
|
|
prop.baser = ((rates == 44100) || (rates == 88200)) ?
|
|
SLIM_RATE_11025HZ : SLIM_RATE_4000HZ;
|
|
prop.dataf = ((rates == 48000) || (rates == 44100) ||
|
|
(rates == 88200) || (rates == 96000)) ?
|
|
SLIM_CH_DATAF_NOT_DEFINED : SLIM_CH_DATAF_LPCM_AUDIO;
|
|
|
|
/* 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;
|
|
}
|
|
prop.auxf = SLIM_CH_AUXF_NOT_APPLICABLE;
|
|
prop.ratem = ((rates == 44100) || (rates == 88200)) ?
|
|
(rates/11025) : (rates/4000);
|
|
prop.sampleszbits = 16;
|
|
|
|
ch_h[0] = ch->ch_hdl;
|
|
ch_h[1] = (grp) ? (ch+1)->ch_hdl : 0;
|
|
|
|
BTFMSLIM_INFO("channel define - prot:%d, dataf:%d, auxf:%d",
|
|
prop.prot, prop.dataf, prop.auxf);
|
|
BTFMSLIM_INFO("channel define - rates:%d, baser:%d, ratem:%d",
|
|
rates, prop.baser, prop.ratem);
|
|
|
|
ret = slim_define_ch(btfmslim->slim_pgd, &prop, ch_h, nchan, grp,
|
|
&ch->grph);
|
|
if (ret < 0) {
|
|
BTFMSLIM_ERR("slim_define_ch failed ret[%d]", ret);
|
|
goto error;
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
if (rxport) {
|
|
BTFMSLIM_INFO("slim_connect_sink(port: %d, ch: %d)",
|
|
ch->port, ch->ch);
|
|
/* Connect Port with channel given by Machine driver*/
|
|
ret = slim_connect_sink(btfmslim->slim_pgd,
|
|
&ch->port_hdl, 1, ch->ch_hdl);
|
|
if (ret < 0) {
|
|
BTFMSLIM_ERR("slim_connect_sink failed ret[%d]",
|
|
ret);
|
|
goto remove_channel;
|
|
}
|
|
|
|
} else {
|
|
BTFMSLIM_INFO("slim_connect_src(port: %d, ch: %d)",
|
|
ch->port, ch->ch);
|
|
/* Connect Port with channel given by Machine driver*/
|
|
ret = slim_connect_src(btfmslim->slim_pgd, ch->port_hdl,
|
|
ch->ch_hdl);
|
|
if (ret < 0) {
|
|
BTFMSLIM_ERR("slim_connect_src failed ret[%d]",
|
|
ret);
|
|
goto remove_channel;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Activate the channel immediately */
|
|
BTFMSLIM_INFO(
|
|
"port: %d, ch: %d, grp: %d, ch->grph: 0x%x, ch_hdl: 0x%x",
|
|
chan->port, chan->ch, grp, chan->grph, chan->ch_hdl);
|
|
ret = slim_control_ch(btfmslim->slim_pgd, (grp ? chan->grph :
|
|
chan->ch_hdl), SLIM_CH_ACTIVATE, true);
|
|
if (ret < 0) {
|
|
BTFMSLIM_ERR("slim_control_ch failed ret[%d]", ret);
|
|
goto remove_channel;
|
|
}
|
|
|
|
error:
|
|
return ret;
|
|
|
|
remove_channel:
|
|
/* Remove the channel immediately*/
|
|
ret = slim_control_ch(btfmslim->slim_pgd, (grp ? ch->grph : ch->ch_hdl),
|
|
SLIM_CH_REMOVE, true);
|
|
if (ret < 0)
|
|
BTFMSLIM_ERR("slim_control_ch failed ret[%d]", ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int btfm_slim_disable_ch(struct btfmslim *btfmslim, struct btfmslim_ch *ch,
|
|
uint8_t rxport, uint8_t grp, uint8_t nchan)
|
|
{
|
|
int ret, i;
|
|
|
|
if (!btfmslim || !ch)
|
|
return -EINVAL;
|
|
|
|
BTFMSLIM_INFO("port:%d, grp: %d, ch->grph:0x%x, ch->ch_hdl:0x%x ",
|
|
ch->port, grp, ch->grph, ch->ch_hdl);
|
|
|
|
/* For 44.1/88.2 Khz A2DP Rx, disconnect the port first */
|
|
if (rxport &&
|
|
(btfmslim->sample_rate == 44100 ||
|
|
btfmslim->sample_rate == 88200)) {
|
|
BTFMSLIM_DBG("disconnecting the ports, removing the channel");
|
|
ret = slim_disconnect_ports(btfmslim->slim_pgd,
|
|
&ch->port_hdl, 1);
|
|
if (ret < 0) {
|
|
BTFMSLIM_ERR("slim_disconnect_ports failed ret[%d]",
|
|
ret);
|
|
}
|
|
}
|
|
|
|
/* Remove the channel immediately*/
|
|
ret = slim_control_ch(btfmslim->slim_pgd, (grp ? ch->grph : ch->ch_hdl),
|
|
SLIM_CH_REMOVE, true);
|
|
if (ret < 0) {
|
|
BTFMSLIM_ERR("slim_control_ch failed ret[%d]", ret);
|
|
if (btfmslim->sample_rate != 44100 &&
|
|
btfmslim->sample_rate != 88200) {
|
|
ret = slim_disconnect_ports(btfmslim->slim_pgd,
|
|
&ch->port_hdl, 1);
|
|
if (ret < 0) {
|
|
BTFMSLIM_ERR("disconnect_ports failed ret[%d]",
|
|
ret);
|
|
goto error;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* 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 ret[%d]",
|
|
ret);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
error:
|
|
return ret;
|
|
}
|
|
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);
|
|
|
|
do {
|
|
ret = slim_get_logical_addr(slim, slim->e_addr,
|
|
ARRAY_SIZE(slim->e_addr), &slim->laddr);
|
|
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;
|
|
}
|
|
|
|
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 = 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_0300) &&
|
|
chipset_ver <= QCA_CHEROKEE_SOC_ID_0320) {
|
|
for (i = 0; (tx_chs->port != BTFM_SLIM_PGD_PORT_LAST) &&
|
|
(i < BTFM_SLIM_NUM_CODEC_DAIS); i++, tx_chs++) {
|
|
if (tx_chs->port == CHRK_SB_PGD_PORT_TX1_FM)
|
|
tx_chs->port = CHRKVER3_SB_PGD_PORT_TX1_FM;
|
|
else if (tx_chs->port == CHRK_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;
|
|
|
|
BTFMSLIM_DBG("Rx: id\tname\tport\thdl\tch\tch_hdl");
|
|
for (i = 0 ; (rx_chs->port != BTFM_SLIM_PGD_PORT_LAST) &&
|
|
(i < BTFM_SLIM_NUM_CODEC_DAIS); i++, rx_chs++) {
|
|
|
|
/* Get Rx port handler from slimbus driver based
|
|
* on port number
|
|
*/
|
|
ret = slim_get_slaveport(btfmslim->slim_pgd->laddr,
|
|
rx_chs->port, &rx_chs->port_hdl, SLIM_SINK);
|
|
if (ret < 0) {
|
|
BTFMSLIM_ERR("slave port failure port#%d - ret[%d]",
|
|
rx_chs->port, SLIM_SINK);
|
|
return ret;
|
|
}
|
|
BTFMSLIM_DBG(" %d\t%s\t%d\t%x\t%d\t%x", rx_chs->id,
|
|
rx_chs->name, rx_chs->port, rx_chs->port_hdl,
|
|
rx_chs->ch, rx_chs->ch_hdl);
|
|
}
|
|
|
|
BTFMSLIM_DBG("Tx: id\tname\tport\thdl\tch\tch_hdl");
|
|
for (i = 0; (tx_chs->port != BTFM_SLIM_PGD_PORT_LAST) &&
|
|
(i < BTFM_SLIM_NUM_CODEC_DAIS); i++, tx_chs++) {
|
|
|
|
/* Get Tx port handler from slimbus driver based
|
|
* on port number
|
|
*/
|
|
ret = slim_get_slaveport(btfmslim->slim_pgd->laddr,
|
|
tx_chs->port, &tx_chs->port_hdl, SLIM_SRC);
|
|
if (ret < 0) {
|
|
BTFMSLIM_ERR("slave port failure port#%d - ret[%d]",
|
|
tx_chs->port, SLIM_SRC);
|
|
return ret;
|
|
}
|
|
BTFMSLIM_DBG(" %d\t%s\t%d\t%x\t%d\t%x", tx_chs->id,
|
|
tx_chs->name, tx_chs->port, tx_chs->port_hdl,
|
|
tx_chs->ch, tx_chs->ch_hdl);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int btfm_slim_hw_init(struct btfmslim *btfmslim)
|
|
{
|
|
int ret;
|
|
|
|
BTFMSLIM_DBG("");
|
|
if (!btfmslim)
|
|
return -EINVAL;
|
|
|
|
if (btfmslim->enabled) {
|
|
BTFMSLIM_DBG("Already enabled");
|
|
return 0;
|
|
}
|
|
mutex_lock(&btfmslim->io_lock);
|
|
|
|
/* 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 %s logical address: %d",
|
|
btfmslim->slim_pgd->name, 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 %s logical address: %d",
|
|
btfmslim->slim_ifd.name, ret);
|
|
goto error;
|
|
}
|
|
|
|
/* Allocate ports with logical address to get port handler from
|
|
* slimbus driver
|
|
*/
|
|
ret = btfm_slim_alloc_port(btfmslim);
|
|
if (ret)
|
|
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;
|
|
|
|
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_get_dt_info(struct btfmslim *btfmslim)
|
|
{
|
|
int ret = 0;
|
|
struct slim_device *slim = btfmslim->slim_pgd;
|
|
struct slim_device *slim_ifd = &btfmslim->slim_ifd;
|
|
struct property *prop;
|
|
|
|
if (!slim || !slim_ifd)
|
|
return -EINVAL;
|
|
|
|
if (slim->dev.of_node) {
|
|
BTFMSLIM_DBG("Platform data from device tree (%s)",
|
|
slim->name);
|
|
ret = of_property_read_string(slim->dev.of_node,
|
|
"qcom,btfm-slim-ifd", &slim_ifd->name);
|
|
if (ret) {
|
|
BTFMSLIM_ERR("Looking up %s property in node %s failed",
|
|
"qcom,btfm-slim-ifd",
|
|
slim->dev.of_node->full_name);
|
|
return -ENODEV;
|
|
}
|
|
BTFMSLIM_DBG("qcom,btfm-slim-ifd (%s)", slim_ifd->name);
|
|
|
|
prop = of_find_property(slim->dev.of_node,
|
|
"qcom,btfm-slim-ifd-elemental-addr", NULL);
|
|
if (!prop) {
|
|
BTFMSLIM_ERR("Looking up %s property in node %s failed",
|
|
"qcom,btfm-slim-ifd-elemental-addr",
|
|
slim->dev.of_node->full_name);
|
|
return -ENODEV;
|
|
} else if (prop->length != 6) {
|
|
BTFMSLIM_ERR(
|
|
"invalid codec slim ifd addr. addr length= %d",
|
|
prop->length);
|
|
return -ENODEV;
|
|
}
|
|
memcpy(slim_ifd->e_addr, prop->value, 6);
|
|
BTFMSLIM_DBG(
|
|
"PGD Enum Addr: %.02x:%.02x:%.02x:%.02x:%.02x: %.02x",
|
|
slim->e_addr[0], slim->e_addr[1], slim->e_addr[2],
|
|
slim->e_addr[3], slim->e_addr[4], slim->e_addr[5]);
|
|
BTFMSLIM_DBG(
|
|
"IFD Enum Addr: %.02x:%.02x:%.02x:%.02x:%.02x: %.02x",
|
|
slim_ifd->e_addr[0], slim_ifd->e_addr[1],
|
|
slim_ifd->e_addr[2], slim_ifd->e_addr[3],
|
|
slim_ifd->e_addr[4], slim_ifd->e_addr[5]);
|
|
} else {
|
|
BTFMSLIM_ERR("Platform data is not valid");
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int btfm_slim_probe(struct slim_device *slim)
|
|
{
|
|
int ret = 0;
|
|
struct btfmslim *btfm_slim;
|
|
|
|
BTFMSLIM_DBG("");
|
|
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);
|
|
|
|
/* Get Device tree node for Interface Device enumeration address */
|
|
ret = btfm_slim_get_dt_info(btfm_slim);
|
|
if (ret)
|
|
goto dealloc;
|
|
|
|
/* Add Interface Device for slimbus driver */
|
|
ret = slim_add_device(btfm_slim->slim_pgd->ctrl, &btfm_slim->slim_ifd);
|
|
if (ret) {
|
|
BTFMSLIM_ERR("error, adding SLIMBUS device failed");
|
|
goto dealloc;
|
|
}
|
|
|
|
/* Platform driver data allocation */
|
|
slim->dev.platform_data = btfm_slim;
|
|
|
|
/* Driver specific data allocation */
|
|
btfm_slim->dev = &slim->dev;
|
|
ret = btfm_slim_register_codec(&slim->dev);
|
|
if (ret) {
|
|
BTFMSLIM_ERR("error, registering slimbus codec failed");
|
|
goto free;
|
|
}
|
|
ret = bt_register_slimdev(&slim->dev);
|
|
if (ret < 0) {
|
|
btfm_slim_unregister_codec(&slim->dev);
|
|
goto free;
|
|
}
|
|
return ret;
|
|
free:
|
|
slim_remove_device(&btfm_slim->slim_ifd);
|
|
dealloc:
|
|
mutex_destroy(&btfm_slim->io_lock);
|
|
mutex_destroy(&btfm_slim->xfer_lock);
|
|
kfree(btfm_slim);
|
|
return ret;
|
|
}
|
|
static int btfm_slim_remove(struct slim_device *slim)
|
|
{
|
|
struct btfmslim *btfm_slim = slim->dev.platform_data;
|
|
|
|
BTFMSLIM_DBG("");
|
|
mutex_destroy(&btfm_slim->io_lock);
|
|
mutex_destroy(&btfm_slim->xfer_lock);
|
|
snd_soc_unregister_codec(&slim->dev);
|
|
|
|
BTFMSLIM_DBG("slim_remove_device() - btfm_slim->slim_ifd");
|
|
slim_remove_device(&btfm_slim->slim_ifd);
|
|
|
|
kfree(btfm_slim);
|
|
|
|
BTFMSLIM_DBG("slim_remove_device() - btfm_slim->slim_pgd");
|
|
slim_remove_device(slim);
|
|
return 0;
|
|
}
|
|
|
|
static const struct slim_device_id btfm_slim_id[] = {
|
|
{SLIM_SLAVE_COMPATIBLE_STR, 0},
|
|
{}
|
|
};
|
|
|
|
static struct slim_driver btfm_slim_driver = {
|
|
.driver = {
|
|
.name = "btfmslim-driver",
|
|
.owner = THIS_MODULE,
|
|
},
|
|
.probe = btfm_slim_probe,
|
|
.remove = btfm_slim_remove,
|
|
.id_table = btfm_slim_id
|
|
};
|
|
|
|
static int __init btfm_slim_init(void)
|
|
{
|
|
int ret;
|
|
|
|
BTFMSLIM_DBG("");
|
|
ret = slim_driver_register(&btfm_slim_driver);
|
|
if (ret)
|
|
BTFMSLIM_ERR("Failed to register slimbus driver: %d", ret);
|
|
return ret;
|
|
}
|
|
|
|
static void __exit btfm_slim_exit(void)
|
|
{
|
|
BTFMSLIM_DBG("");
|
|
slim_driver_unregister(&btfm_slim_driver);
|
|
}
|
|
|
|
module_init(btfm_slim_init);
|
|
module_exit(btfm_slim_exit);
|
|
|
|
MODULE_LICENSE("GPL v2");
|
|
MODULE_DESCRIPTION("BTFM Slimbus Slave driver");
|
|
|