1、电路原理:
2、需要配置的接口
控制管脚:
AP_WAKE_BT<---->PL06
BT_LDO_EN<----->PL07
BT_POWER <------>VCC_CTP(CLDO1-3.3V)
通信管脚:
UART1_TXD<----->PG06
UART1_RXD<----->PG07
UART1_RTS<----->PG08
UART1_CTS<----->PG09
PCM蓝牙音频通路管脚(对应I2S1,需要配置成PCM模式):
PCM_SYNC<------->PG10
PCM_BCLK<------->PG11
PCM_DO <------->PG12
PCO_DI <------->PG13
32K_OUT <------->PG14
3、上电时序,需要对控制空间和串口流控进行操作,具体如下:
lichee\linux-4.9\drivers\misc\sunxi-rf\sunxi-bluetooth.c
//以下代码是linux4.9 android8.x的代码,满足开机打开电源,上层Open时打开使能,close时关闭使能。
struct sunxi_bt_platdata {
struct regulator *bt_power;
struct regulator *io_regulator;
struct clk *lpo;
int gpio_bt_rst;
char *bt_power_name;
char *io_regulator_name;
int power_state;
int uart_enable;
struct rfkill *rfkill;
struct platform_device *pdev;
};
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/gpio.h>
#include <linux/err.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/of_gpio.h>
#include <linux/clk.h>
#include <linux/rfkill.h>
#include <linux/regulator/consumer.h>
#include <linux/platform_device.h>
#include <linux/sunxi-gpio.h>
#include <linux/pm_runtime.h>
#include "sunxi-rfkill.h"
static struct sunxi_bt_platdata *bluetooth_data = NULL;
static int sunxi_bt_on(struct sunxi_bt_platdata *data, bool on_off);
static DEFINE_MUTEX(sunxi_bluetooth_mutex);
static int set_bt_uart_enable(struct sunxi_bt_platdata *platdata, int enable)
{
int gpio_tx = GPIOG(6);
int gpio_rx = GPIOG(7);
int UART_FUNC = 2;
char pin_name_tx[SUNXI_PIN_NAME_MAX_LEN];
char pin_name_rx[SUNXI_PIN_NAME_MAX_LEN];
long unsigned int config_set;
long unsigned int config_get;
sunxi_gpio_to_name(gpio_tx, pin_name_tx);
sunxi_gpio_to_name(gpio_rx, pin_name_rx);
if (0 == enable) {
config_set = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_FUNC,
SUNXI_PIN_OUTPUT_FUNC);
pin_config_set(SUNXI_PINCTRL, pin_name_tx, config_set);
config_get = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_FUNC, 0XFFFF);
pin_config_get(SUNXI_PINCTRL, pin_name_tx, &config_get);
if (SUNXI_PIN_OUTPUT_FUNC != SUNXI_PINCFG_UNPACK_VALUE(config_get)) {
dev_err(&platdata->pdev->dev, "[%s] set output func failed\n",
pin_name_tx);
return -EINVAL;
} else {
dev_info(&platdata->pdev->dev, "[%s] set output func success\n",
pin_name_tx);
}
config_set = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_DAT,
SUNXI_PIN_DATA_LOW);
pin_config_set(SUNXI_PINCTRL, pin_name_tx, config_set);
config_get = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_DAT, 0XFFFF);
pin_config_get(SUNXI_PINCTRL, pin_name_tx, &config_get);
if (SUNXI_PIN_DATA_LOW != SUNXI_PINCFG_UNPACK_VALUE(config_get)) {
dev_err(&platdata->pdev->dev, "[%s] set output LOW failed\n",
pin_name_tx);
return -EINVAL;
} else {
dev_info(&platdata->pdev->dev, "[%s] set output LOW success\n",
pin_name_tx);
}
config_set = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_FUNC,
SUNXI_PIN_OUTPUT_FUNC);
pin_config_set(SUNXI_PINCTRL, pin_name_rx, config_set);
config_get = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_FUNC, 0XFFFF);
pin_config_get(SUNXI_PINCTRL, pin_name_rx, &config_get);
if (SUNXI_PIN_OUTPUT_FUNC != SUNXI_PINCFG_UNPACK_VALUE(config_get)) {
dev_err(&platdata->pdev->dev, "[%s] set output func failed\n",
pin_name_rx);
return -EINVAL;
} else {
dev_info(&platdata->pdev->dev, "[%s] set output func success\n",
pin_name_rx);
}
config_set = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_DAT,
SUNXI_PIN_DATA_LOW);
pin_config_set(SUNXI_PINCTRL, pin_name_rx, config_set);
config_get = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_DAT, 0XFFFF);
pin_config_get(SUNXI_PINCTRL, pin_name_rx, &config_get);
if (SUNXI_PIN_DATA_LOW != SUNXI_PINCFG_UNPACK_VALUE(config_get)) {
dev_err(&platdata->pdev->dev, "[%s] set output LOW failed\n",
pin_name_rx);
return -EINVAL;
} else {
dev_info(&platdata->pdev->dev, "[%s] set output LOW success\n",
pin_name_rx);
}
} else if (1 == enable) {
config_set = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_FUNC, UART_FUNC);
pin_config_set(SUNXI_PINCTRL, pin_name_tx, config_set);
config_get = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_FUNC, 0XFFFF);
pin_config_get(SUNXI_PINCTRL, pin_name_tx, &config_get);
if (UART_FUNC != SUNXI_PINCFG_UNPACK_VALUE(config_get)) {
dev_err(&platdata->pdev->dev, "[%s] set uart func failed\n",
pin_name_tx);
return -EINVAL;
} else {
dev_info(&platdata->pdev->dev, "[%s] set uart func success\n",
pin_name_tx);
}
config_set = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_PUD,
SUNXI_PIN_PULL_UP);
pin_config_set(SUNXI_PINCTRL, pin_name_tx, config_set);
config_get = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_PUD, 0XFFFF);
pin_config_get(SUNXI_PINCTRL, pin_name_tx, &config_get);
if (SUNXI_PIN_PULL_UP != SUNXI_PINCFG_UNPACK_VALUE(config_get)) {
dev_err(&platdata->pdev->dev, "[%s] set uart pull failed\n",
pin_name_tx);
return -EINVAL;
} else {
dev_info(&platdata->pdev->dev, "[%s] set uart pull success\n",
pin_name_tx);
}
config_set = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_FUNC, UART_FUNC);
pin_config_set(SUNXI_PINCTRL, pin_name_rx, config_set);
config_get = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_FUNC, 0XFFFF);
pin_config_get(SUNXI_PINCTRL, pin_name_rx, &config_get);
if (UART_FUNC != SUNXI_PINCFG_UNPACK_VALUE(config_get)) {
dev_err(&platdata->pdev->dev, "[%s] set uart func failed\n",
pin_name_rx);
return -EINVAL;
} else {
dev_info(&platdata->pdev->dev, "[%s] set uart func success\n",
pin_name_rx);
}
config_set = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_PUD,
SUNXI_PIN_PULL_UP);
pin_config_set(SUNXI_PINCTRL, pin_name_rx, config_set);
config_get = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_PUD, 0XFFFF);
pin_config_get(SUNXI_PINCTRL, pin_name_rx, &config_get);
if (SUNXI_PIN_PULL_UP != SUNXI_PINCFG_UNPACK_VALUE(config_get)) {
dev_err(&platdata->pdev->dev, "[%s] set uart pull failed\n",
pin_name_rx);
return -EINVAL;
} else {
dev_info(&platdata->pdev->dev, "[%s] set uart pull success\n",
pin_name_rx);
}
}
platdata->uart_enable = enable;
return 0;
}
static int sunxi_bt_on(struct sunxi_bt_platdata *data, bool on_off)
{
struct platform_device *pdev = data->pdev;
struct device *dev = &pdev->dev;
int ret = 0;
pr_err("%s ---sunxi_bt_on()\n", __FUNCTION__);
#if 0
if (!on_off && gpio_is_valid(data->gpio_bt_rst))
gpio_set_value(data->gpio_bt_rst, 0);
#endif
if (data->bt_power_name) { //bt_power
data->bt_power = regulator_get(dev, data->bt_power_name);
if (!IS_ERR(data->bt_power)) {
if (on_off) {
regulator_set_voltage(data->bt_power,
(int)3300*1000,
(int)3300*1000);
ret = regulator_enable(data->bt_power);
if (ret < 0) {
dev_err(dev, "regulator bt_power enable failed\n");
regulator_put(data->bt_power);
return ret;
}
ret = regulator_get_voltage(data->bt_power);
if (ret < 0) {
dev_err(dev, "regulator bt_power get voltage failed\n");
regulator_put(data->bt_power);
return ret;
}
dev_info(dev, "check bluetooth bt_power voltage: %d\n", ret);
} else {
ret = regulator_disable(data->bt_power);
if (ret < 0) {
dev_err(dev, "regulator bt_power disable failed\n");
regulator_put(data->bt_power);
return ret;
}
}
regulator_put(data->bt_power);
}
}
#if 0
if(data->io_regulator_name){
data->io_regulator = regulator_get(dev, data->io_regulator_name);
if (!IS_ERR(data->io_regulator)) {
if (on_off) {
regulator_set_voltage(data->io_regulator,
(int)3300*1000,
(int)3300*1000);
ret = regulator_enable(data->io_regulator);
if (ret < 0) {
dev_err(dev, "regulator io_regulator enable failed\n");
regulator_put(data->io_regulator);
return ret;
}
ret = regulator_get_voltage(data->io_regulator);
if (ret < 0) {
dev_err(dev, "regulator io_regulator get voltage failed\n");
regulator_put(data->io_regulator);
return ret;
}
dev_info(dev, "check bluetooth io_regulator voltage: %d\n",ret);
}else{
ret = regulator_disable(data->io_regulator);
if (ret < 0) {
dev_err(dev, "regulator io_regulator disable failed\n");
regulator_put(data->io_regulator);
return ret;
}
}
regulator_put(data->io_regulator);
}
}
if (on_off && gpio_is_valid(data->gpio_bt_rst)) {
mdelay(10);
gpio_set_value(data->gpio_bt_rst, 1);
}
#endif
data->power_state = on_off;
return 0;
}
static int sunxi_bt_set_block(void *data, bool blocked)
{
struct sunxi_bt_platdata *platdata = data;
struct platform_device *pdev = platdata->pdev;
int ret;
if (blocked != platdata->power_state) {
dev_warn(&pdev->dev, "block state already is %d\n", blocked);
return 0;
}
dev_info(&pdev->dev, "set block: %d\n", blocked);
//ret = sunxi_bt_on(platdata, !blocked);
//if (ret) {
// dev_err(&pdev->dev, "set block failed\n");
// return ret;
//}
//sunxi_wl_chipen_set(1, !blocked);
return 0;
}
static const struct rfkill_ops sunxi_bt_rfkill_ops = {
.set_block = sunxi_bt_set_block,
};
static ssize_t power_state_show(struct class *class,
struct class_attribute *attr, char *buf)
{
return sprintf(buf, "%d\n", bluetooth_data->power_state);
}
void bt_xr829_enable(int enable);
//主要由这个函数在上层调用/sys/class/bt_power/power_state接口,1:使能 0:不使能
static ssize_t power_state_store(struct class *class,
struct class_attribute *attr, const char *buf, size_t count)
{
unsigned long state;
int err;
pr_err("%s ---power_state_store()\n", __FUNCTION__);
err = kstrtoul(buf, 0, &state);
if (err)
return err;
bluetooth_data->power_state = state;
pr_err("%s ---bt_xr829_enable = %ld\n",__FUNCTION__,state);
if(state)
bt_xr829_enable(1);
else
bt_xr829_enable(0);
return count;
#if 0
if (state > 1)
return count;
mutex_lock(&sunxi_bluetooth_mutex);
if (state != bluetooth_data->power_state) {
dev_info(&bluetooth_data->pdev->dev, "set power: %s\n", state ? "on" : "off");
err = sunxi_bt_on(bluetooth_data, state);
if (err) {
dev_err(&bluetooth_data->pdev->dev, "set power failed\n");
}
}
sunxi_wl_chipen_set(1, state);
mutex_unlock(&sunxi_bluetooth_mutex);
return count;
#endif
}
static ssize_t uart_enable_store(struct class *class,
struct class_attribute *attr, const char *buf, size_t count)
{
unsigned long enable;
int err;
err = kstrtoul(buf, 0, &enable);
if (err)
return err;
if (enable > 1)
return count;
pr_err("%s ---enable:%d\n", __FUNCTION__,enable);
if (enable != bluetooth_data->uart_enable) {
dev_info(&bluetooth_data->pdev->dev, "bt uart: %s\n", enable ? "on" : "off");
err = set_bt_uart_enable(bluetooth_data, enable);
if(0==err)
{
if (0 == enable){
bluetooth_data->uart_enable = 0;
} else {
bluetooth_data->uart_enable = 1;
}
}
else{
dev_err(&bluetooth_data->pdev->dev, "set uart failed\n");
}
}
return count;
}
static struct class_attribute bt_attribute_group[] = {
__ATTR(power_state, S_IRUGO | S_IWUSR | S_IWGRP,
power_state_show, power_state_store),
__ATTR(uart_enable, S_IWUSR | S_IWGRP,
NULL, uart_enable_store),
__ATTR_NULL
};
static struct class bt_power_class = {
.name = "bt_power",
.class_attrs = bt_attribute_group,
};
static int bt_xr829_reset = 0;
static int bt_xr829_wake = 0;
void bt_xr829_enable(int enable)
{
pr_err("%s aa enable=%d\n",__FUNCTION__,enable);
if(enable){
gpio_direction_output(bt_xr829_reset, 1); //AP_WAKE_BT<---->PL06和BT_LDO_EN<----->PL07同时打开
gpio_direction_output(bt_xr829_wake, 1);
}else{
gpio_direction_output(bt_xr829_wake, 0);
gpio_direction_output(bt_xr829_reset, 0);
}
}
static int sunxi_bt_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct device *dev = &pdev->dev;
struct sunxi_bt_platdata *data;
struct gpio_config config;
const char *power, *io_regulator;
int ret = 0;
dev_info(dev, "sunxi_bt_probe()\n");
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
if (!dev)
return -ENOMEM;
data->pdev = pdev;
bluetooth_data = data;
if (of_property_read_string(np, "bt_power", &power)) {
dev_warn(dev, "Missing bt_power.\n");
} else {
data->bt_power_name = devm_kzalloc(dev, 64, GFP_KERNEL);
if (!data->bt_power_name)
return -ENOMEM;
else
strcpy(data->bt_power_name,power);
}
dev_info(dev, "bt_power_name (%s)\n", data->bt_power_name);
bt_xr829_reset = of_get_named_gpio_flags(np, "bt_rst_n", 0, (enum of_gpio_flags *)&config);
if (!gpio_is_valid(bt_xr829_reset)) {
pr_err("get gpio bt_rst failed\n");
} else {
pr_err("bt_rst gpio=%d gpio=%d mul-sel=%d pull=%d drv_level=%d data=%d\n",bt_xr829_reset,
config.gpio,
config.mul_sel,
config.pull,
config.drv_level,
config.data);
ret = devm_gpio_request(dev, bt_xr829_reset, "bt_xr829_reset");
if (ret < 0) {
dev_err(dev, "can't request bt_rst gpio %d\n",
bt_xr829_reset);
return ret;
}
ret = gpio_direction_output(bt_xr829_reset, 0);
if (ret < 0) {
dev_err(dev, "can't request output direction bt_rst gpio %d\n",
bt_xr829_reset);
return ret;
}
}
bt_xr829_wake = of_get_named_gpio_flags(np, "ap_wakeup_bt", 0, (enum of_gpio_flags *)&config);
if (!gpio_is_valid(bt_xr829_wake)) {
pr_err("get gpio bt_xr829_wake failed\n");
} else {
pr_err("bt_xr829_wake gpio=%d gpio=%d mul-sel=%d pull=%d drv_level=%d data=%d\n",bt_xr829_wake,
config.gpio,
config.mul_sel,
config.pull,
config.drv_level,
config.data);
ret = devm_gpio_request(dev, bt_xr829_wake, "ap_wakeup_bt");
if (ret < 0) {
dev_err(dev, "can't request bt_xr829_wake gpio %d\n",
bt_xr829_wake);
return ret;
}
ret = gpio_direction_output(bt_xr829_wake, 0);
if (ret < 0) {
dev_err(dev, "can't request output direction bt_xr829_wake %d\n",
bt_xr829_wake);
return ret;
}
}
#if 0
if (of_property_read_string(np, "bt_io_regulator", &io_regulator)) {
dev_warn(dev, "Missing bt_io_regulator.\n");
} else {
data->io_regulator_name = devm_kzalloc(dev, 64, GFP_KERNEL);
if (!data->io_regulator_name)
return -ENOMEM;
else
strcpy(data->io_regulator_name,io_regulator);
}
dev_info(dev, "io_regulator_name (%s)\n", data->io_regulator_name);
data->gpio_bt_rst = of_get_named_gpio_flags(np, "bt_rst_n", 0, (enum of_gpio_flags *)&config);
if (!gpio_is_valid(data->gpio_bt_rst)) {
dev_err(dev, "get gpio bt_rst failed\n");
} else {
dev_info(dev, "bt_rst gpio=%d mul-sel=%d pull=%d drv_level=%d data=%d\n",
config.gpio,
config.mul_sel,
config.pull,
config.drv_level,
config.data);
ret = devm_gpio_request(dev, data->gpio_bt_rst, "bt_rst");
if (ret < 0) {
dev_err(dev, "can't request bt_rst gpio %d\n",
data->gpio_bt_rst);
return ret;
}
ret = gpio_direction_output(data->gpio_bt_rst, 0);
if (ret < 0) {
dev_err(dev, "can't request output direction bt_rst gpio %d\n",
data->gpio_bt_rst);
return ret;
}
}
data->lpo = devm_clk_get(dev, NULL);
if (IS_ERR_OR_NULL(data->lpo)) {
dev_warn(dev, "clk not config\n");
} else {
ret = clk_prepare_enable(data->lpo);
if (ret < 0)
dev_warn(dev, "can't enable clk\n");
}
#endif
data->rfkill = rfkill_alloc("sunxi-bt", dev, RFKILL_TYPE_BLUETOOTH,
&sunxi_bt_rfkill_ops, data);
if (!data->rfkill) {
ret = -ENOMEM;
goto failed_alloc;
}
rfkill_set_states(data->rfkill, true, false);
ret = rfkill_register(data->rfkill);
if (ret){
goto fail_rfkill;
}
platform_set_drvdata(pdev, data);
class_register(&bt_power_class);
data->power_state = 0; //power_state为0,不使能
data->uart_enable = 1; //UART口OK,状态为1
//电源管理
pm_runtime_set_active(dev);
pm_runtime_get(dev);
pm_runtime_enable(dev);
if (1 != data->power_state) {
sunxi_bt_on(data, 1); //打开模块电源
}
return 0;
fail_rfkill:
if (data->rfkill)
rfkill_destroy(data->rfkill);
failed_alloc:
//if (!IS_ERR_OR_NULL(data->lpo)) {
// clk_disable_unprepare(data->lpo);
// clk_put(data->lpo);
//}
return ret;
}
static int sunxi_bt_remove(struct platform_device *pdev)
{
struct sunxi_bt_platdata *data = platform_get_drvdata(pdev);
struct rfkill *rfk = data->rfkill;
pm_runtime_disable(&pdev->dev);
pm_runtime_set_suspended(&pdev->dev);
class_unregister(&bt_power_class);
platform_set_drvdata(pdev, NULL);
if(rfk){
rfkill_unregister(rfk);
rfkill_destroy(rfk);
}
//if (!IS_ERR_OR_NULL(data->lpo)) {
// clk_disable_unprepare(data->lpo);
// clk_put(data->lpo);
//}
return 0;
}
static int bt_pm_suspend(struct device *dev)
{
struct sunxi_bt_platdata *platdata = dev_get_drvdata(dev);
printk("CONFIG_PM:enter bt_pm_suspend.\n");
if (pm_runtime_suspended(dev))
return 0;
if (platdata) {
if (0 != platdata->power_state) {
sunxi_bt_on(platdata, 0);
}
}
return 0;
}
static int bt_pm_resume(struct device *dev)
{
struct sunxi_bt_platdata *platdata = dev_get_drvdata(dev);
printk("CONFIG_PM:enter bt_pm_resume.\n");
if (pm_runtime_suspended(dev))
return 0;
if (platdata) {
if (1 != platdata->power_state) {
sunxi_bt_on(platdata, 1);
}
}
return 0;
}
static struct dev_pm_ops bt_pm_ops = {
.suspend = bt_pm_suspend,
.resume = bt_pm_resume,
};
static const struct of_device_id sunxi_bt_ids[] = {
{ .compatible = "allwinner,sunxi-bt" },
{ /* Sentinel */ }
};
static struct platform_driver sunxi_bt_driver = {
.probe = sunxi_bt_probe,
.remove = sunxi_bt_remove,
.driver = {
.owner = THIS_MODULE,
.name = "sunxi-bt",
.pm = &bt_pm_ops,
.of_match_table = sunxi_bt_ids,
},
};
module_platform_driver(sunxi_bt_driver);
MODULE_DESCRIPTION("sunxi bluetooth driver");
MODULE_LICENSE(GPL);
//BT的管脚系统配置
tools\pack\chips\sun8iw17p1\configs\k23x3-wb\sys_config.fex
;--------------------------------------------------------------------------------
;bluetooth configuration
;bt_used: 0- no used, 1- used
;clocks: external low power clock input (32.768KHz)
;bt_power: input supply voltage
;bt_io_regulator: bluetooth I/O voltage
;bt_rst_n: power up/down internal regulators used by BT section
;--------------------------------------------------------------------------------
[bt]
bt_used = 1
compatible = "allwinner,sunxi-bt"
bt_power = "vcc-ctp" //cldo1 vcc-ctp
;clocks = 0x55
bt_rst_n = port:PL07<1><default><default><0> //BT_LDO_EN<----->PL07
ap_wakeup_bt = port:PL06<1><default><1><0> //AP_WAKE_BT<---->PL06
4、UART口和PCM通道的系统管脚和模式配置:
lichee\tools\pack\chips\sun8iw17p1\configs\k23x3-wb\sys_config.fex
...............
[uart1]
uart1_used = 1 //这里必须设置成1
uart1_port = 1
uart1_type = 2
uart1_tx = port:PG06<2><1><default><default>
uart1_rx = port:PG07<2><1><default><default>
uart1_rts = port:PG08<2><1><default><default>
uart1_cts = port:PG09<2><1><default><default>
[uart1_suspend]
uart1_tx = port:PG06<7><1><default><default>
uart1_rx = port:PG07<7><1><default><default>
uart1_rts = port:PG08<7><1><default><default>
uart1_cts = port:PG09<7><1><default><default>
..............
;--------------------------------------------------------------------------------
; NOTE :Make sure daudio1mach_used = 0x1,daudio1_used = 0x1,
; if register the sound card DAUDIO1.
;--------------------------------------------------------------------------------
;--------------------------------------------------------------------------------
;allwinner,pcm_lrck_period :16/32/64/128/256
;allwinner,pcm_lrckr_period :no use
;allwinner,slot_width_select :16bits/20bits/24bits/32bits
;allwinner,pcm_lsb_first :0: msb first; 1: lsb first
;allwinner,tx_data_mode :0: 16bit linear PCM; 1: 8bit linear PCM; 2: 8bit u-law; 3: 8bit a-law
;allwinner,rx_data_mode :0: 16bit linear PCM; 1: 8bit linear PCM; 2: 8bit u-law; 3: 8bit a-law
;allwinner,daudio_master :1: SND_SOC_DAIFMT_CBM_CFM(codec clk & FRM master) use
; 2: SND_SOC_DAIFMT_CBS_CFM(codec clk slave & FRM master) not use
; 3: SND_SOC_DAIFMT_CBM_CFS(codec clk master & frame slave) not use
; 4: SND_SOC_DAIFMT_CBS_CFS(codec clk & FRM slave) use
;allwinner,audio_format: 1:SND_SOC_DAIFMT_I2S(standard i2s format). use
; 2:SND_SOC_DAIFMT_RIGHT_J(right justfied format).
; 3:SND_SOC_DAIFMT_LEFT_J(left justfied format)
; 4:SND_SOC_DAIFMT_DSP_A(pcm. MSB is available on 2nd BCLK rising edge after LRC rising edge). use
; 5:SND_SOC_DAIFMT_DSP_B(pcm. MSB is available on 1nd BCLK rising edge after LRC rising edge)
;allwinner,signal_inversion:1:SND_SOC_DAIFMT_NB_NF(normal bit clock + frame) use
; 2:SND_SOC_DAIFMT_NB_IF(normal BCLK + inv FRM)
; 3:SND_SOC_DAIFMT_IB_NF(invert BCLK + nor FRM) use
; 4:SND_SOC_DAIFMT_IB_IF(invert BCLK + FRM)
;allwinner,frametype :0: short frame = 1 clock width; 1: long frame = 2 clock width
;allwinner,tdm_config :0:pcm 1:i2s
;allwinner,mclk_div :0: not output(normal setting this) 1/2/4/6/8/12/16/24/32/48/64/96/128/176/192:
; setting mclk as input clock to external codec, freq is pll_audio/mclk_div
;allwinner,daudio0_used :0:not use 1:use
;-------------------------------------------------------------------------------
; NOTE :Make sure daudio0mach_used = 0x1,daudio0_used = 0x1,
; if register the sound card DAUDIO0.
;--------------------------------------------------------------------------------
[snddaudio1]
snddaudio1_used = 1
;-----------------------------------------------------------------------------
[daudio1]
;pcm_lrck_period = 0x20
pcm_lrck_period = 0x40
pcm_lrckr_period = 0x01
slot_width_select = 0x10
pcm_lsb_first = 0x0
tx_data_mode = 0x0
rx_data_mode = 0x0
;daudio_master = 0x04
;audio_format = 0x01
;signal_inversion = 0x01
daudio_master = 0x01
audio_format = 0x04
signal_inversion = 0x02
frametype = 0x0
;tdm_config = 0x01
tdm_config = 0x00
mclk_div = 0x0
;daudio1_used = 0
daudio1_used = 1 //这里一定要配置成1
//以上参数请参考BT模组中PCM输出的时序
5、修改上层访问的设备节点的权限和添加固件
android\device\softwinner\common\init.wireless.qcom.rc
在文件中添加:
......................
# to observe dnsmasq.leases file for dhcp information of soft ap.
chown dhcp system /data/misc/dhcp
chmod 0666 /sys/class/bt_power/power_state //添加权限
android\device\softwinner\t7-common\ueventd.sun8iw17p1.rc
............................
/dev/ttyS* 0666 system system
我们使用UART1接口连接蓝牙,使用/dev/ttyS1设备节点
添加固件:
需要将以下固件文件拷贝到/system/etc/firmware/目录中
android\device\softwinner\k23x3-wb\hardware\bt\firmware\nvm_tlv_3.2.bin
android\device\softwinner\k23x3-wb\hardware\bt\firmware\rampatch_tlv_3.2.tlv
android\device\softwinner\k23x3-wb\hardware\bt\firmware\rampatch_tlv_3.2.tlv
android\device\softwinner\k23x3-wb\hardware\bt\firmware\rampatch_tlv_tf_1.1.tlv
修改:
android\device\softwinner\k23x3-wb\k23x3_wb.mk
# BT
PRODUCT_COPY_FILES += \ $(PRODUCT_DEVICE_PATH)/hardware/bt/firmware/nvm_tlv_3.2.bin:/system/etc/firmware/nvm_tlv_3.2.bin \ $(PRODUCT_DEVICE_PATH)/hardware/bt/firmware/rampatch_tlv_3.2.tlv:/system/etc/firmware/rampatch_tlv_3.2.tlv \ $(PRODUCT_DEVICE_PATH)/hardware/bt/firmware/nvm_tlv_tf_1.1.bin:/system/etc/firmware/nvm_tlv_tf_1.1.bin \ $(PRODUCT_DEVICE_PATH)/hardware/bt/firmware/rampatch_tlv_tf_1.1.tlv:/system/etc/firmware/rampatch_tlv_tf_1.1.tlv \
以上的信息配置OK,之后。直接找模组供应商,要协议栈的相关程序,因为模组用于车载项目,不能使用Android自带的framework层部分,直接使用BT模块的CHI模式,不是CMD模式,如果使用CMD模式,直接跟模组发AT指令即可,如果使用chi模式,需要模组厂家或第第三方给出协议栈的相关文件,具体跟APP有关,到这个步骤,OS层面就OK了。而通过跟速鼎调试来看,速鼎将提供:
system/bin/gocsdk
system/etc/firmware/nvm_tlv_tf_1.1.bin
system/etc/firmware/rampatch_tlv_tf_1.1.tlv
system/config_具体公司.ini //不同厂家内容可能不同
其中system/bin/gocsdk文件将在系统启动的时候用服务进行加载,将使能 BT模块,打开/dev/ttyS1,打开PCM通道,同时处理蓝牙连接,播放音乐,打电话,同步电话本等操作,主要是协议栈的处理。
系统启动加载的蓝牙服务
android\device\softwinner\k23x3-wb\init.device.rc
service gocsdk /system/bin/gocsdk /dev/ttyS1 500000 //这个主要是供应商给的协议栈等
class main
user root
group root
service gocsdk_be130 /system/bin/gocsdk_be130 /dev/ttyS1 500000
class main
user root
group root
disabled
将以上文件导入到系统中对应目录之后,重启系统,然后基本就可以连接手机,打电话、播放音乐了。