^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0-only
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) * MFD core driver for X-Powers' AC100 Audio Codec IC
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) * The AC100 is a highly integrated audio codec and RTC subsystem designed
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) * for mobile applications. It has 3 I2S/PCM interfaces, a 2 channel DAC,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) * a 2 channel ADC with 5 inputs and a builtin mixer. The RTC subsystem has
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) * 3 clock outputs.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) * The audio codec and RTC parts are completely separate, sharing only the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) * host interface for access to its registers.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) * Copyright (2016) Chen-Yu Tsai
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) * Author: Chen-Yu Tsai <wens@csie.org>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) #include <linux/interrupt.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) #include <linux/kernel.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) #include <linux/mfd/core.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) #include <linux/mfd/ac100.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) #include <linux/module.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) #include <linux/of.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) #include <linux/regmap.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) #include <linux/sunxi-rsb.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) static const struct regmap_range ac100_writeable_ranges[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) regmap_reg_range(AC100_CHIP_AUDIO_RST, AC100_I2S_SR_CTRL),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) regmap_reg_range(AC100_I2S1_CLK_CTRL, AC100_I2S1_MXR_GAIN),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) regmap_reg_range(AC100_I2S2_CLK_CTRL, AC100_I2S2_MXR_GAIN),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) regmap_reg_range(AC100_I2S3_CLK_CTRL, AC100_I2S3_SIG_PATH_CTRL),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) regmap_reg_range(AC100_ADC_DIG_CTRL, AC100_ADC_VOL_CTRL),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) regmap_reg_range(AC100_HMIC_CTRL1, AC100_HMIC_STATUS),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) regmap_reg_range(AC100_DAC_DIG_CTRL, AC100_DAC_MXR_GAIN),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) regmap_reg_range(AC100_ADC_APC_CTRL, AC100_LINEOUT_CTRL),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) regmap_reg_range(AC100_ADC_DAP_L_CTRL, AC100_ADC_DAP_OPT),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) regmap_reg_range(AC100_DAC_DAP_CTRL, AC100_DAC_DAP_OPT),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) regmap_reg_range(AC100_ADC_DAP_ENA, AC100_DAC_DAP_ENA),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) regmap_reg_range(AC100_SRC1_CTRL1, AC100_SRC1_CTRL2),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) regmap_reg_range(AC100_SRC2_CTRL1, AC100_SRC2_CTRL2),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) regmap_reg_range(AC100_CLK32K_ANALOG_CTRL, AC100_CLKOUT_CTRL3),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) regmap_reg_range(AC100_RTC_RST, AC100_RTC_UPD),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) regmap_reg_range(AC100_ALM_INT_ENA, AC100_ALM_INT_STA),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) regmap_reg_range(AC100_ALM_SEC, AC100_RTC_GP(15)),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) static const struct regmap_range ac100_volatile_ranges[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) regmap_reg_range(AC100_CHIP_AUDIO_RST, AC100_PLL_CTRL2),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) regmap_reg_range(AC100_HMIC_STATUS, AC100_HMIC_STATUS),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) regmap_reg_range(AC100_ADC_DAP_L_STA, AC100_ADC_DAP_L_STA),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) regmap_reg_range(AC100_SRC1_CTRL1, AC100_SRC1_CTRL1),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) regmap_reg_range(AC100_SRC1_CTRL3, AC100_SRC2_CTRL1),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) regmap_reg_range(AC100_SRC2_CTRL3, AC100_SRC2_CTRL4),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) regmap_reg_range(AC100_RTC_RST, AC100_RTC_RST),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) regmap_reg_range(AC100_RTC_SEC, AC100_ALM_INT_STA),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) regmap_reg_range(AC100_ALM_SEC, AC100_ALM_UPD),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) static const struct regmap_access_table ac100_writeable_table = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) .yes_ranges = ac100_writeable_ranges,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) .n_yes_ranges = ARRAY_SIZE(ac100_writeable_ranges),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) static const struct regmap_access_table ac100_volatile_table = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) .yes_ranges = ac100_volatile_ranges,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) .n_yes_ranges = ARRAY_SIZE(ac100_volatile_ranges),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) static const struct regmap_config ac100_regmap_config = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) .reg_bits = 8,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) .val_bits = 16,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) .wr_table = &ac100_writeable_table,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) .volatile_table = &ac100_volatile_table,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) .max_register = AC100_RTC_GP(15),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) .cache_type = REGCACHE_RBTREE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) static struct mfd_cell ac100_cells[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) .name = "ac100-codec",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) .of_compatible = "x-powers,ac100-codec",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) }, {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) .name = "ac100-rtc",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) .of_compatible = "x-powers,ac100-rtc",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) static int ac100_rsb_probe(struct sunxi_rsb_device *rdev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) struct ac100_dev *ac100;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) ac100 = devm_kzalloc(&rdev->dev, sizeof(*ac100), GFP_KERNEL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) if (!ac100)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) return -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) ac100->dev = &rdev->dev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) sunxi_rsb_device_set_drvdata(rdev, ac100);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) ac100->regmap = devm_regmap_init_sunxi_rsb(rdev, &ac100_regmap_config);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) if (IS_ERR(ac100->regmap)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) ret = PTR_ERR(ac100->regmap);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) dev_err(ac100->dev, "regmap init failed: %d\n", ret);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) ret = devm_mfd_add_devices(ac100->dev, PLATFORM_DEVID_NONE, ac100_cells,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) ARRAY_SIZE(ac100_cells), NULL, 0, NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) if (ret) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) dev_err(ac100->dev, "failed to add MFD devices: %d\n", ret);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) static const struct of_device_id ac100_of_match[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) { .compatible = "x-powers,ac100" },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) { },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) MODULE_DEVICE_TABLE(of, ac100_of_match);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) static struct sunxi_rsb_driver ac100_rsb_driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) .driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) .name = "ac100",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) .of_match_table = of_match_ptr(ac100_of_match),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) .probe = ac100_rsb_probe,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) module_sunxi_rsb_driver(ac100_rsb_driver);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132) MODULE_DESCRIPTION("Audio codec MFD core driver for AC100");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) MODULE_AUTHOR("Chen-Yu Tsai <wens@csie.org>");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) MODULE_LICENSE("GPL v2");