quoted text > The WM8400 is a highly integrated audio CODEC and power management unit
> optimised for use in mobile multimedia applications. This patch adds
> core support for the WM8400 to the MFD subsystem.
>
> Both I2C and SPI access are supported by the hardware but currently only
> I2C access is implemented. The code is structureed to allow SPI support
> to be slotted in later.
>
> Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
> ---
> drivers/mfd/Kconfig | 9 +
> drivers/mfd/Makefile | 2 +
> drivers/mfd/wm8400-core.c | 454 ++++++++
> include/linux/mfd/wm8400-private.h | 2095 ++++++++++++++++++++++++++++++++++++
> 4 files changed, 2560 insertions(+), 0 deletions(-)
> create mode 100644 drivers/mfd/wm8400-core.c
> create mode 100644 include/linux/mfd/wm8400-private.h
>
> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
> index 10c44d3..d83dbf0 100644
> --- a/drivers/mfd/Kconfig
> +++ b/drivers/mfd/Kconfig
> @@ -78,6 +78,15 @@ config MFD_TC6393XB
> help
> Support for Toshiba Mobile IO Controller TC6393XB
>
> +config MFD_WM8400
> + tristate "Support Wolfson Microelectronics WM8400"
> + select MFD_CORE
> + help
> + Support for the Wolfson Microelecronics WM8400 PMIC and audio
> + CODEC. This driver adds provides common support for accessing
> + the device, additional drivers must be enabled in order to use
> + the functionality of the device.
> +
> endmenu
>
> menu "Multimedia Capabilities Port drivers"
> diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
> index 03ad239..172439d 100644
> --- a/drivers/mfd/Makefile
> +++ b/drivers/mfd/Makefile
> @@ -12,6 +12,8 @@ obj-$(CONFIG_MFD_T7L66XB) += t7l66xb.o
> obj-$(CONFIG_MFD_TC6387XB) += tc6387xb.o
> obj-$(CONFIG_MFD_TC6393XB) += tc6393xb.o
>
> +obj-$(CONFIG_MFD_WM8400) += wm8400-core.o
> +
> obj-$(CONFIG_MFD_CORE) += mfd-core.o
>
> obj-$(CONFIG_MCP) += mcp-core.o
> diff --git a/drivers/mfd/wm8400-core.c b/drivers/mfd/wm8400-core.c
> new file mode 100644
> index 0000000..125afca
> --- /dev/null
> +++ b/drivers/mfd/wm8400-core.c
> @@ -0,0 +1,454 @@
> +/*
> + * Core driver for WM8400.
> + *
> + * Copyright 2008 Wolfson Microelectronics PLC.
> + *
> + * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
> + *
> + * 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.
> + *
> + */
> +
> +#include <linux/bug.h>
> +#include <linux/i2c.h>
> +#include <linux/kernel.h>
> +#include <linux/mfd/wm8400-private.h>
> +
> +static struct
> +{
> + u16 readable; /* Mask of readable bits */
> + u16 writable; /* Mask of writable bits */
> + u16 vol; /* Mask of volatile bits */
> + int is_codec; /* Register controlled by codec reset */
> + u16 default_val; /* Value on reset */
> +} reg_data[] =
> +{
> + { 0xFFFF, 0xFFFF, 0x0000, 0, 0x6172 }, /* R0 */
> + { 0x7000, 0x0000, 0x8000, 0, 0x0000 }, /* R1 */
> + { 0xFF17, 0xFF17, 0x0000, 0, 0x0000 }, /* R2 */
> + { 0xEBF3, 0xEBF3, 0x0000, 1, 0x6000 }, /* R3 */
> + { 0x3CF3, 0x3CF3, 0x0000, 1, 0x0000 }, /* R4 */
> + { 0xF1F8, 0xF1F8, 0x0000, 1, 0x4050 }, /* R5 */
> + { 0xFC1F, 0xFC1F, 0x0000, 1, 0x4000 }, /* R6 */
> + { 0xDFDE, 0xDFDE, 0x0000, 1, 0x01C8 }, /* R7 */
> + { 0xFCFC, 0xFCFC, 0x0000, 1, 0x0000 }, /* R8 */
> + { 0xEFFF, 0xEFFF, 0x0000, 1, 0x0040 }, /* R9 */
> + { 0xEFFF, 0xEFFF, 0x0000, 1, 0x0040 }, /* R10 */
> + { 0x27F7, 0x27F7, 0x0000, 1, 0x0004 }, /* R11 */
> + { 0x01FF, 0x01FF, 0x0000, 1, 0x00C0 }, /* R12 */
> + { 0x01FF, 0x01FF, 0x0000, 1, 0x00C0 }, /* R13 */
> + { 0x1FEF, 0x1FEF, 0x0000, 1, 0x0000 }, /* R14 */
> + { 0x0163, 0x0163, 0x0000, 1, 0x0100 }, /* R15 */
> + { 0x01FF, 0x01FF, 0x0000, 1, 0x00C0 }, /* R16 */
> + { 0x01FF, 0x01FF, 0x0000, 1, 0x00C0 }, /* R17 */
> + { 0x1FFF, 0x0FFF, 0x0000, 1, 0x0000 }, /* R18 */
> + { 0xFFFF, 0xFFFF, 0x0000, 1, 0x1000 }, /* R19 */
> + { 0xFFFF, 0xFFFF, 0x0000, 1, 0x1010 }, /* R20 */
> + { 0xFFFF, 0xFFFF, 0x0000, 1, 0x1010 }, /* R21 */
> + { 0x0FDD, 0x0FDD, 0x0000, 1, 0x8000 }, /* R22 */
> + { 0x1FFF, 0x1FFF, 0x0000, 1, 0x0800 }, /* R23 */
> + { 0x0000, 0x01DF, 0x0000, 1, 0x008B }, /* R24 */
> + { 0x0000, 0x01DF, 0x0000, 1, 0x008B }, /* R25 */
> + { 0x0000, 0x01DF, 0x0000, 1, 0x008B }, /* R26 */
> + { 0x0000, 0x01DF, 0x0000, 1, 0x008B }, /* R27 */
> + { 0x0000, 0x01FF, 0x0000, 1, 0x0000 }, /* R28 */
> + { 0x0000, 0x01FF, 0x0000, 1, 0x0000 }, /* R29 */
> + { 0x0000, 0x0077, 0x0000, 1, 0x0066 }, /* R30 */
> + { 0x0000, 0x0033, 0x0000, 1, 0x0022 }, /* R31 */
> + { 0x0000, 0x01FF, 0x0000, 1, 0x0079 }, /* R32 */
> + { 0x0000, 0x01FF, 0x0000, 1, 0x0079 }, /* R33 */
> + { 0x0000, 0x0003, 0x0000, 1, 0x0003 }, /* R34 */
> + { 0x0000, 0x01FF, 0x0000, 1, 0x0003 }, /* R35 */
> + { 0x0000, 0x0000, 0x0000, 0, 0x0000 }, /* R36 */
> + { 0x0000, 0x003F, 0x0000, 1, 0x0100 }, /* R37 */
> + { 0x0000, 0x0000, 0x0000, 0, 0x0000 }, /* R38 */
> + { 0x0000, 0x000F, 0x0000, 0, 0x0000 }, /* R39 */
> + { 0x0000, 0x00FF, 0x0000, 1, 0x0000 }, /* R40 */
> + { 0x0000, 0x01B7, 0x0000, 1, 0x0000 }, /* R41 */
> + { 0x0000, 0x01B7, 0x0000, 1, 0x0000 }, /* R42 */
> + { 0x0000, 0x01FF, 0x0000, 1, 0x0000 }, /* R43 */
> + { 0x0000, 0x01FF, 0x0000, 1, 0x0000 }, /* R44 */
> + { 0x0000, 0x00FD, 0x0000, 1, 0x0000 }, /* R45 */
> + { 0x0000, 0x00FD, 0x0000, 1, 0x0000 }, /* R46 */
> + { 0x0000, 0x01FF, 0x0000, 1, 0x0000 }, /* R47 */
> + { 0x0000, 0x01FF, 0x0000, 1, 0x0000 }, /* R48 */
> + { 0x0000, 0x01FF, 0x0000, 1, 0x0000 }, /* R49 */
> + { 0x0000, 0x01FF, 0x0000, 1, 0x0000 }, /* R50 */
> + { 0x0000, 0x01B3, 0x0000, 1, 0x0180 }, /* R51 */
> + { 0x0000, 0x0077, 0x0000, 1, 0x0000 }, /* R52 */
> + { 0x0000, 0x0077, 0x0000, 1, 0x0000 }, /* R53 */
> + { 0x0000, 0x00FF, 0x0000, 1, 0x0000 }, /* R54 */
> + { 0x0000, 0x0001, 0x0000, 1, 0x0000 }, /* R55 */
> + { 0x0000, 0x003F, 0x0000, 1, 0x0000 }, /* R56 */
> + { 0x0000, 0x004F, 0x0000, 1, 0x0000 }, /* R57 */
> + { 0x0000, 0x00FD, 0x0000, 1, 0x0000 }, /* R58 */
> + { 0x0000, 0x0000, 0x0000, 0, 0x0000 }, /* R59 */
> + { 0x1FFF, 0x1FFF, 0x0000, 1, 0x0000 }, /* R60 */
> + { 0xFFFF, 0xFFFF, 0x0000, 1, 0x0000 }, /* R61 */
> + { 0x03FF, 0x03FF, 0x0000, 1, 0x0000 }, /* R62 */
> + { 0x007F, 0x007F, 0x0000, 1, 0x0000 }, /* R63 */
> + { 0x0000, 0x0000, 0x0000, 0, 0x0000 }, /* R64 */
> + { 0xDFFF, 0xDFFF, 0x0000, 0, 0x0000 }, /* R65 */
> + { 0xDFFF, 0xDFFF, 0x0000, 0, 0x0000 }, /* R66 */
> + { 0xDFFF, 0xDFFF, 0x0000, 0, 0x0000 }, /* R67 */
> + { 0xDFFF, 0xDFFF, 0x0000, 0, 0x0000 }, /* R68 */
> + { 0x0000, 0x0000, 0x0000, 0, 0x0000 }, /* R69 */
> + { 0xFFFF, 0xFFFF, 0x0000, 0, 0x4400 }, /* R70 */
> + { 0x23FF, 0x23FF, 0x0000, 0, 0x0000 }, /* R71 */
> + { 0xFFFF, 0xFFFF, 0x0000, 0, 0x4400 }, /* R72 */
> + { 0x23FF, 0x23FF, 0x0000, 0, 0x0000 }, /* R73 */
> + { 0x0000, 0x0000, 0x0000, 0, 0x0000 }, /* R74 */
> + { 0x000E, 0x000E, 0x0000, 0, 0x0008 }, /* R75 */
> + { 0xE00F, 0xE00F, 0x0000, 0, 0x0000 }, /* R76 */
> + { 0x0000, 0x0000, 0x0000, 0, 0x0000 }, /* R77 */
> + { 0x03C0, 0x03C0, 0x0000, 0, 0x02C0 }, /* R78 */
> + { 0xFFFF, 0x0000, 0xffff, 0, 0x0000 }, /* R79 */
> + { 0xFFFF, 0xFFFF, 0x0000, 0, 0x0000 }, /* R80 */
> + { 0xFFFF, 0x0000, 0xffff, 0, 0x0000 }, /* R81 */
> + { 0x2BFF, 0x0000, 0xffff, 0, 0x0000 }, /* R82 */
> + { 0x0000, 0x0000, 0x0000, 0, 0x0000 }, /* R83 */
> + { 0x80FF, 0x80FF, 0x0000, 0, 0x00ff }, /* R84 */
> +};
> +
> +static int wm8400_read(struct wm8400 *wm8400, u8 reg, int num_regs, u16 *dest)
> +{
> + int i, ret = 0;
> +
> + BUG_ON(reg + num_regs - 1 > ARRAY_SIZE(wm8400->reg_cache));
> +
> + /* If there are any volatile reads then read back the entire block */
> + for (i = reg; i < reg + num_regs; i++)
> + if (reg_data[i].vol) {
> + ret = wm8400->read_dev(wm8400->io_data, reg,
> + num_regs, dest);
> + if (ret != 0)
> + return ret;
> + for (i = 0; i < num_regs; i++)
> + dest[i] = be16_to_cpu(dest[i]);
> +
> + return 0;
> + }
> +
> + /* Otherwise use the cache */
> + memcpy(dest, &wm8400->reg_cache[reg], num_regs * sizeof(u16));
> +
> + return 0;
> +}
> +
> +static int wm8400_write(struct wm8400 *wm8400, u8 reg, int num_regs,
> + u16 *src)
> +{
> + int ret, i;
> +
> + BUG_ON(reg + num_regs - 1 > ARRAY_SIZE(wm8400->reg_cache));
> +
> + for (i = 0; i < num_regs; i++) {
> + BUG_ON(!reg_data[reg + i].writable);
> + wm8400->reg_cache[reg + i] = src[i];
> + src[i] = cpu_to_be16(src[i]);
> + }
> +
> + /* Do the actual I/O */
> + ret = wm8400->write_dev(wm8400->io_data, reg, num_regs, src);
> + if (ret != 0)
> + return -EIO;
> +
> + return 0;
> +}
> +
> +/**
> + * wm8400_reg_read - Single register read
> + *
> + * @wm8400: Pointer to wm8400 control structure
> + * @reg: Register to read
> + *
> + * @return Read value
> + */
> +u16 wm8400_reg_read(struct wm8400 *wm8400, u8 reg)
> +{
> + u16 val;
> +
> + mutex_lock(&wm8400->io_lock);
> + wm8400_read(wm8400, reg, 1, &val);
> + mutex_unlock(&wm8400->io_lock);
> +
> + return val;
> +}
> +EXPORT_SYMBOL_GPL(wm8400_reg_read);
> +
> +int wm8400_block_read(struct wm8400 *wm8400, u8 reg, int count, u16 *data)
> +{
> + int ret;
> +
> + mutex_lock(&wm8400->io_lock);
> +
> + ret = wm8400_read(wm8400, reg, count, data);
> +
> + mutex_unlock(&wm8400->io_lock);
> +
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(wm8400_block_read);
> +
Slight formatting difference with read and block read.
I would probably rename this since it's really resetting the codec cache
and not resetting the codec.