sdk-hwV1.3/lichee/linux-4.9/drivers/char/hichs-has/snd_has_play_sound.c

1089 lines
27 KiB
C
Raw Normal View History

#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/regmap.h>
#include <linux/of_address.h>
#include <sound/dmaengine_pcm.h>
#include <linux/dma-mapping.h>
#include <linux/kthread.h>
#include <linux/mtd/mtd.h>
// #include <linux/delay.h>
#include <linux/dma/sunxi-dma.h>
#include <linux/types.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/regulator/consumer.h>
#include <linux/semaphore.h>
/* 引用全志驱动 */
#include "snd_has_sunxi_common.h"
#include "snd_has_sun8iw21_codec.h"
#define DRV_NAME "has-snd-codec"
/* all init code ref to snd_sun8iw21_codec.c */
#define DMA_ALLOC_SIZE 131072 // dma的buffer大小
#define FREQ_IN_OUT 24576000 // clk频率同步全志驱动
/* 采样率16000单声道16bit */
#define SAMPLE_RATE 16000
#define PCM_FORMAT_BITS 16 // SNDRV_PCM_FORMAT_S16_LE
#define PCM_CHANNEL_NUMBER 1
#define SOUND_VOLUME (0x000000A2)// TODO:从806获取音量 0~0xFFdefault 0xA0
#define HAS_WAIT_WAKEUP_SRC_TIMEOUT 500 // ms
#define HAS_WAIT_PLAY_END_TIMEOUT 3000 // ms
#define WAKEUP_SOURCE_NONE 0x00 // 复位就是0
#define WAKEUP_SOURCE_TOUCH 0x01
#define WAKEUP_SOURCE_STREAM 0x02
#define WAKEUP_SOURCE_DOORBELL 0x03
#define WAKEUP_SOURCE_PEEPHOLE 0x04
#define WAKEUP_SOURCE_INVALID 0xFF // 无效值,忽略,表示已经获取过唤醒源
#define DOOLBELL_PARTITION_NAME "doorbell"
// TODO:可以动态分配管理全局变量,放入device数据中
static struct sunxi_has_mem g_mem;
static struct sunxi_has_clk g_clk;
static struct has_pa_config *g_pa_config = NULL;
static struct platform_device *g_pdev = NULL;
static unsigned int g_pa_pin_max = 0;
struct resource g_res;
static unsigned int g_reg_val = 0xA0;
static atomic_t g_wakeup_src = ATOMIC_INIT(0);
// static atomic_t g_wakeup_src;
static DECLARE_COMPLETION(kill_doorbell);
static DECLARE_COMPLETION(relase_done);
static DECLARE_COMPLETION(wakeup_get);
/* WAV文件格式 */
#pragma pack(1)
struct wav_header_t {
char riff[4]; // "RIFF"
uint32_t file_size; // 文件总大小 - 8
char wave[4]; // "WAVE"
char fmt[4]; // "fmt "
uint32_t fmt_size; // fmt块大小16 for PCM
uint16_t audio_format; // 1 for PCM
uint16_t num_channels; // 声道数
uint32_t sample_rate; // 采样率
uint32_t byte_rate; // 每秒字节数sample_rate * block_align
uint16_t block_align; // 每样本字节数bits_per_sample/8 * num_channels
uint16_t bits_per_sample;// 位深
char data[4]; // "data"
uint32_t data_size; // PCM数据大小
};
#pragma pack()
struct sample_rate
{
unsigned int samplerate;
unsigned int rate_bit;
};
static const struct sample_rate sample_rate_conv[] = {
{8000, 5},
{11025, 4},
{12000, 4},
{16000, 3},
{22050, 2},
{24000, 2},
{32000, 1},
{44100, 0},
{48000, 0},
{96000, 7},
{192000, 6},
};
/* soc寄存器参数 */
static struct regmap_config g_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
.max_register = SUNXI_CODEC_REG_MAX,
.cache_type = REGCACHE_NONE,
};
static struct dma_slave_config slave_config = {
.direction = DMA_MEM_TO_DEV,
.dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES,
.device_fc = 0,
.dst_addr = 0x02030020, // DMA寄存器地址 ref to datasheet
.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES,
.slave_id = sunxi_slave_id(DRQDST_AUDIO_CODEC, DRQSRC_SDRAM), // 全志DMA controller ID
.dst_maxburst = 4,
.src_maxburst = 4,
};
void doorbell_control(unsigned char wakeup_src) {
if (wakeup_src == WAKEUP_SOURCE_DOORBELL)
{
atomic_set(&g_wakeup_src, wakeup_src);
}
complete(&wakeup_get);
}
EXPORT_SYMBOL(doorbell_control);
static void snd_sunxi_has_mem_deinit(struct platform_device *pdev)
{
devm_iounmap(&pdev->dev, g_mem.membase);
devm_release_mem_region(&pdev->dev, g_mem.memregion->start, resource_size(g_mem.memregion));
}
static void snd_sunxi_has_clk_deinit(void)
{
clk_disable_unprepare(g_clk.adcclk); // disable + unprepare
clk_disable_unprepare(g_clk.dacclk);
clk_disable_unprepare(g_clk.pllaudio);
clk_put(g_clk.adcclk); // free
clk_put(g_clk.dacclk);
clk_put(g_clk.pllaudio);
}
static void snd_sunxi_has_pa_deinit(struct platform_device *pdev, struct has_pa_config *pa_config)
{
int i;
if (pa_config == NULL)
{
return;
}
for (i = 0; i < g_pa_pin_max; i++) {
if (!pa_config[i].used)
continue;
devm_gpio_free(&pdev->dev, pa_config[i].pin);
}
kfree(pa_config);
}
static int has_free_own_device(struct platform_device *pdev)
{
if (pdev == NULL)
{
pr_emerg("pdev is NULL\n");
return -1;
}
/* set default volume */
regmap_update_bits(g_mem.regmap, SUNXI_DAC_VOL_CTRL, 0x0000FFFF, g_reg_val);
/* clk */
snd_sunxi_has_clk_deinit();
/* memery io unmap */
snd_sunxi_has_mem_deinit(pdev);
/* TODO:regulator, 可以不用去初始化 */
/* pa pin free 不需要关闭pa引脚 */
snd_sunxi_has_pa_deinit(pdev, g_pa_config);
g_pa_config = NULL;
return 0;
}
static void has_dma_complete(void *arg)
{
// enum dma_status status;
// struct dma_tx_state state;
complete(&kill_doorbell);
// status = dmaengine_tx_status(dma_chan, dma_cookie, &state);
// pr_emerg("status:%d residue:%d\n", status, state.residue);
}
static size_t has_decode_WAV_and_getPCM(unsigned char *pcm_data)
{
struct mtd_info *mtd;
size_t retlen = 0;
struct wav_header_t wav_header;
mtd = get_mtd_device_nm(DOOLBELL_PARTITION_NAME); // 名称
if (IS_ERR(mtd))
{
pr_emerg("Failed to get MTD device: %ld\n", PTR_ERR(mtd));
return -1;
}
mtd_read(mtd, 0, sizeof(struct wav_header_t), &retlen, (unsigned char *)&wav_header);
if (retlen != sizeof(struct wav_header_t))
{
pr_emerg("read err wav!");
return -1;
}
if ((memcmp(wav_header.riff, "RIFF", sizeof(wav_header.riff)) != 0) || (memcmp(wav_header.wave, "WAVE", sizeof(wav_header.wave)) != 0)
|| (memcmp(wav_header.fmt, "fmt ", sizeof(wav_header.fmt)) != 0) || ((wav_header.file_size + 8 - sizeof(struct wav_header_t)) != wav_header.data_size)
|| (wav_header.audio_format != 1) || (wav_header.num_channels != PCM_CHANNEL_NUMBER) || (wav_header.sample_rate != SAMPLE_RATE)
|| (wav_header.bits_per_sample != PCM_FORMAT_BITS) || (memcmp(wav_header.data, "data", sizeof(wav_header.data)) != 0))
{
pr_emerg("WAV format error, please check the wav file or fix this driver to adapt different sample data.\n");
pr_emerg("this driver supports bits:%d channel:%d sample rate:%d\n", PCM_FORMAT_BITS, PCM_CHANNEL_NUMBER, SAMPLE_RATE);
return -1;
}
#if 0
// pr_emerg("riff:%s\n", wav_header.riff);
pr_emerg("file size:%d\n", wav_header.file_size);
// pr_emerg("wave:%s\n", wav_header.wave);
// pr_emerg("fmt:%s\n", wav_header.fmt);
pr_emerg("fmt size:%d\n", wav_header.fmt_size);
pr_emerg("audio format:%d\n", wav_header.audio_format);
pr_emerg("channel:%d\n", wav_header.num_channels);
pr_emerg("sample rate:%d\n", wav_header.sample_rate);
pr_emerg("bps:%d\n", wav_header.byte_rate);
pr_emerg("sample bytes:%d\n", wav_header.block_align);
pr_emerg("bits:%d\n", wav_header.bits_per_sample);
// pr_emerg("data:%s\n", wav_header.data);
pr_emerg("pcm size:%d\n", wav_header.data_size);
#endif
if (wav_header.data_size > DMA_ALLOC_SIZE) // 限制128K
{
pr_emerg("file size(%d) out of limit(%d)\n", wav_header.data_size, DMA_ALLOC_SIZE);
return -1;
}
mtd_read(mtd, sizeof(struct wav_header_t), wav_header.data_size, &retlen, (unsigned char *)pcm_data);
if (retlen != wav_header.data_size)
{
pr_emerg("read pcm err\n");
return -1;
}
return (size_t)wav_header.data_size;
}
static int snd_has_pcm_playsound(void *param)
{
int ret;
struct dma_async_tx_descriptor *desc = NULL;
struct dma_chan *dma_chan = NULL;
size_t pcm_len = 0;
dma_cap_mask_t mask;
dma_addr_t dma_addr = 0;
unsigned char *dma_area = NULL;
wait_for_completion_interruptible_timeout(&wakeup_get, msecs_to_jiffies(HAS_WAIT_WAKEUP_SRC_TIMEOUT));
if (atomic_read(&g_wakeup_src) != WAKEUP_SOURCE_DOORBELL)
{
goto skip_doorbell;
}
dma_area = dma_alloc_coherent(NULL, DMA_ALLOC_SIZE, &dma_addr, GFP_KERNEL); // 申请dma
// pr_emerg("dma_area:0x%x dma_addr:0x%x\n", (int)dma_area, (int)dma_addr);
if ((!dma_area) || (!dma_addr))
{
pr_emerg("malloc dma buffer err!\n");
goto err_alloc_init;
}
pcm_len = has_decode_WAV_and_getPCM(dma_area);
if (pcm_len == -1)
{
goto err_read_init;
}
dma_cap_zero(mask);
dma_cap_set(DMA_SLAVE, mask);
dma_cap_set(DMA_CYCLIC, mask); // DMA_LINEAR
dma_chan = dma_request_channel(mask, NULL, NULL);
if (!dma_chan)
{
pr_emerg("dma_request_channel err\n");
goto err_request_init;
}
ret = dmaengine_slave_config(dma_chan, &slave_config);
if (ret < 0)
{
pr_emerg("dma slave config failed, err %d\n", ret);
goto err_config_init;
}
desc = dmaengine_prep_dma_cyclic(dma_chan,
dma_addr,
pcm_len, // buffersize*27680*2
pcm_len, // periodsize*2(960*2)
DMA_MEM_TO_DEV, DMA_CTRL_ACK | DMA_PREP_INTERRUPT);
if (!desc)
{
pr_emerg("dmaengine_prep_dma_cyclic err!\n");
goto err_desc_init;
}
desc->callback = has_dma_complete;
desc->callback_param = NULL;
msleep(10); // 要延时,否则会导致前面一小段音缺失
dmaengine_submit(desc);
// dma_cookie = dmaengine_submit(desc);
dma_async_issue_pending(dma_chan);
// pr_emerg("plad sound start!\n");
wait_for_completion_interruptible_timeout(&kill_doorbell, msecs_to_jiffies(HAS_WAIT_PLAY_END_TIMEOUT));
// dmaengine_synchronize(dma_chan);
dmaengine_terminate_async(dma_chan);
dma_release_channel(dma_chan);
dma_free_coherent(NULL, DMA_ALLOC_SIZE, dma_area, dma_addr); // 如果有申请就删除
has_free_own_device(g_pdev);
g_pdev = NULL;
complete(&relase_done);
// pr_emerg("plad sound end!\n");
return 0;
err_desc_init:
err_config_init:
err_request_init:
dma_release_channel(dma_chan);
err_read_init:
dma_free_coherent(NULL, DMA_ALLOC_SIZE, dma_area, dma_addr);
err_alloc_init:
skip_doorbell:
has_free_own_device(g_pdev);
g_pdev = NULL;
complete(&relase_done);
return -1;
}
static void has_start_dma_codec(void)
{
struct task_struct *snd_thread = NULL;
snd_thread = kthread_create(snd_has_pcm_playsound, NULL, "has_sound");
if (IS_ERR(snd_thread))
{
snd_thread = NULL;
pr_warning("unable to start kernel process_file\n");
}
else
{
wake_up_process(snd_thread);
}
}
static int snd_sunxi_clk_enable(struct platform_device *pdev, struct sunxi_has_clk *clk)
{
int ret = 0;
/* (22579200 or 24576000) * n */
if (clk_set_rate(clk->pllaudio, 22579200))
{
pr_emerg("pllaudio set rate failed\n");
goto err_set_rate;
}
if (clk_prepare_enable(clk->pllaudio))
{
pr_emerg("pllaudio enable failed\n");
goto err_enable_pllaudio;
}
if (clk_prepare_enable(clk->dacclk))
{
pr_emerg("dacclk enable failed\n");
goto err_enable_dacclk;
}
if (clk_prepare_enable(clk->adcclk))
{
pr_emerg("adcclk enable failed\n");
goto err_enable_adcclk;
}
return 0;
err_enable_adcclk:
clk_disable_unprepare(clk->dacclk);
err_enable_dacclk:
clk_disable_unprepare(clk->pllaudio);
err_enable_pllaudio:
err_set_rate:
return ret;
}
/*******************************************************************************
* *** kernel source ***
* @1 regmap
* @2 clk
* @3 regulator
* @4 pa pin
* @5 dts params
******************************************************************************/
static int snd_sunxi_has_clk_init(struct platform_device *pdev, struct sunxi_has_clk *clk)
{
int ret = 0;
struct device_node *np = pdev->dev.of_node;
clk->pllaudio = of_clk_get(np, 0);
if (IS_ERR_OR_NULL(clk->pllaudio))
{
pr_emerg("pllaudio get failed\n");
ret = PTR_ERR(clk->pllaudio);
goto err_pllaudio;
}
clk->dacclk = of_clk_get(np, 1);
if (IS_ERR_OR_NULL(clk->dacclk))
{
pr_emerg("dacclk get failed\n");
ret = PTR_ERR(clk->dacclk);
goto err_dacclk;
}
clk->adcclk = of_clk_get(np, 2);
if (IS_ERR_OR_NULL(clk->adcclk))
{
pr_emerg("adcclk get failed\n");
ret = PTR_ERR(clk->adcclk);
goto err_adcclk;
}
if (clk_set_parent(clk->dacclk, clk->pllaudio))
{
pr_emerg("set parent of dacclk to pllaudio failed\n");
ret = -EINVAL;
goto err_set_parent;
}
if (clk_set_parent(clk->adcclk, clk->pllaudio))
{
pr_emerg("set parent of adcclk to pllaudio failed\n");
ret = -EINVAL;
goto err_set_parent;
}
ret = snd_sunxi_clk_enable(pdev, clk);
if (ret)
{
pr_emerg("clk enable failed\n");
ret = -EINVAL;
goto err_clk_enable;
}
return 0;
err_clk_enable:
err_set_parent:
clk_put(clk->adcclk);
err_adcclk:
clk_put(clk->dacclk);
err_dacclk:
clk_put(clk->pllaudio);
err_pllaudio:
return ret;
}
static int snd_sunxi_has_regulator_init(struct platform_device *pdev, struct sunxi_regulator *rglt)
{
int ret = 0;
unsigned int temp_val;
struct device_node *np = pdev->dev.of_node;
rglt->external_avcc = of_property_read_bool(np, "external-avcc");
ret = of_property_read_u32(np, "avcc-vol", &temp_val);
if (ret < 0)
{
/* default avcc voltage: 1.8v */
rglt->avcc_vol = 1800000;
}
else
{
rglt->avcc_vol = temp_val;
}
if (rglt->external_avcc)
{
rglt->avcc = regulator_get(&pdev->dev, "avcc");
if (IS_ERR_OR_NULL(rglt->avcc))
{
return 0;
}
}
else
{
return 0;
}
ret = regulator_set_voltage(rglt->avcc, rglt->avcc_vol, rglt->avcc_vol);
if (ret < 0)
{
pr_emerg("set avcc voltage failed\n");
ret = -EFAULT;
goto err_regulator_set_vol_avcc;
}
ret = regulator_enable(rglt->avcc);
if (ret < 0)
{
pr_emerg("enable avcc failed\n");
ret = -EFAULT;
goto err_regulator_enable_avcc;
}
return 0;
err_regulator_enable_avcc:
err_regulator_set_vol_avcc:
if (rglt->avcc)
regulator_put(rglt->avcc);
return ret;
}
/* for regmap */
static int snd_has_sunxi_mem_init(struct platform_device *pdev, struct sunxi_has_mem *mem)
{
int ret = 0;
struct device_node *np = pdev->dev.of_node;
ret = of_address_to_resource(np, 0, mem->res);
if (ret)
{
pr_emerg("parse device node resource failed\n");
ret = -EINVAL;
goto err_of_addr_to_resource;
}
mem->memregion = devm_request_mem_region(&pdev->dev, mem->res->start,
resource_size(mem->res), mem->dev_name);
if (IS_ERR_OR_NULL(mem->memregion))
{
pr_emerg("memory region already claimed\n");
ret = -EBUSY;
goto err_devm_request_region;
}
mem->membase = devm_ioremap(&pdev->dev, mem->memregion->start,
resource_size(mem->memregion));
if (IS_ERR_OR_NULL(mem->membase))
{
pr_emerg("ioremap failed\n");
ret = -EBUSY;
goto err_devm_ioremap;
}
mem->regmap = devm_regmap_init_mmio(&pdev->dev, mem->membase, mem->regmap_config);
if (IS_ERR_OR_NULL(mem->regmap))
{
pr_emerg("regmap init failed\n");
ret = -EINVAL;
goto err_devm_regmap_init;
}
return 0;
err_devm_regmap_init:
devm_iounmap(&pdev->dev, mem->membase);
err_devm_ioremap:
devm_release_mem_region(&pdev->dev, mem->memregion->start, resource_size(mem->memregion));
err_devm_request_region:
err_of_addr_to_resource:
return ret;
}
static int snd_has_sunxi_pa_pin(struct has_pa_config *pa_cfg, u32 pa_pin_max, u8 enable)
{
int i;
if (pa_pin_max < 1)
{
return 0;
}
if (enable)
{
msleep(pa_cfg[0].msleep);
}
for (i = 0; i < pa_pin_max; i++)
{
if (!pa_cfg[i].used)
continue;
gpio_direction_output(pa_cfg[i].pin, 1);
if (enable)
gpio_set_value(pa_cfg[i].pin, pa_cfg[i].level);
else
gpio_set_value(pa_cfg[i].pin, !pa_cfg[i].level);
}
return 0;
}
/* for pa config */
static struct has_pa_config *snd_has_sunxi_pa_pin_init(struct platform_device *pdev,
u32 *pa_pin_max)
{
int ret, i;
u32 pin_max;
u32 gpio_tmp;
u32 temp_val;
char str[20] = {0};
struct has_pa_config *pa_cfg;
struct device_node *np = pdev->dev.of_node;
*pa_pin_max = 0;
ret = of_property_read_u32(np, "pa-pin-max", &temp_val);
if (ret < 0)
{
return NULL;
}
else
{
pin_max = temp_val;
}
pa_cfg = kzalloc(sizeof(struct has_pa_config) * pin_max, GFP_KERNEL);
if (!pa_cfg)
{
pr_emerg("can't has_pa_config memory\n");
return NULL;
}
for (i = 0; i < pin_max; i++)
{
sprintf(str, "pa-pin-%d", i);
ret = of_get_named_gpio(np, str, 0);
if (ret < 0)
{
pr_emerg("pa-pin-%u get failed\n", i);
pa_cfg[i].used = 0;
continue;
}
gpio_tmp = ret;
if (!gpio_is_valid(gpio_tmp))
{
pr_emerg("pa-pin-%u (%u) is invalid\n", i, gpio_tmp);
pa_cfg[i].used = 0;
continue;
}
ret = devm_gpio_request(&pdev->dev, gpio_tmp, str);
if (ret)
{
pr_emerg("pa-pin-%u (%u) request failed\n", i, gpio_tmp);
pa_cfg[i].used = 0;
continue;
}
pa_cfg[i].used = 1;
pa_cfg[i].pin = gpio_tmp;
sprintf(str, "pa-pin-level-%d", i);
ret = of_property_read_u32(np, str, &temp_val);
if (ret < 0)
{
pa_cfg[i].level = 0;
}
else
{
if (temp_val > 0)
pa_cfg[i].level = 1;
}
sprintf(str, "pa-pin-msleep-%d", i);
ret = of_property_read_u32(np, str, &temp_val);
if (ret < 0)
{
pa_cfg[i].msleep = 0;
}
else
{
pa_cfg[i].msleep = temp_val;
}
}
*pa_pin_max = pin_max;
snd_has_sunxi_pa_pin(pa_cfg, pin_max, 0);
return pa_cfg;
}
static void snd_sunxi_has_dts_params_init(struct platform_device *pdev, struct sunxi_has_dts *dts)
{
int ret = 0;
unsigned int temp_val;
struct device_node *np = pdev->dev.of_node;
/* lineout volume */
ret = of_property_read_u32(np, "lineout-vol", &temp_val);
if (ret < 0)
{
dts->lineout_vol = 0;
}
else
{
dts->lineout_vol = temp_val;
}
/* mic gain for capturing */
ret = of_property_read_u32(np, "mic1gain", &temp_val);
if (ret < 0)
{
dts->mic1gain = 31;
}
else
{
dts->mic1gain = temp_val;
}
ret = of_property_read_u32(np, "mic2gain", &temp_val);
if (ret < 0)
{
dts->mic2gain = 31;
}
else
{
dts->mic2gain = temp_val;
}
ret = of_property_read_u32(np, "adcdelaytime", &temp_val);
if (ret < 0)
{
dts->adc_dtime = 0;
}
else
{
switch (temp_val)
{
case 0:
case 5:
case 10:
case 20:
case 30:
dts->adc_dtime = temp_val;
break;
default:
pr_emerg("adc delay time supoort only 0,5,10,20,30ms\n");
dts->adc_dtime = 0;
break;
}
}
/* lineout & mic1 & mic2 diff or single */
dts->lineout_single = of_property_read_bool(np, "lineout-single");
dts->mic1_single = of_property_read_bool(np, "mic1-single");
dts->mic2_single = of_property_read_bool(np, "mic2-single");
/* tx_hub */
dts->tx_hub_en = of_property_read_bool(np, "tx-hub-en");
/* components func -> rx_sync */
dts->rx_sync_en = of_property_read_bool(np, "rx-sync-en");
if (dts->rx_sync_en)
{
dts->rx_sync_ctl = false;
}
}
static int has_trigger_codec(void)
{
int i;
// unsigned int reg_val;
struct regmap *regmap = g_mem.regmap;
/* 1 */
// regmap_read(regmap, SUNXI_DAC_DPC, &reg_val); // reg_val:0x80000001
/* 2 */
regmap_update_bits(regmap, SUNXI_DAC_DPC, 0x1 << DAC_HUB_EN, 0x1 << DAC_HUB_EN);
regmap_update_bits(regmap, SUNXI_DAC_DPC, 0x1 << EN_DAC, 0x1 << EN_DAC);
regmap_update_bits(regmap, SUNXI_DAC_REG, 0x1 << DACLMUTE, 0x1 << DACLMUTE);
regmap_update_bits(regmap, SUNXI_DAC_REG, 0x1 << DACLEN, 0x1 << DACLEN);
regmap_update_bits(regmap, SUNXI_DAC_REG, 0x1 << LINEOUTLEN, 0x1 << LINEOUTLEN);
/* 3 */
/* capture */
/* 4 */
if (clk_set_rate(g_clk.pllaudio, FREQ_IN_OUT))
{
pr_emerg("pllaudio set rate failed\n");
return -EINVAL;
}
if (clk_set_rate(g_clk.dacclk, FREQ_IN_OUT))
{
pr_emerg("dacclk set rate failed\n");
return -EINVAL;
}
/* 5 */
/* set bits */
#if (PCM_FORMAT_BITS != 16)
#error "change code here to adapt format bits"
#endif
regmap_update_bits(regmap, SUNXI_DAC_FIFOC, 3 << FIFO_MODE, 3 << FIFO_MODE);
regmap_update_bits(regmap, SUNXI_DAC_FIFOC, 1 << TX_SAMPLE_BITS, 0 << TX_SAMPLE_BITS);
/* set rate */
for (i = 0; i < ARRAY_SIZE(sample_rate_conv); i++)
{
if (sample_rate_conv[i].samplerate == SAMPLE_RATE)
{
regmap_update_bits(regmap, SUNXI_DAC_FIFOC, 0x7 << DAC_FS, sample_rate_conv[i].rate_bit << DAC_FS);
}
}
#if (SAMPLE_RATE != 16000)
#error "change code here to adapt sample rate"
#endif
/* reset the adchpf func setting for different sampling. case 16000:*/
regmap_write(regmap, SUNXI_ADC_DRC_HHPFC, (0x00F623A5 >> 16) & 0xFFFF);
regmap_write(regmap, SUNXI_ADC_DRC_LHPFC, 0x00F623A5 & 0xFFFF);
/* set channels. case 1: one channel, DACL & DACR send same data */
#if (PCM_CHANNEL_NUMBER != 1)
#error "change code here to adapt channel"
#endif
regmap_update_bits(regmap, SUNXI_DAC_FIFOC, 0x1 << DAC_MONO_EN, 0x1 << DAC_MONO_EN);
/* 6 */
regmap_update_bits(regmap, SUNXI_DAC_FIFOC, 1 << FIFO_FLUSH, 1 << FIFO_FLUSH);
regmap_write(regmap, SUNXI_DAC_FIFOS, 1 << DAC_TXE_INT | 1 << DAC_TXU_INT | 1 << DAC_TXO_INT);
regmap_write(regmap, SUNXI_DAC_CNT, 0);
/* 7 */
regmap_update_bits(regmap, SUNXI_DAC_REG, 0x1 << DACLEN, 0x1 << DACLEN);
regmap_update_bits(regmap, SUNXI_DAC_REG, 0x1 << DACLMUTE, 0x1 << DACLMUTE);
regmap_update_bits(regmap, SUNXI_DAC_DPC, 0x1 << EN_DAC, 0x1 << EN_DAC);
/* 8 */
regmap_update_bits(regmap, SUNXI_DAC_REG, 0x1 << LINEOUTLEN, 0x1 << LINEOUTLEN);
/* 9 */
snd_has_sunxi_pa_pin(g_pa_config, g_pa_pin_max, 1);
/* 10 */
regmap_update_bits(regmap, SUNXI_DAC_FIFOC, 1 << DAC_DRQ_EN, 1 << DAC_DRQ_EN);
return 0;
}
static void sunxi_has_codec_init(struct sunxi_has_dts *dts)
{
unsigned int adc_dtime_map;
struct regmap *regmap = g_mem.regmap;
regmap_update_bits(regmap, SUNXI_POWER_REG, 0x7 << ALDO_OUTPUT_VOLTAGE, 3 << ALDO_OUTPUT_VOLTAGE);
regmap_update_bits(regmap, SUNXI_POWER_REG, 0x1 << ALDO_EN, 0x1 << ALDO_EN);
regmap_update_bits(regmap, SUNXI_RAMP_REG, 0x1 << RMC_EN, 0x1 << RMC_EN);
regmap_update_bits(regmap, SUNXI_ADC_FIFOC, 0x1 << ADCDFEN, 0x1 << ADCDFEN);
switch (dts->adc_dtime)
{
case 5:
adc_dtime_map = 0;
break;
case 10:
adc_dtime_map = 1;
break;
case 20:
adc_dtime_map = 2;
break;
case 30:
adc_dtime_map = 3;
break;
case 0:
default:
regmap_update_bits(regmap, SUNXI_ADC_FIFOC, 0x1 << ADCDFEN, 0x0 << ADCDFEN);
break;
}
regmap_update_bits(regmap, SUNXI_ADC_FIFOC, 0x3 << ADCFDT, adc_dtime_map << ADCFDT);
regmap_update_bits(regmap, SUNXI_ADC_FIFOC, 0x1 << RX_SYNC_EN, 0x0 << RX_SYNC_EN);
/* Digital VOL defeult setting */
regmap_update_bits(regmap, SUNXI_DAC_DPC, 0x3F << DVOL, 0 << DVOL);
/* LINEOUT VOL defeult setting */
regmap_update_bits(regmap, SUNXI_DAC_REG, 0x1F << LINEOUT_VOL,
dts->lineout_vol << LINEOUT_VOL);
/* ADCL MIC1 gain defeult setting */
regmap_update_bits(regmap, SUNXI_ADC1_REG, 0x1F << ADC1_PGA_GAIN_CTRL, dts->mic1gain << ADC1_PGA_GAIN_CTRL);
/* ADCR MIC2 gain defeult setting */
regmap_update_bits(regmap, SUNXI_ADC2_REG, 0x1F << ADC2_PGA_GAIN_CTRL, dts->mic2gain << ADC2_PGA_GAIN_CTRL);
/* ADC IOP params default setting */
regmap_update_bits(regmap, SUNXI_ADC1_REG, 0xFF << ADC1_IOPMIC, 0x55 << ADC1_IOPMIC);
regmap_update_bits(regmap, SUNXI_ADC2_REG, 0xFF << ADC2_IOPMIC, 0x55 << ADC2_IOPMIC);
/* lineout & mic1 & mic2 diff or single setting */
if (dts->lineout_single)
regmap_update_bits(regmap, SUNXI_DAC_REG,
0x1 << LINEOUTLDIFFEN, 0x0 << LINEOUTLDIFFEN);
else
regmap_update_bits(regmap, SUNXI_DAC_REG,
0x1 << LINEOUTLDIFFEN, 0x1 << LINEOUTLDIFFEN);
if (dts->mic1_single)
regmap_update_bits(regmap, SUNXI_ADC1_REG,
0x1 << MIC1_SIN_EN, 0x1 << MIC1_SIN_EN);
else
regmap_update_bits(regmap, SUNXI_ADC1_REG,
0x1 << MIC1_SIN_EN, 0x0 << MIC1_SIN_EN);
if (dts->mic2_single)
regmap_update_bits(regmap, SUNXI_ADC2_REG,
0x1 << MIC2_SIN_EN, 0x1 << MIC2_SIN_EN);
else
regmap_update_bits(regmap, SUNXI_ADC2_REG,
0x1 << MIC2_SIN_EN, 0x0 << MIC2_SIN_EN);
regmap_update_bits(regmap, SUNXI_DAC_VOL_CTRL, 1 << DAC_VOL_SEL, 1 << DAC_VOL_SEL);
regmap_update_bits(regmap, SUNXI_ADC_DIG_CTRL, 1 << ADC1_2_VOL_EN, 1 << ADC1_2_VOL_EN);
regmap_read(regmap, SUNXI_DAC_VOL_CTRL, &g_reg_val);
// reg_R_val = (g_reg_val & 0xFF);
// reg_L_val = ((g_reg_val >> 8) & 0xFF);
// pr_emerg("reg_val:0x%08x, L:0x%02x R:0x%02x\n", reg_val, reg_L_val, reg_R_val);
// regmap_update_bits(regmap, SUNXI_DAC_VOL_CTRL, 0x000000FF, SOUND_VOLUME); // R
regmap_update_bits(regmap, SUNXI_DAC_VOL_CTRL, 0x0000FFFF, (SOUND_VOLUME << 8 | SOUND_VOLUME)); // L
}
static int doolbell_drv_open(struct inode *node, struct file *file)
{
// printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
return 0;
}
static int doolbell_drv_close(struct inode *node, struct file *file)
{
// printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
return 0;
}
#define DOOLBELL_STOP _IOWR('D', 100, unsigned int)
#define DOOLBELL_GET_STATUS _IOR('D', 101, unsigned int)
#define DOOLBELL_PLAYING 0
#define DOOLBELL_DONE 1
static long doolbell_drv_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
int value;
switch (cmd) {
case DOOLBELL_STOP: {
complete(&kill_doorbell);
complete(&wakeup_get);
wait_for_completion_interruptible(&relase_done);
if (g_pdev != NULL)
{
return -EINVAL;
}
break;
}
case DOOLBELL_GET_STATUS: {
/* 用户态应该轮询查询状态轮询不用考虑并发读取g_pdev */
if (g_pdev != NULL) // 启动门铃流程未结束
{
value = DOOLBELL_PLAYING;
}
else // 启动门铃流程已经结束
{
value = DOOLBELL_DONE;
}
if (copy_to_user((char __user *)arg, &value, sizeof(value)))
{
return -EINVAL;
}
break;
}
default:
return -EINVAL;
break;
}
return 0;
}
static struct file_operations doorbell_drv = {
.owner = THIS_MODULE,
.open = doolbell_drv_open,
.release = doolbell_drv_close,
.unlocked_ioctl = doolbell_drv_ioctl,
};
static int has_play_sound_dev_probe(struct platform_device *pdev)
{
struct sunxi_regulator rglt;
struct device_node *np;
unsigned int temp_val = 0;
struct sunxi_has_dts temp_dts;
int major = 0;
struct class *doorbell_class;
g_pdev = pdev;
g_mem.dev_name = DRV_NAME;
g_mem.res = &g_res;
g_mem.regmap_config = &g_regmap_config;
// atomic_set(&g_wakeup_src, WAKEUP_SOURCE_NONE);
np = of_find_compatible_node(NULL, NULL, "allwinner,sunxi-snd-plat-aaudio");
if (np) {
if (of_property_read_u32(np, "dac-txdata", &temp_val) < 0) {
pr_emerg("get dac-txdata err!\n");
}
else
{
slave_config.dst_addr = temp_val;
}
}
/* 寄存器地址 */
if (snd_has_sunxi_mem_init(pdev, &g_mem))
{
goto err_mem_init;
}
/* 时钟初始化 */
if (snd_sunxi_has_clk_init(pdev, &g_clk))
{
goto err_clk_init;
}
/* 低压差线性稳压器 */
if (snd_sunxi_has_regulator_init(pdev, &rglt))
{
goto err_regulator_init;
}
/* pa引脚 */
g_pa_config = snd_has_sunxi_pa_pin_init(pdev, &g_pa_pin_max);
if (g_pa_config == NULL)
{
goto err_pa_init;
}
snd_sunxi_has_dts_params_init(pdev, &temp_dts);
/* codec 初始化 */
sunxi_has_codec_init(&temp_dts);
has_trigger_codec();
major = register_chrdev(0, "doorbell", &doorbell_drv);
doorbell_class = class_create(THIS_MODULE, "doorbell_class");
if (IS_ERR(doorbell_class)) {
unregister_chrdev(major, "doorbell");
goto err_pa_init;
}
device_create(doorbell_class, NULL, MKDEV(major, 0), NULL, "doorbell");
has_start_dma_codec();
return 0;
err_pa_init:
err_regulator_init:
err_clk_init:
snd_sunxi_has_clk_deinit();
err_mem_init:
snd_sunxi_has_mem_deinit(pdev);
g_pdev = NULL;
return -EINVAL;
}
static int has_play_sound_dev_remove(struct platform_device *pdev)
{
// has_free_own_device(pdev);
// device_destroy(doorbell_class, MKDEV(major, 0));
// class_destroy(doorbell_class);
// unregister_chrdev(major, "doorbell");
return 0;
}
// 使用全志codec寄存器
static const struct of_device_id has_play_sound_of_match[] = {
{
.compatible = "has," DRV_NAME,
},
{},
};
MODULE_DEVICE_TABLE(of, has_play_sound_of_match);
static struct platform_driver has_play_sond_driver = {
.driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
.of_match_table = has_play_sound_of_match,
},
.probe = has_play_sound_dev_probe,
.remove = has_play_sound_dev_remove,
};
module_platform_driver(has_play_sond_driver);
MODULE_AUTHOR("@hichs.com.cn");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("play sound during start up");