[alsa-devel] [PATCH v7 0/8] AC97 device/driver model revamp

Hi Lars, Mark, Charles, Lee,
This is a revision for Lee and Charles, only targetted at wm97xx-core changes (second round) suggested by Lee and codecs remove path for Charles.
I removed the leading 4 patches as Mark has already scheduled them.
For easier spotting, I included in [1] the diff from the last serie.
As before Charles, I think your ack is not granted anymore for codecs, could you have a look please ?
Cheers.
-- Robert
Robert Jarzmik (8): Input: wm97xx: split out touchscreen registering mfd: wm97xx-core: core support for wm97xx Codec Input: wm97xx: add new AC97 bus support ASoC: wm9713: add ac97 new bus support ASoC: wm9712: add ac97 new bus support ASoC: wm9705: add private structure ASoC: wm9705: add ac97 new bus support ASoC: pxa: switch to new ac97 bus support
drivers/input/touchscreen/Kconfig | 2 +- drivers/input/touchscreen/wm97xx-core.c | 252 +++++++++++++++------- drivers/mfd/Kconfig | 14 ++ drivers/mfd/Makefile | 1 + drivers/mfd/wm97xx-core.c | 366 ++++++++++++++++++++++++++++++++ include/linux/mfd/wm97xx.h | 25 +++ sound/arm/Kconfig | 1 - sound/soc/codecs/Kconfig | 9 +- sound/soc/codecs/wm9705.c | 66 ++++-- sound/soc/codecs/wm9712.c | 44 ++-- sound/soc/codecs/wm9713.c | 39 ++-- sound/soc/pxa/Kconfig | 5 +- sound/soc/pxa/pxa2xx-ac97.c | 46 ++-- 13 files changed, 712 insertions(+), 158 deletions(-) create mode 100644 drivers/mfd/wm97xx-core.c create mode 100644 include/linux/mfd/wm97xx.h

wm97xx-core does several things in it initialization : - touchscreen input device setup - battery device creation
As the wm97xx is actually a multi-function device handling an audio codec, a touchscreen, a gpio block and an ADC, reshape the probing to isolate what is truly input/touchscreen specific from the remaining part.
This is only code shuffling, there is no functional change.
Signed-off-by: Robert Jarzmik robert.jarzmik@free.fr Acked-by: Dmitry Torokhov dmitry.torokhov@gmail.com Acked-by: Charles Keepax ckeepax@opensource.wolfsonmicro.com --- Since v1: - took into account Charles's comment --- drivers/input/touchscreen/wm97xx-core.c | 196 +++++++++++++++++++------------- 1 file changed, 115 insertions(+), 81 deletions(-)
diff --git a/drivers/input/touchscreen/wm97xx-core.c b/drivers/input/touchscreen/wm97xx-core.c index c9d1c91e1887..39869ffdc4fa 100644 --- a/drivers/input/touchscreen/wm97xx-core.c +++ b/drivers/input/touchscreen/wm97xx-core.c @@ -581,27 +581,85 @@ static void wm97xx_ts_input_close(struct input_dev *idev) wm->codec->acc_enable(wm, 0); }
-static int wm97xx_probe(struct device *dev) +static int wm97xx_register_touch(struct wm97xx *wm) { - struct wm97xx *wm; - struct wm97xx_pdata *pdata = dev_get_platdata(dev); - int ret = 0, id = 0; + struct wm97xx_pdata *pdata = dev_get_platdata(wm->dev); + int ret;
- wm = kzalloc(sizeof(struct wm97xx), GFP_KERNEL); - if (!wm) + wm->input_dev = devm_input_allocate_device(wm->dev); + if (wm->input_dev == NULL) return -ENOMEM; - mutex_init(&wm->codec_mutex);
- wm->dev = dev; - dev_set_drvdata(dev, wm); - wm->ac97 = to_ac97_t(dev); + /* set up touch configuration */ + wm->input_dev->name = "wm97xx touchscreen"; + wm->input_dev->phys = "wm97xx"; + wm->input_dev->open = wm97xx_ts_input_open; + wm->input_dev->close = wm97xx_ts_input_close; + + __set_bit(EV_ABS, wm->input_dev->evbit); + __set_bit(EV_KEY, wm->input_dev->evbit); + __set_bit(BTN_TOUCH, wm->input_dev->keybit); + + input_set_abs_params(wm->input_dev, ABS_X, abs_x[0], abs_x[1], + abs_x[2], 0); + input_set_abs_params(wm->input_dev, ABS_Y, abs_y[0], abs_y[1], + abs_y[2], 0); + input_set_abs_params(wm->input_dev, ABS_PRESSURE, abs_p[0], abs_p[1], + abs_p[2], 0); + + input_set_drvdata(wm->input_dev, wm); + wm->input_dev->dev.parent = wm->dev; + + ret = input_register_device(wm->input_dev); + if (ret) + return ret; + + /* + * register our extended touch device (for machine specific + * extensions) + */ + wm->touch_dev = platform_device_alloc("wm97xx-touch", -1); + if (!wm->touch_dev) { + ret = -ENOMEM; + goto touch_err; + } + platform_set_drvdata(wm->touch_dev, wm); + wm->touch_dev->dev.parent = wm->dev; + wm->touch_dev->dev.platform_data = pdata; + ret = platform_device_add(wm->touch_dev); + if (ret < 0) + goto touch_reg_err; + + return 0; +touch_reg_err: + platform_device_put(wm->touch_dev); +touch_err: + input_unregister_device(wm->input_dev); + wm->input_dev = NULL; + + return ret; +} + +static void wm97xx_unregister_touch(struct wm97xx *wm) +{ + platform_device_unregister(wm->touch_dev); + input_unregister_device(wm->input_dev); + wm->input_dev = NULL; +} + +static int _wm97xx_probe(struct wm97xx *wm) +{ + int id = 0; + + mutex_init(&wm->codec_mutex); + dev_set_drvdata(wm->dev, wm);
/* check that we have a supported codec */ id = wm97xx_reg_read(wm, AC97_VENDOR_ID1); if (id != WM97XX_ID1) { - dev_err(dev, "Device with vendor %04x is not a wm97xx\n", id); - ret = -ENODEV; - goto alloc_err; + dev_err(wm->dev, + "Device with vendor %04x is not a wm97xx\n", id); + return -ENODEV; }
wm->id = wm97xx_reg_read(wm, AC97_VENDOR_ID2); @@ -629,8 +687,7 @@ static int wm97xx_probe(struct device *dev) default: dev_err(wm->dev, "Support for wm97%02x not compiled in.\n", wm->id & 0xff); - ret = -ENODEV; - goto alloc_err; + return -ENODEV; }
/* set up physical characteristics */ @@ -644,79 +701,58 @@ static int wm97xx_probe(struct device *dev) wm->gpio[4] = wm97xx_reg_read(wm, AC97_GPIO_STATUS); wm->gpio[5] = wm97xx_reg_read(wm, AC97_MISC_AFE);
- wm->input_dev = input_allocate_device(); - if (wm->input_dev == NULL) { - ret = -ENOMEM; - goto alloc_err; - } - - /* set up touch configuration */ - wm->input_dev->name = "wm97xx touchscreen"; - wm->input_dev->phys = "wm97xx"; - wm->input_dev->open = wm97xx_ts_input_open; - wm->input_dev->close = wm97xx_ts_input_close; - - __set_bit(EV_ABS, wm->input_dev->evbit); - __set_bit(EV_KEY, wm->input_dev->evbit); - __set_bit(BTN_TOUCH, wm->input_dev->keybit); - - input_set_abs_params(wm->input_dev, ABS_X, abs_x[0], abs_x[1], - abs_x[2], 0); - input_set_abs_params(wm->input_dev, ABS_Y, abs_y[0], abs_y[1], - abs_y[2], 0); - input_set_abs_params(wm->input_dev, ABS_PRESSURE, abs_p[0], abs_p[1], - abs_p[2], 0); + return wm97xx_register_touch(wm); +}
- input_set_drvdata(wm->input_dev, wm); - wm->input_dev->dev.parent = dev; +static void wm97xx_remove_battery(struct wm97xx *wm) +{ + platform_device_unregister(wm->battery_dev); +}
- ret = input_register_device(wm->input_dev); - if (ret < 0) - goto dev_alloc_err; +static int wm97xx_add_battery(struct wm97xx *wm, + struct wm97xx_batt_pdata *pdata) +{ + int ret;
- /* register our battery device */ wm->battery_dev = platform_device_alloc("wm97xx-battery", -1); - if (!wm->battery_dev) { - ret = -ENOMEM; - goto batt_err; - } + if (!wm->battery_dev) + return -ENOMEM; + platform_set_drvdata(wm->battery_dev, wm); - wm->battery_dev->dev.parent = dev; - wm->battery_dev->dev.platform_data = pdata ? pdata->batt_pdata : NULL; + wm->battery_dev->dev.parent = wm->dev; + wm->battery_dev->dev.platform_data = pdata; ret = platform_device_add(wm->battery_dev); - if (ret < 0) - goto batt_reg_err; + if (ret) + platform_device_put(wm->battery_dev);
- /* register our extended touch device (for machine specific - * extensions) */ - wm->touch_dev = platform_device_alloc("wm97xx-touch", -1); - if (!wm->touch_dev) { - ret = -ENOMEM; - goto touch_err; - } - platform_set_drvdata(wm->touch_dev, wm); - wm->touch_dev->dev.parent = dev; - wm->touch_dev->dev.platform_data = pdata; - ret = platform_device_add(wm->touch_dev); + return ret; +} + +static int wm97xx_probe(struct device *dev) +{ + struct wm97xx *wm; + int ret; + struct wm97xx_pdata *pdata = dev_get_platdata(dev); + + wm = devm_kzalloc(dev, sizeof(struct wm97xx), GFP_KERNEL); + if (!wm) + return -ENOMEM; + + wm->dev = dev; + wm->ac97 = to_ac97_t(dev); + + ret = _wm97xx_probe(wm); + if (ret) + return ret; + + ret = wm97xx_add_battery(wm, pdata ? pdata->batt_pdata : NULL); if (ret < 0) - goto touch_reg_err; + goto batt_err;
return ret;
- touch_reg_err: - platform_device_put(wm->touch_dev); - touch_err: - platform_device_del(wm->battery_dev); - batt_reg_err: - platform_device_put(wm->battery_dev); - batt_err: - input_unregister_device(wm->input_dev); - wm->input_dev = NULL; - dev_alloc_err: - input_free_device(wm->input_dev); - alloc_err: - kfree(wm); - +batt_err: + wm97xx_unregister_touch(wm); return ret; }
@@ -724,10 +760,8 @@ static int wm97xx_remove(struct device *dev) { struct wm97xx *wm = dev_get_drvdata(dev);
- platform_device_unregister(wm->battery_dev); - platform_device_unregister(wm->touch_dev); - input_unregister_device(wm->input_dev); - kfree(wm); + wm97xx_remove_battery(wm); + wm97xx_unregister_touch(wm);
return 0; }

