#include #include #include #include #include #include #include #include #include // #include #include #include #include #include #include #include #include /* 引用全志驱动 */ #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 struct sunxi_has_mem g_mem; struct sunxi_has_clk g_clk; struct has_pa_config *g_pa_config = NULL; struct platform_device *g_pdev = NULL; unsigned int g_pa_pin_max = 0; struct resource g_res; static DECLARE_COMPLETION(doorbell_end); static DECLARE_COMPLETION(relase_done); /* 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, }; 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; } /* 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(&doorbell_end); // 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("logo"); // 名称 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; 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*2(7680*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(&doorbell_end); // 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: 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, ®_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); } 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(&doorbell_end); wait_for_completion_interruptible(&relase_done); if (g_pdev != NULL) { return -EINVAL; } break; } case DOOLBELL_GET_STATUS: { /* 用户态应该轮询查询状态 */ 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; 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");