361 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Wolfson WM97xx -- Core device4 *5 * Copyright (C) 2017 Robert Jarzmik6 *7 * Features:8 * - an AC97 audio codec9 * - a touchscreen driver10 * - a GPIO block11 */12 13#include <linux/device.h>14#include <linux/mfd/core.h>15#include <linux/mfd/wm97xx.h>16#include <linux/module.h>17#include <linux/regmap.h>18#include <linux/slab.h>19#include <linux/wm97xx.h>20#include <sound/ac97/codec.h>21#include <sound/ac97/compat.h>22 23#define WM9705_VENDOR_ID 0x574d4c0524#define WM9712_VENDOR_ID 0x574d4c1225#define WM9713_VENDOR_ID 0x574d4c1326#define WM97xx_VENDOR_ID_MASK 0xffffffff27 28struct wm97xx_priv {29 struct regmap *regmap;30 struct snd_ac97 *ac97;31 struct device *dev;32 struct wm97xx_platform_data codec_pdata;33};34 35static bool wm97xx_readable_reg(struct device *dev, unsigned int reg)36{37 switch (reg) {38 case AC97_RESET ... AC97_PCM_SURR_DAC_RATE:39 case AC97_PCM_LR_ADC_RATE:40 case AC97_CENTER_LFE_MASTER:41 case AC97_SPDIF ... AC97_LINE1_LEVEL:42 case AC97_GPIO_CFG ... 0x5c:43 case AC97_CODEC_CLASS_REV ... AC97_PCI_SID:44 case 0x74 ... AC97_VENDOR_ID2:45 return true;46 default:47 return false;48 }49}50 51static bool wm97xx_writeable_reg(struct device *dev, unsigned int reg)52{53 switch (reg) {54 case AC97_VENDOR_ID1:55 case AC97_VENDOR_ID2:56 return false;57 default:58 return wm97xx_readable_reg(dev, reg);59 }60}61 62static const struct reg_default wm9705_reg_defaults[] = {63 { 0x02, 0x8000 },64 { 0x04, 0x8000 },65 { 0x06, 0x8000 },66 { 0x0a, 0x8000 },67 { 0x0c, 0x8008 },68 { 0x0e, 0x8008 },69 { 0x10, 0x8808 },70 { 0x12, 0x8808 },71 { 0x14, 0x8808 },72 { 0x16, 0x8808 },73 { 0x18, 0x8808 },74 { 0x1a, 0x0000 },75 { 0x1c, 0x8000 },76 { 0x20, 0x0000 },77 { 0x22, 0x0000 },78 { 0x26, 0x000f },79 { 0x28, 0x0605 },80 { 0x2a, 0x0000 },81 { 0x2c, 0xbb80 },82 { 0x32, 0xbb80 },83 { 0x34, 0x2000 },84 { 0x5a, 0x0000 },85 { 0x5c, 0x0000 },86 { 0x72, 0x0808 },87 { 0x74, 0x0000 },88 { 0x76, 0x0006 },89 { 0x78, 0x0000 },90 { 0x7a, 0x0000 },91};92 93static const struct regmap_config wm9705_regmap_config = {94 .reg_bits = 16,95 .reg_stride = 2,96 .val_bits = 16,97 .max_register = 0x7e,98 .cache_type = REGCACHE_MAPLE,99 100 .reg_defaults = wm9705_reg_defaults,101 .num_reg_defaults = ARRAY_SIZE(wm9705_reg_defaults),102 .volatile_reg = regmap_ac97_default_volatile,103 .readable_reg = wm97xx_readable_reg,104 .writeable_reg = wm97xx_writeable_reg,105};106 107static struct mfd_cell wm9705_cells[] = {108 { .name = "wm9705-codec", },109 { .name = "wm97xx-ts", },110};111 112static bool wm9712_volatile_reg(struct device *dev, unsigned int reg)113{114 switch (reg) {115 case AC97_REC_GAIN:116 return true;117 default:118 return regmap_ac97_default_volatile(dev, reg);119 }120}121 122static const struct reg_default wm9712_reg_defaults[] = {123 { 0x02, 0x8000 },124 { 0x04, 0x8000 },125 { 0x06, 0x8000 },126 { 0x08, 0x0f0f },127 { 0x0a, 0xaaa0 },128 { 0x0c, 0xc008 },129 { 0x0e, 0x6808 },130 { 0x10, 0xe808 },131 { 0x12, 0xaaa0 },132 { 0x14, 0xad00 },133 { 0x16, 0x8000 },134 { 0x18, 0xe808 },135 { 0x1a, 0x3000 },136 { 0x1c, 0x8000 },137 { 0x20, 0x0000 },138 { 0x22, 0x0000 },139 { 0x26, 0x000f },140 { 0x28, 0x0605 },141 { 0x2a, 0x0410 },142 { 0x2c, 0xbb80 },143 { 0x2e, 0xbb80 },144 { 0x32, 0xbb80 },145 { 0x34, 0x2000 },146 { 0x4c, 0xf83e },147 { 0x4e, 0xffff },148 { 0x50, 0x0000 },149 { 0x52, 0x0000 },150 { 0x56, 0xf83e },151 { 0x58, 0x0008 },152 { 0x5c, 0x0000 },153 { 0x60, 0xb032 },154 { 0x62, 0x3e00 },155 { 0x64, 0x0000 },156 { 0x76, 0x0006 },157 { 0x78, 0x0001 },158 { 0x7a, 0x0000 },159};160 161static const struct regmap_config wm9712_regmap_config = {162 .reg_bits = 16,163 .reg_stride = 2,164 .val_bits = 16,165 .max_register = 0x7e,166 .cache_type = REGCACHE_MAPLE,167 168 .reg_defaults = wm9712_reg_defaults,169 .num_reg_defaults = ARRAY_SIZE(wm9712_reg_defaults),170 .volatile_reg = wm9712_volatile_reg,171 .readable_reg = wm97xx_readable_reg,172 .writeable_reg = wm97xx_writeable_reg,173};174 175static struct mfd_cell wm9712_cells[] = {176 { .name = "wm9712-codec", },177 { .name = "wm97xx-ts", },178};179 180static const struct reg_default wm9713_reg_defaults[] = {181 { 0x02, 0x8080 }, /* Speaker Output Volume */182 { 0x04, 0x8080 }, /* Headphone Output Volume */183 { 0x06, 0x8080 }, /* Out3/OUT4 Volume */184 { 0x08, 0xc880 }, /* Mono Volume */185 { 0x0a, 0xe808 }, /* LINEIN Volume */186 { 0x0c, 0xe808 }, /* DAC PGA Volume */187 { 0x0e, 0x0808 }, /* MIC PGA Volume */188 { 0x10, 0x00da }, /* MIC Routing Control */189 { 0x12, 0x8000 }, /* Record PGA Volume */190 { 0x14, 0xd600 }, /* Record Routing */191 { 0x16, 0xaaa0 }, /* PCBEEP Volume */192 { 0x18, 0xaaa0 }, /* VxDAC Volume */193 { 0x1a, 0xaaa0 }, /* AUXDAC Volume */194 { 0x1c, 0x0000 }, /* Output PGA Mux */195 { 0x1e, 0x0000 }, /* DAC 3D control */196 { 0x20, 0x0f0f }, /* DAC Tone Control*/197 { 0x22, 0x0040 }, /* MIC Input Select & Bias */198 { 0x24, 0x0000 }, /* Output Volume Mapping & Jack */199 { 0x26, 0x7f00 }, /* Powerdown Ctrl/Stat*/200 { 0x28, 0x0405 }, /* Extended Audio ID */201 { 0x2a, 0x0410 }, /* Extended Audio Start/Ctrl */202 { 0x2c, 0xbb80 }, /* Audio DACs Sample Rate */203 { 0x2e, 0xbb80 }, /* AUXDAC Sample Rate */204 { 0x32, 0xbb80 }, /* Audio ADCs Sample Rate */205 { 0x36, 0x4523 }, /* PCM codec control */206 { 0x3a, 0x2000 }, /* SPDIF control */207 { 0x3c, 0xfdff }, /* Powerdown 1 */208 { 0x3e, 0xffff }, /* Powerdown 2 */209 { 0x40, 0x0000 }, /* General Purpose */210 { 0x42, 0x0000 }, /* Fast Power-Up Control */211 { 0x44, 0x0080 }, /* MCLK/PLL Control */212 { 0x46, 0x0000 }, /* MCLK/PLL Control */213 214 { 0x4c, 0xfffe }, /* GPIO Pin Configuration */215 { 0x4e, 0xffff }, /* GPIO Pin Polarity / Type */216 { 0x50, 0x0000 }, /* GPIO Pin Sticky */217 { 0x52, 0x0000 }, /* GPIO Pin Wake-Up */218 /* GPIO Pin Status */219 { 0x56, 0xfffe }, /* GPIO Pin Sharing */220 { 0x58, 0x4000 }, /* GPIO PullUp/PullDown */221 { 0x5a, 0x0000 }, /* Additional Functions 1 */222 { 0x5c, 0x0000 }, /* Additional Functions 2 */223 { 0x60, 0xb032 }, /* ALC Control */224 { 0x62, 0x3e00 }, /* ALC / Noise Gate Control */225 { 0x64, 0x0000 }, /* AUXDAC input control */226 { 0x74, 0x0000 }, /* Digitiser Reg 1 */227 { 0x76, 0x0006 }, /* Digitiser Reg 2 */228 { 0x78, 0x0001 }, /* Digitiser Reg 3 */229 { 0x7a, 0x0000 }, /* Digitiser Read Back */230};231 232static const struct regmap_config wm9713_regmap_config = {233 .reg_bits = 16,234 .reg_stride = 2,235 .val_bits = 16,236 .max_register = 0x7e,237 .cache_type = REGCACHE_MAPLE,238 239 .reg_defaults = wm9713_reg_defaults,240 .num_reg_defaults = ARRAY_SIZE(wm9713_reg_defaults),241 .volatile_reg = regmap_ac97_default_volatile,242 .readable_reg = wm97xx_readable_reg,243 .writeable_reg = wm97xx_writeable_reg,244};245 246static struct mfd_cell wm9713_cells[] = {247 { .name = "wm9713-codec", },248 { .name = "wm97xx-ts", },249};250 251static int wm97xx_ac97_probe(struct ac97_codec_device *adev)252{253 struct wm97xx_priv *wm97xx;254 const struct regmap_config *config;255 struct wm97xx_platform_data *codec_pdata;256 struct mfd_cell *cells;257 int ret = -ENODEV, nb_cells, i;258 struct wm97xx_pdata *pdata = snd_ac97_codec_get_platdata(adev);259 260 wm97xx = devm_kzalloc(ac97_codec_dev2dev(adev),261 sizeof(*wm97xx), GFP_KERNEL);262 if (!wm97xx)263 return -ENOMEM;264 265 wm97xx->dev = ac97_codec_dev2dev(adev);266 wm97xx->ac97 = snd_ac97_compat_alloc(adev);267 if (IS_ERR(wm97xx->ac97))268 return PTR_ERR(wm97xx->ac97);269 270 271 ac97_set_drvdata(adev, wm97xx);272 dev_info(wm97xx->dev, "wm97xx core found, id=0x%x\n",273 adev->vendor_id);274 275 codec_pdata = &wm97xx->codec_pdata;276 codec_pdata->ac97 = wm97xx->ac97;277 codec_pdata->batt_pdata = pdata ? pdata->batt_pdata : NULL;278 279 switch (adev->vendor_id) {280 case WM9705_VENDOR_ID:281 config = &wm9705_regmap_config;282 cells = wm9705_cells;283 nb_cells = ARRAY_SIZE(wm9705_cells);284 break;285 case WM9712_VENDOR_ID:286 config = &wm9712_regmap_config;287 cells = wm9712_cells;288 nb_cells = ARRAY_SIZE(wm9712_cells);289 break;290 case WM9713_VENDOR_ID:291 config = &wm9713_regmap_config;292 cells = wm9713_cells;293 nb_cells = ARRAY_SIZE(wm9713_cells);294 break;295 default:296 goto err_free_compat;297 }298 299 for (i = 0; i < nb_cells; i++) {300 cells[i].platform_data = codec_pdata;301 cells[i].pdata_size = sizeof(*codec_pdata);302 }303 304 codec_pdata->regmap = devm_regmap_init_ac97(wm97xx->ac97, config);305 if (IS_ERR(codec_pdata->regmap)) {306 ret = PTR_ERR(codec_pdata->regmap);307 goto err_free_compat;308 }309 310 ret = devm_mfd_add_devices(wm97xx->dev, PLATFORM_DEVID_NONE,311 cells, nb_cells, NULL, 0, NULL);312 if (ret)313 goto err_free_compat;314 315 return ret;316 317err_free_compat:318 snd_ac97_compat_release(wm97xx->ac97);319 return ret;320}321 322static void wm97xx_ac97_remove(struct ac97_codec_device *adev)323{324 struct wm97xx_priv *wm97xx = ac97_get_drvdata(adev);325 326 snd_ac97_compat_release(wm97xx->ac97);327}328 329static const struct ac97_id wm97xx_ac97_ids[] = {330 { .id = WM9705_VENDOR_ID, .mask = WM97xx_VENDOR_ID_MASK },331 { .id = WM9712_VENDOR_ID, .mask = WM97xx_VENDOR_ID_MASK },332 { .id = WM9713_VENDOR_ID, .mask = WM97xx_VENDOR_ID_MASK },333 { }334};335 336static struct ac97_codec_driver wm97xx_ac97_driver = {337 .driver = {338 .name = "wm97xx-core",339 },340 .probe = wm97xx_ac97_probe,341 .remove = wm97xx_ac97_remove,342 .id_table = wm97xx_ac97_ids,343};344 345static int __init wm97xx_module_init(void)346{347 return snd_ac97_codec_driver_register(&wm97xx_ac97_driver);348}349module_init(wm97xx_module_init);350 351static void __exit wm97xx_module_exit(void)352{353 snd_ac97_codec_driver_unregister(&wm97xx_ac97_driver);354}355module_exit(wm97xx_module_exit);356 357MODULE_DESCRIPTION("WM9712, WM9713 core driver");358MODULE_AUTHOR("Robert Jarzmik <robert.jarzmik@free.fr>");359MODULE_LICENSE("GPL");360 361