The WM9705, WM9712 and WM9713 are highly integrated codecs, with an audio codec, DAC and ADC, GPIO unit and a touchscreen interface.
Historically the support was spread across drivers/input/touchscreen and sound/soc/codecs. The sharing was done through ac97 bus sharing. This model will not withstand the new AC97 bus model, where codecs are discovered on runtime.
Signed-off-by: Robert Jarzmik robert.jarzmik@free.fr Acked-by: Charles Keepax ckeepax@opensource.wolfsonmicro.com Acked-by: Lee Jones lee.jones@linaro.org --- Since v3: - added a "depends on AC97_BUS_NEW" Kconfig statement - added default values for wm9705, wm9712 per Charles's comment Since v4: - added Charles's ack Since v5: - took into account Lee's comments Since v6: - took into account Lee's comments --- drivers/mfd/Kconfig | 14 ++ drivers/mfd/Makefile | 1 + drivers/mfd/wm97xx-core.c | 366 +++++++++++++++++++++++++++++++++++++++++++++ include/linux/mfd/wm97xx.h | 25 ++++ 4 files changed, 406 insertions(+) create mode 100644 drivers/mfd/wm97xx-core.c create mode 100644 include/linux/mfd/wm97xx.h
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 55ecdfb74d31..6d175698a49a 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -1628,6 +1628,20 @@ config MFD_WM8994 core support for the WM8994, in order to use the actual functionaltiy of the device other drivers must be enabled.
+config MFD_WM97xx + tristate "Wolfson Microelectronics WM97xx" + select MFD_CORE + select REGMAP_AC97 + select AC97_BUS_COMPAT + depends on AC97_BUS_NEW + help + The WM9705, WM9712 and WM9713 is a highly integrated hi-fi CODEC + designed for smartphone applications. As well as audio functionality + it has on board GPIO and a touchscreen functionality which is + supported via the relevant subsystems. This driver provides core + support for the WM97xx, in order to use the actual functionaltiy of + the device other drivers must be enabled. + config MFD_STW481X tristate "Support for ST Microelectronics STw481x" depends on I2C && (ARCH_NOMADIK || COMPILE_TEST) diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 31ce07611a6f..902c2e46f310 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -69,6 +69,7 @@ obj-$(CONFIG_MFD_WM8350) += wm8350.o obj-$(CONFIG_MFD_WM8350_I2C) += wm8350-i2c.o wm8994-objs := wm8994-core.o wm8994-irq.o wm8994-regmap.o obj-$(CONFIG_MFD_WM8994) += wm8994.o +obj-$(CONFIG_MFD_WM97xx) += wm97xx-core.o
obj-$(CONFIG_TPS6105X) += tps6105x.o obj-$(CONFIG_TPS65010) += tps65010.o diff --git a/drivers/mfd/wm97xx-core.c b/drivers/mfd/wm97xx-core.c new file mode 100644 index 000000000000..4141ee52a70b --- /dev/null +++ b/drivers/mfd/wm97xx-core.c @@ -0,0 +1,366 @@ +/* + * Wolfson WM97xx -- Core device + * + * Copyright (C) 2017 Robert Jarzmik + * + * 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 License, or + * (at your option) any later version. + * + * Features: + * - an AC97 audio codec + * - a touchscreen driver + * - a GPIO block + */ + +#include <linux/device.h> +#include <linux/mfd/core.h> +#include <linux/mfd/wm97xx.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <linux/wm97xx.h> +#include <sound/ac97/codec.h> +#include <sound/ac97/compat.h> + +#define WM9705_VENDOR_ID 0x574d4c05 +#define WM9712_VENDOR_ID 0x574d4c12 +#define WM9713_VENDOR_ID 0x574d4c13 +#define WM97xx_VENDOR_ID_MASK 0xffffffff + +struct wm97xx_priv { + struct regmap *regmap; + struct snd_ac97 *ac97; + struct device *dev; + struct wm97xx_platform_data codec_pdata; +}; + +static bool wm97xx_readable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case AC97_RESET ... AC97_PCM_SURR_DAC_RATE: + case AC97_PCM_LR_ADC_RATE: + case AC97_CENTER_LFE_MASTER: + case AC97_SPDIF ... AC97_LINE1_LEVEL: + case AC97_GPIO_CFG ... 0x5c: + case AC97_CODEC_CLASS_REV ... AC97_PCI_SID: + case 0x74 ... AC97_VENDOR_ID2: + return true; + default: + return false; + } +} + +static bool wm97xx_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case AC97_VENDOR_ID1: + case AC97_VENDOR_ID2: + return false; + default: + return wm97xx_readable_reg(dev, reg); + } +} + +static const struct reg_default wm9705_reg_defaults[] = { + { 0x02, 0x8000 }, + { 0x04, 0x8000 }, + { 0x06, 0x8000 }, + { 0x0a, 0x8000 }, + { 0x0c, 0x8008 }, + { 0x0e, 0x8008 }, + { 0x10, 0x8808 }, + { 0x12, 0x8808 }, + { 0x14, 0x8808 }, + { 0x16, 0x8808 }, + { 0x18, 0x8808 }, + { 0x1a, 0x0000 }, + { 0x1c, 0x8000 }, + { 0x20, 0x0000 }, + { 0x22, 0x0000 }, + { 0x26, 0x000f }, + { 0x28, 0x0605 }, + { 0x2a, 0x0000 }, + { 0x2c, 0xbb80 }, + { 0x32, 0xbb80 }, + { 0x34, 0x2000 }, + { 0x5a, 0x0000 }, + { 0x5c, 0x0000 }, + { 0x72, 0x0808 }, + { 0x74, 0x0000 }, + { 0x76, 0x0006 }, + { 0x78, 0x0000 }, + { 0x7a, 0x0000 }, +}; + +static const struct regmap_config wm9705_regmap_config = { + .reg_bits = 16, + .reg_stride = 2, + .val_bits = 16, + .max_register = 0x7e, + .cache_type = REGCACHE_RBTREE, + + .reg_defaults = wm9705_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(wm9705_reg_defaults), + .volatile_reg = regmap_ac97_default_volatile, + .readable_reg = wm97xx_readable_reg, + .writeable_reg = wm97xx_writeable_reg, +}; + +static struct mfd_cell wm9705_cells[] = { + { .name = "wm9705-codec", }, + { .name = "wm97xx-ts", }, +}; + +static bool wm9712_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case AC97_REC_GAIN: + return true; + default: + return regmap_ac97_default_volatile(dev, reg); + } +} + +static const struct reg_default wm9712_reg_defaults[] = { + { 0x02, 0x8000 }, + { 0x04, 0x8000 }, + { 0x06, 0x8000 }, + { 0x08, 0x0f0f }, + { 0x0a, 0xaaa0 }, + { 0x0c, 0xc008 }, + { 0x0e, 0x6808 }, + { 0x10, 0xe808 }, + { 0x12, 0xaaa0 }, + { 0x14, 0xad00 }, + { 0x16, 0x8000 }, + { 0x18, 0xe808 }, + { 0x1a, 0x3000 }, + { 0x1c, 0x8000 }, + { 0x20, 0x0000 }, + { 0x22, 0x0000 }, + { 0x26, 0x000f }, + { 0x28, 0x0605 }, + { 0x2a, 0x0410 }, + { 0x2c, 0xbb80 }, + { 0x2e, 0xbb80 }, + { 0x32, 0xbb80 }, + { 0x34, 0x2000 }, + { 0x4c, 0xf83e }, + { 0x4e, 0xffff }, + { 0x50, 0x0000 }, + { 0x52, 0x0000 }, + { 0x56, 0xf83e }, + { 0x58, 0x0008 }, + { 0x5c, 0x0000 }, + { 0x60, 0xb032 }, + { 0x62, 0x3e00 }, + { 0x64, 0x0000 }, + { 0x76, 0x0006 }, + { 0x78, 0x0001 }, + { 0x7a, 0x0000 }, +}; + +static const struct regmap_config wm9712_regmap_config = { + .reg_bits = 16, + .reg_stride = 2, + .val_bits = 16, + .max_register = 0x7e, + .cache_type = REGCACHE_RBTREE, + + .reg_defaults = wm9712_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(wm9712_reg_defaults), + .volatile_reg = wm9712_volatile_reg, + .readable_reg = wm97xx_readable_reg, + .writeable_reg = wm97xx_writeable_reg, +}; + +static struct mfd_cell wm9712_cells[] = { + { .name = "wm9712-codec", }, + { .name = "wm97xx-ts", }, +}; + +static const struct reg_default wm9713_reg_defaults[] = { + { 0x02, 0x8080 }, /* Speaker Output Volume */ + { 0x04, 0x8080 }, /* Headphone Output Volume */ + { 0x06, 0x8080 }, /* Out3/OUT4 Volume */ + { 0x08, 0xc880 }, /* Mono Volume */ + { 0x0a, 0xe808 }, /* LINEIN Volume */ + { 0x0c, 0xe808 }, /* DAC PGA Volume */ + { 0x0e, 0x0808 }, /* MIC PGA Volume */ + { 0x10, 0x00da }, /* MIC Routing Control */ + { 0x12, 0x8000 }, /* Record PGA Volume */ + { 0x14, 0xd600 }, /* Record Routing */ + { 0x16, 0xaaa0 }, /* PCBEEP Volume */ + { 0x18, 0xaaa0 }, /* VxDAC Volume */ + { 0x1a, 0xaaa0 }, /* AUXDAC Volume */ + { 0x1c, 0x0000 }, /* Output PGA Mux */ + { 0x1e, 0x0000 }, /* DAC 3D control */ + { 0x20, 0x0f0f }, /* DAC Tone Control*/ + { 0x22, 0x0040 }, /* MIC Input Select & Bias */ + { 0x24, 0x0000 }, /* Output Volume Mapping & Jack */ + { 0x26, 0x7f00 }, /* Powerdown Ctrl/Stat*/ + { 0x28, 0x0405 }, /* Extended Audio ID */ + { 0x2a, 0x0410 }, /* Extended Audio Start/Ctrl */ + { 0x2c, 0xbb80 }, /* Audio DACs Sample Rate */ + { 0x2e, 0xbb80 }, /* AUXDAC Sample Rate */ + { 0x32, 0xbb80 }, /* Audio ADCs Sample Rate */ + { 0x36, 0x4523 }, /* PCM codec control */ + { 0x3a, 0x2000 }, /* SPDIF control */ + { 0x3c, 0xfdff }, /* Powerdown 1 */ + { 0x3e, 0xffff }, /* Powerdown 2 */ + { 0x40, 0x0000 }, /* General Purpose */ + { 0x42, 0x0000 }, /* Fast Power-Up Control */ + { 0x44, 0x0080 }, /* MCLK/PLL Control */ + { 0x46, 0x0000 }, /* MCLK/PLL Control */ + + { 0x4c, 0xfffe }, /* GPIO Pin Configuration */ + { 0x4e, 0xffff }, /* GPIO Pin Polarity / Type */ + { 0x50, 0x0000 }, /* GPIO Pin Sticky */ + { 0x52, 0x0000 }, /* GPIO Pin Wake-Up */ + /* GPIO Pin Status */ + { 0x56, 0xfffe }, /* GPIO Pin Sharing */ + { 0x58, 0x4000 }, /* GPIO PullUp/PullDown */ + { 0x5a, 0x0000 }, /* Additional Functions 1 */ + { 0x5c, 0x0000 }, /* Additional Functions 2 */ + { 0x60, 0xb032 }, /* ALC Control */ + { 0x62, 0x3e00 }, /* ALC / Noise Gate Control */ + { 0x64, 0x0000 }, /* AUXDAC input control */ + { 0x74, 0x0000 }, /* Digitiser Reg 1 */ + { 0x76, 0x0006 }, /* Digitiser Reg 2 */ + { 0x78, 0x0001 }, /* Digitiser Reg 3 */ + { 0x7a, 0x0000 }, /* Digitiser Read Back */ +}; + +static const struct regmap_config wm9713_regmap_config = { + .reg_bits = 16, + .reg_stride = 2, + .val_bits = 16, + .max_register = 0x7e, + .cache_type = REGCACHE_RBTREE, + + .reg_defaults = wm9713_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(wm9713_reg_defaults), + .volatile_reg = regmap_ac97_default_volatile, + .readable_reg = wm97xx_readable_reg, + .writeable_reg = wm97xx_writeable_reg, +}; + +static struct mfd_cell wm9713_cells[] = { + { .name = "wm9713-codec", }, + { .name = "wm97xx-ts", }, +}; + +static int wm97xx_ac97_probe(struct ac97_codec_device *adev) +{ + struct wm97xx_priv *wm97xx; + const struct regmap_config *config; + struct wm97xx_platform_data *codec_pdata; + struct mfd_cell *cells; + int ret = -ENODEV, nb_cells, i; + struct wm97xx_pdata *pdata = snd_ac97_codec_get_platdata(adev); + + wm97xx = devm_kzalloc(ac97_codec_dev2dev(adev), + sizeof(*wm97xx), GFP_KERNEL); + if (!wm97xx) + return -ENOMEM; + + wm97xx->dev = ac97_codec_dev2dev(adev); + wm97xx->ac97 = snd_ac97_compat_alloc(adev); + if (IS_ERR(wm97xx->ac97)) + return PTR_ERR(wm97xx->ac97); + + + ac97_set_drvdata(adev, wm97xx); + dev_info(wm97xx->dev, "wm97xx core found, id=0x%x\n", + adev->vendor_id); + + codec_pdata = &wm97xx->codec_pdata; + codec_pdata->ac97 = wm97xx->ac97; + codec_pdata->batt_pdata = pdata->batt_pdata; + + switch (adev->vendor_id) { + case WM9705_VENDOR_ID: + config = &wm9705_regmap_config; + cells = wm9705_cells; + nb_cells = ARRAY_SIZE(wm9705_cells); + break; + case WM9712_VENDOR_ID: + config = &wm9712_regmap_config; + cells = wm9712_cells; + nb_cells = ARRAY_SIZE(wm9712_cells); + break; + case WM9713_VENDOR_ID: + config = &wm9713_regmap_config; + cells = wm9713_cells; + nb_cells = ARRAY_SIZE(wm9713_cells); + break; + default: + goto err_free_compat; + } + + for (i = 0; i < nb_cells; i++) { + cells[i].platform_data = codec_pdata; + cells[i].pdata_size = sizeof(*codec_pdata); + } + + codec_pdata->regmap = devm_regmap_init_ac97(wm97xx->ac97, config); + if (IS_ERR(codec_pdata->regmap)) { + ret = PTR_ERR(codec_pdata->regmap); + goto err_free_compat; + } + + ret = devm_mfd_add_devices(wm97xx->dev, PLATFORM_DEVID_NONE, + cells, nb_cells, NULL, 0, NULL); + if (ret) + goto err_free_compat; + + return ret; + +err_free_compat: + snd_ac97_compat_release(wm97xx->ac97); + return ret; +} + +static int wm97xx_ac97_remove(struct ac97_codec_device *adev) +{ + struct wm97xx_priv *wm97xx = ac97_get_drvdata(adev); + + snd_ac97_compat_release(wm97xx->ac97); + + return 0; +} + +static const struct ac97_id wm97xx_ac97_ids[] = { + { .id = WM9705_VENDOR_ID, .mask = WM97xx_VENDOR_ID_MASK }, + { .id = WM9712_VENDOR_ID, .mask = WM97xx_VENDOR_ID_MASK }, + { .id = WM9713_VENDOR_ID, .mask = WM97xx_VENDOR_ID_MASK }, + { } +}; + +static struct ac97_codec_driver wm97xx_ac97_driver = { + .driver = { + .name = "wm97xx-core", + }, + .probe = wm97xx_ac97_probe, + .remove = wm97xx_ac97_remove, + .id_table = wm97xx_ac97_ids, +}; + +static int __init wm97xx_module_init(void) +{ + return snd_ac97_codec_driver_register(&wm97xx_ac97_driver); +} +module_init(wm97xx_module_init); + +static void __exit wm97xx_module_exit(void) +{ + snd_ac97_codec_driver_unregister(&wm97xx_ac97_driver); +} +module_exit(wm97xx_module_exit); + +MODULE_DESCRIPTION("WM9712, WM9713 core driver"); +MODULE_AUTHOR("Robert Jarzmik robert.jarzmik@free.fr"); +MODULE_LICENSE("GPL"); + diff --git a/include/linux/mfd/wm97xx.h b/include/linux/mfd/wm97xx.h new file mode 100644 index 000000000000..45fb54f19d09 --- /dev/null +++ b/include/linux/mfd/wm97xx.h @@ -0,0 +1,25 @@ +/* + * wm97xx client interface + * + * Copyright (C) 2017 Robert Jarzmik + * + * 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 License, or + * (at your option) any later version. + */ + +#ifndef __LINUX_MFD_WM97XX_H +#define __LINUX_MFD_WM97XX_H + +struct regmap; +struct wm97xx_batt_pdata; +struct snd_ac97; + +struct wm97xx_platform_data { + struct snd_ac97 *ac97; + struct regmap *regmap; + struct wm97xx_batt_pdata *batt_pdata; +}; + +#endif

