| /* |
| * arch/arm/cpu/armv8/gx12a/sound.c |
| * |
| * 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 named License, |
| * or any later version. |
| * |
| * This program is distributed in the hope that 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. |
| * |
| */ |
| #include <asm/arch/io.h> |
| #include <asm/arch/secure_apb.h> |
| #include <common.h> |
| #include <amlogic/auge_sound.h> |
| #include <asm/arch/cpu.h> |
| #include <asm/cpu_id.h> |
| |
| #define EE_AUDIO_FRDDR_A_CTRL2 (0xff642000 + (0x07a << 2)) |
| |
| struct aiu_958_channel_status { |
| unsigned short chstat0_l; |
| unsigned short chstat1_l; |
| unsigned short chstat0_r; |
| unsigned short chstat1_r; |
| }; |
| |
| /* g12a audio base address: 0xFF642000 */ |
| /* sm1 audio base address: 0xFF660000 */ |
| static int audiobus_read(unsigned long reg, unsigned int *val) |
| { |
| if (get_cpu_id().family_id == MESON_CPU_MAJOR_ID_SM1) { |
| *val = readl(reg + 0x1E000); |
| } else { |
| *val = readl(reg); |
| } |
| |
| return 0; |
| } |
| |
| static void hhi_write(unsigned long reg, unsigned int val) |
| { |
| writel(val, reg); |
| } |
| |
| static void audiobus_write(unsigned long reg, unsigned int val) |
| { |
| if (get_cpu_id().family_id == MESON_CPU_MAJOR_ID_SM1) { |
| writel(val, reg + 0x1E000); |
| } else { |
| writel(val, reg); |
| } |
| } |
| |
| static int audiobus_update_bits(unsigned int reg, unsigned int mask, unsigned int value) |
| { |
| bool change; |
| unsigned int old, new; |
| |
| audiobus_read(reg, &old); |
| |
| new = (old & ~mask) | (value & mask); |
| change = old != new; |
| if (change) |
| audiobus_write(reg, new); |
| |
| return change; |
| } |
| |
| static void aml_set_audio_spdif_clk(void) |
| { |
| /*mpll0: 25m*/ |
| hhi_write(HHI_MPLL_CNTL0, 0x543); |
| hhi_write(HHI_MPLL_CNTL1, 0xC5101856); |
| hhi_write(HHI_MPLL_CNTL2, 0x40000033); |
| |
| /* audio clk gate */ |
| audiobus_write(EE_AUDIO_CLK_GATE_EN, |
| 1 << 17 /* spdifout */ |
| | 1 << 9 /* frddra */ |
| | 1 << 0 /* ddr_arb */); |
| |
| /*SPDIFOUT_CTRL clk:6m*/ |
| audiobus_write(EE_AUDIO_CLK_SPDIFOUT_CTRL, |
| 1 << 31 /* enable */ |
| | 0 << 24 /* mpll0 */ |
| | 3 << 0 /* dividor */); |
| } |
| |
| static void aml_spdif_fifo_ctrl(void) |
| { |
| /* reg mute val */ |
| audiobus_write(EE_AUDIO_SPDIFOUT_MUTE_VAL, 0x0); |
| |
| /* mask lane0 L/R, lsb first, insert data from 31bits */ |
| audiobus_update_bits(EE_AUDIO_SPDIFOUT_CTRL0, |
| 0x1<<20 | 0x1<<19 | 0xff << 4, |
| 0x0<<20 | 0x0<<19 | 0x3 << 4); |
| |
| /* split 64bits ddr data to 2 sample */ |
| /* msb position of data */ |
| /* frddr_A */ |
| audiobus_update_bits(EE_AUDIO_SPDIFOUT_CTRL1, |
| 0x3 << 24 | 0x1f << 8 | 0x7 << 4, |
| 0 << 24 | (32 - 1) << 8 | 3<<4); |
| |
| audiobus_write(EE_AUDIO_SPDIFOUT_SWAP, 0x1 << 4); |
| } |
| |
| static void spdifout_set_pcm_chsts(struct aiu_958_channel_status *set) |
| { |
| if (!set) { |
| printf("spdifout_set_pcm_chsts, error set NULL point\n"); |
| return; |
| } |
| |
| audiobus_write(EE_AUDIO_SPDIFOUT_CHSTS0, set->chstat1_l << 16 | set->chstat0_l); |
| audiobus_write(EE_AUDIO_SPDIFOUT_CHSTS6, set->chstat1_r << 16 | set->chstat0_r); |
| } |
| |
| void frddr_init_without_mngr(void) |
| { |
| unsigned int start_addr, end_addr, int_addr; |
| static int buf[256]; |
| |
| memset(buf, 0x0, sizeof(buf)); |
| start_addr = virt_to_phys(buf); |
| end_addr = start_addr + sizeof(buf) - 1; |
| int_addr = sizeof(buf) / 64; |
| |
| audiobus_write(EE_AUDIO_ARB_CTRL, 0x800000ff); |
| |
| audiobus_write(EE_AUDIO_FRDDR_A_START_ADDR, start_addr); |
| |
| audiobus_write(EE_AUDIO_FRDDR_A_INIT_ADDR, start_addr); |
| |
| audiobus_write(EE_AUDIO_FRDDR_A_FINISH_ADDR, end_addr); |
| |
| audiobus_write(EE_AUDIO_FRDDR_A_INT_ADDR, int_addr); |
| |
| audiobus_write(EE_AUDIO_FRDDR_A_CTRL1, |
| (0x40 - 1) << 24 | (0x20 - 1) << 16 | 2 << 8 | 0 << 0); |
| |
| if (get_cpu_id().family_id == MESON_CPU_MAJOR_ID_SM1) { |
| audiobus_write(EE_AUDIO_FRDDR_A_CTRL0, |
| 1 << 31 |
| | 0 << 24 |
| | 4 << 16 |
| ); |
| |
| /* src0 enable, src0 select spdifout */ |
| audiobus_write(EE_AUDIO_FRDDR_A_CTRL2, 1 << 4 | 3 << 0); |
| } else { |
| audiobus_write(EE_AUDIO_FRDDR_A_CTRL0, |
| 1 << 31 |
| | 0 << 24 |
| | 4 << 16 |
| | 1 << 3 /* src0 enable */ |
| | 3 << 0 /* src0 select spdifout*/ |
| ); |
| } |
| } |
| |
| static void aml_spdif_enable(void) |
| { |
| /* reset */ |
| audiobus_update_bits(EE_AUDIO_SPDIFOUT_CTRL0, 3<<28, 0); |
| audiobus_update_bits(EE_AUDIO_SPDIFOUT_CTRL0, 1<<29, 1<<29); |
| audiobus_update_bits(EE_AUDIO_SPDIFOUT_CTRL0, 1<<28, 1<<28); |
| /* enable */ |
| audiobus_update_bits(EE_AUDIO_SPDIFOUT_CTRL0, 1 << 31, 1 << 31); |
| |
| /* tohdmitx enable */ |
| audiobus_write(EE_AUDIO_TOHDMITX_CTRL0, |
| 1 << 31 |
| | 1 << 3 /* spdif_clk_cap_inv */ |
| | 0 << 2 /* spdif_clk_inv */ |
| | 0 << 1 /* spdif_out */ |
| | 0 << 0 /* spdif_clk */ |
| ); |
| } |
| |
| static void aml_spdif_play(void) |
| { |
| struct aiu_958_channel_status chstat; |
| |
| chstat.chstat0_l = 0x0100; |
| chstat.chstat0_r = 0x0100; |
| chstat.chstat1_l = 0X200; |
| chstat.chstat1_r = 0X200; |
| |
| aml_spdif_fifo_ctrl(); |
| spdifout_set_pcm_chsts(&chstat); |
| frddr_init_without_mngr(); |
| aml_spdif_enable(); |
| } |
| |
| int aml_audio_init(void) |
| { |
| printf("aml_audio_init\n"); |
| |
| aml_set_audio_spdif_clk(); |
| aml_spdif_play(); |
| |
| return 0; |
| } |