Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

mfd: ac100: Add driver for X-Powers AC100 audio codec / RTC combo IC

The AC100 is a multifunction device with an audio codec subsystem and
an RTC subsystem. These two subsystems share a common register space
and host interface.

Signed-off-by: Chen-Yu Tsai <wens@csie.org>
Signed-off-by: Lee Jones <lee.jones@linaro.org>

authored by

Chen-Yu Tsai and committed by
Lee Jones
585083c5 44fb25d0

+327
+10
drivers/mfd/Kconfig
··· 112 112 help 113 113 Support for the BCM590xx PMUs from Broadcom 114 114 115 + config MFD_AC100 116 + tristate "X-Powers AC100" 117 + select MFD_CORE 118 + depends on SUNXI_RSB 119 + help 120 + If you say Y here you get support for the X-Powers AC100 audio codec 121 + IC. 122 + This driver include only the core APIs. You have to select individual 123 + components like codecs or RTC under the corresponding menus. 124 + 115 125 config MFD_AXP20X 116 126 tristate 117 127 select MFD_CORE
+2
drivers/mfd/Makefile
··· 113 113 obj-$(CONFIG_PMIC_DA9052) += da9052-core.o 114 114 obj-$(CONFIG_MFD_DA9052_SPI) += da9052-spi.o 115 115 obj-$(CONFIG_MFD_DA9052_I2C) += da9052-i2c.o 116 + 117 + obj-$(CONFIG_MFD_AC100) += ac100.o 116 118 obj-$(CONFIG_MFD_AXP20X) += axp20x.o 117 119 obj-$(CONFIG_MFD_AXP20X_I2C) += axp20x-i2c.o 118 120 obj-$(CONFIG_MFD_AXP20X_RSB) += axp20x-rsb.o
+137
drivers/mfd/ac100.c
··· 1 + /* 2 + * MFD core driver for X-Powers' AC100 Audio Codec IC 3 + * 4 + * The AC100 is a highly integrated audio codec and RTC subsystem designed 5 + * for mobile applications. It has 3 I2S/PCM interfaces, a 2 channel DAC, 6 + * a 2 channel ADC with 5 inputs and a builtin mixer. The RTC subsystem has 7 + * 3 clock outputs. 8 + * 9 + * The audio codec and RTC parts are completely separate, sharing only the 10 + * host interface for access to its registers. 11 + * 12 + * Copyright (2016) Chen-Yu Tsai 13 + * 14 + * Author: Chen-Yu Tsai <wens@csie.org> 15 + * 16 + * This program is free software; you can redistribute it and/or modify 17 + * it under the terms of the GNU General Public License version 2 as 18 + * published by the Free Software Foundation. 19 + */ 20 + 21 + #include <linux/interrupt.h> 22 + #include <linux/kernel.h> 23 + #include <linux/mfd/core.h> 24 + #include <linux/mfd/ac100.h> 25 + #include <linux/module.h> 26 + #include <linux/of.h> 27 + #include <linux/regmap.h> 28 + #include <linux/sunxi-rsb.h> 29 + 30 + static const struct regmap_range ac100_writeable_ranges[] = { 31 + regmap_reg_range(AC100_CHIP_AUDIO_RST, AC100_I2S_SR_CTRL), 32 + regmap_reg_range(AC100_I2S1_CLK_CTRL, AC100_I2S1_MXR_GAIN), 33 + regmap_reg_range(AC100_I2S2_CLK_CTRL, AC100_I2S2_MXR_GAIN), 34 + regmap_reg_range(AC100_I2S3_CLK_CTRL, AC100_I2S3_SIG_PATH_CTRL), 35 + regmap_reg_range(AC100_ADC_DIG_CTRL, AC100_ADC_VOL_CTRL), 36 + regmap_reg_range(AC100_HMIC_CTRL1, AC100_HMIC_STATUS), 37 + regmap_reg_range(AC100_DAC_DIG_CTRL, AC100_DAC_MXR_GAIN), 38 + regmap_reg_range(AC100_ADC_APC_CTRL, AC100_LINEOUT_CTRL), 39 + regmap_reg_range(AC100_ADC_DAP_L_CTRL, AC100_ADC_DAP_OPT), 40 + regmap_reg_range(AC100_DAC_DAP_CTRL, AC100_DAC_DAP_OPT), 41 + regmap_reg_range(AC100_ADC_DAP_ENA, AC100_DAC_DAP_ENA), 42 + regmap_reg_range(AC100_SRC1_CTRL1, AC100_SRC1_CTRL2), 43 + regmap_reg_range(AC100_SRC2_CTRL1, AC100_SRC2_CTRL2), 44 + regmap_reg_range(AC100_CLK32K_ANALOG_CTRL, AC100_CLKOUT_CTRL3), 45 + regmap_reg_range(AC100_RTC_RST, AC100_RTC_UPD), 46 + regmap_reg_range(AC100_ALM_INT_ENA, AC100_ALM_INT_STA), 47 + regmap_reg_range(AC100_ALM_SEC, AC100_RTC_GP(15)), 48 + }; 49 + 50 + static const struct regmap_range ac100_volatile_ranges[] = { 51 + regmap_reg_range(AC100_CHIP_AUDIO_RST, AC100_PLL_CTRL2), 52 + regmap_reg_range(AC100_HMIC_STATUS, AC100_HMIC_STATUS), 53 + regmap_reg_range(AC100_ADC_DAP_L_STA, AC100_ADC_DAP_L_STA), 54 + regmap_reg_range(AC100_SRC1_CTRL1, AC100_SRC1_CTRL1), 55 + regmap_reg_range(AC100_SRC1_CTRL3, AC100_SRC2_CTRL1), 56 + regmap_reg_range(AC100_SRC2_CTRL3, AC100_SRC2_CTRL4), 57 + regmap_reg_range(AC100_RTC_RST, AC100_RTC_RST), 58 + regmap_reg_range(AC100_RTC_SEC, AC100_ALM_INT_STA), 59 + regmap_reg_range(AC100_ALM_SEC, AC100_ALM_UPD), 60 + }; 61 + 62 + static const struct regmap_access_table ac100_writeable_table = { 63 + .yes_ranges = ac100_writeable_ranges, 64 + .n_yes_ranges = ARRAY_SIZE(ac100_writeable_ranges), 65 + }; 66 + 67 + static const struct regmap_access_table ac100_volatile_table = { 68 + .yes_ranges = ac100_volatile_ranges, 69 + .n_yes_ranges = ARRAY_SIZE(ac100_volatile_ranges), 70 + }; 71 + 72 + static const struct regmap_config ac100_regmap_config = { 73 + .reg_bits = 8, 74 + .val_bits = 16, 75 + .wr_table = &ac100_writeable_table, 76 + .volatile_table = &ac100_volatile_table, 77 + .max_register = AC100_RTC_GP(15), 78 + .cache_type = REGCACHE_RBTREE, 79 + }; 80 + 81 + static struct mfd_cell ac100_cells[] = { 82 + { 83 + .name = "ac100-codec", 84 + .of_compatible = "x-powers,ac100-codec", 85 + }, { 86 + .name = "ac100-rtc", 87 + .of_compatible = "x-powers,ac100-rtc", 88 + }, 89 + }; 90 + 91 + static int ac100_rsb_probe(struct sunxi_rsb_device *rdev) 92 + { 93 + struct ac100_dev *ac100; 94 + int ret; 95 + 96 + ac100 = devm_kzalloc(&rdev->dev, sizeof(*ac100), GFP_KERNEL); 97 + if (!ac100) 98 + return -ENOMEM; 99 + 100 + ac100->dev = &rdev->dev; 101 + sunxi_rsb_device_set_drvdata(rdev, ac100); 102 + 103 + ac100->regmap = devm_regmap_init_sunxi_rsb(rdev, &ac100_regmap_config); 104 + if (IS_ERR(ac100->regmap)) { 105 + ret = PTR_ERR(ac100->regmap); 106 + dev_err(ac100->dev, "regmap init failed: %d\n", ret); 107 + return ret; 108 + } 109 + 110 + ret = devm_mfd_add_devices(ac100->dev, PLATFORM_DEVID_NONE, ac100_cells, 111 + ARRAY_SIZE(ac100_cells), NULL, 0, NULL); 112 + if (ret) { 113 + dev_err(ac100->dev, "failed to add MFD devices: %d\n", ret); 114 + return ret; 115 + } 116 + 117 + return 0; 118 + } 119 + 120 + static const struct of_device_id ac100_of_match[] = { 121 + { .compatible = "x-powers,ac100" }, 122 + { }, 123 + }; 124 + MODULE_DEVICE_TABLE(of, ac100_of_match); 125 + 126 + static struct sunxi_rsb_driver ac100_rsb_driver = { 127 + .driver = { 128 + .name = "ac100", 129 + .of_match_table = of_match_ptr(ac100_of_match), 130 + }, 131 + .probe = ac100_rsb_probe, 132 + }; 133 + module_sunxi_rsb_driver(ac100_rsb_driver); 134 + 135 + MODULE_DESCRIPTION("Audio codec MFD core driver for AC100"); 136 + MODULE_AUTHOR("Chen-Yu Tsai <wens@csie.org>"); 137 + MODULE_LICENSE("GPL v2");
+178
include/linux/mfd/ac100.h
··· 1 + /* 2 + * Functions and registers to access AC100 codec / RTC combo IC. 3 + * 4 + * Copyright (C) 2016 Chen-Yu Tsai 5 + * 6 + * Chen-Yu Tsai <wens@csie.org> 7 + * 8 + * This program is free software; you can redistribute it and/or modify 9 + * it under the terms of the GNU General Public License version 2 as 10 + * published by the Free Software Foundation. 11 + */ 12 + 13 + #ifndef __LINUX_MFD_AC100_H 14 + #define __LINUX_MFD_AC100_H 15 + 16 + #include <linux/regmap.h> 17 + 18 + struct ac100_dev { 19 + struct device *dev; 20 + struct regmap *regmap; 21 + }; 22 + 23 + /* Audio codec related registers */ 24 + #define AC100_CHIP_AUDIO_RST 0x00 25 + #define AC100_PLL_CTRL1 0x01 26 + #define AC100_PLL_CTRL2 0x02 27 + #define AC100_SYSCLK_CTRL 0x03 28 + #define AC100_MOD_CLK_ENA 0x04 29 + #define AC100_MOD_RST_CTRL 0x05 30 + #define AC100_I2S_SR_CTRL 0x06 31 + 32 + /* I2S1 interface */ 33 + #define AC100_I2S1_CLK_CTRL 0x10 34 + #define AC100_I2S1_SND_OUT_CTRL 0x11 35 + #define AC100_I2S1_SND_IN_CTRL 0x12 36 + #define AC100_I2S1_MXR_SRC 0x13 37 + #define AC100_I2S1_VOL_CTRL1 0x14 38 + #define AC100_I2S1_VOL_CTRL2 0x15 39 + #define AC100_I2S1_VOL_CTRL3 0x16 40 + #define AC100_I2S1_VOL_CTRL4 0x17 41 + #define AC100_I2S1_MXR_GAIN 0x18 42 + 43 + /* I2S2 interface */ 44 + #define AC100_I2S2_CLK_CTRL 0x20 45 + #define AC100_I2S2_SND_OUT_CTRL 0x21 46 + #define AC100_I2S2_SND_IN_CTRL 0x22 47 + #define AC100_I2S2_MXR_SRC 0x23 48 + #define AC100_I2S2_VOL_CTRL1 0x24 49 + #define AC100_I2S2_VOL_CTRL2 0x25 50 + #define AC100_I2S2_VOL_CTRL3 0x26 51 + #define AC100_I2S2_VOL_CTRL4 0x27 52 + #define AC100_I2S2_MXR_GAIN 0x28 53 + 54 + /* I2S3 interface */ 55 + #define AC100_I2S3_CLK_CTRL 0x30 56 + #define AC100_I2S3_SND_OUT_CTRL 0x31 57 + #define AC100_I2S3_SND_IN_CTRL 0x32 58 + #define AC100_I2S3_SIG_PATH_CTRL 0x33 59 + 60 + /* ADC digital controls */ 61 + #define AC100_ADC_DIG_CTRL 0x40 62 + #define AC100_ADC_VOL_CTRL 0x41 63 + 64 + /* HMIC plug sensing / key detection */ 65 + #define AC100_HMIC_CTRL1 0x44 66 + #define AC100_HMIC_CTRL2 0x45 67 + #define AC100_HMIC_STATUS 0x46 68 + 69 + /* DAC digital controls */ 70 + #define AC100_DAC_DIG_CTRL 0x48 71 + #define AC100_DAC_VOL_CTRL 0x49 72 + #define AC100_DAC_MXR_SRC 0x4c 73 + #define AC100_DAC_MXR_GAIN 0x4d 74 + 75 + /* Analog controls */ 76 + #define AC100_ADC_APC_CTRL 0x50 77 + #define AC100_ADC_SRC 0x51 78 + #define AC100_ADC_SRC_BST_CTRL 0x52 79 + #define AC100_OUT_MXR_DAC_A_CTRL 0x53 80 + #define AC100_OUT_MXR_SRC 0x54 81 + #define AC100_OUT_MXR_SRC_BST 0x55 82 + #define AC100_HPOUT_CTRL 0x56 83 + #define AC100_ERPOUT_CTRL 0x57 84 + #define AC100_SPKOUT_CTRL 0x58 85 + #define AC100_LINEOUT_CTRL 0x59 86 + 87 + /* ADC digital audio processing (high pass filter & auto gain control */ 88 + #define AC100_ADC_DAP_L_STA 0x80 89 + #define AC100_ADC_DAP_R_STA 0x81 90 + #define AC100_ADC_DAP_L_CTRL 0x82 91 + #define AC100_ADC_DAP_R_CTRL 0x83 92 + #define AC100_ADC_DAP_L_T_L 0x84 /* Left Target Level */ 93 + #define AC100_ADC_DAP_R_T_L 0x85 /* Right Target Level */ 94 + #define AC100_ADC_DAP_L_H_A_C 0x86 /* Left High Avg. Coef */ 95 + #define AC100_ADC_DAP_L_L_A_C 0x87 /* Left Low Avg. Coef */ 96 + #define AC100_ADC_DAP_R_H_A_C 0x88 /* Right High Avg. Coef */ 97 + #define AC100_ADC_DAP_R_L_A_C 0x89 /* Right Low Avg. Coef */ 98 + #define AC100_ADC_DAP_L_D_T 0x8a /* Left Decay Time */ 99 + #define AC100_ADC_DAP_L_A_T 0x8b /* Left Attack Time */ 100 + #define AC100_ADC_DAP_R_D_T 0x8c /* Right Decay Time */ 101 + #define AC100_ADC_DAP_R_A_T 0x8d /* Right Attack Time */ 102 + #define AC100_ADC_DAP_N_TH 0x8e /* Noise Threshold */ 103 + #define AC100_ADC_DAP_L_H_N_A_C 0x8f /* Left High Noise Avg. Coef */ 104 + #define AC100_ADC_DAP_L_L_N_A_C 0x90 /* Left Low Noise Avg. Coef */ 105 + #define AC100_ADC_DAP_R_H_N_A_C 0x91 /* Right High Noise Avg. Coef */ 106 + #define AC100_ADC_DAP_R_L_N_A_C 0x92 /* Right Low Noise Avg. Coef */ 107 + #define AC100_ADC_DAP_H_HPF_C 0x93 /* High High-Pass-Filter Coef */ 108 + #define AC100_ADC_DAP_L_HPF_C 0x94 /* Low High-Pass-Filter Coef */ 109 + #define AC100_ADC_DAP_OPT 0x95 /* AGC Optimum */ 110 + 111 + /* DAC digital audio processing (high pass filter & dynamic range control) */ 112 + #define AC100_DAC_DAP_CTRL 0xa0 113 + #define AC100_DAC_DAP_H_HPF_C 0xa1 /* High High-Pass-Filter Coef */ 114 + #define AC100_DAC_DAP_L_HPF_C 0xa2 /* Low High-Pass-Filter Coef */ 115 + #define AC100_DAC_DAP_L_H_E_A_C 0xa3 /* Left High Energy Avg Coef */ 116 + #define AC100_DAC_DAP_L_L_E_A_C 0xa4 /* Left Low Energy Avg Coef */ 117 + #define AC100_DAC_DAP_R_H_E_A_C 0xa5 /* Right High Energy Avg Coef */ 118 + #define AC100_DAC_DAP_R_L_E_A_C 0xa6 /* Right Low Energy Avg Coef */ 119 + #define AC100_DAC_DAP_H_G_D_T_C 0xa7 /* High Gain Delay Time Coef */ 120 + #define AC100_DAC_DAP_L_G_D_T_C 0xa8 /* Low Gain Delay Time Coef */ 121 + #define AC100_DAC_DAP_H_G_A_T_C 0xa9 /* High Gain Attack Time Coef */ 122 + #define AC100_DAC_DAP_L_G_A_T_C 0xaa /* Low Gain Attack Time Coef */ 123 + #define AC100_DAC_DAP_H_E_TH 0xab /* High Energy Threshold */ 124 + #define AC100_DAC_DAP_L_E_TH 0xac /* Low Energy Threshold */ 125 + #define AC100_DAC_DAP_H_G_K 0xad /* High Gain K parameter */ 126 + #define AC100_DAC_DAP_L_G_K 0xae /* Low Gain K parameter */ 127 + #define AC100_DAC_DAP_H_G_OFF 0xaf /* High Gain offset */ 128 + #define AC100_DAC_DAP_L_G_OFF 0xb0 /* Low Gain offset */ 129 + #define AC100_DAC_DAP_OPT 0xb1 /* DRC optimum */ 130 + 131 + /* Digital audio processing enable */ 132 + #define AC100_ADC_DAP_ENA 0xb4 133 + #define AC100_DAC_DAP_ENA 0xb5 134 + 135 + /* SRC control */ 136 + #define AC100_SRC1_CTRL1 0xb8 137 + #define AC100_SRC1_CTRL2 0xb9 138 + #define AC100_SRC1_CTRL3 0xba 139 + #define AC100_SRC1_CTRL4 0xbb 140 + #define AC100_SRC2_CTRL1 0xbc 141 + #define AC100_SRC2_CTRL2 0xbd 142 + #define AC100_SRC2_CTRL3 0xbe 143 + #define AC100_SRC2_CTRL4 0xbf 144 + 145 + /* RTC clk control */ 146 + #define AC100_CLK32K_ANALOG_CTRL 0xc0 147 + #define AC100_CLKOUT_CTRL1 0xc1 148 + #define AC100_CLKOUT_CTRL2 0xc2 149 + #define AC100_CLKOUT_CTRL3 0xc3 150 + 151 + /* RTC module */ 152 + #define AC100_RTC_RST 0xc6 153 + #define AC100_RTC_CTRL 0xc7 154 + #define AC100_RTC_SEC 0xc8 /* second */ 155 + #define AC100_RTC_MIN 0xc9 /* minute */ 156 + #define AC100_RTC_HOU 0xca /* hour */ 157 + #define AC100_RTC_WEE 0xcb /* weekday */ 158 + #define AC100_RTC_DAY 0xcc /* day */ 159 + #define AC100_RTC_MON 0xcd /* month */ 160 + #define AC100_RTC_YEA 0xce /* year */ 161 + #define AC100_RTC_UPD 0xcf /* update trigger */ 162 + 163 + /* RTC alarm */ 164 + #define AC100_ALM_INT_ENA 0xd0 165 + #define AC100_ALM_INT_STA 0xd1 166 + #define AC100_ALM_SEC 0xd8 167 + #define AC100_ALM_MIN 0xd9 168 + #define AC100_ALM_HOU 0xda 169 + #define AC100_ALM_WEE 0xdb 170 + #define AC100_ALM_DAY 0xdc 171 + #define AC100_ALM_MON 0xdd 172 + #define AC100_ALM_YEA 0xde 173 + #define AC100_ALM_UPD 0xdf 174 + 175 + /* RTC general purpose register 0 ~ 15 */ 176 + #define AC100_RTC_GP(x) (0xe0 + (x)) 177 + 178 + #endif /* __LINUX_MFD_AC100_H */