From 2daf93c952f6dcb09c1d1bcc3ed77c776024d8a9 Mon Sep 17 00:00:00 2001 From: zhangzhaopeng Date: Fri, 11 Apr 2025 18:29:20 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=86=85=E6=A0=B8=E5=90=AF?= =?UTF-8?q?=E5=8A=A8=E9=98=B6=E6=AE=B5=E6=92=AD=E6=94=BE=E5=A3=B0=E9=9F=B3?= =?UTF-8?q?=E7=9A=84=E9=A9=B1=E5=8A=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../configs/fastboot_sl100_back/board.dts | 20 + .../fastboot_sl100_back/linux/config-4.9 | 1 + .../fastboot_sl100_front/linux/config-4.9 | 1 + .../arch/arm/boot/dts/sun8iw21p1.dtsi | 9 + lichee/linux-4.9/drivers/char/Kconfig | 1 + lichee/linux-4.9/drivers/char/Makefile | 1 + .../linux-4.9/drivers/char/hichs-has/Kconfig | 12 + .../linux-4.9/drivers/char/hichs-has/Makefile | 5 + .../char/hichs-has/snd_has_play_sound.c | 1044 +++++++++++++++++ .../char/hichs-has/snd_has_sun8iw21_codec.h | 460 ++++++++ .../char/hichs-has/snd_has_sunxi_common.h | 55 + 11 files changed, 1609 insertions(+) create mode 100755 lichee/linux-4.9/drivers/char/hichs-has/Kconfig create mode 100755 lichee/linux-4.9/drivers/char/hichs-has/Makefile create mode 100755 lichee/linux-4.9/drivers/char/hichs-has/snd_has_play_sound.c create mode 100755 lichee/linux-4.9/drivers/char/hichs-has/snd_has_sun8iw21_codec.h create mode 100755 lichee/linux-4.9/drivers/char/hichs-has/snd_has_sunxi_common.h diff --git a/device/config/chips/v851s/configs/fastboot_sl100_back/board.dts b/device/config/chips/v851s/configs/fastboot_sl100_back/board.dts index 0144c3108..4cd2cfd29 100755 --- a/device/config/chips/v851s/configs/fastboot_sl100_back/board.dts +++ b/device/config/chips/v851s/configs/fastboot_sl100_back/board.dts @@ -1457,6 +1457,26 @@ }; +&codec_has { + /* external-avcc; */ + /* avcc-supply = <®_aldo1>; */ + avcc-vol = <1800000>; /* uv */ + lineout-vol = <31>; + mic1gain = <31>; + mic2gain = <31>; + adcdelaytime = <0>; + dma_tx_fifo = <0x02030000>; + /* lineout-single; */ + /* mic1-single; */ + /* mic2-single; */ + pa-pin-max = <1>; /* set pa */ + pa-pin-0 = <&pio PD 21 1 1 1 0>; + pa-pin-level-0 = <1>; + pa-pin-msleep-0 = <0>; + tx-hub-en; + rx-sync-en; + status = "disabled"; +}; /* audio dirver module -> audio codec */ &codec { diff --git a/device/config/chips/v851s/configs/fastboot_sl100_back/linux/config-4.9 b/device/config/chips/v851s/configs/fastboot_sl100_back/linux/config-4.9 index 7248f69df..6171bce00 100755 --- a/device/config/chips/v851s/configs/fastboot_sl100_back/linux/config-4.9 +++ b/device/config/chips/v851s/configs/fastboot_sl100_back/linux/config-4.9 @@ -1127,6 +1127,7 @@ CONFIG_SUNXI_SYS_INFO=y CONFIG_DUMP_REG=y CONFIG_DUMP_REG_MISC=y # CONFIG_SUNXI_TIMER_TEST is not set +# CONFIG_HAS_INTERNALCODEC is not set # CONFIG_MEM_OPERATION is not set # CONFIG_SUNXI_TRANSFORM is not set # CONFIG_SUNXI_DI is not set diff --git a/device/config/chips/v851s3/configs/fastboot_sl100_front/linux/config-4.9 b/device/config/chips/v851s3/configs/fastboot_sl100_front/linux/config-4.9 index 666a16949..4da9deebe 100755 --- a/device/config/chips/v851s3/configs/fastboot_sl100_front/linux/config-4.9 +++ b/device/config/chips/v851s3/configs/fastboot_sl100_front/linux/config-4.9 @@ -1254,6 +1254,7 @@ CONFIG_SUNXI_SYS_INFO=y CONFIG_DUMP_REG=y CONFIG_DUMP_REG_MISC=y # CONFIG_SUNXI_TIMER_TEST is not set +# CONFIG_HAS_INTERNALCODEC is not set CONFIG_MEM_OPERATION=y # CONFIG_SUNXI_TRANSFORM is not set # CONFIG_SUNXI_DI is not set diff --git a/lichee/linux-4.9/arch/arm/boot/dts/sun8iw21p1.dtsi b/lichee/linux-4.9/arch/arm/boot/dts/sun8iw21p1.dtsi index fe3bfeb47..82309184e 100755 --- a/lichee/linux-4.9/arch/arm/boot/dts/sun8iw21p1.dtsi +++ b/lichee/linux-4.9/arch/arm/boot/dts/sun8iw21p1.dtsi @@ -1170,6 +1170,15 @@ gpr_cur_pos = <6>; }; + codec_has:codec_has@0x02030000 { + #sound-dai-cells = <0>; + compatible = "has,has-snd-codec"; + reg = <0x0 0x02030000 0x0 0x34C>; + clocks = <&clk_pll_audio>, + <&clk_codec_dac>, + <&clk_codec_adc>; + status = "disabled"; + }; /* audio dirver module -> audio codec */ codec:codec@0x02030000 { #sound-dai-cells = <0>; diff --git a/lichee/linux-4.9/drivers/char/Kconfig b/lichee/linux-4.9/drivers/char/Kconfig index 836ad1870..89c3e0e98 100644 --- a/lichee/linux-4.9/drivers/char/Kconfig +++ b/lichee/linux-4.9/drivers/char/Kconfig @@ -596,6 +596,7 @@ source "drivers/char/sunxi-scr/Kconfig" source "drivers/char/sunxi-sysinfo/Kconfig" source "drivers/char/dump_reg/Kconfig" source "drivers/char/timer_test/Kconfig" +source "drivers/char/hichs-has/Kconfig" source "drivers/char/mem_operation/Kconfig" source "drivers/char/sunxi_tr/Kconfig" source "drivers/char/sunxi-di/Kconfig" diff --git a/lichee/linux-4.9/drivers/char/Makefile b/lichee/linux-4.9/drivers/char/Makefile index e3636a46b..3f0f013ed 100644 --- a/lichee/linux-4.9/drivers/char/Makefile +++ b/lichee/linux-4.9/drivers/char/Makefile @@ -64,6 +64,7 @@ obj-$(CONFIG_POWERNV_OP_PANEL) += powernv-op-panel.o obj-$(CONFIG_ARCH_SUNXI) += sunxi-sysinfo/ obj-$(CONFIG_SUNXI_TIMER_TEST) += timer_test/ +obj-$(CONFIG_HAS_INTERNALCODEC) += hichs-has/ obj-$(CONFIG_MEM_OPERATION) += mem_operation/ obj-$(CONFIG_DUMP_REG) += dump_reg/ obj-$(CONFIG_SUNXI_TRANSFORM) += sunxi_tr/ diff --git a/lichee/linux-4.9/drivers/char/hichs-has/Kconfig b/lichee/linux-4.9/drivers/char/hichs-has/Kconfig new file mode 100755 index 000000000..475de3287 --- /dev/null +++ b/lichee/linux-4.9/drivers/char/hichs-has/Kconfig @@ -0,0 +1,12 @@ +# +# Touchscreen driver configuration +# +config HAS_INTERNALCODEC + tristate "play pcm file from Specific partition to internalcodec(DAC) during start up" + # depends on INPUT && I2C + default N + help + Say Y here if you need to plat sound during start up. + If unsure, say N. + + diff --git a/lichee/linux-4.9/drivers/char/hichs-has/Makefile b/lichee/linux-4.9/drivers/char/hichs-has/Makefile new file mode 100755 index 000000000..0c9e951a8 --- /dev/null +++ b/lichee/linux-4.9/drivers/char/hichs-has/Makefile @@ -0,0 +1,5 @@ + +obj-$(CONFIG_HAS_INTERNALCODEC) += has_boot_doorbell.o + + +has_boot_doorbell-objs := snd_has_play_sound.o diff --git a/lichee/linux-4.9/drivers/char/hichs-has/snd_has_play_sound.c b/lichee/linux-4.9/drivers/char/hichs-has/snd_has_play_sound.c new file mode 100755 index 000000000..a1f4c4571 --- /dev/null +++ b/lichee/linux-4.9/drivers/char/hichs-has/snd_has_play_sound.c @@ -0,0 +1,1044 @@ +#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"); diff --git a/lichee/linux-4.9/drivers/char/hichs-has/snd_has_sun8iw21_codec.h b/lichee/linux-4.9/drivers/char/hichs-has/snd_has_sun8iw21_codec.h new file mode 100755 index 000000000..0055b9f39 --- /dev/null +++ b/lichee/linux-4.9/drivers/char/hichs-has/snd_has_sun8iw21_codec.h @@ -0,0 +1,460 @@ +/* sound\soc\sunxi\snd_sun8iw21_codec.h + * (C) Copyright 2021-2025 + * Allwinner Technology Co., Ltd. + * Dby + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ + +#ifndef __SND_HAS_SUN8IW21_CODEC_H +#define __SND_HAS_SUN8IW21_CODEC_H + +#define SUNXI_DAC_DPC 0x00 +#define SUNXI_DAC_VOL_CTRL 0x04 +#define SUNXI_DAC_FIFOC 0x10 +#define SUNXI_DAC_FIFOS 0x14 + +#define SUNXI_DAC_TXDATA 0X20 +#define SUNXI_DAC_CNT 0x24 +#define SUNXI_DAC_DG 0x28 + +#define SUNXI_ADC_FIFOC 0x30 +#define SUNXI_ADC_VOL_CTRL 0x34 +#define SUNXI_ADC_FIFOS 0x38 +#define SUNXI_ADC_RXDATA 0x40 +#define SUNXI_ADC_CNT 0x44 +#define SUNXI_ADC_DG 0x4C +#define SUNXI_ADC_DIG_CTRL 0x50 +#define SUNXI_VRA1SPEEDUP_DOWN_CTRL 0x54 + +#define SUNXI_DAC_DAP_CTL 0xF0 +#define SUNXI_ADC_DAP_CTL 0xF8 + +#define SUNXI_DAC_DRC_HHPFC 0x100 +#define SUNXI_DAC_DRC_LHPFC 0x104 +#define SUNXI_DAC_DRC_CTRL 0x108 +#define SUNXI_DAC_DRC_LPFHAT 0x10C +#define SUNXI_DAC_DRC_LPFLAT 0x110 +#define SUNXI_DAC_DRC_RPFHAT 0x114 +#define SUNXI_DAC_DRC_RPFLAT 0x118 +#define SUNXI_DAC_DRC_LPFHRT 0x11C +#define SUNXI_DAC_DRC_LPFLRT 0x120 +#define SUNXI_DAC_DRC_RPFHRT 0x124 +#define SUNXI_DAC_DRC_RPFLRT 0x128 +#define SUNXI_DAC_DRC_LRMSHAT 0x12C +#define SUNXI_DAC_DRC_LRMSLAT 0x130 +#define SUNXI_DAC_DRC_RRMSHAT 0x134 +#define SUNXI_DAC_DRC_RRMSLAT 0x138 +#define SUNXI_DAC_DRC_HCT 0x13C +#define SUNXI_DAC_DRC_LCT 0x140 +#define SUNXI_DAC_DRC_HKC 0x144 +#define SUNXI_DAC_DRC_LKC 0x148 +#define SUNXI_DAC_DRC_HOPC 0x14C +#define SUNXI_DAC_DRC_LOPC 0x150 +#define SUNXI_DAC_DRC_HLT 0x154 +#define SUNXI_DAC_DRC_LLT 0x158 +#define SUNXI_DAC_DRC_HKI 0x15C +#define SUNXI_DAC_DRC_LKI 0x160 +#define SUNXI_DAC_DRC_HOPL 0x164 +#define SUNXI_DAC_DRC_LOPL 0x168 +#define SUNXI_DAC_DRC_HET 0x16C +#define SUNXI_DAC_DRC_LET 0x170 +#define SUNXI_DAC_DRC_HKE 0x174 +#define SUNXI_DAC_DRC_LKE 0x178 +#define SUNXI_DAC_DRC_HOPE 0x17C +#define SUNXI_DAC_DRC_LOPE 0x180 +#define SUNXI_DAC_DRC_HKN 0x184 +#define SUNXI_DAC_DRC_LKN 0x188 +#define SUNXI_DAC_DRC_SFHAT 0x18C +#define SUNXI_DAC_DRC_SFLAT 0x190 +#define SUNXI_DAC_DRC_SFHRT 0x194 +#define SUNXI_DAC_DRC_SFLRT 0x198 +#define SUNXI_DAC_DRC_MXGHS 0x19C +#define SUNXI_DAC_DRC_MXGLS 0x1A0 +#define SUNXI_DAC_DRC_MNGHS 0x1A4 +#define SUNXI_DAC_DRC_MNGLS 0x1A8 +#define SUNXI_DAC_DRC_EPSHC 0x1AC +#define SUNXI_DAC_DRC_EPSLC 0x1B0 +#define SUNXI_DAC_DRC_OPT 0x1B4 +#define SUNXI_DAC_DRC_HPFHGAIN 0x1B8 +#define SUNXI_DAC_DRC_HPFLGAIN 0x1BC + +#define SUNXI_ADC_DRC_HHPFC 0x200 +#define SUNXI_ADC_DRC_LHPFC 0x204 +#define SUNXI_ADC_DRC_CTRL 0x208 +#define SUNXI_ADC_DRC_LPFHAT 0x20C +#define SUNXI_ADC_DRC_LPFLAT 0x210 +#define SUNXI_ADC_DRC_RPFHAT 0x214 +#define SUNXI_ADC_DRC_RPFLAT 0x218 +#define SUNXI_ADC_DRC_LPFHRT 0x21C +#define SUNXI_ADC_DRC_LPFLRT 0x220 +#define SUNXI_ADC_DRC_RPFHRT 0x224 +#define SUNXI_ADC_DRC_RPFLRT 0x228 +#define SUNXI_ADC_DRC_LRMSHAT 0x22C +#define SUNXI_ADC_DRC_LRMSLAT 0x230 +#define SUNXI_ADC_DRC_HCT 0x23C +#define SUNXI_ADC_DRC_LCT 0x240 +#define SUNXI_ADC_DRC_HKC 0x244 +#define SUNXI_ADC_DRC_LKC 0x248 +#define SUNXI_ADC_DRC_HOPC 0x24C +#define SUNXI_ADC_DRC_LOPC 0x250 +#define SUNXI_ADC_DRC_HLT 0x254 +#define SUNXI_ADC_DRC_LLT 0x258 +#define SUNXI_ADC_DRC_HKI 0x25C +#define SUNXI_ADC_DRC_LKI 0x260 +#define SUNXI_ADC_DRC_HOPL 0x264 +#define SUNXI_ADC_DRC_LOPL 0x268 +#define SUNXI_ADC_DRC_HET 0x26C +#define SUNXI_ADC_DRC_LET 0x270 +#define SUNXI_ADC_DRC_HKE 0x274 +#define SUNXI_ADC_DRC_LKE 0x278 +#define SUNXI_ADC_DRC_HOPE 0x27C +#define SUNXI_ADC_DRC_LOPE 0x280 +#define SUNXI_ADC_DRC_HKN 0x284 +#define SUNXI_ADC_DRC_LKN 0x288 +#define SUNXI_ADC_DRC_SFHAT 0x28C +#define SUNXI_ADC_DRC_SFLAT 0x290 +#define SUNXI_ADC_DRC_SFHRT 0x294 +#define SUNXI_ADC_DRC_SFLRT 0x298 +#define SUNXI_ADC_DRC_MXGHS 0x29C +#define SUNXI_ADC_DRC_MXGLS 0x2A0 +#define SUNXI_ADC_DRC_MNGHS 0x2A4 +#define SUNXI_ADC_DRC_MNGLS 0x2A8 +#define SUNXI_ADC_DRC_EPSHC 0x2AC +#define SUNXI_ADC_DRC_EPSLC 0x2B0 +#define SUNXI_ADC_DRC_OPT 0x2B4 +#define SUNXI_ADC_DRC_HPFHGAIN 0x2B8 +#define SUNXI_ADC_DRC_HPFLGAIN 0x2BC + +#define SUNXI_AC_VERSION 0x2C0 + +/* Analog register */ +#define SUNXI_ADC1_REG 0x300 +#define SUNXI_ADC2_REG 0x304 + +#define SUNXI_DAC_REG 0x310 +#define SUNXI_MICBIAS_REG 0x318 +#define SUNXI_RAMP_REG 0x31C /* only set bit[1] at init */ +#define SUNXI_BIAS_REG 0x320 + +#define SUNXI_POWER_REG 0x348 +#define SUNXI_ADC_CUR_REG 0x34C +#define SUNXI_CODEC_REG_MAX SUNXI_ADC_CUR_REG + +/* SUNXI_DAC_DPC:0x00 */ +#define EN_DAC 31 +#define MODQU 25 +#define DWA_EN 24 +#define HPF_EN 18 +#define DVOL 12 +#define DAC_HUB_EN 0 + +/* SUNXI_DAC_VOL_CTRL:0x04 */ +#define DAC_VOL_SEL 16 +#define DAC_VOL_L 8 +#define DAC_VOL_R 0 + +/* SUNXI_DAC_FIFOC:0x10 */ +#define DAC_FS 29 +#define FIR_VER 28 +#define SEND_LASAT 26 +#define FIFO_MODE 24 +#define DAC_DRQ_CLR_CNT 21 +#define TX_TRIG_LEVEL 8 +#define DAC_MONO_EN 6 +#define TX_SAMPLE_BITS 5 +#define DAC_DRQ_EN 4 +#define DAC_IRQ_EN 3 +#define FIFO_UNDERRUN_IRQ_EN 2 +#define FIFO_OVERRUN_IRQ_EN 1 +#define FIFO_FLUSH 0 + +/* SUNXI_DAC_FIFOS:0x14 */ +#define TX_EMPTY 23 +#define DAC_TXE_CNT 8 +#define DAC_TXE_INT 3 +#define DAC_TXU_INT 2 +#define DAC_TXO_INT 1 + +/* SUNXI_DAC_DG:0x28 */ +#define DAC_MODU_SEL 11 +#define DAC_PATTERN_SEL 9 +#define DAC_CODEC_CLK_SEL 8 +#define DAC_SWP 6 +#define ADDA_LOOP_MODE 0 + +/* SUNXI_ADC_FIFOC:0x30 */ +#define ADC_FS 29 +#define EN_AD 28 +#define ADCFDT 26 +#define ADCDFEN 25 +#define RX_FIFO_MODE 24 +#define RX_SYNC_EN_START 21 +#define RX_SYNC_EN 20 +#define RX_SAMPLE_BITS 16 +#define RX_FIFO_TRG_LEVEL 4 +#define ADC_DRQ_EN 3 +#define ADC_IRQ_EN 2 +#define ADC_OVERRUN_IRQ_EN 1 +#define ADC_FIFO_FLUSH 0 + +/* SUNXI_ADC_VOL_CTRL:0x34 */ +#define ADC2_VOL 8 +#define ADC1_VOL 0 + +/* SUNXI_ADC_FIFOS:0x38 */ +#define RXA 23 +#define ADC_RXA_CNT 8 +#define ADC_RXA_INT 3 +#define ADC_RXO_INT 1 + +/* SUNXI_ADC_DG:0x4C */ +#define AD_SWP1 24 + +/* SUNXI_ADC_DIG_CTRL:0x50 */ +#define ADC1_2_VOL_EN 16 +#define ADC2_CHANNEL_EN 1 +#define ADC1_CHANNEL_EN 0 +#define ADC_CHANNEL_EN 0 + +/* SUNXI_VRA1SPEEDUP_DOWN_CTRL:0x54 */ +#define VRA1SPEEDUP_DOWN_STATE 4 +#define VRA1SPEEDUP_DOWN_CTRL 1 +#define VRA1SPEEDUP_DOWN_RST_CTRL 0 + +/* SUNXI_DAC_DAP_CTL:0xf0 */ +#define DDAP_EN 31 +#define DDAP_DRC_EN 29 +#define DDAP_HPF_EN 28 + +/* SUNXI_ADC_DAP_CTL:0xf8 */ +#define ADC_DAP0_EN 31 +#define ADC_DRC0_EN 29 +#define ADC_HPF0_EN 28 +#define ADC_DAP1_EN 27 +#define ADC_DRC1_EN 25 +#define ADC_HPF1_EN 24 + +/* SUNXI_DAC_DRC_HHPFC: 0x100*/ +#define DAC_HHPF_CONF 0 + +/* SUNXI_DAC_DRC_LHPFC: 0x104*/ +#define DAC_LHPF_CONF 0 + +/* SUNXI_DAC_DRC_CTRL: 0x108*/ +#define DAC_DRC_DELAY_OUT_STATE 15 +#define DAC_DRC_SIGNAL_DELAY 8 +#define DAC_DRC_DELAY_BUF_EN 7 +#define DAC_DRC_GAIN_MAX_EN 6 +#define DAC_DRC_GAIN_MIN_EN 5 +#define DAC_DRC_NOISE_DET_EN 4 +#define DAC_DRC_SIGNAL_SEL 3 +#define DAC_DRC_DELAY_EN 2 +#define DAC_DRC_LT_EN 1 +#define DAC_DRC_ET_EN 0 + +/* SUNXI_ADC_DRC_HHPFC: 0x200*/ +#define ADC_HHPF_CONF 0 + +/* SUNXI_ADC_DRC_LHPFC: 0x204*/ +#define ADC_LHPF_CONF 0 + +/* SUNXI_ADC_DRC_CTRL: 0x208*/ +#define ADC_DRC_DELAY_OUT_STATE 15 +#define ADC_DRC_SIGNAL_DELAY 8 +#define ADC_DRC_DELAY_BUF_EN 7 +#define ADC_DRC_GAIN_MAX_EN 6 +#define ADC_DRC_GAIN_MIN_EN 5 +#define ADC_DRC_NOISE_DET_EN 4 +#define ADC_DRC_SIGNAL_SEL 3 +#define ADC_DRC_DELAY_EN 2 +#define ADC_DRC_LT_EN 1 +#define ADC_DRC_ER_EN 0 + +/* SUNXI_DAC_DRC_HHPFC: 0x100*/ +#define DAC_HHPF_CONF 0 + +/* SUNXI_DAC_DRC_LHPFC: 0x104*/ +#define DAC_LHPF_CONF 0 + +/* SUNXI_ADC_DRC_HHPFC: 0x200*/ +#define ADC_HHPF_CONF 0 + +/* SUNXI_ADC_DRC_LHPFC: 0x204*/ +#define ADC_LHPF_CONF 0 + +/* SUNXI_ADC1_REG : 0x300 */ +#define ADC1_EN 31 +#define MIC1_PGA_EN 30 +#define ADC1_DITHER_CTRL 29 +#define MIC1_SIN_EN 28 +#define FMINLEN 27 +#define FMINLG 26 +#define ADC1_DSM_DITHER_LVL 24 +#define LINEINLEN 23 +#define LINEINLG 22 +#define ADC1_IOPBUFFER 20 +#define ADC1_PGA_CTRL_RCM 18 +#define ADC1_PGA_IN_VCM_CTRL 16 +#define ADC1_2_CURRENT_SEL 14 +#define ADC1_SINGLE_NOISE_CTL 13 +#define ADC1_PGA_GAIN_CTRL 8 +#define ADC1_IOPAAF 6 +#define ADC1_IOPSDM1 4 +#define ADC1_IOPSDM2 2 +#define ADC1_IOPMIC 0 + +/* SUNXI_ADC2_REG : 0x304 */ +#define ADC2_EN 31 +#define MIC2_PGA_EN 30 +#define ADC2_DITHER_CTRL 29 +#define MIC2_SIN_EN 28 +#define FMINREN 27 +#define FMINRG 26 +#define ADC2_DSM_DITHER_LVL 24 +#define LINEINREN 23 +#define LINEINRG 22 +#define ADC2_IOPBUFFER 20 +#define ADC2_PGA_CTRL_RCM 18 +#define ADC2_PGA_IN_VCM_CTRL 16 +#define ADC2_SINGLE_NOISE_CTL 13 +#define ADC2_PGA_GAIN_CTRL 8 +#define ADC2_IOPAAF 6 +#define ADC2_IOPSDM1 4 +#define ADC2_IOPSDM2 2 +#define ADC2_IOPMIC 0 + +/* SUNXI_DAC_REG : 0x310 */ +#define P_CURRENT_TEST_SELECT 24 +#define N_CURRENT_TEST_SELECT 22 +#define VRA2_IOPVRS 20 +#define ILINEOUTAMPS 18 +#define IOPDACS 16 +#define DACLEN 15 +#define LINEOUTLEN 13 +#define DACLMUTE 12 +#define VRA2_OPVR_OI_CTRL 7 +#define LINEOUTLDIFFEN 6 +#define LINEOUT_VOL 0 + +/* SUNXI_MICBIAS_REG : 0x318 */ +#define MMICBIASEN 7 +#define MBIASSEL 5 +#define MMICBIAS_CHOP_EN 4 +#define MMICBIAS_CHOP_CLK_SEL 2 + +/* SUNXI_RAMP_REG : 0x31C */ +#define RMC_EN 1 + +/* SUNXI_BIAS_REG : 0x320 */ +#define AC_BIASDATA 0 + +/* SUNXI_POWER_REG :0x348 */ +#define ALDO_EN 31 +#define VAR1SPEEDUP_DOWN_FURTHER_CTRL 29 +#define BG_BUFFER_DISABLE 15 +#define ALDO_OUTPUT_VOLTAGE 12 +#define BG_ROUGH_TRIM 8 +#define BG_FINE_TRIM 0 + +/* SUNXI_ADC_CUR_REG :0x34C */ +#define ADC2_IOPMIC2 12 +#define ADC2_OP_MIC1_CUR 10 +#define ADC2_OP_MIC2_CUR 8 +#define ADC1_IOPMIC2 4 +#define ADC1_OP_MIC1_CUR 2 +#define ADC1_OP_MIC2_CUR 0 + +struct sunxi_has_clk { + struct clk *pllaudio; + struct clk *dacclk; + struct clk *adcclk; +}; + +struct sunxi_regulator { + bool external_avcc; + unsigned int avcc_vol; + struct regulator *avcc; +}; + +struct sunxi_has_dts { + unsigned int lineout_vol; + unsigned int mic1gain; + unsigned int mic2gain; + unsigned int adc_dtime; + bool lineout_single; /* true: single mode; false: differ mode */ + bool mic1_single; + bool mic2_single; + + /* tx_hub */ + bool tx_hub_en; + + /* components func -> rx sync */ + bool rx_sync_en; /* read from dts */ + bool rx_sync_ctl; + int rx_sync_id; + // rx_sync_domain_t rx_sync_domain; +}; + +struct sunxi_dap { + unsigned int dap_enable; + struct mutex mutex; +}; + +// struct sunxi_codec_runtime { +// /* input: micin and linein have common parts, need manage */ +// unsigned int mic1gain; +// unsigned int mic2gain; +// bool mic1_single; +// bool mic2_single; + +// unsigned int lineinlgain; +// unsigned int lineinrgain; + +// bool mic1_run; +// bool mic2_run; +// bool linein_run; + +// struct mutex input_mutex; + +// /* output: only lineout, unneed manage. */ +// }; + +// enum SUNXI_kCONTROL_SHIFT { +// KCONTROL_SHIFT_MIC1_GAIN = 0, +// KCONTROL_SHIFT_MIC2_GAIN, +// KCONTROL_SHIFT_LINEINL_GAIN, +// KCONTROL_SHIFT_LINEINR_GAIN, +// }; + +// enum SUNXI_WIDGET_SHIFT { +// WIDGET_SHIFT_ADC1 = 0, +// WIDGET_SHIFT_ADC2, +// WIDGET_SHIFT_MIC1_INPUT_SELECT, +// WIDGET_SHIFT_MIC2_INPUT_SELECT, +// WIDGET_SHIFT_MIC1_GAIN, +// WIDGET_SHIFT_MIC2_GAIN, +// WIDGET_SHIFT_LINEINL_GAIN, +// WIDGET_SHIFT_LINEINR_GAIN, +// }; + +// struct sunxi_codec { +// struct platform_device *pdev; + +// struct sunxi_has_mem mem; +// struct sunxi_clk clk; +// struct sunxi_regulator rglt; +// // struct sunxi_dts dts; +// struct sunxi_dap dac_dap; +// struct sunxi_dap adc_dap; + +// unsigned int pa_pin_max; +// struct pa_config *pa_config; + +// struct sunxi_codec_runtime runtime; +// }; + +#endif /* __SND_HAS_SUN8IW21_CODEC_H */ diff --git a/lichee/linux-4.9/drivers/char/hichs-has/snd_has_sunxi_common.h b/lichee/linux-4.9/drivers/char/hichs-has/snd_has_sunxi_common.h new file mode 100755 index 000000000..a08b59cbc --- /dev/null +++ b/lichee/linux-4.9/drivers/char/hichs-has/snd_has_sunxi_common.h @@ -0,0 +1,55 @@ +/* sound\soc\sunxi\snd_sunxi_common.h + * (C) Copyright 2021-2025 + * Allwinner Technology Co., Ltd. + * Dby + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ + +#ifndef __SND_HAS_SUNXI_COMMON_H +#define __SND_HAS_SUNXI_COMMON_H + +/* for regmap */ +struct sunxi_has_mem { + char *dev_name; + struct resource *res; + struct regmap_config *regmap_config; + + void __iomem *membase; + struct resource *memregion; + struct regmap *regmap; +}; + +// int snd_sunxi_mem_init(struct platform_device *pdev, struct sunxi_mem *mem); +// void snd_sunxi_mem_exit(struct platform_device *pdev, struct sunxi_mem *mem); + +// /* for reg debug */ +// #define REG_LABEL(constant) {#constant, constant, 0} +// #define REG_LABEL_END {NULL, 0, 0} + +struct reg_label { + const char *name; + const unsigned int address; + unsigned int value; +}; + +// int snd_sunxi_save_reg(struct regmap *regmap, struct reg_label *reg_labels); +// int snd_sunxi_echo_reg(struct regmap *regmap, struct reg_label *reg_labels); + +/* for pa config */ +struct has_pa_config { + u32 pin; + u32 msleep; + bool used; + bool level; +}; + +// struct pa_config *snd_sunxi_pa_pin_init(struct platform_device *pdev, u32 *pa_pin_max); +// void snd_sunxi_pa_pin_exit(struct platform_device *pdev, struct pa_config *pa_cfg, u32 pa_pin_max); +// int snd_sunxi_pa_pin_enable(struct pa_config *pa_cfg, u32 pa_pin_max); +// int snd_sunxi_pa_pin_disable(struct pa_config *pa_cfg, u32 pa_pin_max); + +#endif /* __SND_HAS_SUNXI_COMMON_H */