On Wed, 13 Sep 2017, Robert Jarzmik wrote:
The WM9705, WM9712 and WM9713 are highly integrated codecs, with an audio codec, DAC and ADC, GPIO unit and a touchscreen interface.
Historically the support was spread across drivers/input/touchscreen and sound/soc/codecs. The sharing was done through ac97 bus sharing. This model will not withstand the new AC97 bus model, where codecs are discovered on runtime.
Signed-off-by: Robert Jarzmik robert.jarzmik@free.fr Acked-by: Charles Keepax ckeepax@opensource.wolfsonmicro.com Acked-by: Lee Jones lee.jones@linaro.org
Since v3:
- added a "depends on AC97_BUS_NEW" Kconfig statement
- added default values for wm9705, wm9712 per Charles's comment
Since v4:
- added Charles's ack
Since v5:
- took into account Lee's comments
Since v6:
- took into account Lee's comments
drivers/mfd/Kconfig | 14 ++ drivers/mfd/Makefile | 1 + drivers/mfd/wm97xx-core.c | 366 +++++++++++++++++++++++++++++++++++++++++++++ include/linux/mfd/wm97xx.h | 25 ++++ 4 files changed, 406 insertions(+) create mode 100644 drivers/mfd/wm97xx-core.c create mode 100644 include/linux/mfd/wm97xx.h
Applied, thanks.

On Thu, 14 Sep 2017, Lee Jones wrote:
On Wed, 13 Sep 2017, Robert Jarzmik wrote:
The WM9705, WM9712 and WM9713 are highly integrated codecs, with an audio codec, DAC and ADC, GPIO unit and a touchscreen interface.
Historically the support was spread across drivers/input/touchscreen and sound/soc/codecs. The sharing was done through ac97 bus sharing. This model will not withstand the new AC97 bus model, where codecs are discovered on runtime.
Signed-off-by: Robert Jarzmik robert.jarzmik@free.fr Acked-by: Charles Keepax ckeepax@opensource.wolfsonmicro.com Acked-by: Lee Jones lee.jones@linaro.org
Since v3:
- added a "depends on AC97_BUS_NEW" Kconfig statement
- added default values for wm9705, wm9712 per Charles's comment
Since v4:
- added Charles's ack
Since v5:
- took into account Lee's comments
Since v6:
- took into account Lee's comments
drivers/mfd/Kconfig | 14 ++ drivers/mfd/Makefile | 1 + drivers/mfd/wm97xx-core.c | 366 +++++++++++++++++++++++++++++++++++++++++++++ include/linux/mfd/wm97xx.h | 25 ++++ 4 files changed, 406 insertions(+) create mode 100644 drivers/mfd/wm97xx-core.c create mode 100644 include/linux/mfd/wm97xx.h
Applied, thanks.
Change of plan. It looks like there are deps.
Unapplied.

On Thu, Sep 14, 2017 at 08:43:00AM +0100, Lee Jones wrote:
Change of plan. It looks like there are deps.
Unapplied.
The core stuff went in during the merge window, you should be able to wait for -rc1 or pick up the asoc-v4.14 tag.

On Thu, 14 Sep 2017, Mark Brown wrote:
On Thu, Sep 14, 2017 at 08:43:00AM +0100, Lee Jones wrote:
Change of plan. It looks like there are deps.
Unapplied.
The core stuff went in during the merge window, you should be able to wait for -rc1 or pick up the asoc-v4.14 tag.
No, I mean patches; 3, 4, 5 and 7 depend on this file.

On Mon, Sep 18, 2017 at 09:52:36AM +0100, Lee Jones wrote:
On Thu, 14 Sep 2017, Mark Brown wrote:
On Thu, Sep 14, 2017 at 08:43:00AM +0100, Lee Jones wrote:
Change of plan. It looks like there are deps.
Unapplied.
The core stuff went in during the merge window, you should be able to wait for -rc1 or pick up the asoc-v4.14 tag.
No, I mean patches; 3, 4, 5 and 7 depend on this file.
Right, but I'd have expected a pull request more than the patch being unapplied?

On Tue, 19 Sep 2017, Mark Brown wrote:
On Mon, Sep 18, 2017 at 09:52:36AM +0100, Lee Jones wrote:
On Thu, 14 Sep 2017, Mark Brown wrote:
On Thu, Sep 14, 2017 at 08:43:00AM +0100, Lee Jones wrote:
Change of plan. It looks like there are deps.
Unapplied.
The core stuff went in during the merge window, you should be able to wait for -rc1 or pick up the asoc-v4.14 tag.
No, I mean patches; 3, 4, 5 and 7 depend on this file.
Right, but I'd have expected a pull request more than the patch being unapplied?
Normally I wait until all patches patches in the set have been reviewed (the ASOC ones still have not), then ask the OP and the other affected Maintainers how they want to deal with the patch. Far more often than not, the the solution is just to take the set through the MFD tree.
However, since this is a new driver, you can just take it with my:
Acked-by: Lee Jones lee.jones@linaro.org

On Tue, 19 Sep 2017, Lee Jones wrote:
On Tue, 19 Sep 2017, Mark Brown wrote:
On Mon, Sep 18, 2017 at 09:52:36AM +0100, Lee Jones wrote:
On Thu, 14 Sep 2017, Mark Brown wrote:
On Thu, Sep 14, 2017 at 08:43:00AM +0100, Lee Jones wrote:
Change of plan. It looks like there are deps.
Unapplied.
The core stuff went in during the merge window, you should be able to wait for -rc1 or pick up the asoc-v4.14 tag.
No, I mean patches; 3, 4, 5 and 7 depend on this file.
Right, but I'd have expected a pull request more than the patch being unapplied?
Normally I wait until all patches patches in the set have been reviewed (the ASOC ones still have not), then ask the OP and the other affected Maintainers how they want to deal with the patch. Far more often than not, the the solution is just to take the set through the MFD tree.
However, since this is a new driver, you can just take it with my:
Acked-by: Lee Jones lee.jones@linaro.org
... although that does mean that you'll have to handle one of the Input patches too.

On Tue, Sep 19, 2017 at 04:22:53PM +0100, Lee Jones wrote:
Normally I wait until all patches patches in the set have been reviewed (the ASOC ones still have not), then ask the OP and the
I've been fine with them for ages.
other affected Maintainers how they want to deal with the patch. Far more often than not, the the solution is just to take the set through the MFD tree.
It really helps if we can get the earlier parts of the series moving even if the rest of it's in review still - I know I back off on reviewing things if it looks like their dependencies aren't making progress.

On Tue, 19 Sep 2017, Mark Brown wrote:
On Tue, Sep 19, 2017 at 04:22:53PM +0100, Lee Jones wrote:
Normally I wait until all patches patches in the set have been reviewed (the ASOC ones still have not), then ask the OP and the
I've been fine with them for ages.
other affected Maintainers how they want to deal with the patch. Far more often than not, the the solution is just to take the set through the MFD tree.
It really helps if we can get the earlier parts of the series moving even if the rest of it's in review still - I know I back off on reviewing things if it looks like their dependencies aren't making progress.
Just for your FYI, I tend to review my parts then wait for the rest of the series to be reviewed, then discuss how the set should be handled.
Holding back on reviewing your parts is not a good idea. If everyone did that we'd end up in a circular Maintainer dependency and nothing would ever get reviewed.

