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