diff --git a/arch/riscv/boot/dts/canaan/k230-canmv.dts b/arch/riscv/boot/dts/canaan/k230-canmv.dts index 5831d860e6c3d..629e8f5e7b301 100644 --- a/arch/riscv/boot/dts/canaan/k230-canmv.dts +++ b/arch/riscv/boot/dts/canaan/k230-canmv.dts @@ -17,6 +17,15 @@ device_type = "memory"; reg = <0x0 0x0 0x0 0x20000000>; }; + + sound { + status = "okay"; + compatible = "canaan,k230-audio-inno"; + canaan,model = "CANAAN_K230_I2S_INNO"; + canaan,k230-i2s-controller = <&i2s>; + canaan,k230-audio-codec = <&inno_codec>; + }; + }; &uart0 { diff --git a/arch/riscv/boot/dts/canaan/k230.dtsi b/arch/riscv/boot/dts/canaan/k230.dtsi index 18efbf0d64d62..b65fc1d4bfab4 100644 --- a/arch/riscv/boot/dts/canaan/k230.dtsi +++ b/arch/riscv/boot/dts/canaan/k230.dtsi @@ -425,6 +425,43 @@ status = "okay"; }; + pdma: pdma@0x80804000 { + #dma-cells = <4>; + dma-channels = <8>; + dma-requests = <35>; + status = "okay"; + compatible = "canaan,k230-pdma"; + reg = <0x0 0x80804000 0x0 0x400>; + /*interrupt-parent = <&intc>;*/ + interrupts = <203 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&pdma_aclk_gate>; + }; + + i2s: i2s@0x9140f000 { + status = "okay"; + compatible = "snps,designware-i2s"; + reg = <0x0 0x9140f000 0x0 0x400>; + dmas = <&pdma 1 0xfff 0 0x14>, <&pdma 1 0xfff 0 0x15>; + dma-names = "tx", "rx"; + clocks = <&audio_dev_clk>; + clock-names = "i2sclk"; + }; + + /* audio */ + audio: audio@0x9140f400 { + status = "okay"; + compatible = "canaan,k230-audio"; + reg = <0x0 0x9140f400 0x0 0xc00>; + }; + + inno_codec:inno_codec@0x9140e000{ + status = "okay"; + compatible = "k230,inno-codec"; + reg = <0x0 0x9140e000 0x0 0x1000>; + clocks = <&codec_adc_mclk>,<&codec_dac_mclk>; + clock-names = "adc", "dac"; + }; + display: display-subsystem { compatible = "canaan,display-subsystem"; ports = <&vo_out>; diff --git a/arch/riscv/configs/defconfig b/arch/riscv/configs/defconfig index 84636302e37c4..cbca053879ef4 100644 --- a/arch/riscv/configs/defconfig +++ b/arch/riscv/configs/defconfig @@ -312,3 +312,9 @@ CONFIG_MTD_UBI=y CONFIG_MAILBOX=y # TH1520 PMIC_WDT CONFIG_TH1520_PMIC_WATCHDOG=y + +CONFIG_SND_DESIGNWARE_I2S=y +CONFIG_K230_PERIDMA=y +CONFIG_SND_SOC_K230_INNO=y +CONFIG_SND_SOC_CANAAN_K230_AUDIO=y +CONFIG_SND_SOC_CANAAN_K230_INNO=y diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index e36506471a4f6..234a796f77653 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -432,7 +432,7 @@ config MOXART_DMA select DMA_VIRTUAL_CHANNELS help Enable support for the MOXA ART SoC DMA controller. - + Say Y here if you enabled MMP ADMA, otherwise say N. config MPC512X_DMA @@ -733,6 +733,14 @@ config XILINX_ZYNQMP_DPDMA driver provides the dmaengine required by the DisplayPort subsystem display driver. +config K230_PERIDMA + tristate "K230 Periphal DMA support" + depends on ARCH_RV64I + select DMA_ENGINE + select DMA_VIRTUAL_CHANNELS + help + Enable support for the Periphal DMA controller present in the K230 SoC. + # driver files source "drivers/dma/bestcomm/Kconfig" diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile index 83553a97a010e..0832793d98cae 100644 --- a/drivers/dma/Makefile +++ b/drivers/dma/Makefile @@ -82,6 +82,7 @@ obj-$(CONFIG_XGENE_DMA) += xgene-dma.o obj-$(CONFIG_ST_FDMA) += st_fdma.o obj-$(CONFIG_FSL_DPAA2_QDMA) += fsl-dpaa2-qdma/ obj-$(CONFIG_INTEL_LDMA) += lgm/ +obj-$(CONFIG_K230_PERIDMA) += k230_peridma.o obj-y += mediatek/ obj-y += qcom/ diff --git a/drivers/dma/k230_peridma.c b/drivers/dma/k230_peridma.c new file mode 100755 index 0000000000000..f989c8971a3d8 --- /dev/null +++ b/drivers/dma/k230_peridma.c @@ -0,0 +1,1406 @@ +/* + * 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. + */ + +//#define DEBUG +//#define VERBOSE_DEBUG + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "virt-dma.h" + +#define PDMA_MAX_LINE_SIZE 0x3FFFFFFF + +/* interrupt mask */ +#define PDONE_INT 0x00000001 +#define PITEM_INT 0x00000100 +#define PPAUSE_INT 0x00010000 +#define PTOUT_INT 0x01000000 + +/* llt structure */ +typedef struct pdma_llt +{ + u32 line_size : 30; + u32 pause : 1; + u32 node_intr : 1; + u32 src_addr; + u32 dst_addr; + u32 next_llt_addr; +} pdma_llt_t; + +/* register structure */ +typedef struct pdma_ch_cfg +{ + u32 ch_src_type : 1; + u32 ch_dev_hsize : 2; + u32 reserved0 : 1; + u32 ch_dat_endian : 2; + u32 reserved1 : 2; + u32 ch_dev_blen : 4; + u32 ch_priority : 4; + u32 ch_dev_tout : 12; + u32 reserved2 : 4; +} pdma_ch_cfg_t; + + +/** + * @param TX: means transmitting data from system memory + * to external peripheral devices + * @param RX: means receiving data from peripheral devices + * to system memory + */ +typedef enum pdma_rxtx +{ + TX = 0, + RX = 1, +} pdma_rxtx_e; + +typedef enum pdma_ch +{ + PDMA_CH_0 = 0, + PDMA_CH_1 = 1, + PDMA_CH_2 = 2, + PDMA_CH_3 = 3, + PDMA_CH_4 = 4, + PDMA_CH_5 = 5, + PDMA_CH_6 = 6, + PDMA_CH_7 = 7, + PDMA_CH_MAX, +} pdma_ch_e; + + +typedef enum pdma_burst_len +{ + PBURST_LEN_1 = 0, + PBURST_LEN_2 = 1, + PBURST_LEN_3 = 2, + PBURST_LEN_4 = 3, + PBURST_LEN_5 = 4, + PBURST_LEN_6 = 5, + PBURST_LEN_7 = 6, + PBURST_LEN_8 = 7, + PBURST_LEN_9 = 8, + PBURST_LEN_10 = 9, + PBURST_LEN_11 = 10, + PBURST_LEN_12 = 11, + PBURST_LEN_13 = 12, + PBURST_LEN_14 = 13, + PBURST_LEN_15 = 14, + PBURST_LEN_16 = 15, +} pdma_burst_len_e; + + +/* Global register offsets */ +#define PDMA_CH_EN 0x0 +#define PDMA_INT_MASK 0x4 +#define PDMA_INT_STAT 0x8 + +/* Channel register offsets */ +#define CH_CTL 0x0 +#define CH_STAT 0x4 +#define CH_CFG 0x8 +#define CH_LLT_SADDR 0xc + + +#define CH_OFF 0x20 +#define CH0_BASE 0x20 + +#define CH_NUM 8 + +#define MAX_LINE_SIZE 0xffff +#define MAX_LINE_NUM 0x3ff +#define RECT_MAX_TRANS_LEN (MAX_LINE_SIZE*MAX_LINE_NUM) + + +#define CH_STAT_BUSY BIT(0) +#define CH_STAT_PAUSE BIT(1) + + +typedef enum ch_peri_dev_sel { + UART0_TX = 0, + UART0_RX = 1, + UART1_TX = 2, + UART1_RX = 3, + UART2_TX = 4, + UART2_RX = 5, + UART3_TX = 6, + UART3_RX = 7, + UART4_TX = 8, + UART4_RX = 9, + I2C0_TX = 10, + I2C0_RX = 11, + I2C1_TX = 12, + I2C1_RX = 13, + I2C2_TX = 14, + I2C2_RX = 15, + I2C3_TX = 16, + I2C3_RX = 17, + I2C4_TX = 18, + I2C4_RX = 19, + AUDIO_TX = 20, + AUDIO_RX = 21, + JAMLINK0_TX = 22, + JAMLINK0_RX = 23, + JAMLINK1_TX = 24, + JAMLINK1_RX = 25, + JAMLINK2_TX = 26, + JAMLINK2_RX = 27, + JAMLINK3_TX = 28, + JAMLINK3_RX = 29, + ADC0 = 30, + ADC1 = 31, + ADC2 = 32, + PDM_IN = 33, + PERI_DEV_SEL_MAX, +} ch_peri_dev_sel_t; + +/* usr pdma structure */ +typedef struct usr_pdma_cfg +{ + pdma_ch_e ch; + ch_peri_dev_sel_t device; + dma_addr_t src_addr; + dma_addr_t dst_addr; + u32 line_size; + pdma_ch_cfg_t pdma_ch_cfg; +} usr_pdma_cfg_t; + +typedef enum peridma_mode { + LINE_MODE, + RECT_MODE, + DMA_MODE_MAX +} peridma_mode_t; + +typedef enum ch_src_type { + SRC_TYPE_MEM, + SRC_TYPE_DEV, + SRC_TYPE_MAX +} ch_src_type_t; + +typedef enum ch_dat_endian { + DAT_ENDIAN_DEF, + DAT_ENDIAN_TWO_BYTE, + DAT_ENDIAN_FOUR_BYTE, + DAT_ENDIAN_EIGHT_BYTE, + DAT_ENDIAN_MAX +} ch_dat_endian_t; + +typedef enum ch_dev_hsize { + DEV_HSIZE_ONE_BYTE, + DEV_HSIZE_TWO_BYTE, + DEV_HSIZE_FOUR_BYTE, + DEV_HSIZE_INVALID +} ch_dev_hsize_t; + +/** DMA Driver **/ +struct k230_peridma_hwdesc { + usr_pdma_cfg_t usr_pdma_chn_cfg; + bool last; + struct list_head list; +}; + +struct k230_peridma_desc { + struct virt_dma_desc vd; + struct k230_peridma_vchan *vchan; + struct list_head xfer_list; + struct list_head completed_list; + struct k230_peridma_hwdesc *hwdesc; + atomic_t hwdesc_remain; + bool next; + struct k230_peridma_hwdesc *cyclic; + u32 remain; + u32 buf_len; +}; + + +struct k230_peridma_vchan { + struct k230_peridma_dev *pdev; + struct virt_dma_chan vchan; + struct dma_slave_config cfg; + struct k230_peridma_desc *desc; + struct k230_peridma_hwdesc *cur_hwdesc; + atomic_t descs_allocated; + struct k230_peridma_pchan *pchan; + enum dma_transfer_direction dir; + dma_addr_t dev_addr; + u32 priority; + u32 dev_tout; + ch_dat_endian_t dat_endian; + ch_peri_dev_sel_t dev_sel; + enum dma_status status; + bool is_paused; + +}; + +struct k230_peridma_pchan { + struct k230_peridma_dev *pdev; + /* Register base of channel */ + void __iomem *ch_regs; + u8 id; + struct k230_peridma_vchan *vchan; + bool is_paused; + +}; + +struct k230_peridma_dev { + DECLARE_BITMAP(pchans_used, CH_NUM); + struct device *dev; + struct dma_device slave; + struct k230_peridma_vchan *vchan; + struct k230_peridma_pchan *pchan; + u32 nr_channels; + u32 nr_requests; + void __iomem *base; + struct clk *clk; + int irq; + spinlock_t lock; +}; + + +typedef struct pdma_chn_llt +{ + dma_addr_t llt_list_p; + pdma_llt_t *llt_list_v; + bool use; +}pdma_chn_llt_t; +static pdma_chn_llt_t g_pdma_llt_list[CH_NUM]; + +static void free_pchan(struct k230_peridma_dev *pdev, struct k230_peridma_pchan *pchan); + +// static struct k230_peridma_dev *to_k230_peridma_dev(struct dma_device *dev) +// { +// return container_of(dev, struct k230_peridma_dev, slave); +// } + +static struct k230_peridma_vchan *to_k230_peridma_vchan(struct dma_chan *chan) +{ + return container_of(chan, struct k230_peridma_vchan, vchan.chan); +} + +static struct device *vchan2dev(struct k230_peridma_vchan *vchan) +{ + return &vchan->vchan.chan.dev->device; +} + +static inline struct k230_peridma_desc *vd_to_k230_peridma_desc(struct virt_dma_desc *vd) +{ + return container_of(vd, struct k230_peridma_desc, vd); +} + +static inline struct k230_peridma_vchan *vc_to_k230_peridma_vchan(struct virt_dma_chan *vc) +{ + return container_of(vc, struct k230_peridma_vchan, vchan); +} + +static inline struct k230_peridma_vchan *dchan_to_k230_peridma_vchan(struct dma_chan *dchan) +{ + return vc_to_k230_peridma_vchan(to_virt_chan(dchan)); +} + +static inline const char *peridma_vchan_name(struct k230_peridma_vchan *vchan) +{ + return dma_chan_name(&vchan->vchan.chan); +} + +static inline bool peridma_pchan_is_busy(struct k230_peridma_pchan *pchan) +{ + u32 val; + + val = ioread32(pchan->ch_regs + CH_STAT); + + return (val & CH_STAT_BUSY); +} + +static inline bool peridma_pchan_is_idle(struct k230_peridma_pchan *pchan) +{ + return true; + //return !peridma_pchan_is_busy(pchan); +} + + +static inline bool peridma_pchan_is_paused(struct k230_peridma_pchan *pchan) +{ + u32 val; + + val = ioread32(pchan->ch_regs + CH_STAT); + + return (val & CH_STAT_PAUSE); +} + +// static void k230_peridma_free_chan_resources(struct dma_chan *chan) +// { +// struct k230_peridma_vchan *pvchan = to_k230_peridma_vchan(chan); + +// vchan_free_chan_resources(&pvchan->vchan); +// } + +static inline void peridma_pchan_irq_clear(struct k230_peridma_pchan *pchan) +{ + struct k230_peridma_dev *pdev = pchan->pdev; + u32 reg; + u32 ch_id = pchan->id; + u32 clear = PDONE_INT | PITEM_INT | PPAUSE_INT | PTOUT_INT; + + reg = ioread32(pdev->base + PDMA_INT_STAT); + reg |= (clear << ch_id); + iowrite32(reg, pdev->base + PDMA_INT_STAT); +} + +static inline void peridma_pchan_irq_enable(struct k230_peridma_pchan *pchan) +{ + struct k230_peridma_dev *pdev = pchan->pdev; + u32 reg; + u32 ch_id = pchan->id; + + reg = ioread32(pdev->base + PDMA_CH_EN); + reg |= (1 << ch_id); + iowrite32(reg, pdev->base + PDMA_CH_EN); +} + +static inline void peridma_pchan_irq_disable(struct k230_peridma_pchan *pchan) +{ + struct k230_peridma_dev *pdev = pchan->pdev; + u32 reg; + u32 ch_id = pchan->id; + + reg = ioread32(pdev->base + PDMA_CH_EN); + reg &= (~(1 << ch_id)); + iowrite32(reg, pdev->base + PDMA_CH_EN); +} + +static inline void peridma_pchan_start(struct k230_peridma_pchan *pchan) +{ + u32 reg = 0; + reg = 0x1; + iowrite32(reg, pchan->ch_regs + CH_CTL); +} + +static inline void peridma_pchan_stop(struct k230_peridma_pchan *pchan) +{ + u32 reg = 0; + reg = 0x2; + iowrite32(reg, pchan->ch_regs + CH_CTL); +} + +static inline void peridma_pchan_pause(struct k230_peridma_pchan *pchan) +{ + return ; +} + +static inline void peridma_pchan_resume(struct k230_peridma_pchan *pchan) +{ + u32 reg = 0; + reg = 0x4; + iowrite32(reg, pchan->ch_regs + CH_CTL); +} + +static void k230_peridma_hw_init(struct k230_peridma_dev *pdev) +{ + u32 i; + + iowrite32(0, pdev->base + PDMA_CH_EN); + iowrite32(0, pdev->base + PDMA_INT_MASK); + iowrite32(0, pdev->base + PDMA_INT_STAT); + + for (i = 0; i < pdev->nr_channels; i++) { + peridma_pchan_stop(&pdev->pchan[i]); + } +} + +static void peridma_desc_put(struct k230_peridma_desc *desc) +{ + struct k230_peridma_vchan *vchan = desc->vchan; + unsigned int descs_put = 1; + struct k230_peridma_hwdesc *hwdesc, *tmp; + +// vchan->desc = NULL; + + list_for_each_entry_safe(hwdesc, tmp, &desc->xfer_list, list) + kfree(hwdesc); + if(!desc->cyclic) + { + list_for_each_entry_safe(hwdesc, tmp, &desc->completed_list, list) + kfree(hwdesc); + } + + kfree(desc); + + atomic_sub(descs_put, &vchan->descs_allocated); + dev_vdbg(vchan2dev(vchan), ": %d descs put, %d still allocated\n", + descs_put, + atomic_read(&vchan->descs_allocated)); +} + +static void vchan_desc_put(struct virt_dma_desc *vdesc) +{ + peridma_desc_put(vd_to_k230_peridma_desc(vdesc)); +} + +static ch_dev_hsize_t slave_buswidth_to_hsize(enum dma_slave_buswidth buswidth) +{ + ch_dev_hsize_t hsize = DEV_HSIZE_INVALID; + + switch (buswidth) { + case DMA_SLAVE_BUSWIDTH_1_BYTE: + hsize = DEV_HSIZE_ONE_BYTE; + break; + + case DMA_SLAVE_BUSWIDTH_2_BYTES: + hsize = DEV_HSIZE_TWO_BYTE; + break; + + case DMA_SLAVE_BUSWIDTH_4_BYTES: + hsize = DEV_HSIZE_FOUR_BYTE; + break; + + default: + hsize = DEV_HSIZE_INVALID; + break; + } + + return hsize; +} + +static struct k230_peridma_hwdesc *generate_hwdesc( + struct k230_peridma_vchan *vchan, dma_addr_t src, dma_addr_t dest, + size_t len, struct dma_slave_config *sconfig, enum dma_transfer_direction dir) +{ + struct k230_peridma_hwdesc *hwdesc = NULL; + ch_dev_hsize_t hsize = DEV_HSIZE_INVALID; + + hwdesc = kzalloc(sizeof(*hwdesc), GFP_NOWAIT); + if(!hwdesc){ + dev_err(vchan2dev(vchan), "alloc hwdesc failure"); + return NULL; + } + + if (dir == DMA_MEM_TO_DEV) { + hsize = slave_buswidth_to_hsize(sconfig->dst_addr_width); + //printk("=======DMA_MEM_TO_DEV hsize:%d\n",hsize); + if(hsize == DEV_HSIZE_INVALID) + goto err_exit; + hwdesc->usr_pdma_chn_cfg.pdma_ch_cfg.ch_src_type = TX; + hwdesc->usr_pdma_chn_cfg.pdma_ch_cfg.ch_dev_hsize = hsize; + } else if(dir == DMA_DEV_TO_MEM){ + hsize = slave_buswidth_to_hsize(sconfig->src_addr_width); + //printk("=======DMA_DEV_TO_MEM hsize:%d\n",hsize); + if(hsize == DEV_HSIZE_INVALID) + goto err_exit; + hwdesc->usr_pdma_chn_cfg.pdma_ch_cfg.ch_src_type = RX; + hwdesc->usr_pdma_chn_cfg.pdma_ch_cfg.ch_dev_hsize = hsize; + } + hwdesc->usr_pdma_chn_cfg.pdma_ch_cfg.ch_dat_endian = 0; + hwdesc->usr_pdma_chn_cfg.pdma_ch_cfg.ch_dev_blen = PBURST_LEN_8; + hwdesc->usr_pdma_chn_cfg.pdma_ch_cfg.ch_priority = vchan->priority; + hwdesc->usr_pdma_chn_cfg.pdma_ch_cfg.ch_dev_tout = vchan->dev_tout; + + hwdesc->usr_pdma_chn_cfg.src_addr = src; + hwdesc->usr_pdma_chn_cfg.dst_addr = dest; + hwdesc->usr_pdma_chn_cfg.device = vchan->dev_sel; + hwdesc->usr_pdma_chn_cfg.line_size = len; + + //printk("=======dev_sel:%d,hsize:%d,src:0x%llx,dst:0x%llx,priority:%d,size:%d,tout:0x%x\n",vchan->dev_sel,hsize,src,dest,vchan->priority,len,vchan->dev_tout); + + //dev_vdbg(vchan2dev(vchan), "generate hwdesc(%px): ctl=%x, cfg0=%x, cfg1=%x, dev_tout=%x, dev_sel=%x, src_addr=%x, dst_addr=%x \n", hwdesc, hwdesc->ctl, hwdesc->cfg0, hwdesc->cfg1, hwdesc->dev_tout, hwdesc->dev_sel, hwdesc->src_addr, hwdesc->dst_addr); + + return hwdesc; + +err_exit: + kfree(hwdesc); + return NULL; + +} + +static struct dma_async_tx_descriptor *k230_peridma_prep_slave_sg( + struct dma_chan *chan, struct scatterlist *sgl, unsigned int sglen, + enum dma_transfer_direction dir, unsigned long flags, void *context) +{ + //printk("$$$$$$$$$$$k230_peridma_prep_slave_sg\n"); + struct k230_peridma_desc *desc = NULL; + struct k230_peridma_hwdesc *hwdesc = NULL; + struct k230_peridma_hwdesc *tmp = NULL; + struct k230_peridma_vchan *vchan = dchan_to_k230_peridma_vchan(chan); + struct dma_slave_config *sconfig = &vchan->cfg; + struct scatterlist *sg; + size_t xfer_size, avail = 0; + dma_addr_t addr, src = 0, dst = 0; + int i = 0; + + dev_vdbg(vchan2dev(vchan), ": prep_slave_sg: sgl: %px sglen: %ud flags: %#lx", + sgl, sglen, flags); +// printk("k230_peridma_prep_slave_sg: sgl: %px sglen: %zd flags: %#lx", +// sgl, sglen, flags); + + desc = kzalloc(sizeof(struct k230_peridma_desc), GFP_NOWAIT); + + if(!desc) + return NULL; + + desc->vchan = vchan; +// vchan->desc = desc; + INIT_LIST_HEAD(&desc->xfer_list); + INIT_LIST_HEAD(&desc->completed_list); +// atomic_set(&desc->hwdesc_remain, 0); + atomic_inc(&vchan->descs_allocated); +// desc->next = false; + + + for_each_sg(sgl, sg, sglen, i) { + addr = sg_dma_address(sg); + avail = sg_dma_len(sg); + + do { + xfer_size = min_t(size_t, avail, MAX_LINE_SIZE); + + if (dir == DMA_MEM_TO_DEV) { + src = addr; + dst = sconfig->dst_addr; + } else if (dir == DMA_DEV_TO_MEM) { + src = sconfig->src_addr; + dst = addr; + } + + dev_vdbg(vchan2dev(vchan), "dma_async_tx_descriptor: src=%llx, dst=%llx, xfer_size=%zu \n", src, dst, xfer_size); + + hwdesc = generate_hwdesc(vchan, src, dst, xfer_size, sconfig, dir); + if(hwdesc) + list_add_tail(&hwdesc->list, &desc->xfer_list); + else { + list_for_each_entry_safe(hwdesc, tmp, &desc->xfer_list, list) + kfree(hwdesc); + + kfree(desc); + dev_err(vchan2dev(vchan), "dma_async_tx_descriptor: generate hwdesc failure \n"); + return NULL; + } +// atomic_inc(&desc->hwdesc_remain); + + addr += xfer_size; + avail -= xfer_size; + + }while(avail); + + } + + dev_vdbg(vchan2dev(vchan), "dma_async_tx_descriptor: prepare desc(%px) \n", desc); + + return vchan_tx_prep(&vchan->vchan, &desc->vd, flags); +} + +static struct dma_async_tx_descriptor *k230_peridma_prep_dma_cyclic( + struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len, + size_t period_len, enum dma_transfer_direction dir, + unsigned long flags) +{ + struct k230_peridma_desc *desc = NULL; + struct k230_peridma_hwdesc *hwdesc = NULL; + struct k230_peridma_hwdesc *tmp = NULL; + struct k230_peridma_vchan *vchan = dchan_to_k230_peridma_vchan(chan); + struct dma_slave_config *sconfig = &vchan->cfg; + size_t xfer_size, avail = 0; + u32 num_periods; + dma_addr_t addr, src = 0, dst = 0; + int i = 0; + + desc = kzalloc(sizeof(struct k230_peridma_desc), GFP_NOWAIT); + + if(!desc) + return NULL; + + desc->vchan = vchan; + desc->buf_len = buf_len; + desc->remain = buf_len; + INIT_LIST_HEAD(&desc->xfer_list); + + atomic_inc(&vchan->descs_allocated); + + num_periods = buf_len / period_len; + for(i = 0; i < num_periods; i++) + { + addr = buf_addr + period_len*i; + avail = period_len; + + do { + xfer_size = min_t(size_t, avail, MAX_LINE_SIZE); + + if (dir == DMA_MEM_TO_DEV) { + src = addr; + dst = sconfig->dst_addr; + } else if (dir == DMA_DEV_TO_MEM) { + src = sconfig->src_addr; + dst = addr; + } + + dev_vdbg(vchan2dev(vchan), "dma_async_tx_descriptor: src=%llx, dst=%llx, xfer_size=%zu \n", src, dst, xfer_size); + + hwdesc = generate_hwdesc(vchan, src, dst, xfer_size, sconfig, dir); + if(hwdesc) + { + list_add_tail(&hwdesc->list, &desc->xfer_list); + if(desc->cyclic == NULL) + desc->cyclic = hwdesc; + } + else { + list_for_each_entry_safe(hwdesc, tmp, &desc->xfer_list, list) + kfree(hwdesc); + + kfree(desc); + dev_vdbg(vchan2dev(vchan), "dma_async_tx_descriptor: generate hwdesc failure \n"); + return NULL; + } + + addr += xfer_size; + avail -= xfer_size; + if(avail == 0) + hwdesc->last = true; + } while(avail); + } + + return vchan_tx_prep(&vchan->vchan, &desc->vd, flags); +} + +static int k230_peridma_terminate_all(struct dma_chan *dchan) +{ + struct k230_peridma_vchan *vchan = dchan_to_k230_peridma_vchan(dchan); + unsigned long flags; + LIST_HEAD(head); + + spin_lock_irqsave(&vchan->vchan.lock, flags); + + if(vchan->pchan){ + dev_vdbg(vchan2dev(vchan), "k230_peridma_terminate_all: release pchan %x \n", vchan->pchan->id); + peridma_pchan_irq_disable(vchan->pchan); + peridma_pchan_stop(vchan->pchan); + free_pchan(vchan->pdev,vchan->pchan); + vchan->pchan = NULL; + } else + dev_vdbg(vchan2dev(vchan), "k230_peridma_terminate_all: vchan->pchan is NULL \n"); + + if(vchan->desc){ +// vchan_terminate_vdesc(&vchan->desc->vd); + vchan->desc = NULL; + } + + vchan->is_paused = false; + + vchan_get_all_descriptors(&vchan->vchan, &head); + + /* + * As vchan_dma_desc_free_list can access to desc_allocated list + * we need to call it in vc.lock context. + */ + vchan_dma_desc_free_list(&vchan->vchan, &head); + + spin_unlock_irqrestore(&vchan->vchan.lock, flags); + + dev_vdbg(vchan2dev(vchan), "terminated \n"); + + return 0; +} + +// 无论是cycle还是sg类型的buffer, +// 从desc的第一个hwdesc节点开始遍历到最后一个hwdesc,得到剩余下的需要传输数据长度 +static size_t k230_peridma_desc_residue(struct k230_peridma_vchan *chan, + struct k230_peridma_desc *desc) +{ + u32 residue = 0; + residue = desc->remain; + + return residue; +} + +static enum dma_status k230_peridma_tx_status(struct dma_chan *dchan, + dma_cookie_t cookie, + struct dma_tx_state *txstate) +{ + struct k230_peridma_vchan *vchan = dchan_to_k230_peridma_vchan(dchan); + enum dma_status ret; + struct virt_dma_desc *vdesc; + unsigned long flags; + u32 residue = 0; + + ret = dma_cookie_status(dchan, cookie, txstate); + if (ret == DMA_COMPLETE) + return ret; + + spin_lock_irqsave(&vchan->vchan.lock, flags); + ret = dma_cookie_status(dchan, cookie, txstate); + if (ret != DMA_COMPLETE) + { + vdesc = vchan_find_desc(&vchan->vchan, cookie); + if (vdesc) + { + residue = k230_peridma_desc_residue(vchan, + vd_to_k230_peridma_desc(vdesc)); + } + } + spin_unlock_irqrestore(&vchan->vchan.lock, flags); + dma_set_residue(txstate, residue); + + if (vchan->is_paused && ret == DMA_IN_PROGRESS) + ret = DMA_PAUSED; + + return ret; +} + + +static void free_pchan(struct k230_peridma_dev *pdev, struct k230_peridma_pchan *pchan) +{ + unsigned long flags; + int nr = pchan - pdev->pchan; + struct k230_peridma_vchan *vchan; + + spin_lock_irqsave(&pdev->lock, flags); + + vchan = pchan->vchan; + vchan->pchan = NULL; + pchan->vchan = NULL; + clear_bit(nr, pdev->pchans_used); + + spin_unlock_irqrestore(&pdev->lock, flags); + +} + +static struct k230_peridma_pchan *find_phy_chan(struct k230_peridma_dev *pdev, struct k230_peridma_vchan *vchan) +{ + struct k230_peridma_pchan *pchan = NULL, *pchans = pdev->pchan; + unsigned long flags; + int i=0; + + spin_lock_irqsave(&pdev->lock, flags); + for_each_clear_bit_from(i, pdev->pchans_used, CH_NUM) { + pchan = &pchans[i]; + pchan->vchan = vchan; + pdev->pchan[i].vchan = vchan; + set_bit(i, pdev->pchans_used); + break; + } + spin_unlock_irqrestore(&pdev->lock, flags); + + return pchan; +} + +static u32* pdma_llt_cal(struct device *dev,struct k230_peridma_hwdesc *hwdesc,u8 chn) +{ + int i; + u32 list_num; + pdma_llt_t *llt_list; + + list_num = (hwdesc->usr_pdma_chn_cfg.line_size - 1) / PDMA_MAX_LINE_SIZE + 1; + + llt_list = g_pdma_llt_list[chn].llt_list_v; + g_pdma_llt_list[chn].use = true; + + for (i = 0; i < list_num; i++) { + llt_list[i].src_addr = ((u32)(uintptr_t)hwdesc->usr_pdma_chn_cfg.src_addr + PDMA_MAX_LINE_SIZE*i); + llt_list[i].dst_addr = ((u32)(uintptr_t)hwdesc->usr_pdma_chn_cfg.dst_addr + PDMA_MAX_LINE_SIZE*i); + + //printk("===pdma_llt_cal src_addr:0x%llx,dst_addr:0x%llx,list_num:%d\n",llt_list[i].src_addr,llt_list[i].dst_addr,list_num); + if (i == list_num -1) { + llt_list[i].line_size = hwdesc->usr_pdma_chn_cfg.line_size % PDMA_MAX_LINE_SIZE; + llt_list[i].next_llt_addr = 0; + } else { + llt_list[i].line_size = PDMA_MAX_LINE_SIZE; + llt_list[i].next_llt_addr = (u32)(uintptr_t)(&llt_list[i+1]); + } + } + + return (u32 *)g_pdma_llt_list[chn].llt_list_p; +} + +static void configure_pchan(struct k230_peridma_pchan *pchan, struct k230_peridma_hwdesc *hwdesc) +{ + struct k230_peridma_dev *pdev = pchan->pdev; + + if (unlikely(!peridma_pchan_is_idle(pchan))) { + dev_err(vchan2dev(pchan->vchan), "pchan %d is non-idle!\n", + pchan->id); + + return; + } + + hwdesc->usr_pdma_chn_cfg.ch = (pdma_ch_e)pchan->id; + + u32 ch_cfg = *(u32*)&hwdesc->usr_pdma_chn_cfg.pdma_ch_cfg; + iowrite32(ch_cfg, pchan->ch_regs+CH_CFG); + iowrite32(hwdesc->usr_pdma_chn_cfg.device, pdev->base + 0x120 + pchan->id*4); + iowrite32((u32)(uintptr_t)pdma_llt_cal(pdev->dev,hwdesc,pchan->id),pchan->ch_regs+CH_LLT_SADDR); +} + +static void peridma_vchan_start_first_queued(struct k230_peridma_vchan *vchan) +{ + struct k230_peridma_desc *desc; + struct virt_dma_desc *vd; + struct k230_peridma_dev *pdev = vchan->pdev; + struct k230_peridma_pchan *pchan = NULL; + struct k230_peridma_hwdesc *hwdesc = NULL; + + if(vchan->desc) { + dev_err(vchan2dev(vchan), "%s already processing something \n", peridma_vchan_name(vchan)); + dump_stack(); + return; + } + + if(vchan->pchan) { + pchan = vchan->pchan; + dev_vdbg(vchan2dev(vchan), "%s already allocated phy channel \n", peridma_vchan_name(vchan)); + } else { + pchan = find_phy_chan(pdev, vchan); + if(pchan) + { + vchan->pchan = pchan; + //printk("============:vchan name:%s allocated phy channel:%d\n",peridma_vchan_name(vchan),vchan->pchan->id); + } + else + return; + } + + + + vd = vchan_next_desc(&vchan->vchan); + if (!vd) { + dev_vdbg(vchan2dev(vchan), "peridma_vchan_start_first_queued: no next vd found \n"); + goto freepchan; + } + + desc = vd_to_k230_peridma_desc(vd); + dev_vdbg(vchan2dev(vchan), "peridma_vchan_start_first_queued: desc %px \n", desc); + + if(!list_empty(&desc->xfer_list)){ + hwdesc = list_first_entry(&desc->xfer_list, struct k230_peridma_hwdesc, list); +// list_del(&hwdesc->list); +// atomic_sub(1, &desc->hwdesc_remain); + + //dev_vdbg(vchan2dev(vchan), "peridma_vchan_start_first_queued: hwdesc(%px) \n", hwdesc); + //dev_vdbg(vchan2dev(vchan), " ctl=%x, cfg0=%x, cfg1=%x, dev_tout=%x, dev_sel=%x, src_addr=%x, dst_addr=%x \n", hwdesc->ctl, hwdesc->cfg0, hwdesc->cfg1, hwdesc->dev_tout, hwdesc->dev_sel, hwdesc->src_addr, hwdesc->dst_addr); + vchan->desc = desc; + desc->hwdesc = hwdesc; + + peridma_pchan_irq_clear(vchan->pchan); + configure_pchan(vchan->pchan, desc->hwdesc); + peridma_pchan_irq_enable(vchan->pchan); + peridma_pchan_start(vchan->pchan); + + + return; + } + + +freepchan: +// dev_vdbg(vchan2dev(vchan), "peridma_vchan_start_first_queued: free_pchan \n"); +// free_pchan(pdev, pchan); + return; + +} + + +static void peridma_vchan_xfer_complete(struct k230_peridma_vchan *vchan) +{ + struct virt_dma_desc *vd; +// spin_lock_irqsave(&vchan->vchan.lock, flags); + + if (unlikely(!peridma_pchan_is_idle(vchan->pchan))) { + dev_err(vchan2dev(vchan), "BUG: %s caught chx_tr_done, but channel not idle!\n", + peridma_vchan_name(vchan)); + peridma_pchan_stop(vchan->pchan); + } + + /* The completed descriptor currently is in the head of vc list */ + vd = vchan_next_desc(&vchan->vchan); + + /* Remove the completed descriptor from issued list before completing */ + list_del(&vd->node); + vchan_cookie_complete(vd); + + /* Submit queued descriptors after processing the completed ones */ + peridma_vchan_start_first_queued(vchan); + +// spin_unlock_irqrestore(&vchan->vchan.lock, flags); +} + + +static void k230_peridma_issue_pending(struct dma_chan *dchan) +{ + struct k230_peridma_vchan *vchan = dchan_to_k230_peridma_vchan(dchan); + unsigned long flags; + + spin_lock_irqsave(&vchan->vchan.lock, flags); + + if (vchan_issue_pending(&vchan->vchan)){ + dev_vdbg(vchan2dev(vchan), "k230_peridma_issue_pending: 1 \n"); + peridma_vchan_start_first_queued(vchan); + } + dev_vdbg(vchan2dev(vchan), "k230_peridma_issue_pending: 2 \n"); + + spin_unlock_irqrestore(&vchan->vchan.lock, flags); +} + +static void _pdma_chn_state_clear(pdma_ch_e pdma_chn, u32* int_state) +{ + if (*int_state & (1 << pdma_chn)) + { + *int_state |= (PDONE_INT << pdma_chn); + + //free 链表 + } + if (*int_state & (1 << (8 + pdma_chn))) + { + *int_state |= (PITEM_INT << pdma_chn); + } + if (*int_state & (1 << (16 + pdma_chn))) + { + *int_state |= (PPAUSE_INT << pdma_chn); + } + if (*int_state & (1 << (24 + pdma_chn))) + { + *int_state |= (PTOUT_INT << pdma_chn); + } +} + +static irqreturn_t k230_peridma_interrupt(int irq, void *dev_id) +{ + + struct k230_peridma_dev *priv = dev_id; + struct k230_peridma_vchan *vchan; + struct k230_peridma_desc *desc = NULL; + struct k230_peridma_hwdesc *hwdesc = NULL; + int i; + u32 ch_id; + + u32 int_stat = 0; + int_stat = ioread32(priv->base + PDMA_INT_STAT); + for (i = 0;i < CH_NUM;i ++) + { + if ((int_stat >> i) & 0x1) + { + _pdma_chn_state_clear(i, &int_stat); + iowrite32(int_stat, priv->base + PDMA_INT_STAT); + g_pdma_llt_list[i].use = false; + } + else + { + continue; + } + ch_id = i; + vchan = priv->pchan[ch_id].vchan; + if (vchan == NULL) + { + return IRQ_HANDLED; + } + spin_lock(&vchan->vchan.lock); + //dev_vdbg(vchan2dev(vchan), "k230_peridma_interrupt: process Transfer done irq of phy ch%d, int_status(%x) \n", ch_id, int_stat0); + desc = vchan->desc; + //printk("========desc->cyclic:0x%llx\n",desc->cyclic); + if(desc->cyclic) + { + hwdesc = desc->cyclic; + //dev_vdbg(vchan2dev(vchan),"hwdesc(%px): last=%d ctl=%x, cfg0=%x, \n cfg1=%x, dev_tout=%x, dev_sel=%x, src_addr=%x, dst_addr=%x \n", hwdesc, hwdesc->last, hwdesc->ctl, hwdesc->cfg0, hwdesc->cfg1, hwdesc->dev_tout, hwdesc->dev_sel, hwdesc->src_addr, hwdesc->dst_addr); + desc->remain -= hwdesc->usr_pdma_chn_cfg.line_size; + if(desc->remain == 0) + desc->remain = desc->buf_len; + if(hwdesc->last) + vchan_cyclic_callback(&vchan->desc->vd); + + if(desc->cyclic->list.next == &(desc->xfer_list)) + desc->cyclic = list_entry(desc->xfer_list.next, typeof(*(desc->cyclic)), list); + else + desc->cyclic = list_next_entry(desc->hwdesc, list); + + hwdesc = desc->cyclic; + desc->hwdesc = hwdesc; + configure_pchan(vchan->pchan, hwdesc); + peridma_pchan_start(vchan->pchan); + } + else + { + list_del(&desc->hwdesc->list); + list_add_tail(&desc->hwdesc->list, &desc->completed_list); + + if(!list_empty(&desc->xfer_list)){ + hwdesc = list_first_entry(&desc->xfer_list, struct k230_peridma_hwdesc, list); + // list_del(&hwdesc->list); + // atomic_sub(1, &desc->hwdesc_remain); + + //dev_vdbg(vchan2dev(vchan), "hwdesc(%px): ctl=%x, cfg0=%x, cfg1=%x, dev_tout=%x, dev_sel=%x, src_addr=%x, dst_addr=%x \n", hwdesc, hwdesc->ctl, hwdesc->cfg0, hwdesc->cfg1, hwdesc->dev_tout, hwdesc->dev_sel, hwdesc->src_addr, hwdesc->dst_addr); + desc->hwdesc = hwdesc; + // desc->next = true; + configure_pchan(vchan->pchan, hwdesc); + peridma_pchan_start(vchan->pchan); + } else { + vchan->desc = NULL; + peridma_vchan_xfer_complete(vchan); + } + } + spin_unlock(&vchan->vchan.lock); + } + + return IRQ_HANDLED; +} + +static int k230_peridma_vchan_pause(struct dma_chan *dchan) +{ + struct k230_peridma_vchan *vchan = dchan_to_k230_peridma_vchan(dchan); + unsigned long flags; + unsigned int timeout = 20; /* timeout iterations */ + + if(!vchan->pchan){ + dev_err(vchan2dev(vchan), "k230_peridma_vchan_pause: vchan->pchan is NULL \n"); + dump_stack(); + return -EBUSY; + } + + spin_lock_irqsave(&vchan->vchan.lock, flags); + + if (!peridma_pchan_is_busy(vchan->pchan)) { + dev_err(vchan2dev(vchan), "%s is non-busy!\n", + peridma_vchan_name(vchan)); + + spin_unlock_irqrestore(&vchan->vchan.lock, flags); + + return -EBUSY; + } + + peridma_pchan_pause(vchan->pchan); + + do { + if (peridma_pchan_is_paused(vchan->pchan)) + break; + + udelay(2); + } while (--timeout); + + peridma_pchan_irq_disable(vchan->pchan); + + vchan->is_paused = true; + + spin_unlock_irqrestore(&vchan->vchan.lock, flags); + + return timeout ? 0 : -EAGAIN; +} + +static int k230_peridma_vchan_resume(struct dma_chan *dchan) +{ + struct k230_peridma_vchan *vchan = dchan_to_k230_peridma_vchan(dchan); + unsigned long flags; + + spin_lock_irqsave(&vchan->vchan.lock, flags); + + if (vchan->is_paused){ + peridma_pchan_irq_enable(vchan->pchan); + peridma_pchan_resume(vchan->pchan); + } + + vchan->is_paused = false; + + spin_unlock_irqrestore(&vchan->vchan.lock, flags); + + return 0; +} + + +static void k230_peridma_vchan_free_chan_resources(struct dma_chan *dchan) +{ + struct k230_peridma_vchan *vchan = dchan_to_k230_peridma_vchan(dchan); + + if(vchan->pchan) { + /* ASSERT: channel is idle */ + if (!peridma_pchan_is_idle(vchan->pchan)) { + dev_err(vchan2dev(vchan), "%s is non-idle!\n", + peridma_vchan_name(vchan)); + } + + peridma_pchan_stop(vchan->pchan); + peridma_pchan_irq_disable(vchan->pchan); + } + + + vchan_free_chan_resources(&vchan->vchan); + + dev_vdbg(vchan2dev(vchan), + "%s: free resources, descriptor still allocated: %u\n", + peridma_vchan_name(vchan), atomic_read(&vchan->descs_allocated)); + +} + +static int k230_peridma_config(struct dma_chan *chan, + struct dma_slave_config *config) +{ + struct k230_peridma_vchan *vchan = to_k230_peridma_vchan(chan); + + memcpy(&vchan->cfg, config, sizeof(*config)); + + return 0; +} + + +static int parse_device_properties(struct k230_peridma_dev *pdev) +{ + struct device *dev = pdev->dev; + u32 tmp; + int ret; + + ret = device_property_read_u32(dev, "dma-channels", &tmp); + if (ret) + return ret; + + if (tmp == 0 || tmp > CH_NUM) + return -EINVAL; + + pdev->nr_channels = tmp; + + ret = device_property_read_u32(dev, "dma-requests", &tmp); + if (ret) + return ret; + + if (tmp == 0 || tmp > 35) + return -EINVAL; + + pdev->nr_requests = tmp; + + return 0; +} + +static struct dma_chan *k230_pdma_of_xlate(struct of_phandle_args *dma_spec, + struct of_dma *ofdma) +{ + //printk("==========k230_pdma_of_xlate before\n"); + struct k230_peridma_dev *priv = ofdma->of_dma_data; + struct k230_peridma_vchan *vchan; + struct dma_chan *chan; + unsigned int priority = dma_spec->args[0]; + unsigned int dev_tout = dma_spec->args[1]; + unsigned int dat_endian = dma_spec->args[2]; + unsigned int dev_sel = dma_spec->args[3]; + + dev_vdbg(priv->dev, "k230_pdma_of_xlate: priority=%x, dev_tout=%x, dat_endian=%x, dev_sel=%x \n", priority, dev_tout, dat_endian, dev_sel); + + if (dev_sel > (priv->nr_requests -1)) + return NULL; + + chan = dma_get_slave_channel(&(priv->vchan[dev_sel].vchan.chan)); + + if(!chan) + return NULL; + + vchan = to_k230_peridma_vchan(chan); + + vchan->priority = priority; + vchan->dev_tout = dev_tout; + vchan->dat_endian = dat_endian; + vchan->dev_sel = dev_sel; + + dev_vdbg(priv->dev, "k230_pdma_of_xlate: get vchan %s \n", peridma_vchan_name(vchan)); + return chan; +} + +static int k230_peridma_probe(struct platform_device *pdev) +{ + struct k230_peridma_dev *priv; + struct resource *res; + int i, ret; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + priv->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + + priv->dev = &pdev->dev; + priv->slave.dev = &pdev->dev; + + priv->irq = platform_get_irq(pdev, 0); + if (priv->irq < 0) { + dev_err(&pdev->dev, "Cannot claim IRQ\n"); + return priv->irq; + } + + + priv->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(priv->clk)) { + dev_err(&pdev->dev, "No clock specified\n"); + return PTR_ERR(priv->clk); + } + + ret = parse_device_properties(priv); + if (ret) + return ret; + + //printk("=====k230_peridma_probe irq:%d,dma_channels:%d,requests:%d\n",priv->irq,priv->nr_channels,priv->nr_requests); + + /* Initialize physical channels */ + priv->pchan = devm_kcalloc(priv->dev, priv->nr_channels, + sizeof(struct k230_peridma_pchan), GFP_KERNEL); + if (!priv->pchan) + return -ENOMEM; + + for (i = 0; i < priv->nr_channels; i++) { + struct k230_peridma_pchan *pchan = &priv->pchan[i]; + + pchan->pdev = priv; + pchan->id = i; + pchan->ch_regs = priv->base + CH0_BASE + i * CH_OFF; + } + + /* Initialize virtual channels */ + priv->vchan = devm_kcalloc(priv->dev, priv->nr_requests, + sizeof(struct k230_peridma_vchan), GFP_KERNEL); + if (!priv->vchan) + return -ENOMEM; + + INIT_LIST_HEAD(&priv->slave.channels); + for (i = 0; i < priv->nr_requests; i++) { + struct k230_peridma_vchan *vchan = &priv->vchan[i]; + + vchan->pdev = priv; + vchan->desc = NULL; + atomic_set(&vchan->descs_allocated, 0); + + vchan->vchan.desc_free = vchan_desc_put; + vchan_init(&vchan->vchan, &priv->slave); + } + + platform_set_drvdata(pdev, priv); + spin_lock_init(&priv->lock); + + dma_cap_zero(priv->slave.cap_mask); + dma_cap_set(DMA_SLAVE, priv->slave.cap_mask); + +// priv->slave.chancnt = priv->nr_channels; +// priv->slave.src_addr_widths = DMA_SLAVE_BUSWIDTH_8_BYTES; +// priv->slave.dst_addr_widths = DMA_SLAVE_BUSWIDTH_8_BYTES; +// priv->slave.directions = BIT(DMA_MEM_TO_MEM); +// priv->slave.residue_granularity = DMA_RESIDUE_GRANULARITY_DESCRIPTOR; + + priv->slave.directions = BIT(DMA_DEV_TO_MEM) | + BIT(DMA_MEM_TO_DEV); + priv->slave.residue_granularity = DMA_RESIDUE_GRANULARITY_BURST; + priv->slave.src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | + BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | + BIT(DMA_SLAVE_BUSWIDTH_4_BYTES); + priv->slave.dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | + BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | + BIT(DMA_SLAVE_BUSWIDTH_4_BYTES); + + priv->slave.device_tx_status = k230_peridma_tx_status; + priv->slave.device_issue_pending = k230_peridma_issue_pending; + priv->slave.device_terminate_all = k230_peridma_terminate_all; + priv->slave.device_pause = k230_peridma_vchan_pause; + priv->slave.device_resume = k230_peridma_vchan_resume; + +// priv->slave.device_alloc_chan_resources = k230_peridma_vchan_alloc_chan_resources; + priv->slave.device_free_chan_resources = k230_peridma_vchan_free_chan_resources; + + priv->slave.device_prep_slave_sg = k230_peridma_prep_slave_sg; + priv->slave.device_prep_dma_cyclic = k230_peridma_prep_dma_cyclic; + priv->slave.device_config = k230_peridma_config; + + priv->slave.dev = &pdev->dev; + + ret = clk_prepare_enable(priv->clk); + if (ret) { + dev_err(&pdev->dev, "Couldn't enable the clock\n"); + return ret; + } + + /* + * Initialize peridma controller + */ + + k230_peridma_hw_init(priv); + + ret = devm_request_irq(&pdev->dev, priv->irq, k230_peridma_interrupt, + 0, dev_name(&pdev->dev), priv); + if (ret) { + dev_err(&pdev->dev, "Cannot request IRQ\n"); + goto err_clk_disable; + } + + ret = dma_async_device_register(&priv->slave); + if (ret) { + dev_warn(&pdev->dev, "Failed to register DMA engine device\n"); + goto err_clk_disable; + } + + ret = of_dma_controller_register(pdev->dev.of_node, k230_pdma_of_xlate, + priv); + if (ret) { + dev_err(&pdev->dev, "of_dma_controller_register failed\n"); + goto err_clk_disable; + } + + for (i =0;i < CH_NUM;i ++) + { + g_pdma_llt_list[i].llt_list_v = dma_alloc_coherent(&pdev->dev,sizeof(pdma_llt_t),&g_pdma_llt_list[i].llt_list_p,GFP_KERNEL); + g_pdma_llt_list[i].use = false; + //printk("===========g_pdma_llt_list[%d].llt_list_p:0x%llx\n",i,g_pdma_llt_list[i].llt_list_p); + } + + dev_vdbg(&pdev->dev, "Successfully probed K230 peridma controller, register mapped at %pllx(PHY %llu) \n", priv->base ,res->start); + + return 0; + +err_clk_disable: + clk_disable_unprepare(priv->clk); + return ret; +} + +static int k230_peridma_remove(struct platform_device *pdev) +{ + int i =0; + struct k230_peridma_dev *priv = platform_get_drvdata(pdev); + + for (i =0;i < CH_NUM;i ++) + { + dma_free_coherent(&pdev->dev,sizeof(pdma_llt_t),g_pdma_llt_list[i].llt_list_v,g_pdma_llt_list[i].llt_list_p); + g_pdma_llt_list[i].llt_list_v = NULL; + } + + /* Disable IRQ so no more work is scheduled */ + disable_irq(priv->irq); + + of_dma_controller_free(pdev->dev.of_node); + dma_async_device_unregister(&priv->slave); + + clk_disable_unprepare(priv->clk); + + return 0; +} + +static const struct of_device_id k230_peridma_match[] = { + { .compatible = "canaan,k230-pdma" }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, k230_peridma_match); + +static struct platform_driver k230_peridma_driver = { + .probe = k230_peridma_probe, + .remove = k230_peridma_remove, + .driver = { + .name = "k230-peridma", + .of_match_table = k230_peridma_match, + }, +}; + +module_platform_driver(k230_peridma_driver); + +MODULE_DESCRIPTION("K230 peridma driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index 8aa5daacbc816..9756e15b27b07 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig @@ -122,5 +122,8 @@ source "sound/soc/codecs/Kconfig" # generic frame-work source "sound/soc/generic/Kconfig" +# canaan k230 audio +source "sound/soc/canaan/Kconfig" + endif # SND_SOC diff --git a/sound/soc/canaan/Kconfig b/sound/soc/canaan/Kconfig new file mode 100644 index 0000000000000..38b193fdc6834 --- /dev/null +++ b/sound/soc/canaan/Kconfig @@ -0,0 +1,11 @@ +config SND_SOC_CANAAN_K230_INNO + tristate "ASoC support for CANAAN boards using a inno codec" + help + Say Y or M here if you want to add support for SoC audio on CANAAN K230 + boards using the INNO codec. + +config SND_SOC_CANAAN_K230_AUDIO + tristate "CANAAN K230 AUDIO interface support" + help + Say Y or M here if you want to enable audio for canaan soc. + diff --git a/sound/soc/canaan/Makefile b/sound/soc/canaan/Makefile new file mode 100644 index 0000000000000..02894b7456d2f --- /dev/null +++ b/sound/soc/canaan/Makefile @@ -0,0 +1,8 @@ +snd-soc-canaan-k230-audio-objs := canaan_k230_audio.o +obj-$(CONFIG_SND_SOC_CANAAN_K230_AUDIO) += snd-soc-canaan-k230-audio.o + +snd-soc-canaan-k230-inno-objs := canaan_k230_inno.o audio_muxpin_ctrl.o muxpin_reg_ctl.o + +obj-$(CONFIG_SND_SOC_CANAAN_K230_INNO) += snd-soc-canaan-k230-inno.o + + diff --git a/sound/soc/canaan/audio_muxpin_ctrl.c b/sound/soc/canaan/audio_muxpin_ctrl.c new file mode 100644 index 0000000000000..b704c16e13a95 --- /dev/null +++ b/sound/soc/canaan/audio_muxpin_ctrl.c @@ -0,0 +1,113 @@ +/* Copyright (c) 2023, Canaan Bright Sight Co., Ltd + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "audio_muxpin_ctrl.h" +#include "muxpin_reg_ctl.h" + + +void ai_i2s_muxpin_config(void) +{ + muxpin_reg_init(); + +#define AUDIO_I2S_ENABLE_CHANNEL_OUT0_IN1 0 +#define AUDIO_I2S_CLK 32 +#define AUDIO_I2S_WS 33 +#define AUDIO_I2S_I2SIN1 36 + +#define AUDIO_I2S_I2SIN0 34 + + muxpin_config_t config1 = + { + 0x0, //st + 0x4, //ds + 0x0, //pd + 0x0, //pu + 0x1, //oe + 0x0, //ie + 0x1, //msc + 0x0, //sl + 0x2 //io_sel + }; + + muxpin_config_t config2 = + { + 0x0, //st + 0x4, //ds + 0x0, //pd + 0x0, //pu + 0x0, //oe + 0x1, //ie + 0x1, //msc + 0x0, //sl + 0x2 //io_sel + }; + + + //受限于硬件,I2S_0支持声音播放,I2S_1支持话音输入; + //func3 out + muxpin_set_config(AUDIO_I2S_CLK, config1); + muxpin_set_config(AUDIO_I2S_WS, config1); + +#if AUDIO_I2S_ENABLE_CHANNEL_OUT0_IN1 + //func3 in + muxpin_set_config(AUDIO_I2S_I2SIN1, config2); + +#else + //func3 in + muxpin_set_config(AUDIO_I2S_I2SIN0, config2); +#endif +} + +void ao_i2s_muxpin_config(void) +{ + muxpin_reg_init(); +#define AUDIO_I2S_CLK 32 +#define AUDIO_I2S_WS 33 +#define AUDIO_I2S_I2SOUT0 35 +#define AUDIO_I2S_I2SOUT1 37 + + muxpin_config_t config1 = + { + 0x0, //st + 0x4, //ds + 0x0, //pd + 0x0, //pu + 0x1, //oe + 0x0, //ie + 0x1, //msc + 0x0, //sl + 0x2 //io_sel + }; + + + //受限于硬件,I2S_0支持声音播放,I2S_1支持话音输入; + //func3 out + muxpin_set_config(AUDIO_I2S_CLK, config1); + muxpin_set_config(AUDIO_I2S_WS, config1); + + muxpin_set_config(AUDIO_I2S_I2SOUT0, config1); + muxpin_set_config(AUDIO_I2S_I2SOUT1, config1); +} + diff --git a/sound/soc/canaan/audio_muxpin_ctrl.h b/sound/soc/canaan/audio_muxpin_ctrl.h new file mode 100644 index 0000000000000..ce610d7ffdf48 --- /dev/null +++ b/sound/soc/canaan/audio_muxpin_ctrl.h @@ -0,0 +1,34 @@ +/* Copyright (c) 2023, Canaan Bright Sight Co., Ltd + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef __AUDIO_MUXPIN_CTL_H__ +#define __AUDIO_MUXPIN_CTL_H__ +#include + +void ai_i2s_muxpin_config(void); +void ao_i2s_muxpin_config(void); + +#endif + + diff --git a/sound/soc/canaan/canaan_k230_audio.c b/sound/soc/canaan/canaan_k230_audio.c new file mode 100644 index 0000000000000..8e76ee5840d53 --- /dev/null +++ b/sound/soc/canaan/canaan_k230_audio.c @@ -0,0 +1,102 @@ +#include +#include +#include "canaan_k230_audio.h" +#include +#include + +struct canaan_audio_data { + struct platform_device *pdev; + void __iomem *base; +}; + +static struct canaan_audio_data sai = {0}; + +void audio_i2s_in_init(void) +{ + if(sai.base) + { + volatile audio_in_reg_s *audio_in_reg = sai.base; + audio_in_reg->audio_in_pdm_conf_0.audio_in_mode = AUDIO_IO_OUT_MODE_I2S;//默认使用i2s + audio_in_reg->audio_in_agc_para_4.agc_bypass = AUDIO_ENABLE; + } +} + +EXPORT_SYMBOL_GPL(audio_i2s_in_init); + +void audio_i2s_out_init(bool enable, uint32_t word_len) +{ + audio_out_data_width_e out_word_len = AUDIO_OUT_TYPE_32BIT; + if (32 == word_len) + { + out_word_len = AUDIO_OUT_TYPE_32BIT; + } + else if (24 == word_len) + { + out_word_len = AUDIO_OUT_TYPE_24BIT; + } + else if (16 == word_len) + { + out_word_len = AUDIO_OUT_TYPE_16BIT; + } + if(sai.base) + { + volatile audio_out_reg_s *audio_out_reg = sai.base + 0x800; + audio_out_reg->audio_out_ctl.data_type = out_word_len; + audio_out_reg->audio_out_ctl.mode = AUDIO_OUT_MODE_I2S; /* i2s/pdm/tdm mode */ + audio_out_reg->audio_out_ctl.enable = enable ? AUDIO_ENABLE : AUDIO_DISABLE; /* enable audio out */ + } +} + +EXPORT_SYMBOL_GPL(audio_i2s_out_init); + +void audio_i2s_enable_audio_codec(bool use_audio_codec) +{ + if(sai.base){ + volatile audio_in_reg_s *audio_in_reg = sai.base; + audio_in_reg->audio_in_pdm_conf_0.audio_codec_bypass = !use_audio_codec;//是否启用内置codec + } +} + +EXPORT_SYMBOL_GPL(audio_i2s_enable_audio_codec); + +static int canaan_audio_probe(struct platform_device *pdev) +{ + struct resource *res; + int ret = 0; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + sai.base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(sai.base)) + return PTR_ERR(sai.base); + + platform_set_drvdata(pdev, &sai); + + return ret; +} + +static int canaan_audio_remove(struct platform_device *pdev) +{ + //struct canaan_audio_data *priv = platform_get_drvdata(pdev); + + return 0; +} + +static const struct of_device_id canaan_audio_ids[] = { + { .compatible = "canaan,k230-audio" }, + { /* sentinel */ }, +}; + +static struct platform_driver canaan_audio_driver = { + .driver = { + .name = "k230-audio", + .of_match_table = canaan_audio_ids, + }, + .probe = canaan_audio_probe, + .remove = canaan_audio_remove, +}; + +module_platform_driver(canaan_audio_driver); + +MODULE_DESCRIPTION("CANAAN AUDIO Interface"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/canaan/canaan_k230_audio.h b/sound/soc/canaan/canaan_k230_audio.h new file mode 100644 index 0000000000000..c819c4d22ecbf --- /dev/null +++ b/sound/soc/canaan/canaan_k230_audio.h @@ -0,0 +1,507 @@ +/* Copyright (c) 2023, Canaan Bright Sight Co., Ltd + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef _CANAAN_AUDIO_H +#define _CANAAN_AUDIO_H +#include + +typedef enum +{ + AUDIO_DISABLE = 0, + AUDIO_ENABLE = 1, +} audio_enable_e; + +typedef enum +{ + AUDIO_OUT_TYPE_32BIT = 0, + AUDIO_OUT_TYPE_24BIT = 1, + AUDIO_OUT_TYPE_16BIT = 2, +} audio_out_data_width_e; + +typedef enum +{ + AUDIO_OUT_24BIT_ALIGN_RIGTH = 0, + AUDIO_OUT_24BIT_ALIGN_LEFT = 1, +} audio_out_align_e; + +typedef enum +{ + AUDIO_OUT_MODE_TDM = 0, + AUDIO_OUT_MODE_PDM = 1, + AUDIO_OUT_MODE_I2S = 2, +} audio_out_mode_e; + +typedef enum +{ + AUDIO_OUT_PDM_RISING = 0, + AUDIO_OUT_PDM_BOTH = 1, +} audio_out_pdm_edge_e; + +typedef enum +{ + AUDIO_PDM_OUT_CIC16 = 0, + AUDIO_PDM_OUT_CIC32 = 1, +} audio_out_pdm_cic_e; + +typedef enum +{ + AUDIO_PDM_OVERSAMPLE_32 = 0, + AUDIO_PDM_OVERSAMPLE_64, + AUDIO_PDM_OVERSAMPLE_128, +} audio_pdm_oversample_e; +typedef struct _audio_out_ctl +{ + uint32_t enable : 1; /* bit0: 0: disable 1:enable */ + uint32_t data_type : 2; /* bit1-2: 0: channel 32bit; 1:channel 24bit 2:channel 16bit */ + uint32_t align : 1; /* bit3: 0: 24bit right align; 1: 24bit left align */ + uint32_t loop_en : 1; /* bit4: 0: disable 1:enable */ + uint32_t mode : 2; /* bit5-6: 0: pcm 1:pdm 2:i2s */ + uint32_t fir2_out_bypass : 1; /* bit7: 0: enable 1:bypass ENABLE for bypass*/ + uint32_t dma_enable : 1; /* bit8: 0: disable 1:enable */ + uint32_t channel_num : 4; /* bit9-12: channel number */ + uint32_t dma_threshold : 5; /* bit13-17: dma threshold 16bit should be >= 2; 24/32bit should be >= 1 */ + uint32_t reserved : 14; /* bit18-31 */ +} __attribute__((packed, aligned(4))) audio_out_ctl_s; + +typedef struct _audio_out_rcv_fifo +{ + uint32_t audio_fifo_clear : 1; + uint32_t ref_fifo_th : 5; + uint32_t reserved : 26; +} __attribute__((packed, aligned(4))) audio_out_rcv_fifo_s; + +typedef struct _audio_out_tdm_conf +{ + uint32_t frame_sclk_delay : 1; /* bit0: delay or not. 1: frame sync delay 1 cycle */ + uint32_t frame_sync_total : 10; /* bit1-10: how many sclks frame sync 32*channels */ + uint32_t frame_sync_high : 10; /* bit1-10: how many sclks frame sync high, 1~channel bit width */ + uint32_t reserved : 11; /* reserved */ +} __attribute__((packed, aligned(4))) audio_out_tdm_conf_s; + +typedef struct _audio_out_pmd_conf +{ + uint32_t reserved0 : 4; /* bit0 */ + uint32_t pcm_sync_en : 1; /* bit4:*/ + uint32_t pdm_spike_th : 4; /* bit5-8:*/ + uint32_t pdm_ord_sel : 1; /* bit9: 0 for 24bit * 44.1K cic16 = 2.8224MHz, 1for 24bit * 16K cic32 += 2.048MHz */ + uint32_t pdm_pn_sel : 1; /* bit10: 0 for rising edge , 1 for rising&failing edge*/ + uint32_t reserved1 : 21; /* bit11-bit31: reserved*/ +} __attribute__((packed, aligned(4))) audio_out_pmd_conf_s; + +typedef struct _audio_out_condition +{ + uint32_t ref_out_fifo_rdy : 1; /* bit0: */ + uint32_t out_buf_full : 1; /* bit1: */ + uint32_t out_buf_empty : 1; /* bit2: */ + uint32_t out_rdy : 1; /* bit3: */ + uint32_t reserved : 28; /* bit4-bit32:*/ +} __attribute__((packed, aligned(4))) audio_out_condition_s; + +typedef struct _audio_filter_conf +{ + uint32_t loop_fir_bypass : 1; /* bit0 */ + uint32_t pdm_cic_filter_bypass : 1; /* bit1 bypass cic */ + uint32_t pdm_cic_mp_filter_bypass : 1; /* bit2 bypass cic补偿 */ + uint32_t pdm_hbf_filter_bypass : 1; /* bit3 bypass hbf */ + uint32_t reserved : 28; +} __attribute__((packed, aligned(4))) audio_filter_conf_s; + + +typedef struct _audio_iir_fir_conf +{ + uint32_t T_DST : 16; + uint32_t resample_sel_bypass : 3; + uint32_t iir_scnt : 4; + uint32_t iir_clr : 1; + uint32_t reserved : 8; +} __attribute__((packed, aligned(4))) audio_iir_fir_conf_s; + +typedef struct _audio_out_reg +{ + audio_out_ctl_s audio_out_ctl; /* address:0x00 */ + audio_out_rcv_fifo_s audio_out_fifo; /* address:0x04 */ + uint32_t audio_out_reserved0[1]; /* address:0x08 */ + audio_out_tdm_conf_s audio_out_tdm_conf; /* address:0x0c */ + + int audio_out_data; /* address:0x10 audio output register */ + uint32_t audio_out_reserved1[5]; /* address:0x14 */ + uint32_t audio_out_interrupt; /* address:0x28 reserved for future use */ + uint32_t audio_out_reserved2[1]; + audio_out_pmd_conf_s audio_out_pdm_conf; /* address:0x30 */ + uint32_t audio_out_iir_matrix_a[4][4]; /* address:0x34 */ + uint32_t audio_out_iir_matrix_b[4][2]; /* address:0x74 */ + uint32_t audio_out_iir_matrix_c[4]; /* address:0x94 */ + uint32_t audio_out_iir_matrix_d; /* address:0xa4 */ + uint32_t audio_out_ref2apb_data; /* address:0xa8 loopback? echo & mix ?*/ + + audio_out_condition_s audio_out_condition; /* address:0xac read only*/ + uint32_t audio_out_reserved3[16]; /* address:0xb0 read only*/ + uint32_t fir2_tap_coef[11]; /* address:0xf0 read only*/ + uint32_t reserved1[2]; /* address:0x11c-0x120*/ + uint32_t fir2_tap_coef_11[9]; /* address:0x124-0x144*/ + uint32_t reserved2; /* address:0x148*/ + uint32_t fir2_tap_coef_20[12]; /* address:0x14c-0x178*/ + audio_filter_conf_s audio_out_filter; /* address:0x17c*/ + uint32_t fir0_tap_coef[32]; /* address:0x180*/ + uint32_t fir1_tap_coef[32]; /* address:0x260*/ + audio_iir_fir_conf_s iir_fir_conf; /* address:0x340*/ + uint32_t iir_x_parameter[3]; /* address:0x344*/ + uint32_t pn_delay; /* address:0x350*/ +} __attribute__((packed, aligned(4))) audio_out_reg_s; + + +/* --------------------------------------------------audio input +-----------------------------------------------------*/ +/* pdm */ +typedef enum +{ + AUDIO_IO_IN_MODE_PDM = 0,//IO35、IO37:PDM in0和i2s out1复用的IO功能选择为i2s out1,PDM in1和i2s out0复用的IO功能选择为i2s out0; + AUDIO_IO_OUT_MODE_I2S = 2,//IO35、IO37:PDM in0和i2s out1复用的IO功能选择为PDM in0,PDM in1和i2s out0复用的IO功能选择为PDM in1。 +} audio_io_select_mode_e; + +typedef enum +{ + AUDIO_IN_LEFT_ALIGN = 0, + AUDIO_IN_RIGHT_ALIGN = 1, +} audio_in_align_e; + +typedef enum +{ + AUDIO_IN_PDM_BOTH = 0, + AUDIO_IN_PDM_RISING = 2, + AUDIO_IN_PDM_FAILING = 3, +} audio_in_pdm_edge_e; + +typedef enum +{ + AUDIO_IN_PDM_CIC16 = 0, + AUDIO_IN_PDM_CIC32 = 1, +} audio_in_pdm_cic_e; + +typedef struct _audio_in_pdm_conf_0 +{ + uint32_t pdm_enable : 1; /* bit0: 1 enable, 0 disable */ + uint32_t mpf_enable : 1; /* bit2: cic 补偿滤波器 1 enable, 0 disable */ + uint32_t hbf_enable : 1; /* bit1: 1 enable, 0 disable */ + uint32_t pdm_in_endian : 1; /* bit3: pdm +input时读入的pdm转换后的pcm数据位于32bit数据的位置:0:高28bit(4bit通道号+24bit pcm数据) ,1: +低28bit(4bit通道号+24bit pcm数据) */ + uint32_t pdm_fifo_clear : 1; /* bit4: fifo clear */ + uint32_t audio_codec_bypass : 1; /* bit5: bypass audio codec, use I2S directly to IO for digital I2S +microphone */ + uint32_t audio_in_mode : 2; /* bit76: 10: PDM in0和i2s out1复用的IO功能选择为i2s out1,PDM +in1和i2s out0复用的IO功能选择为i2s out0; + 其它:PDM in0和i2s out1复用的IO功能选择为PDM in0,PDM +in1和i2s out0复用的IO功能选择为PDM in1。*/ + uint32_t pdm_dma_fifo_th : 5; /* bit8-12: should as same as dma controller */ + uint32_t audio_dev_clk_sel : 1; /* bit13:audio_clk invert control signal */ + uint32_t pdm_clk_sel : 1; /* bit14:pdm_clk invert control signal*/ + uint32_t reserved2 : 1; /* bit15: */ + uint32_t pdm_buf_rdy : 1; /* bit16: software check this bit and receive data */ + uint32_t reserved : 15; /* bit17 ~ bit31:reserved */ +} __attribute__((packed, aligned(4))) audio_in_pdm_conf_0_s; + +typedef struct _audio_in_pdm_conf_1 +{ + uint32_t ord_select : 1; /* bit0: 0 for 16, 1 for 32 */ + uint32_t reserved : 31; /* bit1-31: */ +} __attribute__((packed, aligned(4))) audio_in_pdm_conf_1_s; + +typedef struct _audio_in_pdm_ch_conf +{ + uint32_t pdm_chl_num : 4; /* bit0-3: pdm input channel num*/ + uint32_t pdm_pn_sel : 2; /* bit4-5: 2'b10: posedge 2'b11: negedge others: double edge*/ + uint32_t reserved : 26; +} __attribute__((packed, aligned(4))) audio_in_pdm_ch_conf_s; + +/* TDM */ +typedef struct _audio_in_tdm_conf_0 +{ + uint32_t tdm_channel_mode : 4; /* bit0-3 : select tdm input channel number; 4'h0: 24 channel; 4'h1: 12; 4'h2: +8; 4'h3: 6; 4'h4: 4; 4'h5:3; 4'h6:2; 4'h7:1 */ + uint32_t tdm_width_mode : 3; /* 0: 32; 1:24;2:20; 3:16; 4:12 */ + uint32_t reserved1 : 4; /* reserved1*/ + uint32_t tdm_clk_dly : 1; /* bit11, data delay one clock or not, frame_sync , 1表示在frame +sync后一拍出数据,0表示在frame sync当前拍出数据*/ + uint32_t tdm_in_endian : 1; /* bit12 0: 24/12 bit left align 1: 24/12 bit right align */ + uint32_t tdm_dma_fifo_th : 5; /* bit13-17:tdm fifo thershold set for making tdm dma request*/ + uint32_t reserved2 : 14; +} __attribute__((packed, aligned(4))) audio_in_tdm_conf_0_s; + +typedef struct _audio_in_tdm_channel_cfg0 +{ + uint32_t io0_channel_num : 4; /* 2-24 channel */ + uint32_t io1_channel_num : 4; /* 2-24 channel */ + uint32_t io2_channel_num : 4; /* 2-24 channel */ + uint32_t io3_channel_num : 4; /* 2-24 channel */ + uint32_t io4_channel_num : 4; /* 2-24 channel */ + uint32_t io5_channel_num : 4; /* 2-24 channel */ + uint32_t io6_channel_num : 4; /* 2-24 channel */ + uint32_t io7_channel_num : 4; /* 2-24 channel */ +} __attribute__((packed, aligned(4))) audio_in_tdm_channel_cfg0_s; + +typedef struct _audio_in_tdm_channel_cfg1 +{ + uint32_t io8_channel_num : 4; /* 2-24 channel */ + uint32_t io9_channel_num : 4; /* 2-24 channel */ + uint32_t io10_channel_num : 4; /* 2-24 channel */ + uint32_t io11_channel_num : 4; /* 2-24 channel */ + uint32_t io12_channel_num : 4; /* 2-24 channel */ + uint32_t io13_channel_num : 4; /* 2-24 channel */ + uint32_t io14_channel_num : 4; /* 2-24 channel */ + uint32_t io15_channel_num : 4; /* 2-24 channel */ +} __attribute__((packed, aligned(4))) audio_in_tdm_channel_cfg1_s; + +typedef struct _audio_in_tdm_fsync_config +{ + uint32_t tdm_fsync_div_high : 10; + uint32_t tdm_fsync_div_low : 10; + uint32_t reserved : 12; +} __attribute__((packed, aligned(4))) audio_in_tdm_fsync_config_s; + +typedef struct _audio_in_tdm_ctl +{ + uint32_t tdm_intr : 1; /* write 1 to clear */ + uint32_t audi_buf_full_n2 : 1; /* tdm fifo full */ + uint32_t audi_buf_rdy_n2 : 1; /* tdm fifo not empty, data could be read from fifo */ + uint32_t tdm_enable : 1; /* enable tdm */ + uint32_t reserved : 28; +} __attribute__((packed, aligned(4))) audio_in_tdm_ctl_s; + +/* audio in agc */ +typedef struct _audio_in_agc_para_0 +{ + uint32_t agc_gatt : 16; + uint32_t agc_headroom : 16; +} __attribute__((packed, aligned(4))) auido_in_agc_para0_s; + +typedef struct _audio_in_agc_para_1 +{ + uint32_t agc_np_margin : 16; + uint32_t agc_gdth : 16; +} __attribute__((packed, aligned(4))) auido_in_agc_para1_s; + +typedef struct _audio_in_agc_para_2 +{ + uint32_t agc_gainstep_slow : 16; + uint32_t agc_gainstep_fast : 16; +} __attribute__((packed, aligned(4))) auido_in_agc_para2_s; + +typedef struct _audio_in_agc_para_3 +{ + uint32_t agc_frame_num : 16; + uint32_t agc_max_gainstep : 16; +} __attribute__((packed, aligned(4))) auido_in_agc_para3_s; + +typedef struct _audio_in_agc_para_4 +{ + uint32_t agc_bypass : 1; + uint32_t agc_model2_en : 1; + uint32_t agc_model1_en : 1; + uint32_t agc_sd_sel : 1; + uint32_t agc_xpkth : 16; + uint32_t agc_nvak_num : 6; + uint32_t agc_npkobs : 6; +} __attribute__((packed, aligned(4))) auido_in_agc_para4_s; + +typedef struct _audio_in_agc_para_5 +{ + uint32_t agc_us_num : 16; + uint32_t agc_dpk : 16; +} __attribute__((packed, aligned(4))) auido_in_agc_para5_s; + +typedef struct _audio_in_agc_para_6 +{ + uint32_t agc_ds_num : 16; + uint32_t agc_ub_num : 16; +} __attribute__((packed, aligned(4))) auido_in_agc_para6_s; + +typedef struct _audio_in_agc_para_7 +{ + uint32_t agc_ku : 16; + uint32_t agc_db_num : 16; +} __attribute__((packed, aligned(4))) auido_in_agc_para7_s; + +typedef struct _audio_in_agc_para_8 +{ + uint32_t agc_d : 16; + uint32_t agc_kd : 16; +} __attribute__((packed, aligned(4))) auido_in_agc_para8_s; + +typedef struct _audio_in_agc_para_9 +{ + uint32_t agc_theta_1 : 16; + uint32_t agc_theta_0 : 16; +} __attribute__((packed, aligned(4))) auido_in_agc_para9_s; + +typedef struct _audio_in_agc_para_10 +{ + uint32_t agc_d2b_num : 16; + uint32_t agc_d2s_num : 16; +} __attribute__((packed, aligned(4))) auido_in_agc_para10_s; + +typedef struct _audio_in_agc_para_11 +{ + uint32_t agc_d2b_num; +} __attribute__((packed, aligned(4))) auido_in_agc_para11_s; + +typedef struct _audio_in_iir_fir_ctl +{ + uint32_t chl_num : 3; /* 只能设置8个通道? */ + uint32_t T_DST : 16; /* I don't known */ + uint32_t re_bypass : 3; /* disable resample, fir, iir */ + uint32_t iir_scnt : 4; /* status move bit */ + uint32_t iir_clear : 1; /* iir clear */ + uint32_t reserved : 5; +} __attribute__((packed, aligned(4))) audio_in_iir_fir_ctl_s; + +typedef struct _audio_in_pdm_clk_spike +{ + /* debounce */ + uint32_t pdmin_clk_spike_cnt : 2; /* 0: 1 pclk cycle, 1: 2 pclk cycles, 2: 3 pclk cycles, 3: 4 pclk cycles*/ + uint32_t pdmin_clk_spike_th : 2; /* 0: 2 pclk cycle, 1: 3 pclk cycles, 2: 4 pclk cycles, 3: 5 pclk cycles */ + uint32_t reserved : 28; +} __attribute__((packed, aligned(4))) audio_in_pdm_clk_spike_s; + +typedef struct _audio_in_pdm_ch_spike +{ + /* debounce */ + uint32_t pdmin_ch0_spike_cnt : 2; /* 0: 1 pclk cycle, 1: 2 pclk cycles, 2: 3 pclk cycles, 3: 4 pclk cycles*/ + uint32_t pdmin_ch0_spike_th : 2; /* 0: 2 pclk cycle, 1: 3 pclk cycles, 2: 4 pclk cycles, 3: 5 pclk cycles */ + uint32_t pdmin_ch1_spike_cnt : 2; /* 0: 1 pclk cycle, 1: 2 pclk cycles, 2: 3 pclk cycles, 3: 4 pclk cycles*/ + uint32_t pdmin_ch1_spike_th : 2; /* 0: 2 pclk cycle, 1: 3 pclk cycles, 2: 4 pclk cycles, 3: 5 pclk cycles */ + uint32_t pdmin_ch2_spike_cnt : 2; /* 0: 1 pclk cycle, 1: 2 pclk cycles, 2: 3 pclk cycles, 3: 4 pclk cycles*/ + uint32_t pdmin_ch2_spike_th : 2; /* 0: 2 pclk cycle, 1: 3 pclk cycles, 2: 4 pclk cycles, 3: 5 pclk cycles */ + uint32_t pdmin_ch3_spike_cnt : 2; /* 0: 1 pclk cycle, 1: 2 pclk cycles, 2: 3 pclk cycles, 3: 4 pclk cycles*/ + uint32_t pdmin_ch3_spike_th : 2; /* 0: 2 pclk cycle, 1: 3 pclk cycles, 2: 4 pclk cycles, 3: 5 pclk cycles */ + uint32_t pdmin_ch4_spike_cnt : 2; /* 0: 1 pclk cycle, 1: 2 pclk cycles, 2: 3 pclk cycles, 3: 4 pclk cycles*/ + uint32_t pdmin_ch4_spike_th : 2; /* 0: 2 pclk cycle, 1: 3 pclk cycles, 2: 4 pclk cycles, 3: 5 pclk cycles */ + uint32_t pdmin_ch5_spike_cnt : 2; /* 0: 1 pclk cycle, 1: 2 pclk cycles, 2: 3 pclk cycles, 3: 4 pclk cycles*/ + uint32_t pdmin_ch5_spike_th : 2; /* 0: 2 pclk cycle, 1: 3 pclk cycles, 2: 4 pclk cycles, 3: 5 pclk cycles */ + uint32_t pdmin_ch6_spike_cnt : 2; /* 0: 1 pclk cycle, 1: 2 pclk cycles, 2: 3 pclk cycles, 3: 4 pclk cycles*/ + uint32_t pdmin_ch6_spike_th : 2; /* 0: 2 pclk cycle, 1: 3 pclk cycles, 2: 4 pclk cycles, 3: 5 pclk cycles */ + uint32_t pdmin_ch7_spike_cnt : 2; /* 0: 1 pclk cycle, 1: 2 pclk cycles, 2: 3 pclk cycles, 3: 4 pclk cycles*/ + uint32_t pdmin_ch7_spike_th : 2; /* 0: 2 pclk cycle, 1: 3 pclk cycles, 2: 4 pclk cycles, 3: 5 pclk cycles */ +} __attribute__((packed, aligned(4))) audio_in_pdm_ch_spike_s; + +typedef struct _audio_in_pdm_bypass +{ + uint32_t hbf_dsmpl_fir_bypass : 1; + uint32_t cic_mp_dsmpl_fir_bypass: 1; + uint32_t cic_dsmple_fir_bypass : 1; + uint32_t reserved : 29; +} __attribute__((packed, aligned(4))) audio_in_pdm_bypass_s; + +typedef struct _audio_in_reg +{ + /* PDM reg */ + audio_in_pdm_conf_0_s audio_in_pdm_conf_0; /* address:0x00 */ + audio_in_pdm_conf_1_s audio_in_pdm_conf_1; /* address:0x04 0 for 24bit * 44.1K cic16= 2. +8224MHz, 1 for 24bit * 16K cic32= 2.048MHz */ + uint32_t audio_in_pdm_mp_para_tap_coef[16]; /* address:0x08 cic 补偿滤波系数 */ + uint32_t audio_in_pdm_hbf_para_tap_coef[29]; /* address:0x48 hbf 滤波器*/ + audio_in_pdm_ch_conf_s audio_in_pdm_ch_conf; /* address:0xbc */ + uint32_t audio_in_pdm2pcm_data; /* address:0xc0 , read 24bit data +数据有效位28bit,其中[27:24]为数据对应的通道编号,[23:0]为实际pcm数据*/ + uint32_t audio_in_pdm_interrupt; /* address:0xc4 , only bit0 valid,write 0 clear */ + + /* TDM reg */ + audio_in_tdm_conf_0_s audio_in_tdm_config; /* address:0xc8 */ + audio_in_tdm_channel_cfg0_s audio_in_tdm_channel_cfg0; /* address:0xcc */ + audio_in_tdm_channel_cfg1_s audio_in_tdm_channel_cfg1; /* address:0xd0 */ + uint32_t reserved0; + audio_in_tdm_fsync_config_s audio_in_tdm_fsync_config; /* address:0xd8 */ + audio_in_tdm_ctl_s audio_in_tdm_ctl; /* address:0xdc */ + uint32_t reserved1; /* address:0xe0 */ + uint32_t audio_in_tdm2apb_data; /* address:0xe4 data read reg */ + uint32_t reserved2[2]; /* address:0xe8 */ + + /* For TDM/I2S/PDM */ + /* fir0 */ + uint32_t audio_in_fir0_tap_coef[11]; /* address:0xf0 fir before resample */ + uint32_t reserved3[2]; /* address:0x11c-0x120 */ + uint32_t audio_in_fir0_tap_coef_11[9]; /* address:0x124 */ + uint32_t reserved4; /* address:0x148 */ + uint32_t audio_in_fir0_tap_coef_20[12]; /* address:0x14c */ + + /* fir1 */ + uint32_t audio_in_fir1_tap_coef[3]; /* address:0x17c fir after resample */ + uint32_t reserved5; /* address:0x188 */ + uint32_t audio_in_fir1_tap_coef_3[29]; /* address:0x18c fir after resample */ + + /* iir x parameter */ + uint32_t audio_in_iir_x_0_0_1; /* address:0x200 , bit0-15 for 0, bit16-31 for 1 */ + uint32_t audio_in_iir_x_0_2; /* address:0x204 , bit0-15 valid */ + uint32_t audio_in_iir_y_0_0_1; /* address:0x208 , bit0-15 for 0, bit16-31 for 1 */ + + /* agc */ + auido_in_agc_para0_s audio_in_agc_para_0; /* address:0x20c*/ + auido_in_agc_para1_s audio_in_agc_para_1; /* address:0x210*/ + auido_in_agc_para2_s audio_in_agc_para_2; /* address:0x214*/ + auido_in_agc_para3_s audio_in_agc_para_3; /* address:0x218*/ + auido_in_agc_para4_s audio_in_agc_para_4; /* address:0x21c*/ + auido_in_agc_para5_s audio_in_agc_para_5; /* address:0x220*/ + auido_in_agc_para6_s audio_in_agc_para_6; /* address:0x224*/ + auido_in_agc_para7_s audio_in_agc_para_7; /* address:0x228*/ + auido_in_agc_para8_s audio_in_agc_para_8; /* address:0x22c*/ + auido_in_agc_para9_s audio_in_agc_para_9; /* address:0x230*/ + auido_in_agc_para10_s audio_in_agc_para_10; /* address:0x234*/ + auido_in_agc_para11_s audio_in_agc_para_11; /* address:0x238*/ + + /* iir fir config */ + audio_in_iir_fir_ctl_s audio_in_iir_fir_ctl; /* address:0x23c*/ + + /* Only for PDM */ + audio_in_pdm_clk_spike_s audio_in_pdm_clk_spike; /* address:0x240*/ + audio_in_pdm_ch_spike_s audio_in_pdm_ch_spike; /* address:0x244*/ + audio_in_pdm_bypass_s audio_in_pdm_bypass; /* address:0x248*/ +} __attribute__((packed, aligned(4))) audio_in_reg_s; + +typedef enum +{ + AUDIO_SAMPLE_8K = 0, + AUDIO_SAMPLE_16K = 1, /*PDM 16K(24bit) --> HBF(高通+2上采样) --32K----> CIC 补偿(2上采样) -- +64K-----> 4级CIC(32上采样)-->2.048MHz (24bit)(饱和处理)--->环路滤波-->2.048MHz (1bit) */ + AUDIO_SAMPLE_32K = 2, + AUDIO_SAMPLE_44D1K = 3, /*PDM 44.1K(24bit) --> HBF(高通+2上采样) --88.2K--> CIC 补偿(2上采样) -- +176.4K--> 4级CIC(16上采样)-->2.8224MHz(24bit)(饱和处理)--->环路滤波-->2.8224MHz(1bit) */ + AUDIO_SAMPLE_48K = 4, +} audio_sample_e; + +//void audio_io3537_select_mode(audio_io_select_mode_e mode);//IO35,37引脚模式选择 +void audio_i2s_in_init(void); +void audio_i2s_enable_audio_codec(bool use_audio_codec);//i2s选择使用内置audio codec + +//pdm in操作寄存器 +//void audio_pdm_in_init(audio_pdm_oversample_e pdm_oversample, uint32_t pdm_rx_num, audio_in_pdm_edge_e pdm_rx_edge, audio_in_align_e pdm_rx_align); +//volatile uint32_t *audio_pdm_rx_dma_fifo(); +//void audio_pdm_fifo_overrun_clear(); + + + +void audio_i2s_out_init(bool enable, uint32_t word_len); + +#endif + diff --git a/sound/soc/canaan/canaan_k230_inno.c b/sound/soc/canaan/canaan_k230_inno.c new file mode 100755 index 0000000000000..5de3434ea9e1f --- /dev/null +++ b/sound/soc/canaan/canaan_k230_inno.c @@ -0,0 +1,171 @@ +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "canaan_k230_audio.h" +#include "audio_muxpin_ctrl.h" + +#define DRV_NAME "canaan-k230-snd-inno" + +struct k230_inno_info { + struct clk *xtal; + struct clk *pclk; + struct mutex clk_lock; + int clk_users; +}; + +static int k230_inno_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + return 0; +} + +static void k230_inno_shutdown(struct snd_pcm_substream *substream) +{ + +} + +static int k230_inno_startup(struct snd_pcm_substream *substream) +{ + return 0; +} + +static const struct snd_soc_ops canaan_k230_inno_ops = { + .startup = k230_inno_startup, + .shutdown = k230_inno_shutdown, + .hw_params = k230_inno_hw_params, +}; + +SND_SOC_DAILINK_DEFS(k230_inno, + DAILINK_COMP_ARRAY(COMP_EMPTY()), + DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "k230-inno-codec-dai")), + DAILINK_COMP_ARRAY(COMP_EMPTY())); + +static struct snd_soc_dai_link canaan_k230_dailink = { + .name = "k230-inno-codec", + .stream_name = "Audio", + .ops = &canaan_k230_inno_ops, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + SND_SOC_DAILINK_REG(k230_inno), +}; + +static struct snd_soc_card snd_soc_card_k230 = { + .name = "CANAAN-K230-I2S", + .owner = THIS_MODULE, + .dai_link = &canaan_k230_dailink, + .num_links = 1, +}; + +static int canaan_k230_inno_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card = &snd_soc_card_k230; + struct device_node *codec_np, *cpu_np; + struct snd_soc_dai_link *dailink = &canaan_k230_dailink; + struct device_node *np = pdev->dev.of_node; + struct k230_inno_info *priv; + int ret; + + if (!np) { + dev_err(&pdev->dev, "only device tree supported\n"); + return -EINVAL; + } + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + mutex_init(&priv->clk_lock); + + card->dev = &pdev->dev; + snd_soc_card_set_drvdata(card, priv); + + codec_np = of_parse_phandle(np, + "canaan,k230-audio-codec", 0); + if (!codec_np) { + dev_err(&pdev->dev, + "Property 'canaan,k230-audio-codec' missing or invalid\n"); + return -EINVAL; + } + dailink->codecs->of_node = codec_np; + of_node_put(codec_np); + + cpu_np = of_parse_phandle(np, + "canaan,k230-i2s-controller", 0); + if (!cpu_np) { + dev_err(&pdev->dev, + "Property 'canaan,k230-i2s-controller' missing or invalid\n"); + return -EINVAL; + } + + dailink->cpus->of_node = cpu_np; + dailink->platforms->of_node = cpu_np; + of_node_put(cpu_np); + + ret = snd_soc_of_parse_card_name(card, "canaan,model"); + if (ret) { + dev_err(&pdev->dev, + "Soc parse card name failed %d\n", ret); + return ret; + } + + + ret = devm_snd_soc_register_card(&pdev->dev, card); + if (ret){ + dev_err(&pdev->dev, "failed to register card: %d\n", ret); + return ret; + } + + // printk("====audio muxpin \n"); + // ai_i2s_muxpin_config(); + // ao_i2s_muxpin_config(); + + audio_i2s_in_init(); + audio_i2s_enable_audio_codec(true); + audio_i2s_out_init(true,32); + + return ret; +} + +static const struct of_device_id canaan_k230_inno_of_match[] = { + { .compatible = "canaan,k230-audio-inno", }, + {}, +}; + +MODULE_DEVICE_TABLE(of, canaan_k230_inno_of_match); + +static struct platform_driver canaan_k230_inno_driver = { + .probe = canaan_k230_inno_probe, + .driver = { + .name = DRV_NAME, + .of_match_table = canaan_k230_inno_of_match, + }, +}; + +module_platform_driver(canaan_k230_inno_driver); + +MODULE_DESCRIPTION("CANAAN k230 inno machine ASoC driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/sound/soc/canaan/muxpin_reg_ctl.c b/sound/soc/canaan/muxpin_reg_ctl.c new file mode 100644 index 0000000000000..4cd619f2a1aa8 --- /dev/null +++ b/sound/soc/canaan/muxpin_reg_ctl.c @@ -0,0 +1,222 @@ +/* Copyright (c) 2023, Canaan Bright Sight Co., Ltd + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "muxpin_reg_ctl.h" +#include + + +/* IOMUX */ +#define IOMUX_BASE_ADDR (0x91105000U) + +static volatile muxpin_t *muxpin = NULL; + +int muxpin_reg_init(void) +{ + if (muxpin == NULL) + { + void __iomem *base; + base = ioremap(IOMUX_BASE_ADDR, sizeof(muxpin_t)); + if (IS_ERR(base)) + return -1; + + muxpin = base; + } + + return 0; +} + + + +/** + * @brief set io configuretion + * + * @param io_num 0 ~ 63 + * @param config be careful + * @return int + */ +int muxpin_set_config(int io_num, muxpin_config_t config) +{ + if (io_num > IO_MAX_NUM || io_num < 0) + { + // kendryte_logi("io num error.\n"); + return -1; + } + +#if 1 + muxpin->io[io_num].st = config.st; + muxpin->io[io_num].ds = config.ds; + muxpin->io[io_num].pd = config.pd; + muxpin->io[io_num].pu = config.pu; + muxpin->io[io_num].oe_en = config.oe_en; + muxpin->io[io_num].ie_en = config.ie_en; + muxpin->io[io_num].msc = config.msc; + muxpin->io[io_num].sl = config.sl; + muxpin->io[io_num].io_sel = config.io_sel; + muxpin->io[io_num].pad_di = config.pad_di; +#else + muxpin->io[io_num] = config; +#endif + return 0; +} + +/** + * @brief get io configuretion + * + * @param io_num 0 ~ 63 + * @param config return value + */ +void muxpin_get_config(int io_num, muxpin_config_t *config) +{ + if (io_num > IO_MAX_NUM || io_num < 0) + { +// kendryte_logi("io num error.\n"); + return; + } + + *config = muxpin->io[io_num]; +} + + +/** + * @brief set io pull up/down or none + * + * @param io_num 0 ~ 63 + * @param pull enum value + * @return int + */ +int muxpin_set_io_pull(int io_num, muxpin_pull_t pull) +{ + muxpin_config_t cfg; + if (io_num < 0 || io_num >= MUXPIN_NUM_IO || pull >= MUXPIN_PULL_MAX) + return -1; + + cfg = muxpin->io[io_num]; + switch (pull) + { + case MUXPIN_PULL_NONE: + cfg.pu = 0; + cfg.pd = 0; + break; + case MUXPIN_PULL_DOWN: + cfg.pu = 0; + cfg.pd = 1; + break; + case MUXPIN_PULL_UP: + cfg.pu = 1; + cfg.pd = 0; + break; + default: + break; + } + + muxpin->io[io_num] = cfg; + return 0; +} + + +/** + * @brief set io drive current + * + * @param io_num 0 ~ 63 + * @param driving enum value + * @return int + */ +int muxpin_set_io_driving(int io_num, muxpin_driving_t driving) +{ + muxpin_config_t cfg; + if (io_num > 1 && driving >= MUXPIN_DRIVING_7) + { + // kendryte_logi("io %d can't configured driving 0x%x.\n",io_num,driving); + return -1; + } + + cfg = muxpin->io[io_num]; + cfg.ds = driving; + + muxpin->io[io_num] = cfg; + return 0; +} + + +/** + * @brief set io function + * + * @param io_num 0 ~ 63 + * @param function check table for this number + * @return int + */ +int muxpin_set_function(int io_num, int function) +{ + muxpin_config_t cfg; + if (io_num > IO_MAX_NUM) + { + // kendryte_logi("io num error.\n"); + return -1; + } + + if (function > 5) + { +// kendryte_logi("please check your function num.\n"); + return -1; + } + + cfg = muxpin->io[io_num]; + cfg.io_sel = function; + + muxpin->io[io_num] = cfg; + return 0; +} + + +/** + * @brief set io voltage + * + * @param io_num 0 ~ 63 + * @param voltage 1:1.8v 0:3.3v + * @return int + */ +int muxpin_set_io_voltage(int io_num, muxpin_io_voltage_t voltage) +{ + if (io_num > IO_MAX_NUM) + { + // kendryte_logi("error: please check your io num and voltage.\n"); + return -1; + } + + if (io_num < 2 && voltage == 0) + { +// kendryte_logi("error: io %d not suport 3.3v.\n",io_num); + return -1; + } + + muxpin->io[io_num].msc = (MUXPIN_IO_VOLTAGE_1V8 == voltage) ? 1 : 0; + return 0; +} + + + + + + diff --git a/sound/soc/canaan/muxpin_reg_ctl.h b/sound/soc/canaan/muxpin_reg_ctl.h new file mode 100644 index 0000000000000..63e72f733c99b --- /dev/null +++ b/sound/soc/canaan/muxpin_reg_ctl.h @@ -0,0 +1,121 @@ +/* Copyright (c) 2023, Canaan Bright Sight Co., Ltd + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef _MUXPIN_REG_CTL_H +#define _MUXPIN_REG_CTL_H + +#include + +#define IO_MAX_NUM 64 + +typedef enum muxpin_pull_e +{ + MUXPIN_PULL_NONE, /*!< No Pull */ + MUXPIN_PULL_DOWN, /*!< Pull Down */ + MUXPIN_PULL_UP, /*!< Pull Up */ + MUXPIN_PULL_MAX /*!< Count of pull settings */ +} muxpin_pull_t; + +typedef enum muxpin_driving_e +{ + MUXPIN_DRIVING_0, /*!< 000 */ + MUXPIN_DRIVING_1, /*!< 001 */ + MUXPIN_DRIVING_2, /*!< 010 */ + MUXPIN_DRIVING_3, /*!< 011 */ + MUXPIN_DRIVING_4, /*!< 100 */ + MUXPIN_DRIVING_5, /*!< 101 */ + MUXPIN_DRIVING_6, /*!< 110 */ + MUXPIN_DRIVING_7, /*!< 111 */ + MUXPIN_DRIVING_8, /*!< 1000 */ + MUXPIN_DRIVING_9, /*!< 1001 */ + MUXPIN_DRIVING_10, /*!< 1010 */ + MUXPIN_DRIVING_11, /*!< 1100 */ + MUXPIN_DRIVING_12, /*!< 1101 */ + MUXPIN_DRIVING_13, /*!< 1110 */ + MUXPIN_DRIVING_14, /*!< 1111 */ +} muxpin_driving_t; + +typedef enum muxpin_io_voltage_e +{ + MUXPIN_IO_VOLTAGE_1V8 = 1, + MUXPIN_IO_VOLTAGE_3V3 = 0, +} muxpin_io_voltage_t; + + +typedef struct mux_config +{ + uint32_t st : 1; + /*!< Schmitt trigger. */ + uint32_t ds : 4; + /*!< Driving selector. */ + uint32_t pd : 1; + /*!< Pull down enable. 0 for nothing, 1 for pull down. */ + uint32_t pu : 1; + /*!< Pull up enable. 0 for nothing, 1 for pull up. */ + uint32_t oe_en : 1; + /*!< Static output enable. */ + uint32_t ie_en : 1; + /*!< Static output enable. */ + uint32_t msc : 1; + /*!< msc control bit. */ + uint32_t sl : 1; + /*!< Slew rate control enable. */ + /*!< IO config setting. */ + uint32_t io_sel : 3; + /*!< set io function mode. */ + uint32_t resv0 : 17; + /*!< Reserved bits. */ + uint32_t pad_di : 1; + /*!< Read current IO's data input. */ +} __attribute__((aligned(4))) muxpin_config_t; + +typedef enum muxpin_io_function +{ + MUXPIN_FUNCTION1 = 0x0, + MUXPIN_FUNCTION2 = 0x1, + MUXPIN_FUNCTION3 = 0x2, + MUXPIN_FUNCTION4 = 0x3, + MUXPIN_FUNCTION5 = 0x4, +} muxpin_io_function_t; + +#define MUXPIN_NUM_IO (64) +typedef struct _muxpin_t +{ + muxpin_config_t io[MUXPIN_NUM_IO]; + /*!< FPIOA GPIO multiplexer io array */ +} __attribute__((aligned(4))) muxpin_t ; + + + +int muxpin_reg_init(void); +int muxpin_set_config(int io_num, muxpin_config_t config); +void muxpin_get_config(int io_num, muxpin_config_t *config); +int muxpin_set_io_pull(int io_num, muxpin_pull_t pull); +int muxpin_set_io_driving(int io_num, muxpin_driving_t driving); +int muxpin_set_function(int io_num, int function); +int muxpin_set_io_voltage(int io_num, muxpin_io_voltage_t voltage); + +#endif + + diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 399fe9b63fd7c..5d1cc1b78f594 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -2413,4 +2413,7 @@ config SND_SOC_LPASS_TX_MACRO select SND_SOC_LPASS_MACRO_COMMON tristate "Qualcomm TX Macro in LPASS(Low Power Audio SubSystem)" +config SND_SOC_K230_INNO + tristate "K230 INNO Codec" + endmenu diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 34764d3d17f6d..fcf54f3857fba 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -385,6 +385,7 @@ snd-soc-tas2764-objs := tas2764.o snd-soc-tas2780-objs := tas2780.o # Mux snd-soc-simple-mux-objs := simple-mux.o +snd-soc-inno-k230_codec-objs := inno_k230_reg.o inno_k230.o obj-$(CONFIG_SND_SOC_88PM860X) += snd-soc-88pm860x.o obj-$(CONFIG_SND_SOC_AB8500_CODEC) += snd-soc-ab8500-codec.o @@ -776,3 +777,5 @@ obj-$(CONFIG_SND_SOC_LPASS_TX_MACRO) += snd-soc-lpass-tx-macro.o # Mux obj-$(CONFIG_SND_SOC_SIMPLE_MUX) += snd-soc-simple-mux.o + +obj-$(CONFIG_SND_SOC_K230_INNO) += snd-soc-inno-k230_codec.o \ No newline at end of file diff --git a/sound/soc/codecs/inno_k230.c b/sound/soc/codecs/inno_k230.c new file mode 100644 index 0000000000000..b32af93840a17 --- /dev/null +++ b/sound/soc/codecs/inno_k230.c @@ -0,0 +1,477 @@ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include "inno_k230_reg.h" + +#define INNO_VOLUME_INVERT_VALUE 39 + +struct k230_inno_codec_priv { + void __iomem *base; + struct clk *pclk_adc; + struct clk *pclk_dac; + struct regmap *regmap; + struct device *dev; +}; + +enum snd_inno_k230_ctrl { + INNO_PCM_PLAYBACK_VOLUME, + INNO_PCM_PLAYBACK_MUTE, + INNO_PCM_CAPTURE_VOLUME, + INNO_PCM_CAPTURE_MUTE, +}; + +static int inno_capture_info_vol(struct snd_kcontrol *kcontrol,struct snd_ctl_elem_info *uinfo) +{ + //printk("====================inno_info_vol\n"); + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 4; + return 0; +} + +static int inno_capture_get_vol(struct snd_kcontrol *kcontrol,struct snd_ctl_elem_value *ucontrol) +{ + + return 0; +} + +static int inno_capture_put_vol(struct snd_kcontrol *kcontrol,struct snd_ctl_elem_value *ucontrol) +{ + + return 0; +} + +static const struct snd_kcontrol_new inno_snd_capture_volume_control = +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, //表示snd_kcontrol结构体用于哪一类设备(表示进行参数设置) + .name = "Capture Volume", //音量控制,每个声卡驱动程序的snd_kcontrol各不相同,为什么应用程序都可以调整它的音量,对于某些常用的属性,它们都有固定的名字。应用程序根据名字找到它的snd_kcontrol项,调用里面的put函数。 + .info = inno_capture_info_vol, //获得一些信息,如音量范围是多少 + .get = inno_capture_get_vol,//获得当前的音量值 + .put = inno_capture_put_vol, //设置音量 +}; + +static int inno_playback_info_vol(struct snd_kcontrol *kcontrol,struct snd_ctl_elem_info *uinfo) +{ + //printk("====================inno_info_vol\n"); + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = -39+INNO_VOLUME_INVERT_VALUE; + uinfo->value.integer.max = 6+INNO_VOLUME_INVERT_VALUE; + return 0; +} + +static int inno_playback_get_vol(struct snd_kcontrol *kcontrol,struct snd_ctl_elem_value *ucontrol) +{ + int value; + + audio_codec_dac_get_hpoutl_gain(&value); + //printk("=========inno_get_vol:%d\n",value+INNO_VOLUME_INVERT_VALUE); + ucontrol->value.integer.value[1] = ucontrol->value.integer.value[0] = value+INNO_VOLUME_INVERT_VALUE; + return 0; +} + +static int inno_playback_put_vol(struct snd_kcontrol *kcontrol,struct snd_ctl_elem_value *ucontrol) +{ + //printk("=========inno_put_vol:%d\n",ucontrol->value.integer.value[0]); + + audio_codec_dac_set_hpoutl_gain(ucontrol->value.integer.value[0]-INNO_VOLUME_INVERT_VALUE); + audio_codec_dac_set_hpoutr_gain(ucontrol->value.integer.value[0]-INNO_VOLUME_INVERT_VALUE); + return 0; +} + +static const struct snd_kcontrol_new inno_snd_playback_volume_control = +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, //表示snd_kcontrol结构体用于哪一类设备(表示进行参数设置) + .name = "Master Playback Volume", //音量控制,每个声卡驱动程序的snd_kcontrol各不相同,为什么应用程序都可以调整它的音量,对于某些常用的属性,它们都有固定的名字。应用程序根据名字找到它的snd_kcontrol项,调用里面的put函数。 + .info = inno_playback_info_vol, //获得一些信息,如音量范围是多少 + .get = inno_playback_get_vol,//获得当前的音量值 + .put = inno_playback_put_vol, //设置音量 +}; + +static int snd_inno_ctl_vol(struct snd_kcontrol *kcontrol,struct snd_ctl_elem_info *uinfo) +{ + if (kcontrol->private_value == INNO_PCM_PLAYBACK_VOLUME) + { + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = -39+INNO_VOLUME_INVERT_VALUE; + uinfo->value.integer.max = 6+INNO_VOLUME_INVERT_VALUE; + uinfo->value.integer.step = 3; + } + else if (kcontrol->private_value == INNO_PCM_PLAYBACK_MUTE) + { + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + } + else if (kcontrol->private_value == INNO_PCM_CAPTURE_VOLUME) + { + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 30; + uinfo->value.integer.step = 10; + } + else if (kcontrol->private_value == INNO_PCM_CAPTURE_MUTE) + { + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + } + return 0; +} + +static int snd_inno_ctl_get(struct snd_kcontrol *kcontrol,struct snd_ctl_elem_value *ucontrol) +{ + int value; + bool mute; + + if (kcontrol->private_value == INNO_PCM_PLAYBACK_VOLUME) + { + audio_codec_dac_get_hpoutl_gain(&value); + ucontrol->value.integer.value[0] = value+INNO_VOLUME_INVERT_VALUE; + } + else if (kcontrol->private_value == INNO_PCM_PLAYBACK_MUTE) + { + audio_codec_dac_get_hpoutl_mute(&mute); + ucontrol->value.integer.value[0] = !mute; + } + else if (kcontrol->private_value == INNO_PCM_CAPTURE_VOLUME) + { + audio_codec_adc_get_micl_gain(&value); + if (value == 6) + { + value = 10; + } + ucontrol->value.integer.value[0] = value; + } + else if (kcontrol->private_value == INNO_PCM_CAPTURE_MUTE) + { + audio_codec_adc_get_micl_mute(&mute); + ucontrol->value.integer.value[0] = !mute; + } + return 0; +} + +static int snd_inno_ctl_put(struct snd_kcontrol *kcontrol,struct snd_ctl_elem_value *ucontrol) +{ + if (kcontrol->private_value == INNO_PCM_PLAYBACK_VOLUME) + { + audio_codec_dac_set_hpoutl_gain(ucontrol->value.integer.value[0]-INNO_VOLUME_INVERT_VALUE); + audio_codec_dac_set_hpoutr_gain(ucontrol->value.integer.value[0]-INNO_VOLUME_INVERT_VALUE); + } + else if (kcontrol->private_value == INNO_PCM_PLAYBACK_MUTE) + { + audio_codec_dac_hpoutl_mute(!ucontrol->value.integer.value[0]); + audio_codec_dac_hpoutr_mute(!ucontrol->value.integer.value[0]); + } + else if (kcontrol->private_value == INNO_PCM_CAPTURE_VOLUME) + { + audio_codec_adc_set_micl_gain(ucontrol->value.integer.value[0]); + audio_codec_adc_set_micl_gain(ucontrol->value.integer.value[0]); + + } + else if (kcontrol->private_value == INNO_PCM_CAPTURE_MUTE) + { + audio_codec_adc_micl_mute(!ucontrol->value.integer.value[0]); + audio_codec_adc_micr_mute(!ucontrol->value.integer.value[0]); + } + return 0; +} + +static const struct snd_kcontrol_new inno_snd_control[] = +{ + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "PCM Playback Volume", + .info = snd_inno_ctl_vol, + .get = snd_inno_ctl_get, + .put = snd_inno_ctl_put, + .private_value = INNO_PCM_PLAYBACK_VOLUME, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "PCM Playback Switch", + .info = snd_inno_ctl_vol, + .get = snd_inno_ctl_get, + .put = snd_inno_ctl_put, + .private_value = INNO_PCM_PLAYBACK_MUTE, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Mic Capture Volume", + .info = snd_inno_ctl_vol, + .get = snd_inno_ctl_get, + .put = snd_inno_ctl_put, + .private_value = INNO_PCM_CAPTURE_VOLUME, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Mic Capture Switch", + .info = snd_inno_ctl_vol, + .get = snd_inno_ctl_get, + .put = snd_inno_ctl_put, + .private_value = INNO_PCM_CAPTURE_MUTE, + }, + +}; + + +static int k230_inno_codec_probe(struct snd_soc_component *component) +{ + snd_soc_add_component_controls(component, inno_snd_control,ARRAY_SIZE(inno_snd_control)); + return 0; +} + +static void k230_inno_codec_remove(struct snd_soc_component *component) +{ + +} + +static int k230_inno_codec_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + return 0; +} + +static const struct snd_soc_dapm_route k230_inno_codec_dapm_routes[] = { + // {"DACL VREF", NULL, "DAC PWR"}, + // {"DACR VREF", NULL, "DAC PWR"}, + // {"DACL HiLo VREF", NULL, "DAC PWR"}, + // {"DACR HiLo VREF", NULL, "DAC PWR"}, + // {"DACL CLK", NULL, "DAC PWR"}, + // {"DACR CLK", NULL, "DAC PWR"}, +}; + +static const struct snd_soc_dapm_widget k230_inno_codec_dapm_widgets[] = { +}; + +static int k230_inno_codec_open(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + //printk("=========k230_inno_codec_open\n"); + return 0; +} + +static const struct snd_soc_component_driver k230_inno_codec_driver = { + .open = k230_inno_codec_open, + .probe = k230_inno_codec_probe, + .remove = k230_inno_codec_remove, + .set_bias_level = k230_inno_codec_set_bias_level, + .dapm_routes = k230_inno_codec_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(k230_inno_codec_dapm_routes), + .dapm_widgets = k230_inno_codec_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(k230_inno_codec_dapm_widgets), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, + .legacy_dai_naming = 1, +}; + +static int k230_inno_codec_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + return 0; +} + +static int k230_inno_codec_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params, + struct snd_soc_dai *dai) +{ + struct k230_inno_codec_priv *priv = snd_soc_dai_get_drvdata(dai); + uint32_t i2s_ws = 16; + uint32_t sample_rate = 0; + uint32_t chn_cnt = 0; + int ret = 0; + struct snd_soc_component *component = dai->component; + + //clock init + sample_rate = params_rate(hw_params); + //adc or dac init + switch (params_format(hw_params)) + { + case SNDRV_PCM_FORMAT_S16_LE: + i2s_ws = 16; + break; + case SNDRV_PCM_FORMAT_S24_LE: + i2s_ws = 24; + break; + case SNDRV_PCM_FORMAT_S32_LE: + i2s_ws = 32; + break; + default: + dev_err(component->dev, "k230_inno_codec: unsupported PCM fmt"); + return -EINVAL; + } + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + { + //adc clock init + ret = clk_set_rate(priv->pclk_adc, sample_rate*256); + //printk("=====inno codec adc clock(%d) init,ret:%d\n",sample_rate*256,ret); + if (ret) { + dev_err(priv->dev, "Can't set inno codec clock rate: %d\n",ret); + return ret; + } + + audio_codec_adc_init(K_STANDARD_MODE,i2s_ws); + } + else + { + //dac clock init + ret = clk_set_rate(priv->pclk_dac, sample_rate*256); + //printk("=====inno codec dac clock(%d) init,ret:%d,i2s_ws:%d\n",sample_rate*256,ret,i2s_ws); + if (ret) { + dev_err(priv->dev, "Can't set inno codec clock rate: %d\n",ret); + return ret; + } + + audio_codec_dac_init(K_STANDARD_MODE,i2s_ws); + } + + //channel count + chn_cnt = params_channels(hw_params); + + /*printk("=====k230_inno_codec_dai_hw_params capture:%d,i2s_ws:%d,samplerate:%d,chn_cnt:%d",\ + substream->stream == SNDRV_PCM_STREAM_CAPTURE,i2s_ws,sample_rate,chn_cnt);*/ + return 0; +} + +static int k230_inno_codec_dai_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) +{ + return 0; +} + +#define K230_INNO_CODEC_RATES (SNDRV_PCM_RATE_8000 | \ + SNDRV_PCM_RATE_16000 | \ + SNDRV_PCM_RATE_32000 | \ + SNDRV_PCM_RATE_44100 | \ + SNDRV_PCM_RATE_48000 ) + + +#define K230_INNO_CODEC_FMTS (SNDRV_PCM_FMTBIT_S16_LE|SNDRV_PCM_FMTBIT_S32_LE) + +static const struct snd_soc_dai_ops k230_inno_codec_dai_ops = { + .startup = k230_inno_codec_dai_startup, + .set_fmt = k230_inno_codec_dai_set_fmt, + .hw_params = k230_inno_codec_dai_hw_params, +}; + +static struct snd_soc_dai_driver k230_inno_codec_dai_driver[] = { + { + .name = "k230-inno-codec-dai", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = K230_INNO_CODEC_RATES, + .formats = K230_INNO_CODEC_FMTS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = K230_INNO_CODEC_RATES, + .formats = K230_INNO_CODEC_FMTS, + }, + .ops = &k230_inno_codec_dai_ops, + .symmetric_rate = 1, + }, +}; + +static int inno_k230_codec_platform_probe(struct platform_device *pdev) +{ + struct k230_inno_codec_priv *priv; + //struct device_node *of_node = pdev->dev.of_node; + void __iomem *base; + int ret; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return PTR_ERR(base); + + priv->base = base; + + priv->pclk_adc = devm_clk_get(&pdev->dev, "adc"); + if (IS_ERR(priv->pclk_adc)) + return PTR_ERR(priv->pclk_adc); + + ret = clk_prepare_enable(priv->pclk_adc); + if (ret < 0) { + dev_err(&pdev->dev, "failed to enable clk_adc\n"); + return ret; + } + + priv->pclk_dac = devm_clk_get(&pdev->dev, "dac"); + if (IS_ERR(priv->pclk_dac)) + return PTR_ERR(priv->pclk_dac); + + ret = clk_prepare_enable(priv->pclk_dac); + if (ret < 0) { + dev_err(&pdev->dev, "failed to enable clk_dac\n"); + return ret; + } + + priv->dev = &pdev->dev; + dev_set_drvdata(&pdev->dev, priv); + + ret = devm_snd_soc_register_component(&pdev->dev, &k230_inno_codec_driver, + k230_inno_codec_dai_driver, + ARRAY_SIZE(k230_inno_codec_dai_driver)); + + if (ret) { + clk_disable_unprepare(priv->pclk_adc); + clk_disable_unprepare(priv->pclk_dac); + dev_set_drvdata(&pdev->dev, NULL); + } + + audio_codec_reg_init(priv->base); + audio_codec_powerup_init(); + + return 0; +} + +static int inno_k230_codec_platform_remove(struct platform_device *pdev) +{ + return 0; +} + + +static const struct of_device_id inno_k230_codec_of_match[] = { + { .compatible = "k230,inno-codec", }, + {} +}; +MODULE_DEVICE_TABLE(of, inno_k230_codec_of_match); + +static struct platform_driver inno_k230_platform_driver = { + .driver = { + .name = "k230,inno-codec", + .of_match_table = of_match_ptr(inno_k230_codec_of_match), + }, + .probe = inno_k230_codec_platform_probe, + .remove = inno_k230_codec_platform_remove, +}; + +module_platform_driver(inno_k230_platform_driver); + +MODULE_DESCRIPTION("ASoC inno codec driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/inno_k230_reg.c b/sound/soc/codecs/inno_k230_reg.c new file mode 100644 index 0000000000000..0eadf9ad1060c --- /dev/null +++ b/sound/soc/codecs/inno_k230_reg.c @@ -0,0 +1,1465 @@ +#include "inno_k230_reg.h" +#include +#include +#include +#include +#include + + +typedef struct +{ + unsigned int gain_micl; + unsigned int gain_micr; + unsigned int adc_volumel; + unsigned int adc_volumer; + + unsigned int gain_alcl; + unsigned int gain_alcr; + + unsigned int gain_hpoutl; + unsigned int gain_hpoutr; + unsigned int dac_volumel; + unsigned int dac_volumer; +}acodec_sound_values; + +static acodec_sound_values g_snd_default_values; +static acodec_sound_values g_snd_current_values; + +#define AUDIO_CODEC_BASE_ADDR (0x9140E000U) +#define AUDIO_ADC_MIX 0 +#define AUDIO_ENABLE_BIST 0 +#define AUDIO_MODIFY_ADC_MIC_DB 1 // 是否修改MIC db值(默认0db) +#define AUDIO_MODIFY_ADC_ALC_DB 1 // 是否修改ALC db值(默认0db) + +#define AUDIO_MODIFY_DAC_DB 1 + +#define AUDIO_REG_CONFIG_NORMAL_DELAY 1 // 1ms +#define AUDIO_REG_CONFIG_LONG_DELAY 20 // 20ms +#define AUDIO_REG_CONFIG_LONG2_DELAY 10 // 10ms +//powerup 配置中sel_vref每次配置都要延迟20ms,adc的step1/step2,dac的step1延迟10ms,其他延迟1ms + +static volatile audio_codec_reg_s *audio_codec_reg = NULL; +//tatic rt_thread_t g_check_hp_thread = 0; +static bool g_adc_left_mute = false; +static bool g_adc_right_mute = false; +static bool g_dac_left_mute = false; +static bool g_dac_right_mute = false; + +static int _reset_snd_values(acodec_sound_values* snd_values ) +{ + snd_values->gain_micl = 0x03; + snd_values->gain_micr = 0x03; + snd_values->adc_volumel = 0xc3; + snd_values->adc_volumer = 0xc3; + +#if 0 + snd_values->gain_alcl = 0x1f; + snd_values->gain_alcr = 0x1f; +#else + snd_values->gain_alcl = 0x12; + snd_values->gain_alcr = 0x12; +#endif + + snd_values->gain_hpoutl = 0x10; + snd_values->gain_hpoutr = 0x10; + snd_values->dac_volumel = 0xf1; + snd_values->dac_volumer = 0xf1; + + return 0; +} + +static int _reset_adc_sound(void) +{ + reg_24_t reg24; + reg_27_t reg27; + reg_8_t reg8; + reg_9_t reg9; + + reg24.reg_data = readl(&audio_codec_reg->reg_24); + reg24.reg_24.gain_micl = g_snd_default_values.gain_micl; + writel(reg24.reg_data, &audio_codec_reg->reg_24); + msleep(AUDIO_REG_CONFIG_NORMAL_DELAY); + + reg27.reg_data = readl(&audio_codec_reg->reg_27); + reg27.reg_27.gain_micr = g_snd_default_values.gain_micr; + writel(reg27.reg_data, &audio_codec_reg->reg_27); + msleep(AUDIO_REG_CONFIG_NORMAL_DELAY); + + reg24.reg_data = readl(&audio_codec_reg->reg_24); + reg24.reg_24.gain_alcl = g_snd_default_values.gain_alcl; + writel(reg24.reg_data, &audio_codec_reg->reg_24); + msleep(AUDIO_REG_CONFIG_NORMAL_DELAY); + + reg27.reg_data = readl(&audio_codec_reg->reg_27); + reg27.reg_27.gain_alcr = g_snd_default_values.gain_alcr; + writel(reg27.reg_data, &audio_codec_reg->reg_27); + msleep(AUDIO_REG_CONFIG_NORMAL_DELAY); + + reg8.reg_data = readl(&audio_codec_reg->reg_08); + reg8.reg_8.adcl_vol = g_snd_default_values.adc_volumel; + writel(reg8.reg_data, &audio_codec_reg->reg_08); + msleep(AUDIO_REG_CONFIG_NORMAL_DELAY); + + reg9.reg_data = readl(&audio_codec_reg->reg_09); + reg9.reg_9.adcr_vol = g_snd_default_values.adc_volumer; + writel(reg9.reg_data, &audio_codec_reg->reg_09); + msleep(AUDIO_REG_CONFIG_NORMAL_DELAY); + + + return 0; +} + +static int _reset_dac_sound(void) +{ + reg_2b_t reg2b; + reg_2e_t reg2e; + reg_6_t reg6; + + reg2b.reg_data = readl(&audio_codec_reg->reg_2b); + reg2b.reg_2b.gain_hpoutl = g_snd_default_values.gain_hpoutl; + writel(reg2b.reg_data, &audio_codec_reg->reg_2b); + msleep(AUDIO_REG_CONFIG_NORMAL_DELAY); + + reg2e.reg_data = readl(&audio_codec_reg->reg_2e); + reg2e.reg_2e.gain_hpoutr = g_snd_default_values.gain_hpoutr; + writel(reg2e.reg_data, &audio_codec_reg->reg_2e); + msleep(AUDIO_REG_CONFIG_NORMAL_DELAY); + + reg6.reg_data = readl(&audio_codec_reg->reg_06); + reg6.reg_6.dac_vol = g_snd_default_values.dac_volumel; + reg6.reg_6.dac_vol = g_snd_default_values.dac_volumer; + writel(reg6.reg_data, &audio_codec_reg->reg_06); + msleep(AUDIO_REG_CONFIG_NORMAL_DELAY); + + return 0; +} + +int audio_codec_reg_init(void* reg_base) +{ + if (audio_codec_reg == NULL) + { + audio_codec_reg = reg_base; + } + _reset_snd_values(&g_snd_default_values); + _reset_snd_values(&g_snd_current_values); + return 0; +} + +static volatile bool g_powerup_init = false; +void audio_codec_powerup_init(void) +{ + reg_29_t reg29; + reg_2c_t reg2c; + reg_21_t reg21; + reg_20_t reg20; + reg_0_t reg0; + int i = 0; + + if (g_powerup_init) + { + return; + } + g_powerup_init = true; + + // 0.reset the audio codec + reg0.reg_data = readl(&audio_codec_reg->reg_00); + reg0.reg_0.sys_bstn = 0; + reg0.reg_0.digcore_bstn = 0; +#if AUDIO_ENABLE_BIST + reg0.reg_0.bist_bstn = 0; +#endif + writel(reg0.reg_data, &audio_codec_reg->reg_00); + // printf("===========reset audio codec\n"); + msleep(AUDIO_REG_CONFIG_NORMAL_DELAY); + + reg0.reg_data = readl(&audio_codec_reg->reg_00); + reg0.reg_0.sys_bstn = 1; +// reg0.reg_0.digcore_bstn = 1; +#if AUDIO_ENABLE_BIST + reg0.reg_0.bist_bstn = 1; +#endif + writel(reg0.reg_data, &audio_codec_reg->reg_00); + // printf("===========work audio codec\n"); + msleep(AUDIO_REG_CONFIG_NORMAL_DELAY); + + // 1.Configure the register POP_CTRL_DACL[1:0] 0x29[5:4] to 2’b01, , to setup the output dc voltage of DAC left channel. + reg29.reg_data = readl(&audio_codec_reg->reg_29); + reg29.reg_29.pop_ctrl_dacl = 1; + writel(reg29.reg_data, &audio_codec_reg->reg_29); + msleep(AUDIO_REG_CONFIG_NORMAL_DELAY); + + // 1.Configure the register POP_CTRL_DACR[1:0] 0x2c[5:4] to 2’b01, , to setup the output dc voltage of DAC right channel. + reg2c.reg_data = readl(&audio_codec_reg->reg_2c); + reg2c.reg_2c.pop_ctrl_dacr = 1; + writel(reg2c.reg_data, &audio_codec_reg->reg_2c); + msleep(AUDIO_REG_CONFIG_NORMAL_DELAY); + + // 2.Configure the register SEL_VREF[7:0] reg0x21[7:0] to 8’b000_0001. + reg21.reg_data = readl(&audio_codec_reg->reg_21); + reg21.reg_21.sel_vref = 1; + writel(reg21.reg_data, &audio_codec_reg->reg_21); + msleep(AUDIO_REG_CONFIG_NORMAL_DELAY); + + // 3.Supply the power of the analog part . + + // 4.Configure the register EN_VREF reg0x20[6] to 1 to setup reference voltage + reg20.reg_data = readl(&audio_codec_reg->reg_20); + reg20.reg_20.en_vref = 1; + writel(reg20.reg_data, &audio_codec_reg->reg_20); + msleep(AUDIO_REG_CONFIG_NORMAL_DELAY); + + // 5.Change the register SEL_VREF[7:0] reg0x21[7:0] :configure the reg0x21[7:0] to 7’b1111_1111 directly. +#if 0 + reg21.reg_data = readl(&audio_codec_reg->reg_21); + reg21.reg_21.sel_vref = 0xff; + writel(reg21.reg_data,&audio_codec_reg->reg_21); + msleep(20); +#else + for (i = 0; i < 8; i++) + { + reg21.reg_data = readl(&audio_codec_reg->reg_21); + if (0 == i) + { + reg21.reg_21.sel_vref = 0x1; + } + else if (1 == i) + { + + reg21.reg_21.sel_vref = 0x3; + } + else if (2 == i) + { + + reg21.reg_21.sel_vref = 0x7; + } + else if (3 == i) + { + + reg21.reg_21.sel_vref = 0xf; + } + else if (4 == i) + { + + reg21.reg_21.sel_vref = 0x1f; + } + else if (5 == i) + { + + reg21.reg_21.sel_vref = 0x3f; + } + else if (6 == i) + { + + reg21.reg_21.sel_vref = 0x7f; + } + else if (7 == i) + { + + reg21.reg_21.sel_vref = 0xff; + } + writel(reg21.reg_data, &audio_codec_reg->reg_21); + msleep(AUDIO_REG_CONFIG_LONG_DELAY); + } + +#endif + + // 6.Wait until the voltage of VCM keeps stable at the AVDD/2. + + // 7.Configure the register SEL_VREF[7:0] reg0x21[7:0] to the appropriate value(except 7’b0000_00000) for reducing power + reg21.reg_data = readl(&audio_codec_reg->reg_21); + reg21.reg_21.sel_vref = 0x02; + writel(reg21.reg_data, &audio_codec_reg->reg_21); + msleep(AUDIO_REG_CONFIG_NORMAL_DELAY); + + //msleep(2000); //delay to prevent ADC from generating noise +} + + +static int audio_reset_micbias(void) +{ + int delay_milli_sec = 100; + //acodec_err_trace("=========audio_reset_micbias:%d ms\n",delay_milli_sec); + reg_20_t reg20; + reg20.reg_data = readl(&audio_codec_reg->reg_20); + reg20.reg_20.en_micbias = 0; + writel(reg20.reg_data, &audio_codec_reg->reg_20); + msleep(delay_milli_sec); + + reg20.reg_data = readl(&audio_codec_reg->reg_20); + reg20.reg_20.en_micbias = 1; + writel(reg20.reg_data, &audio_codec_reg->reg_20); + + return 0; +} + +// #define ACODEC_ADC_CHANNEL 5 +// static bool g_hp_insert_det = false; +// static void _check_adc_hp_thread(void *parameter) +// { +// rt_adc_device_t adc_dev; +// rt_uint32_t reg_value = 0; + +// while(1) +// { +// adc_dev = (rt_adc_device_t)rt_device_find("adc"); +// if (adc_dev == RT_NULL) +// { +// acodec_err_trace("rt_device_find adc failed\n"); +// msleep(500); +// continue; +// } +// rt_adc_enable(adc_dev, ACODEC_ADC_CHANNEL); +// break; +// } + +// bool first_check = true; +// while(1) +// { +// reg_value = rt_adc_read(adc_dev, ACODEC_ADC_CHANNEL); +// if (reg_value > 100) +// { +// if (first_check) +// { +// g_hp_insert_det = true; +// audio_codec_adc_hp_work(g_hp_insert_det); +// first_check = false; +// } +// else +// { +// if (!g_hp_insert_det) +// { +// g_hp_insert_det = true; +// audio_codec_adc_hp_work(g_hp_insert_det); +// } +// } +// } +// else +// { +// if (first_check) +// { +// g_hp_insert_det = false; +// audio_codec_adc_hp_work(g_hp_insert_det); +// first_check = false; +// } +// else +// { +// if (g_hp_insert_det) +// { +// g_hp_insert_det = false; +// audio_codec_adc_hp_work(g_hp_insert_det); +// } +// } +// } +// msleep(500); +// } +// } + +static volatile bool g_adc_sound_init = false; +void audio_codec_adc_init(k_i2s_work_mode mode, unsigned int i2s_ws) +{ + reg_24_t reg24; + reg_27_t reg27; + reg_20_t reg20; + reg_23_t reg23; + reg_26_t reg26; + reg_25_t reg25; + reg_28_t reg28; + reg_4_t reg4; + //reg_8_t reg8; + //reg_9_t reg9; + //reg_2_t reg2; + reg_0_t reg0; + + // 1.Configure the register MUTE_MICL 0x24[7] to 1, to end the mute station of the left ADC channel. + reg24.reg_data = readl(&audio_codec_reg->reg_24); + reg24.reg_24.mute_micl = 1; + writel(reg24.reg_data, &audio_codec_reg->reg_24); + msleep(AUDIO_REG_CONFIG_LONG2_DELAY); + + // 1.Configure the register MUTE_MICR 0x27[7] to 1, to end the mute station of the left ADC channel + reg27.reg_data = readl(&audio_codec_reg->reg_27); + reg27.reg_27.mute_micr = 1; + writel(reg27.reg_data, &audio_codec_reg->reg_27); + msleep(AUDIO_REG_CONFIG_LONG2_DELAY); + + // 2.Configure the register EN_IBIAS_ADC 0x20[5] to 1, to enable the current source of ADC. + reg20.reg_data = readl(&audio_codec_reg->reg_20); + reg20.reg_20.en_ibias_adc = 1; + reg20.reg_20.en_micbias = 1; + writel(reg20.reg_data, &audio_codec_reg->reg_20); + msleep(AUDIO_REG_CONFIG_LONG2_DELAY); + + // 3.Configure the register EN_BUF_ADCL 0x23[7] to 1, to enable the reference voltage buffer in ADC left channel. + reg23.reg_data = readl(&audio_codec_reg->reg_23); + reg23.reg_23.en_buf_adcl = 1; + writel(reg23.reg_data, &audio_codec_reg->reg_23); + msleep(AUDIO_REG_CONFIG_NORMAL_DELAY); + + // 3.Configure the register EN_BUF_ADCR 0x26[7] to 1, to enable the reference voltage buffer in ADC right channel. + reg26.reg_data = readl(&audio_codec_reg->reg_26); + reg26.reg_26.en_buf_adcr = 1; + writel(reg26.reg_data, &audio_codec_reg->reg_26); + msleep(AUDIO_REG_CONFIG_NORMAL_DELAY); + + // 4.Configure the register EN_MICL 0x23[6] to 1, to enable the MIC module in ADC left channel. + reg23.reg_data = readl(&audio_codec_reg->reg_23); + reg23.reg_23.en_micl = 1; + writel(reg23.reg_data, &audio_codec_reg->reg_23); + msleep(AUDIO_REG_CONFIG_NORMAL_DELAY); + + // 4.Configure the register EN_MICR 0x26[6] to 1, to enable the MIC module in ADC right channel. + reg26.reg_data = readl(&audio_codec_reg->reg_26); + reg26.reg_26.en_micr = 1; + writel(reg26.reg_data, &audio_codec_reg->reg_26); + msleep(AUDIO_REG_CONFIG_NORMAL_DELAY); + + // 5.Configure the register EN_ALCL 0x23[5] to 1, to enable the ALC module in ADC left channel + reg23.reg_data = readl(&audio_codec_reg->reg_23); + reg23.reg_23.en_alcl = 1; + writel(reg23.reg_data, &audio_codec_reg->reg_23); + msleep(AUDIO_REG_CONFIG_NORMAL_DELAY); + + // 5.Configure the register EN_ALCR 0x26[5] to 1, to enable the ALC module in ADC right channel. + reg26.reg_data = readl(&audio_codec_reg->reg_26); + reg26.reg_26.en_alcr = 1; + writel(reg26.reg_data, &audio_codec_reg->reg_26); + msleep(AUDIO_REG_CONFIG_NORMAL_DELAY); + + // 6.Configure the register EN_CLK_ADCL 0x23[4] to 1, to enable the clock module in ADC left channel. + reg23.reg_data = readl(&audio_codec_reg->reg_23); + reg23.reg_23.en_clk_adcl = 1; + writel(reg23.reg_data, &audio_codec_reg->reg_23); + msleep(AUDIO_REG_CONFIG_NORMAL_DELAY); + + // 6.Configure the register EN_CLK_ADCR 0x26[4] to 1, to enable the clock module in ADC right channel. + reg26.reg_data = readl(&audio_codec_reg->reg_26); + reg26.reg_26.en_clk_adcr = 1; + writel(reg26.reg_data, &audio_codec_reg->reg_26); + msleep(AUDIO_REG_CONFIG_NORMAL_DELAY); + + // 7.Configure the register EN_ADCL 0x23[3] to 1, to enable the ADC module in ADC left channel. + reg23.reg_data = readl(&audio_codec_reg->reg_23); + reg23.reg_23.en_adcl = 1; + writel(reg23.reg_data, &audio_codec_reg->reg_23); + msleep(AUDIO_REG_CONFIG_NORMAL_DELAY); + + // 7.Configure the register EN_ADCR 0x26[3] to 1, to enable the ADC module in ADC right channel. + reg26.reg_data = readl(&audio_codec_reg->reg_26); + reg26.reg_26.en_adcr = 1; + writel(reg26.reg_data, &audio_codec_reg->reg_26); + msleep(AUDIO_REG_CONFIG_NORMAL_DELAY); + + ////////////////////////////////////////////////////////////////////////////////////////////// + // i2s rx word length + + reg4.reg_data = readl(&audio_codec_reg->reg_04); + if (i2s_ws == 24) + { + // reg4.reg_4.i2s_rx_wl = 3;//32 + reg4.reg_4.i2s_rx_wl = 2; // 24 + } + else if (i2s_ws == 16) + { + reg4.reg_4.i2s_rx_wl = 0; // 16 + } + else if (i2s_ws == 32) + { + reg4.reg_4.i2s_rx_wl = 3;//32 + } + + writel(reg4.reg_data, &audio_codec_reg->reg_04); + msleep(AUDIO_REG_CONFIG_NORMAL_DELAY); + + // i2s rx fmt + reg4.reg_data = readl(&audio_codec_reg->reg_04); + if (K_STANDARD_MODE == mode) + { + reg4.reg_4.i2s_rx_fmt = 2; + } + else if (K_RIGHT_JUSTIFYING_MODE == mode) + { + reg4.reg_4.i2s_rx_fmt = 0; + } + else if (K_LEFT_JUSTIFYING_MODE == mode) + { + reg4.reg_4.i2s_rx_fmt = 1; + } + + writel(reg4.reg_data, &audio_codec_reg->reg_04); + msleep(AUDIO_REG_CONFIG_NORMAL_DELAY); + +#if AUDIO_ADC_MIX + reg2.reg_data = readl(&audio_codec_reg->reg_02); + + // reg2.reg_2.i2s_tx_datsel = 2;//mix right data + reg2.reg_2.i2s_tx_datsel = 1; // mix left data + writel(reg2.reg_data, &audio_codec_reg->reg_02); + msleep(AUDIO_REG_CONFIG_NORMAL_DELAY); + +#endif + + ///////////////////////////////////////////////////////////////////////////////////////////////// + + // 8.Configure the register INITIAL_ADCL 0x23[2] to 1, to end the initialization of the ADCL module. + reg23.reg_data = readl(&audio_codec_reg->reg_23); + reg23.reg_23.initial_adcl = 1; + writel(reg23.reg_data, &audio_codec_reg->reg_23); + msleep(AUDIO_REG_CONFIG_NORMAL_DELAY); + + // 8.Configure the register INITIAL_ADCR 0x26[2] to 1, to end the initialization of the ADCR module. + reg26.reg_data = readl(&audio_codec_reg->reg_26); + reg26.reg_26.initial_adcr = 1; + writel(reg26.reg_data, &audio_codec_reg->reg_26); + msleep(AUDIO_REG_CONFIG_NORMAL_DELAY); + + // 9.Configure the register INITIAL_ALCL 0x23[1] to 1, to end the initialization of the left ALC module. + reg23.reg_data = readl(&audio_codec_reg->reg_23); + reg23.reg_23.initial_alcl = 1; + writel(reg23.reg_data, &audio_codec_reg->reg_23); + msleep(AUDIO_REG_CONFIG_NORMAL_DELAY); + + // 9.Configure the register INITIAL_ALCR 0x26[1] to 1, to end the initialization of the right ALC module. + reg26.reg_data = readl(&audio_codec_reg->reg_26); + reg26.reg_26.initial_alcr = 1; + writel(reg26.reg_data, &audio_codec_reg->reg_26); + msleep(AUDIO_REG_CONFIG_NORMAL_DELAY); + + // 10.Configure the register INITIAL_MICL 0x23[0] to 1, to end the initialization of theleft MIC module. + reg23.reg_data = readl(&audio_codec_reg->reg_23); + reg23.reg_23.initial_micl = 1; + writel(reg23.reg_data, &audio_codec_reg->reg_23); + msleep(AUDIO_REG_CONFIG_NORMAL_DELAY); + + // 10.Configure the register INITIAL_MICR 0x26[0] to 1, to end the initialization of the right MIC module. + reg26.reg_data = readl(&audio_codec_reg->reg_26); + reg26.reg_26.initial_micr = 1; + writel(reg26.reg_data, &audio_codec_reg->reg_26); + msleep(AUDIO_REG_CONFIG_NORMAL_DELAY); + +#if 0 + //11.Configure the register MUTE_MICL 0x24[7] to 1, to end the mute station of the ADC left channel. + reg24.reg_data = readl(&audio_codec_reg->reg_24); + reg24.reg_24.mute_micl = 1; + writel(reg24.reg_data,&audio_codec_reg->reg_24); + + //11.Configure the register MUTE_MICR 0x27[7] to 1, to end the mute station of the ADC right channel. + reg27.reg_data = readl(&audio_codec_reg->reg_27); + reg27.reg_27.mute_micr = 1; + writel(reg27.reg_data,&audio_codec_reg->reg_27); +#endif + + if (!g_adc_sound_init) + { + _reset_adc_sound(); + g_adc_sound_init = true; + } + + // 14.Configure the register EN_ZeroDET_ADCL 0x25[1] to 1, to enable the zerocrossing detection function in ADC left channel. + reg25.reg_data = readl(&audio_codec_reg->reg_25); + reg25.reg_25.en_zerodet_adcl = 1; + writel(reg25.reg_data, &audio_codec_reg->reg_25); + msleep(AUDIO_REG_CONFIG_NORMAL_DELAY); + + // 14.Configure the register EN_ZeroDET_ADCR 0x28[1] to 1, to enable the zerocrossing detection function in ADC right channel + reg28.reg_data = readl(&audio_codec_reg->reg_28); + reg28.reg_28.en_zerodet_adcr = 1; + writel(reg28.reg_data, &audio_codec_reg->reg_28); + msleep(AUDIO_REG_CONFIG_NORMAL_DELAY); + + reg0.reg_data = readl(&audio_codec_reg->reg_00); + reg0.reg_0.digcore_bstn = 1; + writel(reg0.reg_data, &audio_codec_reg->reg_00); + msleep(AUDIO_REG_CONFIG_NORMAL_DELAY); + + // if (0 == g_check_hp_thread) + // { + // g_check_hp_thread = rt_thread_create("acodec_hp", _check_adc_hp_thread, RT_NULL, 1024*10, 10,10); + // rt_thread_startup(g_check_hp_thread); + // } + + audio_reset_micbias(); +} + +static volatile bool g_dac_sound_init = false; +void audio_codec_dac_init(k_i2s_work_mode mode, unsigned int i2s_ws) +{ + reg_20_t reg20; + reg_29_t reg29; + reg_2c_t reg2c; + reg_2a_t reg2a; + reg_2d_t reg2d; + //reg_2b_t reg2b; + //reg_2e_t reg2e; + reg_2_t reg2; + reg_3_t reg3; + //reg_1_t reg1; + //reg_6_t reg6; + //reg_7_t reg7; + reg_0_t reg0; + + // 1.Configure the register EN_IBIAS_DAC 0x20[4] to 1, to enable the current source of DAC. + reg20.reg_data = readl(&audio_codec_reg->reg_20); + reg20.reg_20.en_ibias_dac = 1; + writel(reg20.reg_data, &audio_codec_reg->reg_20); + msleep(AUDIO_REG_CONFIG_LONG2_DELAY); + + // 2.Configure the register EN_BUF_DACL 0x29[6] to 1, to enable the reference voltage buffer of the DAC left channel + reg29.reg_data = readl(&audio_codec_reg->reg_29); + reg29.reg_29.en_buf_dacl = 1; + writel(reg29.reg_data, &audio_codec_reg->reg_29); + msleep(AUDIO_REG_CONFIG_NORMAL_DELAY); + + // 2.Configure the register EN_BUF_DACR 0x2c[6] to 1, to enable the reference voltage buffer of the DAC right channel. + reg2c.reg_data = readl(&audio_codec_reg->reg_2c); + reg2c.reg_2c.en_buf_dacr = 1; + writel(reg2c.reg_data, &audio_codec_reg->reg_2c); + msleep(AUDIO_REG_CONFIG_NORMAL_DELAY); + + // 3.Configure the register POP_CTRL_DACL<1:0> 0x29[5:4] to 2’b10, to enable POP sound in the DAC left channel. + reg29.reg_data = readl(&audio_codec_reg->reg_29); + reg29.reg_29.pop_ctrl_dacl = 2; + writel(reg29.reg_data, &audio_codec_reg->reg_29); + msleep(AUDIO_REG_CONFIG_NORMAL_DELAY); + + // 3.Configure the register POP_CTRL_DACR<1:0> 0x2c[5:4] to 2’b10, to enable POP sound in the DAC right channel + reg2c.reg_data = readl(&audio_codec_reg->reg_2c); + reg2c.reg_2c.pop_ctrl_dacr = 2; + writel(reg2c.reg_data, &audio_codec_reg->reg_2c); + msleep(AUDIO_REG_CONFIG_NORMAL_DELAY); + + // 4.Configure the register EN_HPOUTL 0x2a[5] to 1, to enable the HPDRV module in the DAC left channel + reg2a.reg_data = readl(&audio_codec_reg->reg_2a); + reg2a.reg_2a.en_hpoutl = 1; + writel(reg2a.reg_data, &audio_codec_reg->reg_2a); + msleep(AUDIO_REG_CONFIG_NORMAL_DELAY); + + // 4.Configure the register EN_HPOUTR 0x2d[5] to 1, to enable the HPDRV module in the DAC right channel + reg2d.reg_data = readl(&audio_codec_reg->reg_2d); + reg2d.reg_2d.en_hpoutr = 1; + writel(reg2d.reg_data, &audio_codec_reg->reg_2d); + msleep(AUDIO_REG_CONFIG_NORMAL_DELAY); + +#if AUDIO_ENABLE_BIST + reg7.reg_data = readl(&audio_codec_reg->reg_07); + reg7.reg_7.dacl_bist_sel = 1; + reg7.reg_7.dacr_bist_sel = 1; + writel(reg7.reg_data, &audio_codec_reg->reg_07); + msleep(AUDIO_REG_CONFIG_NORMAL_DELAY); +#endif + + // 5.Configure the register INITIAL_HPOUTL 0x2a[4] to 1, to end the initialization of the HPDRV module in the DAC left channel. + reg2a.reg_data = readl(&audio_codec_reg->reg_2a); + reg2a.reg_2a.initial_hpoutl = 1; + writel(reg2a.reg_data, &audio_codec_reg->reg_2a); + msleep(AUDIO_REG_CONFIG_NORMAL_DELAY); + + // 5.Configure the register INITIAL_HPOUTR 0x2d[4] to 1, to end the initialization of the HPDRV module in the DAC right channel. + reg2d.reg_data = readl(&audio_codec_reg->reg_2d); + reg2d.reg_2d.initial_hpoutr = 1; + writel(reg2d.reg_data, &audio_codec_reg->reg_2d); + msleep(AUDIO_REG_CONFIG_NORMAL_DELAY); + + // 6.Configure the register EN_VREF_DACL 0x29[3] to 1, to enable the reference voltage of DACL module + reg29.reg_data = readl(&audio_codec_reg->reg_29); + reg29.reg_29.en_vref_dacl = 1; + writel(reg29.reg_data, &audio_codec_reg->reg_29); + msleep(AUDIO_REG_CONFIG_NORMAL_DELAY); + + // 6.Configure the register EN_VREF_DACR 0x2c[3] to 1, to enable the reference voltage of DACR module. + reg2c.reg_data = readl(&audio_codec_reg->reg_2c); + reg2c.reg_2c.en_vref_dacr = 1; + writel(reg2c.reg_data, &audio_codec_reg->reg_2c); + msleep(AUDIO_REG_CONFIG_NORMAL_DELAY); + + // 7.Configure the register EN_CLK_DACL 0x29[2] to 1, to enable the clock module of DACL module. + reg29.reg_data = readl(&audio_codec_reg->reg_29); + reg29.reg_29.en_clk_dacl = 1; + writel(reg29.reg_data, &audio_codec_reg->reg_29); + msleep(AUDIO_REG_CONFIG_NORMAL_DELAY); + + // 7.Configure the register EN_CLK_DACR 0x2c[2] to 1, to enable the clock module of DACR module. + reg2c.reg_data = readl(&audio_codec_reg->reg_2c); + reg2c.reg_2c.en_clk_dacr = 1; + writel(reg2c.reg_data, &audio_codec_reg->reg_2c); + msleep(AUDIO_REG_CONFIG_NORMAL_DELAY); + + // 8.Configure the register EN_DACL 0x29[1] to 1, to enable the DACL module. + reg29.reg_data = readl(&audio_codec_reg->reg_29); + reg29.reg_29.en_dacl = 1; + writel(reg29.reg_data, &audio_codec_reg->reg_29); + msleep(AUDIO_REG_CONFIG_NORMAL_DELAY); + + // 8.Configure the register EN_DACR 0x2c[1] to 1, to enable the DACR module. + reg2c.reg_data = readl(&audio_codec_reg->reg_2c); + reg2c.reg_2c.en_dacr = 1; + writel(reg2c.reg_data, &audio_codec_reg->reg_2c); + msleep(AUDIO_REG_CONFIG_NORMAL_DELAY); + + ////////////////////////////////////////////////////////////////////////////////////////////// + // i2s tx word length + reg2.reg_data = readl(&audio_codec_reg->reg_02); + + if (i2s_ws == 24) + { + // reg2.reg_2.i2s_tx_wl = 3;//valid 32 bit + reg2.reg_2.i2s_tx_wl = 2; // valid 24 bit + } + else if (i2s_ws == 16) + { + reg2.reg_2.i2s_tx_wl = 0; // valid 16 bit + } + else if (i2s_ws == 32) + { + reg2.reg_2.i2s_tx_wl = 3;//valid 32 bit + } + + //reg2.reg_2.i2s_tx_datsel = 2; + writel(reg2.reg_data, &audio_codec_reg->reg_02); + msleep(AUDIO_REG_CONFIG_NORMAL_DELAY); + + // i2s tx fmt + reg2.reg_data = readl(&audio_codec_reg->reg_02); + if (K_STANDARD_MODE == mode) + { + reg2.reg_2.i2s_tx_fmt = 2; + } + else if (K_RIGHT_JUSTIFYING_MODE == mode) + { + reg2.reg_2.i2s_tx_fmt = 0; + } + else if (K_LEFT_JUSTIFYING_MODE == mode) + { + reg2.reg_2.i2s_tx_fmt = 1; + } + + writel(reg2.reg_data, &audio_codec_reg->reg_02); + msleep(AUDIO_REG_CONFIG_NORMAL_DELAY); + + reg3.reg_data = readl(&audio_codec_reg->reg_03); + reg3.reg_3.i2s_tx_len = 3; // 32 bit + writel(reg3.reg_data, &audio_codec_reg->reg_03); + msleep(AUDIO_REG_CONFIG_NORMAL_DELAY); + + ///////////////////////////////////////////////////////////////////////////////////////////////// + + // 9.Configure the register INITIAL_DACL 0x29[0] to 1, to end the initialization of the DACL module. + reg29.reg_data = readl(&audio_codec_reg->reg_29); + reg29.reg_29.initial_dacl = 1; + writel(reg29.reg_data, &audio_codec_reg->reg_29); + msleep(AUDIO_REG_CONFIG_NORMAL_DELAY); + + // 9.Configure the register INITIAL_DACR 0x2c[0] to 1, to end the initialization of the DACR module. + reg2c.reg_data = readl(&audio_codec_reg->reg_2c); + reg2c.reg_2c.initial_dacr = 1; + writel(reg2c.reg_data, &audio_codec_reg->reg_2c); + msleep(AUDIO_REG_CONFIG_NORMAL_DELAY); + + // 10.Configure the register MUTE_HPOUTL 0x29[7] to 1, to end the mute station of the HPDRV module in the DAC left channel + reg29.reg_data = readl(&audio_codec_reg->reg_29); + reg29.reg_29.mute_hpoutl = 1; + writel(reg29.reg_data, &audio_codec_reg->reg_29); + msleep(AUDIO_REG_CONFIG_NORMAL_DELAY); + + // 10.Configure the register MUTE_HPOUTR 0x2c[7] to 1, to end the mute station of the HPDRV module in the DAC right channel. + reg2c.reg_data = readl(&audio_codec_reg->reg_2c); + reg2c.reg_2c.mute_hpoutr = 1; + writel(reg2c.reg_data, &audio_codec_reg->reg_2c); + msleep(AUDIO_REG_CONFIG_NORMAL_DELAY); + + if (!g_dac_sound_init) + { + _reset_dac_sound(); + g_dac_sound_init = true; + } + + reg0.reg_data = readl(&audio_codec_reg->reg_00); + reg0.reg_0.digcore_bstn = 1; + writel(reg0.reg_data, &audio_codec_reg->reg_00); + msleep(AUDIO_REG_CONFIG_NORMAL_DELAY); + +} + +static int convert_adc_gain(unsigned int gain, unsigned int *reg_val) +{ + unsigned int reg_value = 0; + if (gain > 30 || gain < 0) + { + return -1; + } + + if (0 == gain) + { + reg_value = 0; + } + else if (gain >0 && gain <= 10) + { + reg_value = 1; + } + else if (gain > 10 && gain <= 20) + { + reg_value = 2; + } + else if (gain > 20 && gain <= 30) + { + reg_value = 3; + } + + *reg_val = reg_value; + + return 0; +} + +static int convert_adc_gain_2(unsigned int reg_val, unsigned int *gain) +{ + unsigned int gain_value = 0; + + if (reg_val > 3 || reg_val < 0) + { + return -1; + } + + if (0 == reg_val) + { + gain_value = 0; + } + else if (1 == reg_val) + { + gain_value = 6; + } + else if (2 == reg_val) + { + gain_value = 20; + } + else if (3 == reg_val) + { + gain_value = 30; + } + + *gain = gain_value; + + return 0; +} + +int audio_codec_adc_set_micl_gain(int gain) +{ + reg_24_t reg24; + unsigned int reg_val = 0; + + printk("audio_codec_adc_set_micl_gain gain:%d\n", gain); + if (0 != convert_adc_gain(gain, ®_val)) + { + return -1; + } + + reg24.reg_data = readl(&audio_codec_reg->reg_24); + reg24.reg_24.gain_micl = reg_val; + writel(reg24.reg_data, &audio_codec_reg->reg_24); + g_snd_current_values.gain_micl = reg_val; + msleep(AUDIO_REG_CONFIG_NORMAL_DELAY); + return 0; +} + +int audio_codec_adc_set_micr_gain(int gain) +{ + unsigned int reg_val = 0; + reg_27_t reg27; + + printk("audio_codec_adc_set_micr_gain gain:%d\n", gain); + if (0 != convert_adc_gain(gain, ®_val)) + { + return -1; + } + + reg27.reg_data = readl(&audio_codec_reg->reg_27); + reg27.reg_27.gain_micr = reg_val; + writel(reg27.reg_data, &audio_codec_reg->reg_27); + g_snd_current_values.gain_micr = reg_val; + msleep(AUDIO_REG_CONFIG_NORMAL_DELAY); + + return 0; +} + +int audio_codec_adc_get_micl_gain(int* gain) +{ + return convert_adc_gain_2(g_snd_current_values.gain_micl,gain); +} + + +int audio_codec_adc_get_micr_gain( int* gain) +{ + return convert_adc_gain_2(g_snd_current_values.gain_micr,gain); +} + + +// static int convert_adc_volume(float volume, unsigned int *reg_val) +// { +// unsigned int reg_value = 0; +// float start_gain = -97; +// bool bfind = false; +// int i = 0; + +// if (volume < -97 || volume > 30) +// { +// return -1; +// } + +// for (i = 0x1; i <= 0xff; i++) +// { +// if (volume == start_gain) +// { +// reg_value = i; +// bfind = true; +// break; +// } +// else +// { +// start_gain += 0.5; +// } +// } +// if (!bfind) +// { +// return -1; +// } + +// *reg_val = reg_value; + +// return 0; +// } + +// static int convert_adc_volume2(unsigned int reg_val,float* volume) +// { +// float start_gain = -97; +// bool bfind = false; +// int i =0; + +// if (reg_val < 1 || reg_val > 0xff) +// { +// return -1; +// } + +// for (i = 0x1; i <= 0xff; i++) +// { +// if (reg_val == i) +// { +// bfind = true; +// break; +// } +// else +// { +// start_gain += 0.5; +// } +// } +// if (!bfind) +// { +// return -1; +// } + +// *volume = start_gain; + +// return 0; +// } + +// int audio_codec_adcl_set_volume(float volume) +// { +// unsigned int reg_val = 0; +// reg_8_t reg8; + +// if (0 != convert_adc_volume(volume, ®_val)) +// { +// return -1; +// } + +// reg8.reg_data = readl(&audio_codec_reg->reg_08); +// reg8.reg_8.adcl_vol = reg_val; +// writel(reg8.reg_data, &audio_codec_reg->reg_08); +// g_snd_current_values.adc_volumel = reg_val; +// msleep(AUDIO_REG_CONFIG_NORMAL_DELAY); +// return 0; +// } +// int audio_codec_adcr_set_volume(float volume) +// { +// unsigned int reg_val = 0; +// reg_9_t reg9; + +// if (0 != convert_adc_volume(volume, ®_val)) +// { +// return -1; +// } + +// reg9.reg_data = readl(&audio_codec_reg->reg_09); +// reg9.reg_9.adcr_vol = reg_val; +// writel(reg9.reg_data, &audio_codec_reg->reg_09); +// g_snd_current_values.adc_volumer = reg_val; +// msleep(AUDIO_REG_CONFIG_NORMAL_DELAY); +// return 0; +// } + +// int audio_codec_adcl_get_volume(float* volume) +// { +// return convert_adc_volume2(g_snd_current_values.adc_volumel, volume); +// } +// int audio_codec_adcr_get_volume(float* volume) +// { +// return convert_adc_volume2(g_snd_current_values.adc_volumer, volume); +// } + +int audio_codec_adc_micl_mute(bool mute) +{ + reg_24_t reg24; + + printk("audio_codec_adc_micl_mute mute:%d\n", mute); + reg24.reg_data = readl(&audio_codec_reg->reg_24); + reg24.reg_24.mute_micl = mute ? 0 : 1; + writel(reg24.reg_data, &audio_codec_reg->reg_24); + msleep(AUDIO_REG_CONFIG_NORMAL_DELAY); + + g_adc_left_mute = mute; + return 0; +} + +int audio_codec_adc_micr_mute(bool mute) +{ + reg_27_t reg27; + printk("audio_codec_adc_micr_mute mute:%d\n", mute); + reg27.reg_data = readl(&audio_codec_reg->reg_27); + reg27.reg_27.mute_micr = mute ? 0 : 1; + writel(reg27.reg_data, &audio_codec_reg->reg_27); + msleep(AUDIO_REG_CONFIG_NORMAL_DELAY); + + g_adc_right_mute = mute; + return 0; +} + +int audio_codec_adc_get_micl_mute(bool* mute) +{ + *mute = g_adc_left_mute; + return 0; +} + +int audio_codec_adc_get_micr_mute(bool* mute) +{ + *mute = g_adc_right_mute; + return 0; +} + +// static int convert_alc_gain(float gain, unsigned int *reg_val) +// { +// unsigned int reg_value = 0; +// float start_gain = -18; +// bool bfind = false; +// int i = 0; + +// if (gain < -18 || gain > 28.5) +// { +// return -1; +// } + +// for (i = 0; i <= 0x1f; i++) +// { +// if (gain == start_gain) +// { +// reg_value = i; +// bfind = true; +// break; +// } +// else +// { +// start_gain += 1.5; +// } +// } +// if (!bfind) +// { +// return -1; +// } + +// *reg_val = reg_value; + +// return 0; +// } + +// static int convert_alc_gain2(unsigned int reg_val,float* gain) +// { +// float start_gain = -18; +// bool bfind = false; +// int i =0; + +// if (reg_val < 0 || reg_val > 0x1f) +// { +// return -1; +// } + +// for (i = 0; i <= 0x1f; i++) +// { +// if (reg_val == i) +// { +// bfind = true; +// break; +// } +// else +// { +// start_gain += 1.5; +// } +// } +// if (!bfind) +// { +// return -1; +// } + +// *gain = start_gain; + +// return 0; +// } + +// int audio_codec_alc_set_micl_gain(float gain) +// { +// unsigned int reg_val = 0; +// reg_24_t reg24; +// //acodec_err_trace("audio_codec_alc_set_micl_gain gain:%.2f\n", gain); + +// if (0 != convert_alc_gain(gain, ®_val)) +// { +// return -1; +// } + +// reg24.reg_data = readl(&audio_codec_reg->reg_24); +// reg24.reg_24.gain_alcl = reg_val; +// writel(reg24.reg_data, &audio_codec_reg->reg_24); +// g_snd_current_values.gain_alcl = reg_val; +// msleep(AUDIO_REG_CONFIG_NORMAL_DELAY); + +// return 0; +// } + +// int audio_codec_alc_set_micr_gain(float gain) +// { +// reg_27_t reg27; +// int reg_val = 0; +// //acodec_err_trace("audio_codec_alc_set_micr_gain gain:%.2f\n", gain); + +// if (0 != convert_alc_gain(gain, ®_val)) +// { +// return -1; +// } + +// reg27.reg_data = readl(&audio_codec_reg->reg_27); +// reg27.reg_27.gain_alcr = reg_val; +// writel(reg27.reg_data, &audio_codec_reg->reg_27); +// g_snd_current_values.gain_alcr = reg_val; +// msleep(AUDIO_REG_CONFIG_NORMAL_DELAY); + +// return 0; +// } + +// int audio_codec_alc_get_micl_gain(float* gain) +// { +// return convert_alc_gain2(g_snd_current_values.gain_alcl, gain); +// } + +// int audio_codec_alc_get_micr_gain(float* gain) +// { +// return convert_alc_gain2(g_snd_current_values.gain_alcr, gain); +// } + +static int convert_dac_gain(int gain, unsigned int *reg_val) +{ + unsigned int reg_value = 0; + int start_gain = -39; + bool bfind = false; + int i = 0; + + if (gain < -39 || gain > 6) + { + return -1; + } + + for (i = 0; i <= 0x1e; i+=2) // 11111/11110 both 6db + { + if (gain <= start_gain) + { + reg_value = i; + bfind = true; + break; + } + else + { + start_gain += 3; + } + } + if (!bfind) + { + return -1; + } + + *reg_val = reg_value; + + return 0; +} + +static int convert_dac_gain2(unsigned int reg_val,int* gain) +{ + int start_gain = -39; + bool bfind = false; + int i = 0; + + if (reg_val < 0 || reg_val > 0x1e) + { + return -1; + } + + for (i = 0; i <= 0x1e; i+=2) // 11111/11110 both 6db + { + if (reg_val == i) + { + bfind = true; + break; + } + else + { + start_gain += 3; + } + } + if (!bfind) + { + return -1; + } + + *gain = start_gain; + + return 0; +} + +int audio_codec_dac_set_hpoutl_gain(int gain) +{ + unsigned int reg_val = 0; + reg_2b_t reg2b; + + if (0 != convert_dac_gain(gain, ®_val)) + { + return -1; + } + + reg2b.reg_data = readl(&audio_codec_reg->reg_2b); + + reg2b.reg_2b.gain_hpoutl = reg_val; + writel(reg2b.reg_data, &audio_codec_reg->reg_2b); + g_snd_current_values.gain_hpoutl = reg_val; + msleep(AUDIO_REG_CONFIG_NORMAL_DELAY); + return 0; +} + +int audio_codec_dac_set_hpoutr_gain(int gain) +{ + unsigned int reg_val = 0; + reg_2e_t reg2e; + + if (0 != convert_dac_gain(gain, ®_val)) + { + return -1; + } + + reg2e.reg_data = readl(&audio_codec_reg->reg_2e); + reg2e.reg_2e.gain_hpoutr = reg_val; // 最大db值 + writel(reg2e.reg_data, &audio_codec_reg->reg_2e); + g_snd_current_values.gain_hpoutr = reg_val; + msleep(AUDIO_REG_CONFIG_NORMAL_DELAY); + return 0; +} + +int audio_codec_dac_get_hpoutl_gain(int* gain) +{ + return convert_dac_gain2( g_snd_current_values.gain_hpoutl,gain); +} + +int audio_codec_dac_get_hpoutr_gain(int* gain) +{ + return convert_dac_gain2( g_snd_current_values.gain_hpoutr,gain); +} + +int audio_codec_dac_hpoutl_mute(bool mute) +{ + reg_29_t reg29; + reg_2a_t reg2a; + + reg29.reg_data = readl(&audio_codec_reg->reg_29); + reg29.reg_29.mute_hpoutl = mute ? 0 : 1; + writel(reg29.reg_data, &audio_codec_reg->reg_29); + msleep(AUDIO_REG_CONFIG_NORMAL_DELAY); + + // MUTE的同时将INITIAL_HPOUTL也置0即可(gain 无需修改) + reg2a.reg_data = readl(&audio_codec_reg->reg_2a); + reg2a.reg_2a.initial_hpoutl = mute ? 0 : 1; + writel(reg2a.reg_data, &audio_codec_reg->reg_2a); + msleep(AUDIO_REG_CONFIG_NORMAL_DELAY); + + g_dac_left_mute = mute; + return 0; +} +int audio_codec_dac_hpoutr_mute(bool mute) +{ + reg_2c_t reg2c; + reg_2d_t reg2d; + + reg2c.reg_data = readl(&audio_codec_reg->reg_2c); + reg2c.reg_2c.mute_hpoutr = mute ? 0 : 1; + writel(reg2c.reg_data, &audio_codec_reg->reg_2c); + msleep(AUDIO_REG_CONFIG_NORMAL_DELAY); + + // MUTE的同时将INITIAL_HPOUTR也置0即可(gain 无需修改) + reg2d.reg_data = readl(&audio_codec_reg->reg_2d); + reg2d.reg_2d.initial_hpoutr = mute ? 0 : 1; + writel(reg2d.reg_data, &audio_codec_reg->reg_2d); + msleep(AUDIO_REG_CONFIG_NORMAL_DELAY); + + g_dac_right_mute = mute; + return 0; +} + +int audio_codec_dac_get_hpoutl_mute(bool* mute) +{ + *mute = g_dac_left_mute; + return 0; +} + +int audio_codec_dac_get_hpoutr_mute(bool* mute) +{ + *mute = g_dac_left_mute; + return 0; +} + +// static int convert_dac_volume(float gain, unsigned int *reg_val) +// { +// unsigned int reg_value = 0; +// float start_gain = -120; +// bool bfind = false; +// int i = 0 ; + +// if (gain < -120 || gain > 7) +// { +// return -1; +// } + +// for (i = 1; i <= 0xff; i++) +// { +// if (gain == start_gain) +// { +// reg_value = i; +// bfind = true; +// break; +// } +// else +// { +// start_gain += 0.5; +// } +// } +// if (!bfind) +// { +// return -1; +// } + +// *reg_val = reg_value; + +// return 0; +// } + +// static int convert_dac_volume2(unsigned int reg_val,float* gain) +// { +// float start_gain = -120; +// bool bfind = false; +// int i = 0; + +// if (reg_val < 1 || reg_val > 0xff) +// { +// return -1; +// } + +// for (i = 1; i <= 0xff; i++) +// { +// if (i == reg_val) +// { +// bfind = true; +// break; +// } +// else +// { +// start_gain += 0.5; +// } +// } +// if (!bfind) +// { +// return -1; +// } + +// *gain = start_gain; + +// return 0; +// } + +// int audio_codec_dacl_set_volume(float volume) +// { +// unsigned int reg_val = 0; +// reg_6_t reg6; + +// if (0 != convert_dac_volume(volume, ®_val)) +// { +// return -1; +// } + +// reg6.reg_data = readl(&audio_codec_reg->reg_06); +// reg6.reg_6.dac_vol = reg_val; +// writel(reg6.reg_data, &audio_codec_reg->reg_06); +// g_snd_current_values.dac_volumel = reg_val; +// g_snd_current_values.dac_volumer = reg_val; +// msleep(AUDIO_REG_CONFIG_NORMAL_DELAY); + +// return 0; +// } +// int audio_codec_dacr_set_volume(float volume) +// { +// unsigned int reg_val = 0; +// reg_6_t reg6; +// if (0 != convert_dac_volume(volume, ®_val)) +// { +// return -1; +// } + +// reg6.reg_data = readl(&audio_codec_reg->reg_06); +// reg6.reg_6.dac_vol = reg_val; +// writel(reg6.reg_data, &audio_codec_reg->reg_06); +// g_snd_current_values.dac_volumel = reg_val; +// g_snd_current_values.dac_volumer = reg_val; +// msleep(AUDIO_REG_CONFIG_NORMAL_DELAY); + +// return 0; +// } + +// int audio_codec_dacl_get_volume(float* volume) +// { +// return convert_dac_volume2(g_snd_current_values.dac_volumel, volume); +// } + +// int audio_codec_dacr_get_volume(float* volume) +// { +// return convert_dac_volume2(g_snd_current_values.dac_volumer, volume); +// } + +int audio_codec_reset(void) +{ + _reset_adc_sound(); + _reset_dac_sound(); + g_snd_current_values = g_snd_default_values; + return 0; +} + +int audio_codec_adc_hp_work(bool work) +{ + reg_23_t reg23; + + printk("audio_codec_adc_hp_work work:%d\n",work); + reg23.reg_data = readl(&audio_codec_reg->reg_23); + reg23.reg_23.initial_micl = work?1:0; + writel(reg23.reg_data, &audio_codec_reg->reg_23); + msleep(AUDIO_REG_CONFIG_NORMAL_DELAY); + + return 0; +} + diff --git a/sound/soc/codecs/inno_k230_reg.h b/sound/soc/codecs/inno_k230_reg.h new file mode 100644 index 0000000000000..a5f2d2870f9b2 --- /dev/null +++ b/sound/soc/codecs/inno_k230_reg.h @@ -0,0 +1,553 @@ +/* Copyright (c) 2023, Canaan Bright Sight Co., Ltd + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _INNO_K230_REG_H_ +#define _INNO_K230_REG_H_ +#include + +typedef enum +{ + K_STANDARD_MODE = 1, + K_RIGHT_JUSTIFYING_MODE = 2, + K_LEFT_JUSTIFYING_MODE = 4 +} k_i2s_work_mode; + +typedef struct _codec_reg_0 +{ + uint32_t sys_bstn : 1; + uint32_t digcore_bstn : 1; + uint32_t reserved : 5; + uint32_t bist_bstn : 1; + + + /* Bits [31:8] is reseved */ + uint32_t resv : 24; +}__attribute__((packed, aligned(4))) codec_reg_0_t; + +typedef union _reg_0u { + codec_reg_0_t reg_0; + uint32_t reg_data; +} reg_0_t; + + + +typedef struct _codec_reg_1 +{ + uint32_t reserved : 4; + uint32_t dac_mute_sr : 3; + uint32_t dac_mute_en : 1; + + + /* Bits [31:8] is reseved */ + uint32_t resv : 24; +}__attribute__((packed, aligned(4))) codec_reg_1_t; + +typedef union _reg_1u { + codec_reg_1_t reg_1; + uint32_t reg_data; +} reg_1_t; + + + + +typedef struct _codec_reg_2 +{ + uint32_t i2s_tx_datsel : 2; + uint32_t reserved : 1; + uint32_t i2s_tx_fmt : 2; + uint32_t i2s_tx_wl : 2; + uint32_t i2s_tx_lrp : 1; + + /* Bits [31:8] is reseved */ + uint32_t resv : 24; +}__attribute__((packed, aligned(4))) codec_reg_2_t; + +typedef union _reg_2u { + codec_reg_2_t reg_2; + uint32_t reg_data; +} reg_2_t; + + +typedef struct _codec_reg_3 +{ + uint32_t i2s_tx_bclkinv : 1; + uint32_t i2s_tx_rstn : 1; + uint32_t i2s_tx_len : 2; + uint32_t i2s_tx_func_mst : 1; + uint32_t i2s_tx_pin_mst : 1; + uint32_t i2s_rx_func_mst : 1; + uint32_t i2s_rx_pin_mst : 1; + + /* Bits [31:8] is reseved */ + uint32_t resv : 24; +}__attribute__((packed, aligned(4))) codec_reg_3_t; + +typedef union _reg_3u { + codec_reg_3_t reg_3; + uint32_t reg_data; +} reg_3_t; + + +typedef struct _codec_reg_4 +{ + uint32_t reserved : 2; + uint32_t i2s_lr_swap : 1; + uint32_t i2s_rx_fmt : 2; + uint32_t i2s_rx_wl : 2; + uint32_t i2s_rx_lrp : 1; + /* Bits [31:8] is reseved */ + uint32_t resv : 24; +}__attribute__((packed, aligned(4))) codec_reg_4_t; + +typedef union _reg_4u { + codec_reg_4_t reg_4; + uint32_t reg_data; +} reg_4_t; + + + +typedef struct _codec_reg_6 +{ + uint32_t dac_vol : 8; + /* Bits [31:8] is reseved */ + uint32_t resv : 24; +}__attribute__((packed, aligned(4))) codec_reg_6_t; + +typedef union _reg_6u { + codec_reg_6_t reg_6; + uint32_t reg_data; +} reg_6_t; + +typedef struct _codec_reg_7 +{ + + uint32_t reserved : 4; + uint32_t dacl_bist_sel : 2; + uint32_t dacr_bist_sel : 2; + /* Bits [31:8] is reseved */ + uint32_t resv : 24; +}__attribute__((packed, aligned(4))) codec_reg_7_t; + +typedef union _reg_7u { + codec_reg_7_t reg_7; + uint32_t reg_data; +} reg_7_t; + + + +typedef struct _codec_reg_8 +{ + uint32_t adcl_vol : 8; + /* Bits [31:8] is reseved */ + uint32_t resv : 24; +}__attribute__((packed, aligned(4))) codec_reg_8_t; + +typedef union _reg_8u { + codec_reg_8_t reg_8; + uint32_t reg_data; +} reg_8_t; + +typedef struct _codec_reg_9 +{ + uint32_t adcr_vol : 8; + /* Bits [31:8] is reseved */ + uint32_t resv : 24; +}__attribute__((packed, aligned(4))) codec_reg_9_t; + +typedef union _reg_9u { + codec_reg_9_t reg_9; + uint32_t reg_data; +} reg_9_t; + + + + + +typedef struct _codec_reg_20 +{ + uint32_t gain_micbias : 3; + uint32_t en_micbias : 1; + uint32_t en_ibias_dac : 1; + uint32_t en_ibias_adc : 1; + uint32_t en_vref : 1; + uint32_t reserved : 1; + + /* Bits [31:8] is reseved */ + uint32_t resv : 24; +}__attribute__((packed, aligned(4))) codec_reg_20_t; + +typedef union _reg_20u { + codec_reg_20_t reg_20; + uint32_t reg_data; +} reg_20_t; + + + +typedef struct _codec_reg_21 +{ + uint32_t sel_vref : 8; + + /* Bits [31:8] is reseved */ + uint32_t resv : 24; +}__attribute__((packed, aligned(4))) codec_reg_21_t; + +typedef union _reg_21u { + codec_reg_21_t reg_21; + uint32_t reg_data; +} reg_21_t; + + +typedef struct _codec_reg_23 +{ + uint32_t initial_micl : 1; + uint32_t initial_alcl : 1; + uint32_t initial_adcl : 1; + uint32_t en_adcl : 1; + uint32_t en_clk_adcl : 1; + uint32_t en_alcl : 1; + uint32_t en_micl : 1; + uint32_t en_buf_adcl : 1; + /* Bits [31:8] is reseved */ + uint32_t resv : 24; +}__attribute__((packed, aligned(4))) codec_reg_23_t; + +typedef union _reg_23u { + codec_reg_23_t reg_23; + uint32_t reg_data; +} reg_23_t; + + + +typedef struct _codec_reg_24 +{ + uint32_t gain_alcl : 5; + uint32_t gain_micl : 2; + uint32_t mute_micl : 1; + + /* Bits [31:8] is reseved */ + uint32_t resv : 24; +}__attribute__((packed, aligned(4))) codec_reg_24_t; + +typedef union _reg_24u { + codec_reg_24_t reg_24; + uint32_t reg_data; +} reg_24_t; + + +typedef struct _codec_reg_25 +{ + uint32_t gain_alcl_det0_bp : 1; + uint32_t en_zerodet_adcl : 1; + uint32_t reserved : 6; + + /* Bits [31:8] is reseved */ + uint32_t resv : 24; +}__attribute__((packed, aligned(4))) codec_reg_25_t; + +typedef union _reg_25u { + codec_reg_25_t reg_25; + uint32_t reg_data; +} reg_25_t; + + +typedef struct _codec_reg_26 +{ + uint32_t initial_micr : 1; + uint32_t initial_alcr : 1; + uint32_t initial_adcr : 1; + uint32_t en_adcr : 1; + uint32_t en_clk_adcr : 1; + uint32_t en_alcr : 1; + uint32_t en_micr : 1; + uint32_t en_buf_adcr : 1; + + + /* Bits [31:8] is reseved */ + uint32_t resv : 24; +}__attribute__((packed, aligned(4))) codec_reg_26_t; + +typedef union _reg_26u { + codec_reg_26_t reg_26; + uint32_t reg_data; +} reg_26_t; + + +typedef struct _codec_reg_27 +{ + uint32_t gain_alcr : 5; + uint32_t gain_micr : 2; + uint32_t mute_micr : 1; + + /* Bits [31:8] is reseved */ + uint32_t resv : 24; +}__attribute__((packed, aligned(4))) codec_reg_27_t; + +typedef union _reg_27u { + codec_reg_27_t reg_27; + uint32_t reg_data; +} reg_27_t; + + +typedef struct _codec_reg_28 +{ + uint32_t gain_alcr_det0_bp : 1; + uint32_t en_zerodet_adcr : 1; + uint32_t reserved : 6; + + /* Bits [31:8] is reseved */ + uint32_t resv : 24; +}__attribute__((packed, aligned(4))) codec_reg_28_t; + +typedef union _reg_28u { + codec_reg_28_t reg_28; + uint32_t reg_data; +} reg_28_t; + + +typedef struct _codec_reg_29 +{ + uint32_t initial_dacl : 1; + uint32_t en_dacl : 1; + uint32_t en_clk_dacl : 1; + uint32_t en_vref_dacl : 1; + uint32_t pop_ctrl_dacl : 2; + uint32_t en_buf_dacl : 1; + uint32_t mute_hpoutl : 1; + + + /* Bits [31:8] is reseved */ + uint32_t resv : 24; +}__attribute__((packed, aligned(4))) codec_reg_29_t; + +typedef union _reg_29u { + codec_reg_29_t reg_29; + uint32_t reg_data; +} reg_29_t; + +typedef struct _codec_reg_2a +{ + uint32_t sel_hpoutl : 4; + uint32_t initial_hpoutl : 1; + uint32_t en_hpoutl : 1; + uint32_t reserved : 2; + + /* Bits [31:8] is reseved */ + uint32_t resv : 24; +}__attribute__((packed, aligned(4))) codec_reg_2a_t; + +typedef union _reg_2au { + codec_reg_2a_t reg_2a; + uint32_t reg_data; +} reg_2a_t; + + +typedef struct _codec_reg_2b +{ + uint32_t gain_hpoutl : 5; + uint32_t reserverd : 3; + + /* Bits [31:8] is reseved */ + uint32_t resv : 24; +}__attribute__((packed, aligned(4))) codec_reg_2b_t; + +typedef union _reg_2bu { + codec_reg_2b_t reg_2b; + uint32_t reg_data; +} reg_2b_t; + + + + +typedef struct _codec_reg_2d +{ + uint32_t sel_hpoutr : 4; + uint32_t initial_hpoutr : 1; + uint32_t en_hpoutr : 1; + uint32_t reserved : 2; + + /* Bits [31:8] is reseved */ + uint32_t resv : 24; +}__attribute__((packed, aligned(4))) codec_reg_2d_t; + +typedef union _reg_2du { + codec_reg_2d_t reg_2d; + uint32_t reg_data; +} reg_2d_t; + + + +typedef struct _codec_reg_2c +{ + uint32_t initial_dacr : 1; + uint32_t en_dacr : 1; + uint32_t en_clk_dacr : 1; + uint32_t en_vref_dacr : 1; + uint32_t pop_ctrl_dacr : 2; + uint32_t en_buf_dacr : 1; + uint32_t mute_hpoutr : 1; + + + /* Bits [31:8] is reseved */ + uint32_t resv : 24; +}__attribute__((packed, aligned(4))) codec_reg_2c_t; + +typedef union _reg_2cu { + codec_reg_2c_t reg_2c; + uint32_t reg_data; +} reg_2c_t; + + +typedef struct _codec_reg_2e +{ + uint32_t gain_hpoutr : 5; + uint32_t reserverd : 3; + + /* Bits [31:8] is reseved */ + uint32_t resv : 24; +}__attribute__((packed, aligned(4))) codec_reg_2e_t; + +typedef union _reg_2eu { + codec_reg_2e_t reg_2e; + uint32_t reg_data; +} reg_2e_t; + +typedef struct _audio_codec_reg +{ + volatile uint32_t reg_00;/* address:0x00 */ + volatile uint32_t reg_01;/* address:0x04 */ + volatile uint32_t reg_02;/* address:0x08 */ + volatile uint32_t reg_03;/* address:0x0c */ + volatile uint32_t reg_04;/* address:0x10 */ + volatile uint32_t reg_05;/* address:0x14 */ + volatile uint32_t reg_06;/* address:0x18 */ + volatile uint32_t reg_07;/* address:0x1c */ + volatile uint32_t reg_08;/* address:0x20 */ + volatile uint32_t reg_09;/* address:0x24 */ + volatile uint32_t reg_0a;/* address:0x28 */ + + volatile uint32_t reg_reserved_0b_1f[21];/* address:0x04 */ + + volatile uint32_t reg_20;/* address:0x80 */ + volatile uint32_t reg_21;/* address:0x84 */ + volatile uint32_t reg_22;/* address:0x88 */ + volatile uint32_t reg_23;/* address:0x8c */ + volatile uint32_t reg_24;/* address:0x90 */ + volatile uint32_t reg_25;/* address:0x04 */ + volatile uint32_t reg_26;/* address:0x98 */ + volatile uint32_t reg_27;/* address:0x9c */ + volatile uint32_t reg_28;/* address:0xa0 */ + volatile uint32_t reg_29;/* address:0xa4 */ + volatile uint32_t reg_2a;/* address:0xa8 */ + volatile uint32_t reg_2b;/* address:0xac */ + volatile uint32_t reg_2c;/* address:0xb0 */ + volatile uint32_t reg_2d;/* address:0xb4 */ + volatile uint32_t reg_2e;/* address:0xb8 */ + + volatile uint32_t reg_reserved_2f_3f[17];/* address:0x04 */ + + //The register's address which related to the ALCL function arrange from 0x40~0x4c. + volatile uint32_t reg_40;/* address:0x100 */ + volatile uint32_t reg_41;/* address:0x104 */ + volatile uint32_t reg_42;/* address:0x108 */ + volatile uint32_t reg_43;/* address:0x10c */ + volatile uint32_t reg_44;/* address:0x110*/ + volatile uint32_t reg_45;/* address:0x114 */ + volatile uint32_t reg_46;/* address:0x118 */ + volatile uint32_t reg_47;/* address:0x11c */ + volatile uint32_t reg_48;/* address:0x120 */ + volatile uint32_t reg_49;/* address:0x124 */ + volatile uint32_t reg_4a;/* address:0x128 */ + volatile uint32_t reg_4b;/* address:0x12c */ + volatile uint32_t reg_4c;/* address:0x130 */ + volatile uint32_t reg_4d;/* address:0x134 */ + volatile uint32_t reg_4e;/* address:0x138 */ + volatile uint32_t reg_4f;/* address:0x13c */ + + //The register's address which related to the ALCR function arrange from 0x50~0x5c. + volatile uint32_t reg_50;/* address:0x140 */ + volatile uint32_t reg_51;/* address:0x144 */ + volatile uint32_t reg_52;/* address:0x148 */ + volatile uint32_t reg_53;/* address:0x14c */ + volatile uint32_t reg_54;/* address:0x150 */ + volatile uint32_t reg_55;/* address:0x154 */ + volatile uint32_t reg_56;/* address:0x158 */ + volatile uint32_t reg_57;/* address:0x15c */ + volatile uint32_t reg_58;/* address:0x160 */ + volatile uint32_t reg_59;/* address:0x164 */ + volatile uint32_t reg_5a;/* address:0x168 */ + volatile uint32_t reg_5b;/* address:0x16c */ + volatile uint32_t reg_5c;/* address:0x170 */ + +}__attribute__((packed, aligned(4))) audio_codec_reg_s; + +int audio_codec_reg_init(void* reg_base); +void audio_codec_powerup_init(void); +void audio_codec_adc_init(k_i2s_work_mode mode,uint32_t i2s_ws);//i2s_ws最大24,设置32bit仍以24bit工作 +void audio_codec_dac_init(k_i2s_work_mode mode,uint32_t i2s_ws);//i2s_ws最大24,设置32bit仍以24bit工作 + +int audio_codec_adc_set_micl_gain(int gain); +int audio_codec_adc_set_micr_gain(int gain); +int audio_codec_adc_get_micl_gain(int* gain); +int audio_codec_adc_get_micr_gain(int* gain); + +// int audio_codec_adcl_set_volume(float volume); +// int audio_codec_adcr_set_volume(float volume); +// int audio_codec_adcl_get_volume(float* volume); +// int audio_codec_adcr_get_volume(float* volume); + +int audio_codec_adc_micl_mute(bool mute); +int audio_codec_adc_micr_mute(bool mute); + +int audio_codec_adc_get_micl_mute(bool* mute); +int audio_codec_adc_get_micr_mute(bool* mute); + +// int audio_codec_alc_set_micl_gain(float gain); +// int audio_codec_alc_set_micr_gain(float gain); +// int audio_codec_alc_get_micl_gain(float* gain); +// int audio_codec_alc_get_micr_gain(float* gain); + +int audio_codec_dac_set_hpoutl_gain(int gain); +int audio_codec_dac_set_hpoutr_gain(int gain); +int audio_codec_dac_get_hpoutl_gain(int* gain); +int audio_codec_dac_get_hpoutr_gain(int* gain); + +int audio_codec_dac_hpoutl_mute(bool mute); +int audio_codec_dac_hpoutr_mute(bool mute); + +int audio_codec_dac_get_hpoutl_mute(bool* mute); +int audio_codec_dac_get_hpoutr_mute(bool* mute); + +// int audio_codec_dacl_set_volume(float volume); +// int audio_codec_dacr_set_volume(float volume); +// int audio_codec_dacl_get_volume(float* volume); +// int audio_codec_dacr_get_volume(float* volume); + +int audio_codec_reset(void); + +int audio_codec_adc_hp_work(bool work);//Reset headphone input + +#endif + +