On Wed, Sep 20, 2017 at 09:24:43AM +0100, Lee Jones wrote:
On Tue, 19 Sep 2017, Mark Brown wrote:
It really helps if we can get the earlier parts of the series moving even if the rest of it's in review still - I know I back off on reviewing things if it looks like their dependencies aren't making progress.
Just for your FYI, I tend to review my parts then wait for the rest of the series to be reviewed, then discuss how the set should be handled.
Holding back on reviewing your parts is not a good idea. If everyone did that we'd end up in a circular Maintainer dependency and nothing would ever get reviewed.
I get fed up with having to re-review the same patches over and over and over again while people work out what's going on with the earlier parts of the series, that gets very time consuming and is error prone. If we're getting churn early on in the series that's causing things to change then it's better to let that settle down and look properly at the results than glaze over looking at each minor change as it comes in and miss other things that get tweaked. Once the dependencies are stable then it becomes worthwhile again.

On Tue, Sep 19, 2017 at 04:22:53PM +0100, Lee Jones wrote:
However, since this is a new driver, you can just take it with my:
The following changes since commit 2bd6bf03f4c1c59381d62c61d03f6cc3fe71f66e:
Linux 4.14-rc1 (2017-09-16 15:47:51 -0700)
are available in the git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git tags/wm97xx-mfd
for you to fetch changes up to ae9d1b5fbd7b7ee1c2d2f62a7cee3c0455e06f94:
Input: wm97xx: add new AC97 bus support (2017-09-19 17:07:28 +0100)
---------------------------------------------------------------- mfd/Input: Changes for the new wm97xx MFD
The newly added proper driver model support for ac97 means we can now have a real MFD for the wm97xx devices rather than the hacks that have been used. Since they're mainly audio devices they're going via ASoC, these are the other changes for cross tree merging.
---------------------------------------------------------------- Robert Jarzmik (3): Input: wm97xx: split out touchscreen registering mfd: wm97xx-core: core support for wm97xx Codec Input: wm97xx: add new AC97 bus support
drivers/input/touchscreen/Kconfig | 2 +- drivers/input/touchscreen/wm97xx-core.c | 252 +++++++++++++++------- drivers/mfd/Kconfig | 14 ++ drivers/mfd/Makefile | 1 + drivers/mfd/wm97xx-core.c | 366 ++++++++++++++++++++++++++++++++ include/linux/mfd/wm97xx.h | 25 +++ 6 files changed, 577 insertions(+), 83 deletions(-) create mode 100644 drivers/mfd/wm97xx-core.c create mode 100644 include/linux/mfd/wm97xx.h

The patch
mfd: wm97xx-core: core support for wm97xx Codec
has been applied to the asoc tree at
git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git
All being well this means that it will be integrated into the linux-next tree (usually sometime in the next 24 hours) and sent to Linus during the next merge window (or sooner if it is a bug fix), however if problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing and review of the tree, please engage with people reporting problems and send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they should be sent as incremental updates against current git, existing patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying to this mail.
Thanks, Mark
From a5c6951c49fbf7509419d877620625ca3cdae9b1 Mon Sep 17 00:00:00 2001
From: Robert Jarzmik robert.jarzmik@free.fr Date: Wed, 13 Sep 2017 21:37:17 +0200 Subject: [PATCH] mfd: wm97xx-core: core support for wm97xx Codec
The WM9705, WM9712 and WM9713 are highly integrated codecs, with an audio codec, DAC and ADC, GPIO unit and a touchscreen interface.
Historically the support was spread across drivers/input/touchscreen and sound/soc/codecs. The sharing was done through ac97 bus sharing. This model will not withstand the new AC97 bus model, where codecs are discovered on runtime.
Signed-off-by: Robert Jarzmik robert.jarzmik@free.fr Acked-by: Charles Keepax ckeepax@opensource.wolfsonmicro.com Acked-by: Lee Jones lee.jones@linaro.org Signed-off-by: Mark Brown broonie@kernel.org --- drivers/mfd/Kconfig | 14 ++ drivers/mfd/Makefile | 1 + drivers/mfd/wm97xx-core.c | 366 +++++++++++++++++++++++++++++++++++++++++++++ include/linux/mfd/wm97xx.h | 25 ++++ 4 files changed, 406 insertions(+) create mode 100644 drivers/mfd/wm97xx-core.c create mode 100644 include/linux/mfd/wm97xx.h
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index fc5e4fef89d2..ac5ad6d0837c 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -1746,6 +1746,20 @@ config MFD_WM8994 core support for the WM8994, in order to use the actual functionaltiy of the device other drivers must be enabled.
+config MFD_WM97xx + tristate "Wolfson Microelectronics WM97xx" + select MFD_CORE + select REGMAP_AC97 + select AC97_BUS_COMPAT + depends on AC97_BUS_NEW + help + The WM9705, WM9712 and WM9713 is a highly integrated hi-fi CODEC + designed for smartphone applications. As well as audio functionality + it has on board GPIO and a touchscreen functionality which is + supported via the relevant subsystems. This driver provides core + support for the WM97xx, in order to use the actual functionaltiy of + the device other drivers must be enabled. + config MFD_STW481X tristate "Support for ST Microelectronics STw481x" depends on I2C && (ARCH_NOMADIK || COMPILE_TEST) diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index c3d0a1b39bb6..3266977f54b8 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -73,6 +73,7 @@ obj-$(CONFIG_MFD_WM8350) += wm8350.o obj-$(CONFIG_MFD_WM8350_I2C) += wm8350-i2c.o wm8994-objs := wm8994-core.o wm8994-irq.o wm8994-regmap.o obj-$(CONFIG_MFD_WM8994) += wm8994.o +obj-$(CONFIG_MFD_WM97xx) += wm97xx-core.o
obj-$(CONFIG_TPS6105X) += tps6105x.o obj-$(CONFIG_TPS65010) += tps65010.o diff --git a/drivers/mfd/wm97xx-core.c b/drivers/mfd/wm97xx-core.c new file mode 100644 index 000000000000..4141ee52a70b --- /dev/null +++ b/drivers/mfd/wm97xx-core.c @@ -0,0 +1,366 @@ +/* + * Wolfson WM97xx -- Core device + * + * Copyright (C) 2017 Robert Jarzmik + * + * 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 License, or + * (at your option) any later version. + * + * Features: + * - an AC97 audio codec + * - a touchscreen driver + * - a GPIO block + */ + +#include <linux/device.h> +#include <linux/mfd/core.h> +#include <linux/mfd/wm97xx.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <linux/wm97xx.h> +#include <sound/ac97/codec.h> +#include <sound/ac97/compat.h> + +#define WM9705_VENDOR_ID 0x574d4c05 +#define WM9712_VENDOR_ID 0x574d4c12 +#define WM9713_VENDOR_ID 0x574d4c13 +#define WM97xx_VENDOR_ID_MASK 0xffffffff + +struct wm97xx_priv { + struct regmap *regmap; + struct snd_ac97 *ac97; + struct device *dev; + struct wm97xx_platform_data codec_pdata; +}; + +static bool wm97xx_readable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case AC97_RESET ... AC97_PCM_SURR_DAC_RATE: + case AC97_PCM_LR_ADC_RATE: + case AC97_CENTER_LFE_MASTER: + case AC97_SPDIF ... AC97_LINE1_LEVEL: + case AC97_GPIO_CFG ... 0x5c: + case AC97_CODEC_CLASS_REV ... AC97_PCI_SID: + case 0x74 ... AC97_VENDOR_ID2: + return true; + default: + return false; + } +} + +static bool wm97xx_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case AC97_VENDOR_ID1: + case AC97_VENDOR_ID2: + return false; + default: + return wm97xx_readable_reg(dev, reg); + } +} + +static const struct reg_default wm9705_reg_defaults[] = { + { 0x02, 0x8000 }, + { 0x04, 0x8000 }, + { 0x06, 0x8000 }, + { 0x0a, 0x8000 }, + { 0x0c, 0x8008 }, + { 0x0e, 0x8008 }, + { 0x10, 0x8808 }, + { 0x12, 0x8808 }, + { 0x14, 0x8808 }, + { 0x16, 0x8808 }, + { 0x18, 0x8808 }, + { 0x1a, 0x0000 }, + { 0x1c, 0x8000 }, + { 0x20, 0x0000 }, + { 0x22, 0x0000 }, + { 0x26, 0x000f }, + { 0x28, 0x0605 }, + { 0x2a, 0x0000 }, + { 0x2c, 0xbb80 }, + { 0x32, 0xbb80 }, + { 0x34, 0x2000 }, + { 0x5a, 0x0000 }, + { 0x5c, 0x0000 }, + { 0x72, 0x0808 }, + { 0x74, 0x0000 }, + { 0x76, 0x0006 }, + { 0x78, 0x0000 }, + { 0x7a, 0x0000 }, +}; + +static const struct regmap_config wm9705_regmap_config = { + .reg_bits = 16, + .reg_stride = 2, + .val_bits = 16, + .max_register = 0x7e, + .cache_type = REGCACHE_RBTREE, + + .reg_defaults = wm9705_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(wm9705_reg_defaults), + .volatile_reg = regmap_ac97_default_volatile, + .readable_reg = wm97xx_readable_reg, + .writeable_reg = wm97xx_writeable_reg, +}; + +static struct mfd_cell wm9705_cells[] = { + { .name = "wm9705-codec", }, + { .name = "wm97xx-ts", }, +}; + +static bool wm9712_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case AC97_REC_GAIN: + return true; + default: + return regmap_ac97_default_volatile(dev, reg); + } +} + +static const struct reg_default wm9712_reg_defaults[] = { + { 0x02, 0x8000 }, + { 0x04, 0x8000 }, + { 0x06, 0x8000 }, + { 0x08, 0x0f0f }, + { 0x0a, 0xaaa0 }, + { 0x0c, 0xc008 }, + { 0x0e, 0x6808 }, + { 0x10, 0xe808 }, + { 0x12, 0xaaa0 }, + { 0x14, 0xad00 }, + { 0x16, 0x8000 }, + { 0x18, 0xe808 }, + { 0x1a, 0x3000 }, + { 0x1c, 0x8000 }, + { 0x20, 0x0000 }, + { 0x22, 0x0000 }, + { 0x26, 0x000f }, + { 0x28, 0x0605 }, + { 0x2a, 0x0410 }, + { 0x2c, 0xbb80 }, + { 0x2e, 0xbb80 }, + { 0x32, 0xbb80 }, + { 0x34, 0x2000 }, + { 0x4c, 0xf83e }, + { 0x4e, 0xffff }, + { 0x50, 0x0000 }, + { 0x52, 0x0000 }, + { 0x56, 0xf83e }, + { 0x58, 0x0008 }, + { 0x5c, 0x0000 }, + { 0x60, 0xb032 }, + { 0x62, 0x3e00 }, + { 0x64, 0x0000 }, + { 0x76, 0x0006 }, + { 0x78, 0x0001 }, + { 0x7a, 0x0000 }, +}; + +static const struct regmap_config wm9712_regmap_config = { + .reg_bits = 16, + .reg_stride = 2, + .val_bits = 16, + .max_register = 0x7e, + .cache_type = REGCACHE_RBTREE, + + .reg_defaults = wm9712_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(wm9712_reg_defaults), + .volatile_reg = wm9712_volatile_reg, + .readable_reg = wm97xx_readable_reg, + .writeable_reg = wm97xx_writeable_reg, +}; + +static struct mfd_cell wm9712_cells[] = { + { .name = "wm9712-codec", }, + { .name = "wm97xx-ts", }, +}; + +static const struct reg_default wm9713_reg_defaults[] = { + { 0x02, 0x8080 }, /* Speaker Output Volume */ + { 0x04, 0x8080 }, /* Headphone Output Volume */ + { 0x06, 0x8080 }, /* Out3/OUT4 Volume */ + { 0x08, 0xc880 }, /* Mono Volume */ + { 0x0a, 0xe808 }, /* LINEIN Volume */ + { 0x0c, 0xe808 }, /* DAC PGA Volume */ + { 0x0e, 0x0808 }, /* MIC PGA Volume */ + { 0x10, 0x00da }, /* MIC Routing Control */ + { 0x12, 0x8000 }, /* Record PGA Volume */ + { 0x14, 0xd600 }, /* Record Routing */ + { 0x16, 0xaaa0 }, /* PCBEEP Volume */ + { 0x18, 0xaaa0 }, /* VxDAC Volume */ + { 0x1a, 0xaaa0 }, /* AUXDAC Volume */ + { 0x1c, 0x0000 }, /* Output PGA Mux */ + { 0x1e, 0x0000 }, /* DAC 3D control */ + { 0x20, 0x0f0f }, /* DAC Tone Control*/ + { 0x22, 0x0040 }, /* MIC Input Select & Bias */ + { 0x24, 0x0000 }, /* Output Volume Mapping & Jack */ + { 0x26, 0x7f00 }, /* Powerdown Ctrl/Stat*/ + { 0x28, 0x0405 }, /* Extended Audio ID */ + { 0x2a, 0x0410 }, /* Extended Audio Start/Ctrl */ + { 0x2c, 0xbb80 }, /* Audio DACs Sample Rate */ + { 0x2e, 0xbb80 }, /* AUXDAC Sample Rate */ + { 0x32, 0xbb80 }, /* Audio ADCs Sample Rate */ + { 0x36, 0x4523 }, /* PCM codec control */ + { 0x3a, 0x2000 }, /* SPDIF control */ + { 0x3c, 0xfdff }, /* Powerdown 1 */ + { 0x3e, 0xffff }, /* Powerdown 2 */ + { 0x40, 0x0000 }, /* General Purpose */ + { 0x42, 0x0000 }, /* Fast Power-Up Control */ + { 0x44, 0x0080 }, /* MCLK/PLL Control */ + { 0x46, 0x0000 }, /* MCLK/PLL Control */ + + { 0x4c, 0xfffe }, /* GPIO Pin Configuration */ + { 0x4e, 0xffff }, /* GPIO Pin Polarity / Type */ + { 0x50, 0x0000 }, /* GPIO Pin Sticky */ + { 0x52, 0x0000 }, /* GPIO Pin Wake-Up */ + /* GPIO Pin Status */ + { 0x56, 0xfffe }, /* GPIO Pin Sharing */ + { 0x58, 0x4000 }, /* GPIO PullUp/PullDown */ + { 0x5a, 0x0000 }, /* Additional Functions 1 */ + { 0x5c, 0x0000 }, /* Additional Functions 2 */ + { 0x60, 0xb032 }, /* ALC Control */ + { 0x62, 0x3e00 }, /* ALC / Noise Gate Control */ + { 0x64, 0x0000 }, /* AUXDAC input control */ + { 0x74, 0x0000 }, /* Digitiser Reg 1 */ + { 0x76, 0x0006 }, /* Digitiser Reg 2 */ + { 0x78, 0x0001 }, /* Digitiser Reg 3 */ + { 0x7a, 0x0000 }, /* Digitiser Read Back */ +}; + +static const struct regmap_config wm9713_regmap_config = { + .reg_bits = 16, + .reg_stride = 2, + .val_bits = 16, + .max_register = 0x7e, + .cache_type = REGCACHE_RBTREE, + + .reg_defaults = wm9713_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(wm9713_reg_defaults), + .volatile_reg = regmap_ac97_default_volatile, + .readable_reg = wm97xx_readable_reg, + .writeable_reg = wm97xx_writeable_reg, +}; + +static struct mfd_cell wm9713_cells[] = { + { .name = "wm9713-codec", }, + { .name = "wm97xx-ts", }, +}; + +static int wm97xx_ac97_probe(struct ac97_codec_device *adev) +{ + struct wm97xx_priv *wm97xx; + const struct regmap_config *config; + struct wm97xx_platform_data *codec_pdata; + struct mfd_cell *cells; + int ret = -ENODEV, nb_cells, i; + struct wm97xx_pdata *pdata = snd_ac97_codec_get_platdata(adev); + + wm97xx = devm_kzalloc(ac97_codec_dev2dev(adev), + sizeof(*wm97xx), GFP_KERNEL); + if (!wm97xx) + return -ENOMEM; + + wm97xx->dev = ac97_codec_dev2dev(adev); + wm97xx->ac97 = snd_ac97_compat_alloc(adev); + if (IS_ERR(wm97xx->ac97)) + return PTR_ERR(wm97xx->ac97); + + + ac97_set_drvdata(adev, wm97xx); + dev_info(wm97xx->dev, "wm97xx core found, id=0x%x\n", + adev->vendor_id); + + codec_pdata = &wm97xx->codec_pdata; + codec_pdata->ac97 = wm97xx->ac97; + codec_pdata->batt_pdata = pdata->batt_pdata; + + switch (adev->vendor_id) { + case WM9705_VENDOR_ID: + config = &wm9705_regmap_config; + cells = wm9705_cells; + nb_cells = ARRAY_SIZE(wm9705_cells); + break; + case WM9712_VENDOR_ID: + config = &wm9712_regmap_config; + cells = wm9712_cells; + nb_cells = ARRAY_SIZE(wm9712_cells); + break; + case WM9713_VENDOR_ID: + config = &wm9713_regmap_config; + cells = wm9713_cells; + nb_cells = ARRAY_SIZE(wm9713_cells); + break; + default: + goto err_free_compat; + } + + for (i = 0; i < nb_cells; i++) { + cells[i].platform_data = codec_pdata; + cells[i].pdata_size = sizeof(*codec_pdata); + } + + codec_pdata->regmap = devm_regmap_init_ac97(wm97xx->ac97, config); + if (IS_ERR(codec_pdata->regmap)) { + ret = PTR_ERR(codec_pdata->regmap); + goto err_free_compat; + } + + ret = devm_mfd_add_devices(wm97xx->dev, PLATFORM_DEVID_NONE, + cells, nb_cells, NULL, 0, NULL); + if (ret) + goto err_free_compat; + + return ret; + +err_free_compat: + snd_ac97_compat_release(wm97xx->ac97); + return ret; +} + +static int wm97xx_ac97_remove(struct ac97_codec_device *adev) +{ + struct wm97xx_priv *wm97xx = ac97_get_drvdata(adev); + + snd_ac97_compat_release(wm97xx->ac97); + + return 0; +} + +static const struct ac97_id wm97xx_ac97_ids[] = { + { .id = WM9705_VENDOR_ID, .mask = WM97xx_VENDOR_ID_MASK }, + { .id = WM9712_VENDOR_ID, .mask = WM97xx_VENDOR_ID_MASK }, + { .id = WM9713_VENDOR_ID, .mask = WM97xx_VENDOR_ID_MASK }, + { } +}; + +static struct ac97_codec_driver wm97xx_ac97_driver = { + .driver = { + .name = "wm97xx-core", + }, + .probe = wm97xx_ac97_probe, + .remove = wm97xx_ac97_remove, + .id_table = wm97xx_ac97_ids, +}; + +static int __init wm97xx_module_init(void) +{ + return snd_ac97_codec_driver_register(&wm97xx_ac97_driver); +} +module_init(wm97xx_module_init); + +static void __exit wm97xx_module_exit(void) +{ + snd_ac97_codec_driver_unregister(&wm97xx_ac97_driver); +} +module_exit(wm97xx_module_exit); + +MODULE_DESCRIPTION("WM9712, WM9713 core driver"); +MODULE_AUTHOR("Robert Jarzmik robert.jarzmik@free.fr"); +MODULE_LICENSE("GPL"); + diff --git a/include/linux/mfd/wm97xx.h b/include/linux/mfd/wm97xx.h new file mode 100644 index 000000000000..45fb54f19d09 --- /dev/null +++ b/include/linux/mfd/wm97xx.h @@ -0,0 +1,25 @@ +/* + * wm97xx client interface + * + * Copyright (C) 2017 Robert Jarzmik + * + * 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 License, or + * (at your option) any later version. + */ + +#ifndef __LINUX_MFD_WM97XX_H +#define __LINUX_MFD_WM97XX_H + +struct regmap; +struct wm97xx_batt_pdata; +struct snd_ac97; + +struct wm97xx_platform_data { + struct snd_ac97 *ac97; + struct regmap *regmap; + struct wm97xx_batt_pdata *batt_pdata; +}; + +#endif

This adds support for the new AC97 bus code, which discovers the devices rather than uses platform data.
As part of this discovery, it enables a multi-function device wm97xx, which supports touchscreen, battery, ADC and an audio codec. This patch adds the code to bind the touchscreen "cell" as the touchscreen driver.
This was tested on the pxa architecture with a pxa270 + wm9713 + the mioa701 touchscreen.
Signed-off-by: Robert Jarzmik robert.jarzmik@free.fr Acked-by: Charles Keepax ckeepax@opensource.wolfsonmicro.com Acked-by: Dmitry Torokhov dmitry.torokhov@gmail.com --- drivers/input/touchscreen/Kconfig | 2 +- drivers/input/touchscreen/wm97xx-core.c | 56 ++++++++++++++++++++++++++++++++- 2 files changed, 56 insertions(+), 2 deletions(-)
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 033599777651..22257f3e1059 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -725,7 +725,7 @@ config TOUCHSCREEN_WM831X
config TOUCHSCREEN_WM97XX tristate "Support for WM97xx AC97 touchscreen controllers" - depends on AC97_BUS + depends on AC97_BUS || AC97_BUS_NEW help Say Y here if you have a Wolfson Microelectronics WM97xx touchscreen connected to your system. Note that this option diff --git a/drivers/input/touchscreen/wm97xx-core.c b/drivers/input/touchscreen/wm97xx-core.c index 39869ffdc4fa..fd714ee881f7 100644 --- a/drivers/input/touchscreen/wm97xx-core.c +++ b/drivers/input/touchscreen/wm97xx-core.c @@ -44,6 +44,7 @@ #include <linux/pm.h> #include <linux/interrupt.h> #include <linux/bitops.h> +#include <linux/mfd/wm97xx.h> #include <linux/workqueue.h> #include <linux/wm97xx.h> #include <linux/uaccess.h> @@ -766,6 +767,39 @@ static int wm97xx_remove(struct device *dev) return 0; }
+static int wm97xx_mfd_probe(struct platform_device *pdev) +{ + struct wm97xx *wm; + struct wm97xx_platform_data *mfd_pdata = dev_get_platdata(&pdev->dev); + int ret; + + wm = devm_kzalloc(&pdev->dev, sizeof(struct wm97xx), GFP_KERNEL); + if (!wm) + return -ENOMEM; + + wm->dev = &pdev->dev; + wm->ac97 = mfd_pdata->ac97; + + ret = _wm97xx_probe(wm); + if (ret) + return ret; + + ret = wm97xx_add_battery(wm, mfd_pdata->batt_pdata); + if (ret < 0) + goto batt_err; + + return ret; + +batt_err: + wm97xx_unregister_touch(wm); + return ret; +} + +static int wm97xx_mfd_remove(struct platform_device *pdev) +{ + return wm97xx_remove(&pdev->dev); +} + static int __maybe_unused wm97xx_suspend(struct device *dev) { struct wm97xx *wm = dev_get_drvdata(dev); @@ -862,21 +896,41 @@ EXPORT_SYMBOL_GPL(wm97xx_unregister_mach_ops);
static struct device_driver wm97xx_driver = { .name = "wm97xx-ts", +#ifdef CONFIG_AC97_BUS .bus = &ac97_bus_type, +#endif .owner = THIS_MODULE, .probe = wm97xx_probe, .remove = wm97xx_remove, .pm = &wm97xx_pm_ops, };
+static struct platform_driver wm97xx_mfd_driver = { + .driver = { + .name = "wm97xx-ts", + .pm = &wm97xx_pm_ops, + }, + .probe = wm97xx_mfd_probe, + .remove = wm97xx_mfd_remove, +}; + static int __init wm97xx_init(void) { - return driver_register(&wm97xx_driver); + int ret; + + ret = platform_driver_register(&wm97xx_mfd_driver); + if (ret) + return ret; + + if (IS_BUILTIN(CONFIG_AC97_BUS)) + ret = driver_register(&wm97xx_driver); + return ret; }
static void __exit wm97xx_exit(void) { driver_unregister(&wm97xx_driver); + platform_driver_unregister(&wm97xx_mfd_driver); }
module_init(wm97xx_init);

Add support for the new ac97 bus model, where devices are automatically discovered on AC-Links.
Signed-off-by: Robert Jarzmik robert.jarzmik@free.fr Acked-by: Charles Keepax ckeepax@opensource.wolfsonmicro.com --- Since v6: reworked wm9713_soc_remove() --- sound/soc/codecs/Kconfig | 3 ++- sound/soc/codecs/wm9713.c | 39 +++++++++++++++++++++++++++------------ 2 files changed, 29 insertions(+), 13 deletions(-)
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index e49e9da7f1f6..aebf148d7e4d 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -207,7 +207,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_WM9090 if I2C select SND_SOC_WM9705 if SND_SOC_AC97_BUS select SND_SOC_WM9712 if SND_SOC_AC97_BUS - select SND_SOC_WM9713 if SND_SOC_AC97_BUS + select SND_SOC_WM9713 if (SND_SOC_AC97_BUS || SND_SOC_AC97_BUS_NEW) help Normally ASoC codec drivers are only built if a machine driver which uses them is also built since they are only usable with a machine @@ -1091,6 +1091,7 @@ config SND_SOC_WM9712 config SND_SOC_WM9713 tristate select REGMAP_AC97 + select AC97_BUS_COMPAT if AC97_BUS_NEW
# Amp config SND_SOC_LM4857 diff --git a/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c index 7e4822185feb..df7220656d98 100644 --- a/sound/soc/codecs/wm9713.c +++ b/sound/soc/codecs/wm9713.c @@ -17,12 +17,15 @@
#include <linux/init.h> #include <linux/slab.h> +#include <linux/mfd/wm97xx.h> #include <linux/module.h> #include <linux/device.h> #include <linux/regmap.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/ac97_codec.h> +#include <sound/ac97/codec.h> +#include <sound/ac97/compat.h> #include <sound/initval.h> #include <sound/pcm_params.h> #include <sound/tlv.h> @@ -38,6 +41,7 @@ struct wm9713_priv { u32 pll_in; /* PLL input frequency */ unsigned int hp_mixer[2]; struct mutex lock; + struct wm97xx_platform_data *mfd_pdata; };
#define HPL_MIXER 0 @@ -1205,17 +1209,23 @@ static int wm9713_soc_resume(struct snd_soc_codec *codec) static int wm9713_soc_probe(struct snd_soc_codec *codec) { struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec); - struct regmap *regmap; + struct regmap *regmap = NULL;
- wm9713->ac97 = snd_soc_new_ac97_codec(codec, WM9713_VENDOR_ID, - WM9713_VENDOR_ID_MASK); - if (IS_ERR(wm9713->ac97)) - return PTR_ERR(wm9713->ac97); - - regmap = regmap_init_ac97(wm9713->ac97, &wm9713_regmap_config); - if (IS_ERR(regmap)) { - snd_soc_free_ac97_codec(wm9713->ac97); - return PTR_ERR(regmap); + if (wm9713->mfd_pdata) { + wm9713->ac97 = wm9713->mfd_pdata->ac97; + regmap = wm9713->mfd_pdata->regmap; + } else { +#ifdef CONFIG_SND_SOC_AC97_BUS + wm9713->ac97 = snd_soc_new_ac97_codec(codec, WM9713_VENDOR_ID, + WM9713_VENDOR_ID_MASK); + if (IS_ERR(wm9713->ac97)) + return PTR_ERR(wm9713->ac97); + regmap = regmap_init_ac97(wm9713->ac97, &wm9713_regmap_config); + if (IS_ERR(regmap)) { + snd_soc_free_ac97_codec(wm9713->ac97); + return PTR_ERR(regmap); + } +#endif }
snd_soc_codec_init_regmap(codec, regmap); @@ -1228,10 +1238,14 @@ static int wm9713_soc_probe(struct snd_soc_codec *codec)
static int wm9713_soc_remove(struct snd_soc_codec *codec) { +#ifdef CONFIG_SND_SOC_AC97_BUS struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec);
- snd_soc_codec_exit_regmap(codec); - snd_soc_free_ac97_codec(wm9713->ac97); + if (!wm9713->mfd_pdata) { + snd_soc_codec_exit_regmap(codec); + snd_soc_free_ac97_codec(wm9713->ac97); + } +#endif return 0; }
@@ -1262,6 +1276,7 @@ static int wm9713_probe(struct platform_device *pdev)
mutex_init(&wm9713->lock);
+ wm9713->mfd_pdata = dev_get_platdata(&pdev->dev); platform_set_drvdata(pdev, wm9713);
return snd_soc_register_codec(&pdev->dev,

Add support for the new ac97 bus model, where devices are automatically discovered on AC-Links.
Signed-off-by: Robert Jarzmik robert.jarzmik@free.fr --- Since v4: - added #ifdef spotted by Charles Since v6: reworked wm9712_soc_remove() --- sound/soc/codecs/Kconfig | 3 ++- sound/soc/codecs/wm9712.c | 44 ++++++++++++++++++++++++++++++-------------- 2 files changed, 32 insertions(+), 15 deletions(-)
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index aebf148d7e4d..f17af3592785 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -206,7 +206,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_WM9081 if I2C select SND_SOC_WM9090 if I2C select SND_SOC_WM9705 if SND_SOC_AC97_BUS - select SND_SOC_WM9712 if SND_SOC_AC97_BUS + select SND_SOC_WM9712 if (SND_SOC_AC97_BUS || SND_SOC_AC97_BUS_NEW) select SND_SOC_WM9713 if (SND_SOC_AC97_BUS || SND_SOC_AC97_BUS_NEW) help Normally ASoC codec drivers are only built if a machine driver which @@ -1087,6 +1087,7 @@ config SND_SOC_WM9705 config SND_SOC_WM9712 tristate select REGMAP_AC97 + select AC97_BUS_COMPAT if AC97_BUS_NEW
config SND_SOC_WM9713 tristate diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c index 1a3e1797994a..1e228bf9f1ae 100644 --- a/sound/soc/codecs/wm9712.c +++ b/sound/soc/codecs/wm9712.c @@ -12,6 +12,7 @@
#include <linux/init.h> #include <linux/slab.h> +#include <linux/mfd/wm97xx.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/device.h> @@ -19,6 +20,8 @@ #include <sound/core.h> #include <sound/pcm.h> #include <sound/ac97_codec.h> +#include <sound/ac97/codec.h> +#include <sound/ac97/compat.h> #include <sound/initval.h> #include <sound/soc.h> #include <sound/tlv.h> @@ -30,6 +33,7 @@ struct wm9712_priv { struct snd_ac97 *ac97; unsigned int hp_mixer[2]; struct mutex lock; + struct wm97xx_platform_data *mfd_pdata; };
static const struct reg_default wm9712_reg_defaults[] = { @@ -636,18 +640,25 @@ static int wm9712_soc_probe(struct snd_soc_codec *codec) struct regmap *regmap; int ret;
- wm9712->ac97 = snd_soc_new_ac97_codec(codec, WM9712_VENDOR_ID, - WM9712_VENDOR_ID_MASK); - if (IS_ERR(wm9712->ac97)) { - ret = PTR_ERR(wm9712->ac97); - dev_err(codec->dev, "Failed to register AC97 codec: %d\n", ret); - return ret; - } - - regmap = regmap_init_ac97(wm9712->ac97, &wm9712_regmap_config); - if (IS_ERR(regmap)) { - ret = PTR_ERR(regmap); - goto err_free_ac97_codec; + if (wm9712->mfd_pdata) { + wm9712->ac97 = wm9712->mfd_pdata->ac97; + regmap = wm9712->mfd_pdata->regmap; + } else { +#ifdef CONFIG_SND_SOC_AC97_BUS + wm9712->ac97 = snd_soc_new_ac97_codec(codec, WM9712_VENDOR_ID, + WM9712_VENDOR_ID_MASK); + if (IS_ERR(wm9712->ac97)) { + ret = PTR_ERR(wm9712->ac97); + dev_err(codec->dev, + "Failed to register AC97 codec: %d\n", ret); + return ret; + } + + regmap = regmap_init_ac97(wm9712->ac97, &wm9712_regmap_config); + if (IS_ERR(regmap)) { + ret = PTR_ERR(regmap); + goto err_free_ac97_codec; +#endif }
snd_soc_codec_init_regmap(codec, regmap); @@ -663,10 +674,14 @@ static int wm9712_soc_probe(struct snd_soc_codec *codec)
static int wm9712_soc_remove(struct snd_soc_codec *codec) { +#ifdef CONFIG_SND_SOC_AC97_BUS struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec);
- snd_soc_codec_exit_regmap(codec); - snd_soc_free_ac97_codec(wm9712->ac97); + if (!wm9712->mfd_pdata) { + snd_soc_codec_exit_regmap(codec); + snd_soc_free_ac97_codec(wm9712->ac97); + } +#endif return 0; }
@@ -697,6 +712,7 @@ static int wm9712_probe(struct platform_device *pdev)
mutex_init(&wm9712->lock);
+ wm9712->mfd_pdata = dev_get_platdata(&pdev->dev); platform_set_drvdata(pdev, wm9712);
return snd_soc_register_codec(&pdev->dev,

The patch
ASoC: wm9712: add ac97 new bus support
has been applied to the asoc tree at
git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git
All being well this means that it will be integrated into the linux-next tree (usually sometime in the next 24 hours) and sent to Linus during the next merge window (or sooner if it is a bug fix), however if problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing and review of the tree, please engage with people reporting problems and send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they should be sent as incremental updates against current git, existing patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying to this mail.
Thanks, Mark
From 2ed1a8e0ce8db6d36f849526db61ce3c85a9f8d1 Mon Sep 17 00:00:00 2001
From: Robert Jarzmik robert.jarzmik@free.fr Date: Wed, 13 Sep 2017 21:37:20 +0200 Subject: [PATCH] ASoC: wm9712: add ac97 new bus support
Add support for the new ac97 bus model, where devices are automatically discovered on AC-Links.
Signed-off-by: Robert Jarzmik robert.jarzmik@free.fr Signed-off-by: Mark Brown broonie@kernel.org --- sound/soc/codecs/Kconfig | 3 ++- sound/soc/codecs/wm9712.c | 44 ++++++++++++++++++++++++++++++-------------- 2 files changed, 32 insertions(+), 15 deletions(-)
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 807901cac1b3..655388d456be 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -215,7 +215,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_WM9081 if I2C select SND_SOC_WM9090 if I2C select SND_SOC_WM9705 if SND_SOC_AC97_BUS - select SND_SOC_WM9712 if SND_SOC_AC97_BUS + select SND_SOC_WM9712 if (SND_SOC_AC97_BUS || SND_SOC_AC97_BUS_NEW) select SND_SOC_WM9713 if (SND_SOC_AC97_BUS || SND_SOC_AC97_BUS_NEW) help Normally ASoC codec drivers are only built if a machine driver which @@ -1132,6 +1132,7 @@ config SND_SOC_WM9705 config SND_SOC_WM9712 tristate select REGMAP_AC97 + select AC97_BUS_COMPAT if AC97_BUS_NEW
config SND_SOC_WM9713 tristate diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c index 1a3e1797994a..1e228bf9f1ae 100644 --- a/sound/soc/codecs/wm9712.c +++ b/sound/soc/codecs/wm9712.c @@ -12,6 +12,7 @@
#include <linux/init.h> #include <linux/slab.h> +#include <linux/mfd/wm97xx.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/device.h> @@ -19,6 +20,8 @@ #include <sound/core.h> #include <sound/pcm.h> #include <sound/ac97_codec.h> +#include <sound/ac97/codec.h> +#include <sound/ac97/compat.h> #include <sound/initval.h> #include <sound/soc.h> #include <sound/tlv.h> @@ -30,6 +33,7 @@ struct wm9712_priv { struct snd_ac97 *ac97; unsigned int hp_mixer[2]; struct mutex lock; + struct wm97xx_platform_data *mfd_pdata; };
static const struct reg_default wm9712_reg_defaults[] = { @@ -636,18 +640,25 @@ static int wm9712_soc_probe(struct snd_soc_codec *codec) struct regmap *regmap; int ret;
- wm9712->ac97 = snd_soc_new_ac97_codec(codec, WM9712_VENDOR_ID, - WM9712_VENDOR_ID_MASK); - if (IS_ERR(wm9712->ac97)) { - ret = PTR_ERR(wm9712->ac97); - dev_err(codec->dev, "Failed to register AC97 codec: %d\n", ret); - return ret; - } - - regmap = regmap_init_ac97(wm9712->ac97, &wm9712_regmap_config); - if (IS_ERR(regmap)) { - ret = PTR_ERR(regmap); - goto err_free_ac97_codec; + if (wm9712->mfd_pdata) { + wm9712->ac97 = wm9712->mfd_pdata->ac97; + regmap = wm9712->mfd_pdata->regmap; + } else { +#ifdef CONFIG_SND_SOC_AC97_BUS + wm9712->ac97 = snd_soc_new_ac97_codec(codec, WM9712_VENDOR_ID, + WM9712_VENDOR_ID_MASK); + if (IS_ERR(wm9712->ac97)) { + ret = PTR_ERR(wm9712->ac97); + dev_err(codec->dev, + "Failed to register AC97 codec: %d\n", ret); + return ret; + } + + regmap = regmap_init_ac97(wm9712->ac97, &wm9712_regmap_config); + if (IS_ERR(regmap)) { + ret = PTR_ERR(regmap); + goto err_free_ac97_codec; +#endif }
snd_soc_codec_init_regmap(codec, regmap); @@ -663,10 +674,14 @@ static int wm9712_soc_probe(struct snd_soc_codec *codec)
static int wm9712_soc_remove(struct snd_soc_codec *codec) { +#ifdef CONFIG_SND_SOC_AC97_BUS struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec);
- snd_soc_codec_exit_regmap(codec); - snd_soc_free_ac97_codec(wm9712->ac97); + if (!wm9712->mfd_pdata) { + snd_soc_codec_exit_regmap(codec); + snd_soc_free_ac97_codec(wm9712->ac97); + } +#endif return 0; }
@@ -697,6 +712,7 @@ static int wm9712_probe(struct platform_device *pdev)
mutex_init(&wm9712->lock);
+ wm9712->mfd_pdata = dev_get_platdata(&pdev->dev); platform_set_drvdata(pdev, wm9712);
return snd_soc_register_codec(&pdev->dev,

Add a private data structure. This is a preparation for a codec which would need an another data on top of snd_ac97, which will be the case when an MFD wm97xx device will probe wm9705.
Signed-off-by: Robert Jarzmik robert.jarzmik@free.fr Acked-by: Charles Keepax ckeepax@opensource.wolfsonmicro.com --- sound/soc/codecs/wm9705.c | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-)
diff --git a/sound/soc/codecs/wm9705.c b/sound/soc/codecs/wm9705.c index f6d5c0f2aea5..08477d040028 100644 --- a/sound/soc/codecs/wm9705.c +++ b/sound/soc/codecs/wm9705.c @@ -24,6 +24,10 @@ #define WM9705_VENDOR_ID 0x574d4c05 #define WM9705_VENDOR_ID_MASK 0xffffffff
+struct wm9705_priv { + struct snd_ac97 *ac97; +}; + static const struct reg_default wm9705_reg_defaults[] = { { 0x02, 0x8000 }, { 0x04, 0x8000 }, @@ -292,10 +296,10 @@ static int wm9705_soc_suspend(struct snd_soc_codec *codec)
static int wm9705_soc_resume(struct snd_soc_codec *codec) { - struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec); + struct wm9705_priv *wm9705 = snd_soc_codec_get_drvdata(codec); int ret;
- ret = snd_ac97_reset(ac97, true, WM9705_VENDOR_ID, + ret = snd_ac97_reset(wm9705->ac97, true, WM9705_VENDOR_ID, WM9705_VENDOR_ID_MASK); if (ret < 0) return ret; @@ -311,38 +315,38 @@ static int wm9705_soc_resume(struct snd_soc_codec *codec)
static int wm9705_soc_probe(struct snd_soc_codec *codec) { - struct snd_ac97 *ac97; + struct wm9705_priv *wm9705 = snd_soc_codec_get_drvdata(codec); struct regmap *regmap; int ret;
- ac97 = snd_soc_new_ac97_codec(codec, WM9705_VENDOR_ID, - WM9705_VENDOR_ID_MASK); - if (IS_ERR(ac97)) { + wm9705->ac97 = snd_soc_new_ac97_codec(codec, WM9705_VENDOR_ID, + WM9705_VENDOR_ID_MASK); + if (IS_ERR(wm9705->ac97)) { dev_err(codec->dev, "Failed to register AC97 codec\n"); - return PTR_ERR(ac97); + return PTR_ERR(wm9705->ac97); }
- regmap = regmap_init_ac97(ac97, &wm9705_regmap_config); + regmap = regmap_init_ac97(wm9705->ac97, &wm9705_regmap_config); if (IS_ERR(regmap)) { ret = PTR_ERR(regmap); goto err_free_ac97_codec; }
- snd_soc_codec_set_drvdata(codec, ac97); + snd_soc_codec_set_drvdata(codec, wm9705->ac97); snd_soc_codec_init_regmap(codec, regmap);
return 0; err_free_ac97_codec: - snd_soc_free_ac97_codec(ac97); + snd_soc_free_ac97_codec(wm9705->ac97); return ret; }
static int wm9705_soc_remove(struct snd_soc_codec *codec) { - struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec); + struct wm9705_priv *wm9705 = snd_soc_codec_get_drvdata(codec);
snd_soc_codec_exit_regmap(codec); - snd_soc_free_ac97_codec(ac97); + snd_soc_free_ac97_codec(wm9705->ac97); return 0; }
@@ -364,6 +368,14 @@ static const struct snd_soc_codec_driver soc_codec_dev_wm9705 = {
static int wm9705_probe(struct platform_device *pdev) { + struct wm9705_priv *wm9705; + + wm9705 = devm_kzalloc(&pdev->dev, sizeof(*wm9705), GFP_KERNEL); + if (wm9705 == NULL) + return -ENOMEM; + + platform_set_drvdata(pdev, wm9705); + return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm9705, wm9705_dai, ARRAY_SIZE(wm9705_dai)); }

Add support for the new ac97 bus model, where devices are automatically discovered on AC-Links.
Signed-off-by: Robert Jarzmik robert.jarzmik@free.fr Acked-by: Charles Keepax ckeepax@opensource.wolfsonmicro.com --- Since v4: - added #ifdef spotted by Charles - tentatively kept Charles's ack Since v6: reworked wm9705_soc_remove() --- sound/soc/codecs/Kconfig | 3 ++- sound/soc/codecs/wm9705.c | 42 +++++++++++++++++++++++++++++------------- 2 files changed, 31 insertions(+), 14 deletions(-)
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index f17af3592785..1d69a1c1f896 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -205,7 +205,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_WM8998 if MFD_WM8998 select SND_SOC_WM9081 if I2C select SND_SOC_WM9090 if I2C - select SND_SOC_WM9705 if SND_SOC_AC97_BUS + select SND_SOC_WM9705 if (SND_SOC_AC97_BUS || SND_SOC_AC97_BUS_NEW) select SND_SOC_WM9712 if (SND_SOC_AC97_BUS || SND_SOC_AC97_BUS_NEW) select SND_SOC_WM9713 if (SND_SOC_AC97_BUS || SND_SOC_AC97_BUS_NEW) help @@ -1083,6 +1083,7 @@ config SND_SOC_WM9090 config SND_SOC_WM9705 tristate select REGMAP_AC97 + select AC97_BUS_COMPAT if AC97_BUS_NEW
config SND_SOC_WM9712 tristate diff --git a/sound/soc/codecs/wm9705.c b/sound/soc/codecs/wm9705.c index 08477d040028..68c204e3599f 100644 --- a/sound/soc/codecs/wm9705.c +++ b/sound/soc/codecs/wm9705.c @@ -11,6 +11,7 @@
#include <linux/init.h> #include <linux/slab.h> +#include <linux/mfd/wm97xx.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/device.h> @@ -18,6 +19,8 @@ #include <sound/core.h> #include <sound/pcm.h> #include <sound/ac97_codec.h> +#include <sound/ac97/codec.h> +#include <sound/ac97/compat.h> #include <sound/initval.h> #include <sound/soc.h>
@@ -26,6 +29,7 @@
struct wm9705_priv { struct snd_ac97 *ac97; + struct wm97xx_platform_data *mfd_pdata; };
static const struct reg_default wm9705_reg_defaults[] = { @@ -319,17 +323,24 @@ static int wm9705_soc_probe(struct snd_soc_codec *codec) struct regmap *regmap; int ret;
- wm9705->ac97 = snd_soc_new_ac97_codec(codec, WM9705_VENDOR_ID, - WM9705_VENDOR_ID_MASK); - if (IS_ERR(wm9705->ac97)) { - dev_err(codec->dev, "Failed to register AC97 codec\n"); - return PTR_ERR(wm9705->ac97); - } - - regmap = regmap_init_ac97(wm9705->ac97, &wm9705_regmap_config); - if (IS_ERR(regmap)) { - ret = PTR_ERR(regmap); - goto err_free_ac97_codec; + if (wm9705->mfd_pdata) { + wm9705->ac97 = wm9705->mfd_pdata->ac97; + regmap = wm9705->mfd_pdata->regmap; + } else { +#ifdef CONFIG_SND_SOC_AC97_BUS + wm9705->ac97 = snd_soc_new_ac97_codec(codec, WM9705_VENDOR_ID, + WM9705_VENDOR_ID_MASK); + if (IS_ERR(wm9705->ac97)) { + dev_err(codec->dev, "Failed to register AC97 codec\n"); + return PTR_ERR(wm9705->ac97); + } + + regmap = regmap_init_ac97(wm9705->ac97, &wm9705_regmap_config); + if (IS_ERR(regmap)) { + ret = PTR_ERR(regmap); + goto err_free_ac97_codec; + } +#endif }
snd_soc_codec_set_drvdata(codec, wm9705->ac97); @@ -343,10 +354,14 @@ static int wm9705_soc_probe(struct snd_soc_codec *codec)
static int wm9705_soc_remove(struct snd_soc_codec *codec) { +#ifdef CONFIG_SND_SOC_AC97_BUS struct wm9705_priv *wm9705 = snd_soc_codec_get_drvdata(codec);
- snd_soc_codec_exit_regmap(codec); - snd_soc_free_ac97_codec(wm9705->ac97); + if (!wm9705->mfd_pdata) { + snd_soc_codec_exit_regmap(codec); + snd_soc_free_ac97_codec(wm9705->ac97); + } +#endif return 0; }
@@ -374,6 +389,7 @@ static int wm9705_probe(struct platform_device *pdev) if (wm9705 == NULL) return -ENOMEM;
+ wm9705->mfd_pdata = dev_get_platdata(&pdev->dev); platform_set_drvdata(pdev, wm9705);
return snd_soc_register_codec(&pdev->dev,

The patch
ASoC: wm9705: add ac97 new bus support
has been applied to the asoc tree at
git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git
All being well this means that it will be integrated into the linux-next tree (usually sometime in the next 24 hours) and sent to Linus during the next merge window (or sooner if it is a bug fix), however if problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing and review of the tree, please engage with people reporting problems and send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they should be sent as incremental updates against current git, existing patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying to this mail.
Thanks, Mark
From aaafcfed9290349555a326fff1147460a54c34f2 Mon Sep 17 00:00:00 2001
From: Robert Jarzmik robert.jarzmik@free.fr Date: Wed, 13 Sep 2017 21:37:22 +0200 Subject: [PATCH] ASoC: wm9705: add ac97 new bus support
Add support for the new ac97 bus model, where devices are automatically discovered on AC-Links.
Signed-off-by: Robert Jarzmik robert.jarzmik@free.fr Acked-by: Charles Keepax ckeepax@opensource.wolfsonmicro.com Signed-off-by: Mark Brown broonie@kernel.org --- sound/soc/codecs/Kconfig | 3 ++- sound/soc/codecs/wm9705.c | 42 +++++++++++++++++++++++++++++------------- 2 files changed, 31 insertions(+), 14 deletions(-)
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 655388d456be..0838ae710941 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -214,7 +214,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_WM8998 if MFD_WM8998 select SND_SOC_WM9081 if I2C select SND_SOC_WM9090 if I2C - select SND_SOC_WM9705 if SND_SOC_AC97_BUS + select SND_SOC_WM9705 if (SND_SOC_AC97_BUS || SND_SOC_AC97_BUS_NEW) select SND_SOC_WM9712 if (SND_SOC_AC97_BUS || SND_SOC_AC97_BUS_NEW) select SND_SOC_WM9713 if (SND_SOC_AC97_BUS || SND_SOC_AC97_BUS_NEW) help @@ -1128,6 +1128,7 @@ config SND_SOC_WM9090 config SND_SOC_WM9705 tristate select REGMAP_AC97 + select AC97_BUS_COMPAT if AC97_BUS_NEW
config SND_SOC_WM9712 tristate diff --git a/sound/soc/codecs/wm9705.c b/sound/soc/codecs/wm9705.c index 08477d040028..68c204e3599f 100644 --- a/sound/soc/codecs/wm9705.c +++ b/sound/soc/codecs/wm9705.c @@ -11,6 +11,7 @@
#include <linux/init.h> #include <linux/slab.h> +#include <linux/mfd/wm97xx.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/device.h> @@ -18,6 +19,8 @@ #include <sound/core.h> #include <sound/pcm.h> #include <sound/ac97_codec.h> +#include <sound/ac97/codec.h> +#include <sound/ac97/compat.h> #include <sound/initval.h> #include <sound/soc.h>
@@ -26,6 +29,7 @@
struct wm9705_priv { struct snd_ac97 *ac97; + struct wm97xx_platform_data *mfd_pdata; };
static const struct reg_default wm9705_reg_defaults[] = { @@ -319,17 +323,24 @@ static int wm9705_soc_probe(struct snd_soc_codec *codec) struct regmap *regmap; int ret;
- wm9705->ac97 = snd_soc_new_ac97_codec(codec, WM9705_VENDOR_ID, - WM9705_VENDOR_ID_MASK); - if (IS_ERR(wm9705->ac97)) { - dev_err(codec->dev, "Failed to register AC97 codec\n"); - return PTR_ERR(wm9705->ac97); - } - - regmap = regmap_init_ac97(wm9705->ac97, &wm9705_regmap_config); - if (IS_ERR(regmap)) { - ret = PTR_ERR(regmap); - goto err_free_ac97_codec; + if (wm9705->mfd_pdata) { + wm9705->ac97 = wm9705->mfd_pdata->ac97; + regmap = wm9705->mfd_pdata->regmap; + } else { +#ifdef CONFIG_SND_SOC_AC97_BUS + wm9705->ac97 = snd_soc_new_ac97_codec(codec, WM9705_VENDOR_ID, + WM9705_VENDOR_ID_MASK); + if (IS_ERR(wm9705->ac97)) { + dev_err(codec->dev, "Failed to register AC97 codec\n"); + return PTR_ERR(wm9705->ac97); + } + + regmap = regmap_init_ac97(wm9705->ac97, &wm9705_regmap_config); + if (IS_ERR(regmap)) { + ret = PTR_ERR(regmap); + goto err_free_ac97_codec; + } +#endif }
snd_soc_codec_set_drvdata(codec, wm9705->ac97); @@ -343,10 +354,14 @@ static int wm9705_soc_probe(struct snd_soc_codec *codec)
static int wm9705_soc_remove(struct snd_soc_codec *codec) { +#ifdef CONFIG_SND_SOC_AC97_BUS struct wm9705_priv *wm9705 = snd_soc_codec_get_drvdata(codec);
- snd_soc_codec_exit_regmap(codec); - snd_soc_free_ac97_codec(wm9705->ac97); + if (!wm9705->mfd_pdata) { + snd_soc_codec_exit_regmap(codec); + snd_soc_free_ac97_codec(wm9705->ac97); + } +#endif return 0; }
@@ -374,6 +389,7 @@ static int wm9705_probe(struct platform_device *pdev) if (wm9705 == NULL) return -ENOMEM;
+ wm9705->mfd_pdata = dev_get_platdata(&pdev->dev); platform_set_drvdata(pdev, wm9705);
return snd_soc_register_codec(&pdev->dev,

Switch to the new ac97 bus support in sound/ac97 instead of the legacy snd_ac97 one.
Signed-off-by: Robert Jarzmik robert.jarzmik@free.fr --- Since v1: split into 2 patches, the former being XXX ac97 codec agnostic Since v2: fix driver unregistration Since v3: added platform driver data for controller removal --- sound/arm/Kconfig | 1 - sound/soc/pxa/Kconfig | 5 ++--- sound/soc/pxa/pxa2xx-ac97.c | 46 ++++++++++++++++++++++----------------------- 3 files changed, 25 insertions(+), 27 deletions(-)
diff --git a/sound/arm/Kconfig b/sound/arm/Kconfig index 65171f6657a2..f1f25704fe52 100644 --- a/sound/arm/Kconfig +++ b/sound/arm/Kconfig @@ -36,7 +36,6 @@ endif # SND_ARM
config SND_PXA2XX_LIB tristate - select SND_AC97_CODEC if SND_PXA2XX_LIB_AC97 select SND_DMAENGINE_PCM
config SND_PXA2XX_LIB_AC97 diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig index 823b5a236d8d..72d652572269 100644 --- a/sound/soc/pxa/Kconfig +++ b/sound/soc/pxa/Kconfig @@ -19,13 +19,12 @@ config SND_MMP_SOC
config SND_PXA2XX_AC97 tristate - select SND_AC97_CODEC
config SND_PXA2XX_SOC_AC97 tristate - select AC97_BUS + select AC97_BUS_NEW select SND_PXA2XX_LIB_AC97 - select SND_SOC_AC97_BUS + select SND_SOC_AC97_BUS_NEW
config SND_PXA2XX_SOC_I2S tristate diff --git a/sound/soc/pxa/pxa2xx-ac97.c b/sound/soc/pxa/pxa2xx-ac97.c index b07d64ef49ba..6f0ea3bf9f75 100644 --- a/sound/soc/pxa/pxa2xx-ac97.c +++ b/sound/soc/pxa/pxa2xx-ac97.c @@ -17,6 +17,7 @@ #include <linux/dmaengine.h> #include <linux/dma/pxa-dma.h>
+#include <sound/ac97/controller.h> #include <sound/core.h> #include <sound/ac97_codec.h> #include <sound/soc.h> @@ -27,43 +28,35 @@ #include <mach/regs-ac97.h> #include <mach/audio.h>
-static void pxa2xx_ac97_warm_reset(struct snd_ac97 *ac97) +static void pxa2xx_ac97_warm_reset(struct ac97_controller *adrv) { pxa2xx_ac97_try_warm_reset();
pxa2xx_ac97_finish_reset(); }
-static void pxa2xx_ac97_cold_reset(struct snd_ac97 *ac97) +static void pxa2xx_ac97_cold_reset(struct ac97_controller *adrv) { pxa2xx_ac97_try_cold_reset();
pxa2xx_ac97_finish_reset(); }
-static unsigned short pxa2xx_ac97_legacy_read(struct snd_ac97 *ac97, - unsigned short reg) +static int pxa2xx_ac97_read_actrl(struct ac97_controller *adrv, int slot, + unsigned short reg) { - int ret; - - ret = pxa2xx_ac97_read(ac97->num, reg); - if (ret < 0) - return 0; - else - return (unsigned short)(ret & 0xffff); + return pxa2xx_ac97_read(slot, reg); }
-static void pxa2xx_ac97_legacy_write(struct snd_ac97 *ac97, - unsigned short reg, unsigned short val) +static int pxa2xx_ac97_write_actrl(struct ac97_controller *adrv, int slot, + unsigned short reg, unsigned short val) { - int ret; - - ret = pxa2xx_ac97_write(ac97->num, reg, val); + return pxa2xx_ac97_write(slot, reg, val); }
-static struct snd_ac97_bus_ops pxa2xx_ac97_ops = { - .read = pxa2xx_ac97_legacy_read, - .write = pxa2xx_ac97_legacy_write, +static struct ac97_controller_ops pxa2xx_ac97_ops = { + .read = pxa2xx_ac97_read_actrl, + .write = pxa2xx_ac97_write_actrl, .warm_reset = pxa2xx_ac97_warm_reset, .reset = pxa2xx_ac97_cold_reset, }; @@ -242,6 +235,8 @@ static const struct snd_soc_component_driver pxa_ac97_component = { static int pxa2xx_ac97_dev_probe(struct platform_device *pdev) { int ret; + struct ac97_controller *ctrl; + pxa2xx_audio_ops_t *pdata = pdev->dev.platform_data;
if (pdev->id != -1) { dev_err(&pdev->dev, "PXA2xx has only one AC97 port.\n"); @@ -254,10 +249,13 @@ static int pxa2xx_ac97_dev_probe(struct platform_device *pdev) return ret; }
- ret = snd_soc_set_ac97_ops(&pxa2xx_ac97_ops); - if (ret != 0) - return ret; + ctrl = snd_ac97_controller_register(&pxa2xx_ac97_ops, &pdev->dev, + AC97_SLOTS_AVAILABLE_ALL, + pdata->codec_pdata); + if (IS_ERR(ctrl)) + return PTR_ERR(ctrl);
+ platform_set_drvdata(pdev, ctrl); /* Punt most of the init to the SoC probe; we may need the machine * driver to do interesting things with the clocking to get us up * and running. @@ -268,8 +266,10 @@ static int pxa2xx_ac97_dev_probe(struct platform_device *pdev)
static int pxa2xx_ac97_dev_remove(struct platform_device *pdev) { + struct ac97_controller *ctrl = platform_get_drvdata(pdev); + snd_soc_unregister_component(&pdev->dev); - snd_soc_set_ac97_ops(NULL); + snd_ac97_controller_unregister(ctrl); pxa2xx_ac97_hw_remove(pdev); return 0; }

On Wed, Sep 13, 2017 at 09:37:23PM +0200, Robert Jarzmik wrote:
Switch to the new ac97 bus support in sound/ac97 instead of the legacy snd_ac97 one.
This doesn't apply against current code, please check and resend.
participants (3)
-
Lee Jones
-
Mark Brown
-
Robert Jarzmik