Message ID | 1345617865-14199-3-git-send-email-rajeshwari.s@samsung.com |
---|---|
State | New |
Headers | show |
Hi Rajeshwari, On Tue, Aug 21, 2012 at 11:44 PM, Rajeshwari Shinde <rajeshwari.s@samsung.com> wrote: > This pastc adds driver for audio codec WM8994 > patch > Signed-off-by: R. Chandrasekar <rcsekar@samsung.com> > Signed-off-by: Rajeshwari Shinde <rajeshwari.s@samsung.com> > --- > Changes in V2: > - None > drivers/sound/Makefile | 1 + > drivers/sound/wm8994.c | 781 ++++++++++++++++++++++++++++++++++++++ > drivers/sound/wm8994.h | 87 +++++ > drivers/sound/wm8994_registers.h | 299 +++++++++++++++ > 4 files changed, 1168 insertions(+), 0 deletions(-) > create mode 100644 drivers/sound/wm8994.c > create mode 100644 drivers/sound/wm8994.h > create mode 100644 drivers/sound/wm8994_registers.h > > diff --git a/drivers/sound/Makefile b/drivers/sound/Makefile > index 18ad2c9..8fdffb1 100644 > --- a/drivers/sound/Makefile > +++ b/drivers/sound/Makefile > @@ -27,6 +27,7 @@ LIB := $(obj)libsound.o > > COBJS-$(CONFIG_SOUND) += sound.o > COBJS-$(CONFIG_I2S) += samsung-i2s.o > +COBJS-$(CONFIG_SOUND_WM8994) += wm8994.o > > COBJS := $(COBJS-y) > SRCS := $(COBJS:.o=.c) > diff --git a/drivers/sound/wm8994.c b/drivers/sound/wm8994.c > new file mode 100644 > index 0000000..21604f1 > --- /dev/null > +++ b/drivers/sound/wm8994.c > @@ -0,0 +1,781 @@ > +/* > + * Copyright (C) 2012 Samsung Electronics > + * R. Chandrasekar <rcsekar@samsung.com> > + * > + * See file CREDITS for list of people who contributed to this > + * project. > + * > + * 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. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + * > + * You should have received a copy of the GNU General Public License > + * along with this program; if not, write to the Free Software > + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, > + * MA 02111-1307 USA > + */ > +#define DEBUG can remove this? > +#include <asm/arch/clk.h> > +#include <asm/arch/cpu.h> > +#include <asm/gpio.h> > +#include <asm/io.h> > +#include <common.h> > +#include <div64.h> > +#include <i2c.h> > +#include <i2s.h> > +#include <sound.h> blank line here maybe? > +#include "wm8994.h" > +#include "wm8994_registers.h" > + > +/* defines for wm8994 system clock selection */ > +#define SEL_MCLK1 0x00 > +#define SEL_MCLK2 0x08 > +#define SEL_FLL1 0x10 > +#define SEL_FLL2 0x18 > + > +/* fll config to configure fll */ What is FLL? > +struct wm8994_fll_config { > + int src; /* Source */ > + int in; /* Input frequency in Hz */ > + int out; /* output frequency in Hz */ > +}; > + > +/* codec private data */ > +struct wm8994_priv { > + enum wm8994_type type; /* codec type of wolfson */ > + int revision; /* Revision */ > + int sysclk[WM8994_MAX_AIF]; /* System clock freqency in Hz */ frequency > + int mclk[WM8994_MAX_AIF]; /* master clock frequency in Hz */ > + int aifclk[WM8994_MAX_AIF]; /* audio interfce clock in Hz */ interface > + struct wm8994_fll_config fll[2]; /* fll config to configure fll */ > +}; > + > +/* wm 8994 supported sampling rate vlaues */ values > +static unsigned int src_rate[] = { > + 8000, 11025, 12000, 16000, 22050, 24000, > + 32000, 44100, 48000, 88200, 96000 > +}; > + > +/* op clock divisions */ > +static int opclk_divs[] = { 10, 20, 30, 40, 55, 60, 80, 120, 160 }; > + > +/* lr clock framsize ratio */ frame size? > +static int fs_ratios[] = { > + 64, 128, 192, 256, 348, 512, 768, 1024, 1408, 1536 > +}; > + > +/* bit clock divisions */ Are these divisors or divisions? > +static int bclk_divs[] = { > + 10, 15, 20, 30, 40, 50, 60, 80, 110, 120, 160, 220, 240, 320, 440, 480, > + 640, 880, 960, 1280, 1760, 1920 > +}; > + > +/* Global */ > +struct wm8994_priv g_wm8994_info; > +unsigned int g_wm8994_i2c_dev_addr; Can these be static? > + > +/* > + * Initialise I2C for wm 8994 > + * > + * @param bus no i2c bus number in which wm8994 is connected > + */ > +static void wm8994_i2c_init(int bus_no) > +{ > + i2c_set_bus_num(bus_no); > +} > + > +/* > + * Writes value to a device register through i2c > + * > + * @param reg reg number to be write > + * @param data data to be writen to the above registor > + * > + * @return int value 1 for change, 0 for no change or negative error code. > + */ > +static int wm8994_i2c_write(unsigned int reg, unsigned short data) > +{ > + unsigned char val[2]; > + > + val[0] = (unsigned char)((data >> 8) & 0xff); > + val[1] = (unsigned char)(data & 0xff); > + debug("Write Addr : 0x%04X, Data : 0x%04X\n", reg, data); > + > + return i2c_write(g_wm8994_i2c_dev_addr, reg, 2, val, 2); > +} > + > +/* > + * Read a value from a device register through i2c > + * > + * @param reg reg number to be read > + * @param data address of read data to be stored > + * > + * @return int value 0 for success, -1 in case of error. > + */ > +static unsigned int wm8994_i2c_read(unsigned int reg , unsigned short *data) > +{ > + unsigned char val[2]; > + int ret; > + > + ret = i2c_read(g_wm8994_i2c_dev_addr, reg, 2, val, 2); > + if (ret != 0) { > + debug("%s: Error while reading register %#04x\n", > + __func__, reg); > + return -1; > + } > + *data = val[0]; > + *data <<= 8; > + *data |= val[1]; Can you do something like: *data = get_unaligned_le16(val); > + > + return 0; > +} > + > +/* > + * update device register bits through i2c > + * > + * @param reg codec register > + * @param mask register mask > + * @param value new value > + * > + * @return int value 1 for change, 0 for no change or negative error code. Would be clearer if you said "1 if the register value was actually changed" > + */ > +static int wm8994_update_bits(unsigned int reg, unsigned short mask, > + unsigned short value) > +{ > + int change , ret = 0; > + unsigned short old, new; > + > + if (wm8994_i2c_read(reg, &old) != 0) > + return -1; > + new = (old & ~mask) | (value & mask); > + change = (old != new) ? 1 : 0; > + if (change) > + ret = wm8994_i2c_write(reg, new); > + if (ret < 0) > + return ret; > + > + return change; > +} > + > +/* > + * Sets i2s set format > + * > + * @param aif_id Interface ID > + * @param fmt i2S format > + * > + * @return -1 for error and 0 Success. > + */ > +int wm8994_set_fmt(int aif_id, unsigned int fmt) > +{ > + int ms_reg; > + int aif_reg; > + int ms = 0; > + int aif = 0; > + int aif_clk = 0; > + int error = 0; > + > + switch (aif_id) { > + case 1: > + ms_reg = WM8994_AIF1_MASTER_SLAVE; > + aif_reg = WM8994_AIF1_CONTROL_1; > + aif_clk = WM8994_AIF1_CLOCKING_1; > + break; > + case 2: > + ms_reg = WM8994_AIF2_MASTER_SLAVE; > + aif_reg = WM8994_AIF2_CONTROL_1; > + aif_clk = WM8994_AIF2_CLOCKING_1; > + break; > + default: > + debug("%s: Invalid audio interface selection\n", __func__); > + return -1; > + } > + > + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { > + case SND_SOC_DAIFMT_CBS_CFS: > + break; > + case SND_SOC_DAIFMT_CBM_CFM: > + ms = WM8994_AIF1_MSTR; > + break; > + default: > + debug("%s: Invalid i2s master selection\n", __func__); > + return -1; > + } > + > + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { > + case SND_SOC_DAIFMT_DSP_B: > + aif |= WM8994_AIF1_LRCLK_INV; > + case SND_SOC_DAIFMT_DSP_A: > + aif |= 0x18; > + break; > + case SND_SOC_DAIFMT_I2S: > + aif |= 0x10; > + break; > + case SND_SOC_DAIFMT_RIGHT_J: > + break; > + case SND_SOC_DAIFMT_LEFT_J: > + aif |= 0x8; > + break; > + default: > + debug("%s: Invalid i2s format selection\n", __func__); > + return -1; > + } > + > + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { > + case SND_SOC_DAIFMT_DSP_A: > + case SND_SOC_DAIFMT_DSP_B: > + /* frame inversion not valid for DSP modes */ > + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { > + case SND_SOC_DAIFMT_NB_NF: > + break; > + case SND_SOC_DAIFMT_IB_NF: > + aif |= WM8994_AIF1_BCLK_INV; > + break; > + default: > + debug("%s: Invalid i2s frame inverse selection\n", > + __func__); > + return -1; > + } > + break; > + > + case SND_SOC_DAIFMT_I2S: > + case SND_SOC_DAIFMT_RIGHT_J: > + case SND_SOC_DAIFMT_LEFT_J: > + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { > + case SND_SOC_DAIFMT_NB_NF: > + break; > + case SND_SOC_DAIFMT_IB_IF: > + aif |= WM8994_AIF1_BCLK_INV | WM8994_AIF1_LRCLK_INV; > + break; > + case SND_SOC_DAIFMT_IB_NF: > + aif |= WM8994_AIF1_BCLK_INV; > + break; > + case SND_SOC_DAIFMT_NB_IF: > + aif |= WM8994_AIF1_LRCLK_INV; > + break; > + default: > + debug("%s: Invalid i2s clock polarity selection\n", > + __func__); > + return -1; > + } > + break; > + default: > + debug("%s: Invalid i2s format selection\n", __func__); > + return -1; > + } > + > + error = wm8994_update_bits(aif_reg, WM8994_AIF1_BCLK_INV | > + WM8994_AIF1_LRCLK_INV_MASK | WM8994_AIF1_FMT_MASK, aif); > + > + error |= wm8994_update_bits(ms_reg, WM8994_AIF1_MSTR_MASK, ms); > + error |= wm8994_update_bits(aif_clk, WM8994_AIF1CLK_ENA_MASK, > + WM8994_AIF1CLK_ENA); > + if (error < 0) { > + debug("%s: codec register access error\n", __func__); > + return -1; > + } > + > + return 0; > +} > + > +/* > + * Sets hw params FOR WM8994 > + * > + * @param sampling_rate Sampling rate > + * @param bits_per_sample Bits per sample > + * @param Channels Channels in the given audio input should document every parameter > + * > + * @return -1 for error and 0 Success. > + */ > +static int wm8994_hw_params(struct wm8994_priv *wm8994, int aif_id, > + unsigned int sampling_rate, unsigned int bits_per_sample, > + unsigned int channels) > +{ > + int aif1_reg; > + int aif2_reg; > + int bclk_reg; > + int bclk = 0; > + int rate_reg; > + int aif1 = 0; > + int aif2 = 0; > + int rate_val = 0; > + int id = aif_id - 1; > + int i, cur_val, best_val, bclk_rate, best; > + unsigned short reg_data; > + int ret = 0; > + > + switch (aif_id) { > + case 1: > + aif1_reg = WM8994_AIF1_CONTROL_1; > + aif2_reg = WM8994_AIF1_CONTROL_2; > + bclk_reg = WM8994_AIF1_BCLK; > + rate_reg = WM8994_AIF1_RATE; > + break; > + case 2: > + aif1_reg = WM8994_AIF2_CONTROL_1; > + aif2_reg = WM8994_AIF2_CONTROL_2; > + bclk_reg = WM8994_AIF2_BCLK; > + rate_reg = WM8994_AIF2_RATE; > + break; > + default: > + return -1; > + } > + > + bclk_rate = sampling_rate * 32; > + switch (bits_per_sample) { > + case 16: > + bclk_rate *= 16; > + break; > + case 20: > + bclk_rate *= 20; > + aif1 |= 0x20; > + break; > + case 24: > + bclk_rate *= 24; > + aif1 |= 0x40; > + break; > + case 32: > + bclk_rate *= 32; > + aif1 |= 0x60; > + break; > + default: > + return -1; > + } > + > + /* Try to find an appropriate sample rate; look for an exact match. */ > + for (i = 0; i < ARRAY_SIZE(src_rate); i++) > + if (src_rate[i] == sampling_rate) > + break; > + > + if (i == ARRAY_SIZE(src_rate)) { > + debug("%s: Could not get the best matching samplingrate\n", > + __func__); > + return -1; > + } > + > + rate_val |= i << WM8994_AIF1_SR_SHIFT; > + > + /* AIFCLK/fs ratio; look for a close match in either direction */ > + best = 0; > + best_val = abs((fs_ratios[0] * sampling_rate) > + - wm8994->aifclk[id]); > + > + for (i = 1; i < ARRAY_SIZE(fs_ratios); i++) { > + cur_val = abs((fs_ratios[i] * sampling_rate) > + - wm8994->aifclk[id]); > + if (cur_val >= best_val) > + continue; > + best = i; > + best_val = cur_val; > + } > + > + rate_val |= best; > + > + /* > + * We may not get quite the right frequency if using > + * approximate clocks so look for the closest match that is > + * higher than the target (we need to ensure that there enough > + * BCLKs to clock out the samples). > + */ > + best = 0; > + for (i = 0; i < ARRAY_SIZE(bclk_divs); i++) { > + cur_val = (wm8994->aifclk[id] * 10 / bclk_divs[i]) - bclk_rate; > + if (cur_val < 0) /* BCLK table is sorted */ > + break; > + best = i; > + } > + > + if (i == ARRAY_SIZE(bclk_divs)) { > + debug("%s: Could not get the best matching bclk division\n", > + __func__); > + return -1; > + } > + > + bclk_rate = wm8994->aifclk[id] * 10 / bclk_divs[best]; > + bclk |= best << WM8994_AIF1_BCLK_DIV_SHIFT; > + > + if (wm8994_i2c_read(aif1_reg, ®_data) != 0) > + return -1; debug() here > + > + if ((channels == 1) && ((reg_data & 0x18) == 0x18)) > + aif2 |= WM8994_AIF1_MONO; > + > + if (wm8994->aifclk[id] == 0) > + return -1; debug() here > + > + ret = wm8994_update_bits(aif1_reg, WM8994_AIF1_WL_MASK, aif1); > + ret |= wm8994_update_bits(aif2_reg, WM8994_AIF1_MONO, aif2); > + ret |= wm8994_update_bits(bclk_reg, WM8994_AIF1_BCLK_DIV_MASK, bclk); > + ret |= wm8994_update_bits(rate_reg, WM8994_AIF1_SR_MASK | > + WM8994_AIF1CLK_RATE_MASK, rate_val); > + > + debug("rate vale = %x , bclk val= %x\n", rate_val, bclk); > + > + if (ret < 0) { > + debug("%s: codec register access error\n", __func__); > + return -1; > + } > + > + return 0; > +} > + > +/* > + * Configures Audio interface Clock > + * > + * @param wm8994 wm8994 information pointer > + * @param aif Audio Interface ID > + * > + * @return -1 for error and 0 Success. > + */ > +static int configure_aif_clock(struct wm8994_priv *wm8994, int aif) > +{ > + int rate; > + int reg1 = 0; > + int offset; > + int ret; > + > + if (aif) > + offset = 4; > + else > + offset = 0; comment? What does offset mean? > + > + switch (wm8994->sysclk[aif]) { > + case WM8994_SYSCLK_MCLK1: > + reg1 |= SEL_MCLK1; > + rate = wm8994->mclk[0]; > + break; > + > + case WM8994_SYSCLK_MCLK2: > + reg1 |= SEL_MCLK2; > + rate = wm8994->mclk[1]; > + break; > + > + case WM8994_SYSCLK_FLL1: > + reg1 |= SEL_FLL1; > + rate = wm8994->fll[0].out; > + break; > + > + case WM8994_SYSCLK_FLL2: > + reg1 |= SEL_FLL2; > + rate = wm8994->fll[1].out; > + break; > + > + default: > + debug("%s: Invalid input clock selection [%d]\n", > + __func__, wm8994->sysclk[aif]); > + return -1; > + } > + > + /* if input clock frequenct is more than 135Mhz then divide */ > + if (rate >= WM8994_MAX_INPUT_CLK_FREQ) { > + rate /= 2; > + reg1 |= WM8994_AIF1CLK_DIV; > + } > + > + wm8994->aifclk[aif] = rate; > + > + ret = wm8994_update_bits(WM8994_AIF1_CLOCKING_1 + offset, > + WM8994_AIF1CLK_SRC_MASK | WM8994_AIF1CLK_DIV, > + reg1); > + > + ret |= wm8994_update_bits(WM8994_CLOCKING_1, > + WM8994_SYSCLK_SRC | WM8994_AIF2DSPCLK_ENA_MASK | > + WM8994_SYSDSPCLK_ENA_MASK, WM8994_SYSCLK_SRC | > + WM8994_AIF2DSPCLK_ENA | WM8994_SYSDSPCLK_ENA); > + > + if (ret < 0) { > + debug("%s: codec register access error\n", __func__); > + return -1; > + } > + > + return 0; > +} > + > +/* > + * Configures Audio interface for the given frequency > + * > + * @param wm8994 wm8994 information > + * @param aif_id Audio Interface > + * @param clk_id Input Clock ID > + * @param freq Sampling frequency in Hz > + * > + * @return -1 for error and 0 success. > + */ > +static int wm8994_set_sysclk(struct wm8994_priv *wm8994, int aif_id, > + int clk_id, unsigned int freq) > +{ > + int i; > + int ret = 0; > + > + wm8994->sysclk[aif_id - 1] = clk_id; > + > + switch (clk_id) { > + case WM8994_SYSCLK_MCLK1: > + wm8994->mclk[0] = freq; > + if (aif_id == 2) { > + ret = wm8994_update_bits(WM8994_AIF1_CLOCKING_2 , > + WM8994_AIF2DAC_DIV_MASK , 0); > + } > + break; > + > + case WM8994_SYSCLK_MCLK2: > + /* TODO: Set GPIO AF */ > + wm8994->mclk[1] = freq; > + break; > + > + case WM8994_SYSCLK_FLL1: > + case WM8994_SYSCLK_FLL2: > + break; > + > + case WM8994_SYSCLK_OPCLK: > + /* Special case - a division (times 10) is given and /* * Special case ... > + * no effect on main clocking. > + */ > + if (freq) { > + for (i = 0; i < ARRAY_SIZE(opclk_divs); i++) > + if (opclk_divs[i] == freq) > + break; > + if (i == ARRAY_SIZE(opclk_divs)) > + return -1; debug() > + ret = wm8994_update_bits(WM8994_CLOCKING_2, > + WM8994_OPCLK_DIV_MASK, i); > + ret |= wm8994_update_bits(WM8994_POWER_MANAGEMENT_2, > + WM8994_OPCLK_ENA, WM8994_OPCLK_ENA); > + } else { > + ret |= wm8994_update_bits(WM8994_POWER_MANAGEMENT_2, > + WM8994_OPCLK_ENA, 0); > + } > + > + default: > + debug("%s Invalid input clock selection [%d]\n", > + __func__, clk_id); > + return -1; > + } > + > + ret |= configure_aif_clock(wm8994, aif_id - 1); > + > + if (ret < 0) { > + debug("%s: codec register access error\n", __func__); > + return -1; > + } > + > + return 0; > +} > + > +/* > + * Initializes Volume for AIF2 to HP path > + * > + * @returns -1 for error and 0 Success. > + * > + */ > +static int wm8994_init_volume_aif2_dac1(void) > +{ > + int ret; > + > + /* Unmute AIF2DAC */ > + ret = wm8994_update_bits(WM8994_AIF2_DAC_FILTERS_1, > + WM8994_AIF2DAC_MUTE_MASK, 0); > + > + > + ret |= wm8994_update_bits(WM8994_AIF2_DAC_LEFT_VOLUME, > + WM8994_AIF2DAC_VU_MASK | WM8994_AIF2DACL_VOL_MASK, > + WM8994_AIF2DAC_VU | 0xff); > + > + ret |= wm8994_update_bits(WM8994_AIF2_DAC_RIGHT_VOLUME, > + WM8994_AIF2DAC_VU_MASK | WM8994_AIF2DACR_VOL_MASK, > + WM8994_AIF2DAC_VU | 0xff); > + > + > + ret |= wm8994_update_bits(WM8994_DAC1_LEFT_VOLUME, > + WM8994_DAC1_VU_MASK | WM8994_DAC1L_VOL_MASK | > + WM8994_DAC1L_MUTE_MASK, WM8994_DAC1_VU | 0xc0); > + > + ret |= wm8994_update_bits(WM8994_DAC1_RIGHT_VOLUME, > + WM8994_DAC1_VU_MASK | WM8994_DAC1R_VOL_MASK | > + WM8994_DAC1R_MUTE_MASK, WM8994_DAC1_VU | 0xc0); > + /* Head Phone Volume */ > + ret |= wm8994_i2c_write(WM8994_LEFT_OUTPUT_VOLUME, 0x12D); > + ret |= wm8994_i2c_write(WM8994_RIGHT_OUTPUT_VOLUME, 0x12D); > + > + if (ret < 0) { > + debug("%s: codec register access error\n", __func__); > + return -1; > + } > + > + return 0; > +} > + > +/* > + * Intialise wm8994 codec device > + * > + * @param wm8994 wm8994 information > + * > + * @returns -1 for error and 0 Success. > + */ > +static int wm8994_device_init(struct wm8994_priv *wm8994) > +{ > + const char *devname; > + unsigned short reg_data; > + int ret; > + > + wm8994_i2c_write(WM8994_SOFTWARE_RESET, WM8994_SW_RESET);/* Reset */ > + > + ret = wm8994_i2c_read(WM8994_SOFTWARE_RESET, ®_data); > + if (ret < 0) { > + debug("Failed to read ID register\n"); > + goto err; > + } > + > + if (reg_data == WM8994_ID) { > + devname = "WM8994"; > + debug("Device registered as type %d\n", wm8994->type); > + wm8994->type = WM8994; > + } else { > + debug("Device is not a WM8994, ID is %x\n", ret); > + ret = -1; > + goto err; > + } > + > + ret = wm8994_i2c_read(WM8994_CHIP_REVISION, ®_data); > + if (ret < 0) { > + debug("Failed to read revision register: %d\n", ret); > + goto err; > + } > + wm8994->revision = reg_data; > + debug("%s revision %c\n", devname, 'A' + wm8994->revision); > + > + /* VMID Selection */ > + ret |= wm8994_update_bits(WM8994_POWER_MANAGEMENT_1, > + WM8994_VMID_SEL_MASK | WM8994_BIAS_ENA_MASK, 0x3); > + > + /* Charge Pump Enable */ > + ret |= wm8994_update_bits(WM8994_CHARGE_PUMP_1, WM8994_CP_ENA_MASK, > + WM8994_CP_ENA); > + > + /* Head Phone Power Enable */ > + ret |= wm8994_update_bits(WM8994_POWER_MANAGEMENT_1, > + WM8994_HPOUT1L_ENA_MASK, WM8994_HPOUT1L_ENA); > + > + ret |= wm8994_update_bits(WM8994_POWER_MANAGEMENT_1, > + WM8994_HPOUT1R_ENA_MASK, WM8994_HPOUT1R_ENA); > + > + /* Power enable for AIF2 and DAC1 */ > + ret |= wm8994_update_bits(WM8994_POWER_MANAGEMENT_5, > + WM8994_AIF2DACL_ENA_MASK | WM8994_AIF2DACR_ENA_MASK | > + WM8994_DAC1L_ENA_MASK | WM8994_DAC1R_ENA_MASK, > + WM8994_AIF2DACL_ENA | WM8994_AIF2DACR_ENA | WM8994_DAC1L_ENA | > + WM8994_DAC1R_ENA); > + > + /* Head Phone Initialisation */ > + ret |= wm8994_update_bits(WM8994_ANALOGUE_HP_1, > + WM8994_HPOUT1L_DLY_MASK | WM8994_HPOUT1R_DLY_MASK, > + WM8994_HPOUT1L_DLY | WM8994_HPOUT1R_DLY); > + > + ret |= wm8994_update_bits(WM8994_DC_SERVO_1, > + WM8994_DCS_ENA_CHAN_0_MASK | > + WM8994_DCS_ENA_CHAN_1_MASK , WM8994_DCS_ENA_CHAN_0 | > + WM8994_DCS_ENA_CHAN_1); > + > + ret |= wm8994_update_bits(WM8994_ANALOGUE_HP_1, > + WM8994_HPOUT1L_DLY_MASK | > + WM8994_HPOUT1R_DLY_MASK | WM8994_HPOUT1L_OUTP_MASK | > + WM8994_HPOUT1R_OUTP_MASK | > + WM8994_HPOUT1L_RMV_SHORT_MASK | > + WM8994_HPOUT1R_RMV_SHORT_MASK, WM8994_HPOUT1L_DLY | > + WM8994_HPOUT1R_DLY | WM8994_HPOUT1L_OUTP | > + WM8994_HPOUT1R_OUTP | WM8994_HPOUT1L_RMV_SHORT | > + WM8994_HPOUT1R_RMV_SHORT); > + > + /* MIXER Config DAC1 to HP */ > + ret |= wm8994_update_bits(WM8994_OUTPUT_MIXER_1, > + WM8994_DAC1L_TO_HPOUT1L_MASK, WM8994_DAC1L_TO_HPOUT1L); > + > + ret |= wm8994_update_bits(WM8994_OUTPUT_MIXER_2, > + WM8994_DAC1R_TO_HPOUT1R_MASK, WM8994_DAC1R_TO_HPOUT1R); > + > + /* Routing AIF2 to DAC1 */ > + ret |= wm8994_update_bits(WM8994_DAC1_LEFT_MIXER_ROUTING, > + WM8994_AIF2DACL_TO_DAC1L_MASK, > + WM8994_AIF2DACL_TO_DAC1L); > + > + ret |= wm8994_update_bits(WM8994_DAC1_RIGHT_MIXER_ROUTING, > + WM8994_AIF2DACR_TO_DAC1R_MASK, > + WM8994_AIF2DACR_TO_DAC1R); > + > + /* GPIO Settings for AIF2 */ > + /* B CLK */ > + ret |= wm8994_update_bits(WM8994_GPIO_3, WM8994_GPIO_DIR_MASK | > + WM8994_GPIO_FUNCTION_MASK , > + WM8994_GPIO_DIR_OUTPUT | > + WM8994_GPIO_FUNCTION_I2S_CLK); > + > + /* LR CLK */ > + ret |= wm8994_update_bits(WM8994_GPIO_4, WM8994_GPIO_DIR_MASK | > + WM8994_GPIO_FUNCTION_MASK, > + WM8994_GPIO_DIR_OUTPUT | > + WM8994_GPIO_FUNCTION_I2S_CLK); > + > + /* DATA */ > + ret |= wm8994_update_bits(WM8994_GPIO_5, WM8994_GPIO_DIR_MASK | > + WM8994_GPIO_FUNCTION_MASK, > + WM8994_GPIO_DIR_OUTPUT | > + WM8994_GPIO_FUNCTION_I2S_CLK); > + > + ret |= wm8994_init_volume_aif2_dac1(); > + if (ret < 0) > + goto err; > + > + debug("%s: Codec chip init ok\n", __func__); > + return 0; > +err: > + debug("%s: Codec chip init error\n", __func__); > + return -1; > +} > + > +/*wm8994 Device Initialisation */ > +int wm8994_init(struct sound_codec_info *pcodec_info, > + enum en_audio_interface aif_id, > + int sampling_rate, int mclk_freq, > + int bits_per_sample, unsigned int channels) > +{ > + int ret = 0; > + > + /* shift the device address by 1 for 7 bit addressing */ > + g_wm8994_i2c_dev_addr = pcodec_info->i2c_dev_addr >> 1; Would be better if the address was a 7-bit address? > + wm8994_i2c_init(pcodec_info->i2c_bus); > + > + if (pcodec_info->codec_type == CODEC_WM_8994) > + g_wm8994_info.type = WM8994; > + else { > + debug("%s: Codec id [%d] not defined\n", __func__, > + pcodec_info->codec_type); > + return -1; > + } > + > + ret = wm8994_device_init(&g_wm8994_info); > + if (ret < 0) { > + debug("%s: wm8994 codec chip init failed\n", __func__); > + return ret; > + } > + > + ret = wm8994_set_sysclk(&g_wm8994_info, aif_id, WM8994_SYSCLK_MCLK1, > + mclk_freq); > + if (ret < 0) { > + debug("%s: wm8994 codec set sys clock failed\n", __func__); > + return ret; > + } > + > + ret = wm8994_hw_params(&g_wm8994_info, aif_id, sampling_rate, > + bits_per_sample, channels); > + > + if (ret == 0) { > + ret = wm8994_set_fmt(aif_id, SND_SOC_DAIFMT_I2S | > + SND_SOC_DAIFMT_NB_NF | > + SND_SOC_DAIFMT_CBS_CFS); > + } > + return ret; > +} > diff --git a/drivers/sound/wm8994.h b/drivers/sound/wm8994.h > new file mode 100644 > index 0000000..3c12cbb > --- /dev/null > +++ b/drivers/sound/wm8994.h > @@ -0,0 +1,87 @@ > +/* > + * Copyright (C) 2012 Samsung Electronics > + * R. Chadrasekar <rcsekar@samsung.com> > + * > + * See file CREDITS for list of people who contributed to this > + * project. > + * > + * 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. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + * > + * You should have received a copy of the GNU General Public License > + * along with this program; if not, write to the Free Software > + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, > + * MA 02111-1307 USA > + */ > + > +#ifndef __WM8994_H__ > +#define __WM8994_H__ > + > +/* Sources for AIF1/2 SYSCLK - use with set_dai_sysclk() */ > +#define WM8994_SYSCLK_MCLK1 1 > +#define WM8994_SYSCLK_MCLK2 2 > +#define WM8994_SYSCLK_FLL1 3 > +#define WM8994_SYSCLK_FLL2 4 > + > +/* Avilable audi interface ports in wm8994 codec */ > +enum en_audio_interface { > + WM8994_AIF1 = 1, > + WM8994_AIF2, > + WM8994_AIF3 > +}; > + > +/* OPCLK is also configured with set_dai_sysclk, specify division*10 as rate. */ > +#define WM8994_SYSCLK_OPCLK 5 > + > +#define WM8994_FLL1 1 > +#define WM8994_FLL2 2 > + > +#define WM8994_FLL_SRC_MCLK1 1 > +#define WM8994_FLL_SRC_MCLK2 2 > +#define WM8994_FLL_SRC_LRCLK 3 > +#define WM8994_FLL_SRC_BCLK 4 > + > +/* maximum available digital interfac in the dac to configure */ > +#define WM8994_MAX_AIF 2 > + > +#define WM8994_MAX_INPUT_CLK_FREQ 13500000 > +#define WM8994_ID 0x8994 > + > +enum wm8994_vmid_mode { > + WM8994_VMID_NORMAL, > + WM8994_VMID_FORCE, > +}; > + > +/* wm 8994 family devices */ > +enum wm8994_type { > + WM8994 = 0, > + WM8958 = 1, > + WM1811 = 2, > +}; > + > +/* > + * intialise wm8994 sound codec device for the given configuration > + * > + * @param pcodec_info pointer value of the sound codec info structure > + * parsed from device tree > + * @param aif_id enum value of codec interface port in which > + * soc i2s is connected > + * @param sampling_rate Sampling rate > + * @param mclk_freq MCLK Frequency > + * @param bits_per_sample bits per Sample > + * @param channels Number of channnels Really there should be some information about suitable values here. The comments are almost just repeating the variable names. What are valid values for 'channels'. For mclk_freq, what should this be set to? > + * > + * @returns -1 for error and 0 Success. > + */ > +int wm8994_init(struct sound_codec_info *pcodec_info, > + enum en_audio_interface aif_id, > + int sampling_rate, int mclk_freq, > + int bits_per_sample, unsigned int channels); > +#endif /*__WM8994_H__ */ > diff --git a/drivers/sound/wm8994_registers.h b/drivers/sound/wm8994_registers.h > new file mode 100644 > index 0000000..dacf6b6 > --- /dev/null > +++ b/drivers/sound/wm8994_registers.h > @@ -0,0 +1,299 @@ > +/* > + * (C) Copyright 2012 Samsung Electronics > + * > + * 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 __MFD_WM8994_REGISTERS_H__ > +#define __MFD_WM8994_REGISTERS_H__ Should remove MFD_ I think > + > +/* > + * Register values. > + */ > +#define WM8994_SOFTWARE_RESET 0x00 > +#define WM8994_POWER_MANAGEMENT_1 0x01 > +#define WM8994_POWER_MANAGEMENT_2 0x02 > +#define WM8994_POWER_MANAGEMENT_5 0x05 > +#define WM8994_LEFT_OUTPUT_VOLUME 0x1C > +#define WM8994_RIGHT_OUTPUT_VOLUME 0x1D > +#define WM8994_OUTPUT_MIXER_1 0x2D > +#define WM8994_OUTPUT_MIXER_2 0x2E > +#define WM8994_CHARGE_PUMP_1 0x4C > +#define WM8994_DC_SERVO_1 0x54 > +#define WM8994_ANALOGUE_HP_1 0x60 > +#define WM8994_CHIP_REVISION 0x100 > +#define WM8994_AIF1_CLOCKING_1 0x200 > +#define WM8994_AIF1_CLOCKING_2 0x201 > +#define WM8994_AIF2_CLOCKING_1 0x204 > +#define WM8994_CLOCKING_1 0x208 > +#define WM8994_CLOCKING_2 0x209 > +#define WM8994_AIF1_RATE 0x210 > +#define WM8994_AIF2_RATE 0x211 > +#define WM8994_RATE_STATUS 0x212 > +#define WM8994_AIF1_CONTROL_1 0x300 > +#define WM8994_AIF1_CONTROL_2 0x301 > +#define WM8994_AIF1_MASTER_SLAVE 0x302 > +#define WM8994_AIF1_BCLK 0x303 > +#define WM8994_AIF2_CONTROL_1 0x310 > +#define WM8994_AIF2_CONTROL_2 0x311 > +#define WM8994_AIF2_MASTER_SLAVE 0x312 > +#define WM8994_AIF2_BCLK 0x313 > +#define WM8994_AIF2_DAC_LEFT_VOLUME 0x502 > +#define WM8994_AIF2_DAC_RIGHT_VOLUME 0x503 > +#define WM8994_AIF2_DAC_FILTERS_1 0x520 > +#define WM8994_DAC1_LEFT_MIXER_ROUTING 0x601 > +#define WM8994_DAC1_RIGHT_MIXER_ROUTING 0x602 > +#define WM8994_DAC1_LEFT_VOLUME 0x610 > +#define WM8994_DAC1_RIGHT_VOLUME 0x611 > +#define WM8994_GPIO_3 0x702 > +#define WM8994_GPIO_4 0x703 > +#define WM8994_GPIO_5 0x704 > + > +/* > + * Field Definitions. > + */ > + > +/* > + * R0 (0x00) - Software Reset > + */ > +/* SW_RESET */ > +#define WM8994_SW_RESET 1 > +/* > + * R1 (0x01) - Power Management (1) > + */ > +/* HPOUT1L_ENA */ > +#define WM8994_HPOUT1L_ENA 0x0200 > +/* HPOUT1L_ENA */ > +#define WM8994_HPOUT1L_ENA_MASK 0x0200 > +/* HPOUT1R_ENA */ > +#define WM8994_HPOUT1R_ENA 0x0100 > +/* HPOUT1R_ENA */ > +#define WM8994_HPOUT1R_ENA_MASK 0x0100 > +/* VMID_SEL - [2:1] */ > +#define WM8994_VMID_SEL_MASK 0x0006 > +/* BIAS_ENA */ > +#define WM8994_BIAS_ENA 0x0001 > +/* BIAS_ENA */ > +#define WM8994_BIAS_ENA_MASK 0x0001 > + > +/* > + * R2 (0x02) - Power Management (2) > + */ > +/* OPCLK_ENA */ > +#define WM8994_OPCLK_ENA 0x0800 > + > +/* > + * R5 (0x05) - Power Management (5) > + */ > +/* AIF2DACL_ENA */ > +#define WM8994_AIF2DACL_ENA 0x2000 > +#define WM8994_AIF2DACL_ENA_MASK 0x2000 > +/* AIF2DACR_ENA */ > +#define WM8994_AIF2DACR_ENA 0x1000 > +#define WM8994_AIF2DACR_ENA_MASK 0x1000 > +/* DAC1L_ENA */ > +#define WM8994_DAC1L_ENA 0x0002 > +#define WM8994_DAC1L_ENA_MASK 0x0002 > +/* DAC1R_ENA */ > +#define WM8994_DAC1R_ENA 0x0001 > +#define WM8994_DAC1R_ENA_MASK 0x0001 > + > +/* > + * R45 (0x2D) - Output Mixer (1) > + */ > +/* DAC1L_TO_HPOUT1L */ > +#define WM8994_DAC1L_TO_HPOUT1L 0x0100 > +#define WM8994_DAC1L_TO_HPOUT1L_MASK 0x0100 > + > +/* > + * R46 (0x2E) - Output Mixer (2) > + */ > +/* DAC1R_TO_HPOUT1R */ > +#define WM8994_DAC1R_TO_HPOUT1R 0x0100 > +#define WM8994_DAC1R_TO_HPOUT1R_MASK 0x0100 > + > +/* > + * R76 (0x4C) - Charge Pump (1) > + */ > +/* CP_ENA */ > +#define WM8994_CP_ENA 0x8000 > +#define WM8994_CP_ENA_MASK 0x8000 > +/* > + * R84 (0x54) - DC Servo (1) > + */ > +/* DCS_ENA_CHAN_1 */ > +#define WM8994_DCS_ENA_CHAN_1 0x0002 > +#define WM8994_DCS_ENA_CHAN_1_MASK 0x0002 > +/* DCS_ENA_CHAN_0 */ > +#define WM8994_DCS_ENA_CHAN_0 0x0001 > +#define WM8994_DCS_ENA_CHAN_0_MASK 0x0001 > + > +/* > + * R96 (0x60) - Analogue HP (1) > + */ > +/* HPOUT1L_RMV_SHORT */ > +#define WM8994_HPOUT1L_RMV_SHORT 0x0080 > +#define WM8994_HPOUT1L_RMV_SHORT_MASK 0x0080 > +/* HPOUT1L_OUTP */ > +#define WM8994_HPOUT1L_OUTP 0x0040 > +#define WM8994_HPOUT1L_OUTP_MASK 0x0040 > +/* HPOUT1L_DLY */ > +#define WM8994_HPOUT1L_DLY 0x0020 > +#define WM8994_HPOUT1L_DLY_MASK 0x0020 > +/* HPOUT1R_RMV_SHORT */ > +#define WM8994_HPOUT1R_RMV_SHORT 0x0008 > +#define WM8994_HPOUT1R_RMV_SHORT_MASK 0x0008 > +/* HPOUT1R_OUTP */ > +#define WM8994_HPOUT1R_OUTP 0x0004 > +#define WM8994_HPOUT1R_OUTP_MASK 0x0004 > +/* HPOUT1R_DLY */ > +#define WM8994_HPOUT1R_DLY 0x0002 > +#define WM8994_HPOUT1R_DLY_MASK 0x0002 > + > +/* > + * R512 (0x200) - AIF1 Clocking (1) > + */ > +/* AIF1CLK_SRC - [4:3] */ > +#define WM8994_AIF1CLK_SRC_MASK 0x0018 > +/* AIF1CLK_DIV */ > +#define WM8994_AIF1CLK_DIV 0x0002 > +/* AIF1CLK_ENA */ > +#define WM8994_AIF1CLK_ENA 0x0001 > +#define WM8994_AIF1CLK_ENA_MASK 0x0001 > + > +/* > + * R517 (0x205) - AIF2 Clocking (2) > + */ > +/* AIF2DAC_DIV - [5:3] */ > +#define WM8994_AIF2DAC_DIV_MASK 0x0038 > + > +/* > + * R520 (0x208) - Clocking (1) > + */ > +/* AIF2DSPCLK_ENA */ > +#define WM8994_AIF2DSPCLK_ENA 0x0004 > +#define WM8994_AIF2DSPCLK_ENA_MASK 0x0004 > +/* SYSDSPCLK_ENA */ > +#define WM8994_SYSDSPCLK_ENA 0x0002 > +#define WM8994_SYSDSPCLK_ENA_MASK 0x0002 > +/* SYSCLK_SRC */ > +#define WM8994_SYSCLK_SRC 0x0001 > + > +/* > + * R521 (0x209) - Clocking (2) > + */ > +/* OPCLK_DIV - [2:0] */ > +#define WM8994_OPCLK_DIV_MASK 0x0007 > + > +/* > + * R528 (0x210) - AIF1 Rate > + */ > +/* AIF1_SR - [7:4] */ > +#define WM8994_AIF1_SR_MASK 0x00F0 > +#define WM8994_AIF1_SR_SHIFT 4 > +/* AIF1CLK_RATE - [3:0] */ > +#define WM8994_AIF1CLK_RATE_MASK 0x000F > + > +/* > + * R768 (0x300) - AIF1 Control (1) > + */ > +/* AIF1_BCLK_INV */ > +#define WM8994_AIF1_BCLK_INV 0x0100 > +/* AIF1_LRCLK_INV */ > +#define WM8994_AIF1_LRCLK_INV 0x0080 > +#define WM8994_AIF1_LRCLK_INV_MASK 0x0080 > +/* AIF1_WL - [6:5] */ > +#define WM8994_AIF1_WL_MASK 0x0060 > +/* AIF1_FMT - [4:3] */ > +#define WM8994_AIF1_FMT_MASK 0x0018 > + > +/* > + * R769 (0x301) - AIF1 Control (2) > + */ > +/* AIF1_MONO */ > +#define WM8994_AIF1_MONO 0x0100 > + > +/* > + * R770 (0x302) - AIF1 Master/Slave > + */ > +/* AIF1_MSTR */ > +#define WM8994_AIF1_MSTR 0x4000 > +#define WM8994_AIF1_MSTR_MASK 0x4000 > + > +/* > + * R771 (0x303) - AIF1 BCLK > + */ > +/* AIF1_BCLK_DIV - [8:4] */ > +#define WM8994_AIF1_BCLK_DIV_MASK 0x01F0 > +#define WM8994_AIF1_BCLK_DIV_SHIFT 4 > + > +/* > + * R1282 (0x502) - AIF2 DAC Left Volume > + */ > +/* AIF2DAC_VU */ > +#define WM8994_AIF2DAC_VU 0x0100 > +#define WM8994_AIF2DAC_VU_MASK 0x0100 > +/* AIF2DACL_VOL - [7:0] */ > +#define WM8994_AIF2DACL_VOL_MASK 0x00FF > + > +/* > + * R1283 (0x503) - AIF2 DAC Right Volume > + */ > +/* AIF2DACR_VOL - [7:0] */ > +#define WM8994_AIF2DACR_VOL_MASK 0x00FF > + > +/* > + * R1312 (0x520) - AIF2 DAC Filters (1) > + */ > +/* AIF2DAC_MUTE */ > +#define WM8994_AIF2DAC_MUTE_MASK 0x0200 > + > +/* > + * R1537 (0x601) - DAC1 Left Mixer Routing > + */ > +/* AIF2DACL_TO_DAC1L */ > +#define WM8994_AIF2DACL_TO_DAC1L 0x0004 > +#define WM8994_AIF2DACL_TO_DAC1L_MASK 0x0004 > + > +/* > + * R1538 (0x602) - DAC1 Right Mixer Routing > + */ > +/* AIF2DACR_TO_DAC1R */ > +#define WM8994_AIF2DACR_TO_DAC1R 0x0004 > +#define WM8994_AIF2DACR_TO_DAC1R_MASK 0x0004 > + > +/* > + * R1552 (0x610) - DAC1 Left Volume > + */ > +/* DAC1L_MUTE */ > +#define WM8994_DAC1L_MUTE_MASK 0x0200 > +/* DAC1_VU */ > +#define WM8994_DAC1_VU 0x0100 > +#define WM8994_DAC1_VU_MASK 0x0100 > +/* DAC1L_VOL - [7:0] */ > +#define WM8994_DAC1L_VOL_MASK 0x00FF > + > +/* > + * R1553 (0x611) - DAC1 Right Volume > + */ > +/* DAC1R_MUTE */ > +#define WM8994_DAC1R_MUTE_MASK 0x0200 > +/* DAC1R_VOL - [7:0] */ > +#define WM8994_DAC1R_VOL_MASK 0x00FF > + > +/* > + * GPIO > + */ > +/* OUTPUT PIN */ > +#define WM8994_GPIO_DIR_OUTPUT 0x8000 > +/* GPIO PIN MASK */ > +#define WM8994_GPIO_DIR_MASK 0xFFE0 > +/* I2S CLK */ > +#define WM8994_GPIO_FUNCTION_I2S_CLK 0x0000 > +/* GPn FN */ > +#define WM8994_GPIO_FUNCTION_MASK 0x001F > +#endif > -- > 1.7.4.4 > Regards, Simon
diff --git a/drivers/sound/Makefile b/drivers/sound/Makefile index 18ad2c9..8fdffb1 100644 --- a/drivers/sound/Makefile +++ b/drivers/sound/Makefile @@ -27,6 +27,7 @@ LIB := $(obj)libsound.o COBJS-$(CONFIG_SOUND) += sound.o COBJS-$(CONFIG_I2S) += samsung-i2s.o +COBJS-$(CONFIG_SOUND_WM8994) += wm8994.o COBJS := $(COBJS-y) SRCS := $(COBJS:.o=.c) diff --git a/drivers/sound/wm8994.c b/drivers/sound/wm8994.c new file mode 100644 index 0000000..21604f1 --- /dev/null +++ b/drivers/sound/wm8994.c @@ -0,0 +1,781 @@ +/* + * Copyright (C) 2012 Samsung Electronics + * R. Chandrasekar <rcsekar@samsung.com> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ +#define DEBUG +#include <asm/arch/clk.h> +#include <asm/arch/cpu.h> +#include <asm/gpio.h> +#include <asm/io.h> +#include <common.h> +#include <div64.h> +#include <i2c.h> +#include <i2s.h> +#include <sound.h> +#include "wm8994.h" +#include "wm8994_registers.h" + +/* defines for wm8994 system clock selection */ +#define SEL_MCLK1 0x00 +#define SEL_MCLK2 0x08 +#define SEL_FLL1 0x10 +#define SEL_FLL2 0x18 + +/* fll config to configure fll */ +struct wm8994_fll_config { + int src; /* Source */ + int in; /* Input frequency in Hz */ + int out; /* output frequency in Hz */ +}; + +/* codec private data */ +struct wm8994_priv { + enum wm8994_type type; /* codec type of wolfson */ + int revision; /* Revision */ + int sysclk[WM8994_MAX_AIF]; /* System clock freqency in Hz */ + int mclk[WM8994_MAX_AIF]; /* master clock frequency in Hz */ + int aifclk[WM8994_MAX_AIF]; /* audio interfce clock in Hz */ + struct wm8994_fll_config fll[2]; /* fll config to configure fll */ +}; + +/* wm 8994 supported sampling rate vlaues */ +static unsigned int src_rate[] = { + 8000, 11025, 12000, 16000, 22050, 24000, + 32000, 44100, 48000, 88200, 96000 +}; + +/* op clock divisions */ +static int opclk_divs[] = { 10, 20, 30, 40, 55, 60, 80, 120, 160 }; + +/* lr clock framsize ratio */ +static int fs_ratios[] = { + 64, 128, 192, 256, 348, 512, 768, 1024, 1408, 1536 +}; + +/* bit clock divisions */ +static int bclk_divs[] = { + 10, 15, 20, 30, 40, 50, 60, 80, 110, 120, 160, 220, 240, 320, 440, 480, + 640, 880, 960, 1280, 1760, 1920 +}; + +/* Global */ +struct wm8994_priv g_wm8994_info; +unsigned int g_wm8994_i2c_dev_addr; + +/* + * Initialise I2C for wm 8994 + * + * @param bus no i2c bus number in which wm8994 is connected + */ +static void wm8994_i2c_init(int bus_no) +{ + i2c_set_bus_num(bus_no); +} + +/* + * Writes value to a device register through i2c + * + * @param reg reg number to be write + * @param data data to be writen to the above registor + * + * @return int value 1 for change, 0 for no change or negative error code. + */ +static int wm8994_i2c_write(unsigned int reg, unsigned short data) +{ + unsigned char val[2]; + + val[0] = (unsigned char)((data >> 8) & 0xff); + val[1] = (unsigned char)(data & 0xff); + debug("Write Addr : 0x%04X, Data : 0x%04X\n", reg, data); + + return i2c_write(g_wm8994_i2c_dev_addr, reg, 2, val, 2); +} + +/* + * Read a value from a device register through i2c + * + * @param reg reg number to be read + * @param data address of read data to be stored + * + * @return int value 0 for success, -1 in case of error. + */ +static unsigned int wm8994_i2c_read(unsigned int reg , unsigned short *data) +{ + unsigned char val[2]; + int ret; + + ret = i2c_read(g_wm8994_i2c_dev_addr, reg, 2, val, 2); + if (ret != 0) { + debug("%s: Error while reading register %#04x\n", + __func__, reg); + return -1; + } + *data = val[0]; + *data <<= 8; + *data |= val[1]; + + return 0; +} + +/* + * update device register bits through i2c + * + * @param reg codec register + * @param mask register mask + * @param value new value + * + * @return int value 1 for change, 0 for no change or negative error code. + */ +static int wm8994_update_bits(unsigned int reg, unsigned short mask, + unsigned short value) +{ + int change , ret = 0; + unsigned short old, new; + + if (wm8994_i2c_read(reg, &old) != 0) + return -1; + new = (old & ~mask) | (value & mask); + change = (old != new) ? 1 : 0; + if (change) + ret = wm8994_i2c_write(reg, new); + if (ret < 0) + return ret; + + return change; +} + +/* + * Sets i2s set format + * + * @param aif_id Interface ID + * @param fmt i2S format + * + * @return -1 for error and 0 Success. + */ +int wm8994_set_fmt(int aif_id, unsigned int fmt) +{ + int ms_reg; + int aif_reg; + int ms = 0; + int aif = 0; + int aif_clk = 0; + int error = 0; + + switch (aif_id) { + case 1: + ms_reg = WM8994_AIF1_MASTER_SLAVE; + aif_reg = WM8994_AIF1_CONTROL_1; + aif_clk = WM8994_AIF1_CLOCKING_1; + break; + case 2: + ms_reg = WM8994_AIF2_MASTER_SLAVE; + aif_reg = WM8994_AIF2_CONTROL_1; + aif_clk = WM8994_AIF2_CLOCKING_1; + break; + default: + debug("%s: Invalid audio interface selection\n", __func__); + return -1; + } + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + break; + case SND_SOC_DAIFMT_CBM_CFM: + ms = WM8994_AIF1_MSTR; + break; + default: + debug("%s: Invalid i2s master selection\n", __func__); + return -1; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_B: + aif |= WM8994_AIF1_LRCLK_INV; + case SND_SOC_DAIFMT_DSP_A: + aif |= 0x18; + break; + case SND_SOC_DAIFMT_I2S: + aif |= 0x10; + break; + case SND_SOC_DAIFMT_RIGHT_J: + break; + case SND_SOC_DAIFMT_LEFT_J: + aif |= 0x8; + break; + default: + debug("%s: Invalid i2s format selection\n", __func__); + return -1; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_A: + case SND_SOC_DAIFMT_DSP_B: + /* frame inversion not valid for DSP modes */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + aif |= WM8994_AIF1_BCLK_INV; + break; + default: + debug("%s: Invalid i2s frame inverse selection\n", + __func__); + return -1; + } + break; + + case SND_SOC_DAIFMT_I2S: + case SND_SOC_DAIFMT_RIGHT_J: + case SND_SOC_DAIFMT_LEFT_J: + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_IF: + aif |= WM8994_AIF1_BCLK_INV | WM8994_AIF1_LRCLK_INV; + break; + case SND_SOC_DAIFMT_IB_NF: + aif |= WM8994_AIF1_BCLK_INV; + break; + case SND_SOC_DAIFMT_NB_IF: + aif |= WM8994_AIF1_LRCLK_INV; + break; + default: + debug("%s: Invalid i2s clock polarity selection\n", + __func__); + return -1; + } + break; + default: + debug("%s: Invalid i2s format selection\n", __func__); + return -1; + } + + error = wm8994_update_bits(aif_reg, WM8994_AIF1_BCLK_INV | + WM8994_AIF1_LRCLK_INV_MASK | WM8994_AIF1_FMT_MASK, aif); + + error |= wm8994_update_bits(ms_reg, WM8994_AIF1_MSTR_MASK, ms); + error |= wm8994_update_bits(aif_clk, WM8994_AIF1CLK_ENA_MASK, + WM8994_AIF1CLK_ENA); + if (error < 0) { + debug("%s: codec register access error\n", __func__); + return -1; + } + + return 0; +} + +/* + * Sets hw params FOR WM8994 + * + * @param sampling_rate Sampling rate + * @param bits_per_sample Bits per sample + * @param Channels Channels in the given audio input + * + * @return -1 for error and 0 Success. + */ +static int wm8994_hw_params(struct wm8994_priv *wm8994, int aif_id, + unsigned int sampling_rate, unsigned int bits_per_sample, + unsigned int channels) +{ + int aif1_reg; + int aif2_reg; + int bclk_reg; + int bclk = 0; + int rate_reg; + int aif1 = 0; + int aif2 = 0; + int rate_val = 0; + int id = aif_id - 1; + int i, cur_val, best_val, bclk_rate, best; + unsigned short reg_data; + int ret = 0; + + switch (aif_id) { + case 1: + aif1_reg = WM8994_AIF1_CONTROL_1; + aif2_reg = WM8994_AIF1_CONTROL_2; + bclk_reg = WM8994_AIF1_BCLK; + rate_reg = WM8994_AIF1_RATE; + break; + case 2: + aif1_reg = WM8994_AIF2_CONTROL_1; + aif2_reg = WM8994_AIF2_CONTROL_2; + bclk_reg = WM8994_AIF2_BCLK; + rate_reg = WM8994_AIF2_RATE; + break; + default: + return -1; + } + + bclk_rate = sampling_rate * 32; + switch (bits_per_sample) { + case 16: + bclk_rate *= 16; + break; + case 20: + bclk_rate *= 20; + aif1 |= 0x20; + break; + case 24: + bclk_rate *= 24; + aif1 |= 0x40; + break; + case 32: + bclk_rate *= 32; + aif1 |= 0x60; + break; + default: + return -1; + } + + /* Try to find an appropriate sample rate; look for an exact match. */ + for (i = 0; i < ARRAY_SIZE(src_rate); i++) + if (src_rate[i] == sampling_rate) + break; + + if (i == ARRAY_SIZE(src_rate)) { + debug("%s: Could not get the best matching samplingrate\n", + __func__); + return -1; + } + + rate_val |= i << WM8994_AIF1_SR_SHIFT; + + /* AIFCLK/fs ratio; look for a close match in either direction */ + best = 0; + best_val = abs((fs_ratios[0] * sampling_rate) + - wm8994->aifclk[id]); + + for (i = 1; i < ARRAY_SIZE(fs_ratios); i++) { + cur_val = abs((fs_ratios[i] * sampling_rate) + - wm8994->aifclk[id]); + if (cur_val >= best_val) + continue; + best = i; + best_val = cur_val; + } + + rate_val |= best; + + /* + * We may not get quite the right frequency if using + * approximate clocks so look for the closest match that is + * higher than the target (we need to ensure that there enough + * BCLKs to clock out the samples). + */ + best = 0; + for (i = 0; i < ARRAY_SIZE(bclk_divs); i++) { + cur_val = (wm8994->aifclk[id] * 10 / bclk_divs[i]) - bclk_rate; + if (cur_val < 0) /* BCLK table is sorted */ + break; + best = i; + } + + if (i == ARRAY_SIZE(bclk_divs)) { + debug("%s: Could not get the best matching bclk division\n", + __func__); + return -1; + } + + bclk_rate = wm8994->aifclk[id] * 10 / bclk_divs[best]; + bclk |= best << WM8994_AIF1_BCLK_DIV_SHIFT; + + if (wm8994_i2c_read(aif1_reg, ®_data) != 0) + return -1; + + if ((channels == 1) && ((reg_data & 0x18) == 0x18)) + aif2 |= WM8994_AIF1_MONO; + + if (wm8994->aifclk[id] == 0) + return -1; + + ret = wm8994_update_bits(aif1_reg, WM8994_AIF1_WL_MASK, aif1); + ret |= wm8994_update_bits(aif2_reg, WM8994_AIF1_MONO, aif2); + ret |= wm8994_update_bits(bclk_reg, WM8994_AIF1_BCLK_DIV_MASK, bclk); + ret |= wm8994_update_bits(rate_reg, WM8994_AIF1_SR_MASK | + WM8994_AIF1CLK_RATE_MASK, rate_val); + + debug("rate vale = %x , bclk val= %x\n", rate_val, bclk); + + if (ret < 0) { + debug("%s: codec register access error\n", __func__); + return -1; + } + + return 0; +} + +/* + * Configures Audio interface Clock + * + * @param wm8994 wm8994 information pointer + * @param aif Audio Interface ID + * + * @return -1 for error and 0 Success. + */ +static int configure_aif_clock(struct wm8994_priv *wm8994, int aif) +{ + int rate; + int reg1 = 0; + int offset; + int ret; + + if (aif) + offset = 4; + else + offset = 0; + + switch (wm8994->sysclk[aif]) { + case WM8994_SYSCLK_MCLK1: + reg1 |= SEL_MCLK1; + rate = wm8994->mclk[0]; + break; + + case WM8994_SYSCLK_MCLK2: + reg1 |= SEL_MCLK2; + rate = wm8994->mclk[1]; + break; + + case WM8994_SYSCLK_FLL1: + reg1 |= SEL_FLL1; + rate = wm8994->fll[0].out; + break; + + case WM8994_SYSCLK_FLL2: + reg1 |= SEL_FLL2; + rate = wm8994->fll[1].out; + break; + + default: + debug("%s: Invalid input clock selection [%d]\n", + __func__, wm8994->sysclk[aif]); + return -1; + } + + /* if input clock frequenct is more than 135Mhz then divide */ + if (rate >= WM8994_MAX_INPUT_CLK_FREQ) { + rate /= 2; + reg1 |= WM8994_AIF1CLK_DIV; + } + + wm8994->aifclk[aif] = rate; + + ret = wm8994_update_bits(WM8994_AIF1_CLOCKING_1 + offset, + WM8994_AIF1CLK_SRC_MASK | WM8994_AIF1CLK_DIV, + reg1); + + ret |= wm8994_update_bits(WM8994_CLOCKING_1, + WM8994_SYSCLK_SRC | WM8994_AIF2DSPCLK_ENA_MASK | + WM8994_SYSDSPCLK_ENA_MASK, WM8994_SYSCLK_SRC | + WM8994_AIF2DSPCLK_ENA | WM8994_SYSDSPCLK_ENA); + + if (ret < 0) { + debug("%s: codec register access error\n", __func__); + return -1; + } + + return 0; +} + +/* + * Configures Audio interface for the given frequency + * + * @param wm8994 wm8994 information + * @param aif_id Audio Interface + * @param clk_id Input Clock ID + * @param freq Sampling frequency in Hz + * + * @return -1 for error and 0 success. + */ +static int wm8994_set_sysclk(struct wm8994_priv *wm8994, int aif_id, + int clk_id, unsigned int freq) +{ + int i; + int ret = 0; + + wm8994->sysclk[aif_id - 1] = clk_id; + + switch (clk_id) { + case WM8994_SYSCLK_MCLK1: + wm8994->mclk[0] = freq; + if (aif_id == 2) { + ret = wm8994_update_bits(WM8994_AIF1_CLOCKING_2 , + WM8994_AIF2DAC_DIV_MASK , 0); + } + break; + + case WM8994_SYSCLK_MCLK2: + /* TODO: Set GPIO AF */ + wm8994->mclk[1] = freq; + break; + + case WM8994_SYSCLK_FLL1: + case WM8994_SYSCLK_FLL2: + break; + + case WM8994_SYSCLK_OPCLK: + /* Special case - a division (times 10) is given and + * no effect on main clocking. + */ + if (freq) { + for (i = 0; i < ARRAY_SIZE(opclk_divs); i++) + if (opclk_divs[i] == freq) + break; + if (i == ARRAY_SIZE(opclk_divs)) + return -1; + ret = wm8994_update_bits(WM8994_CLOCKING_2, + WM8994_OPCLK_DIV_MASK, i); + ret |= wm8994_update_bits(WM8994_POWER_MANAGEMENT_2, + WM8994_OPCLK_ENA, WM8994_OPCLK_ENA); + } else { + ret |= wm8994_update_bits(WM8994_POWER_MANAGEMENT_2, + WM8994_OPCLK_ENA, 0); + } + + default: + debug("%s Invalid input clock selection [%d]\n", + __func__, clk_id); + return -1; + } + + ret |= configure_aif_clock(wm8994, aif_id - 1); + + if (ret < 0) { + debug("%s: codec register access error\n", __func__); + return -1; + } + + return 0; +} + +/* + * Initializes Volume for AIF2 to HP path + * + * @returns -1 for error and 0 Success. + * + */ +static int wm8994_init_volume_aif2_dac1(void) +{ + int ret; + + /* Unmute AIF2DAC */ + ret = wm8994_update_bits(WM8994_AIF2_DAC_FILTERS_1, + WM8994_AIF2DAC_MUTE_MASK, 0); + + + ret |= wm8994_update_bits(WM8994_AIF2_DAC_LEFT_VOLUME, + WM8994_AIF2DAC_VU_MASK | WM8994_AIF2DACL_VOL_MASK, + WM8994_AIF2DAC_VU | 0xff); + + ret |= wm8994_update_bits(WM8994_AIF2_DAC_RIGHT_VOLUME, + WM8994_AIF2DAC_VU_MASK | WM8994_AIF2DACR_VOL_MASK, + WM8994_AIF2DAC_VU | 0xff); + + + ret |= wm8994_update_bits(WM8994_DAC1_LEFT_VOLUME, + WM8994_DAC1_VU_MASK | WM8994_DAC1L_VOL_MASK | + WM8994_DAC1L_MUTE_MASK, WM8994_DAC1_VU | 0xc0); + + ret |= wm8994_update_bits(WM8994_DAC1_RIGHT_VOLUME, + WM8994_DAC1_VU_MASK | WM8994_DAC1R_VOL_MASK | + WM8994_DAC1R_MUTE_MASK, WM8994_DAC1_VU | 0xc0); + /* Head Phone Volume */ + ret |= wm8994_i2c_write(WM8994_LEFT_OUTPUT_VOLUME, 0x12D); + ret |= wm8994_i2c_write(WM8994_RIGHT_OUTPUT_VOLUME, 0x12D); + + if (ret < 0) { + debug("%s: codec register access error\n", __func__); + return -1; + } + + return 0; +} + +/* + * Intialise wm8994 codec device + * + * @param wm8994 wm8994 information + * + * @returns -1 for error and 0 Success. + */ +static int wm8994_device_init(struct wm8994_priv *wm8994) +{ + const char *devname; + unsigned short reg_data; + int ret; + + wm8994_i2c_write(WM8994_SOFTWARE_RESET, WM8994_SW_RESET);/* Reset */ + + ret = wm8994_i2c_read(WM8994_SOFTWARE_RESET, ®_data); + if (ret < 0) { + debug("Failed to read ID register\n"); + goto err; + } + + if (reg_data == WM8994_ID) { + devname = "WM8994"; + debug("Device registered as type %d\n", wm8994->type); + wm8994->type = WM8994; + } else { + debug("Device is not a WM8994, ID is %x\n", ret); + ret = -1; + goto err; + } + + ret = wm8994_i2c_read(WM8994_CHIP_REVISION, ®_data); + if (ret < 0) { + debug("Failed to read revision register: %d\n", ret); + goto err; + } + wm8994->revision = reg_data; + debug("%s revision %c\n", devname, 'A' + wm8994->revision); + + /* VMID Selection */ + ret |= wm8994_update_bits(WM8994_POWER_MANAGEMENT_1, + WM8994_VMID_SEL_MASK | WM8994_BIAS_ENA_MASK, 0x3); + + /* Charge Pump Enable */ + ret |= wm8994_update_bits(WM8994_CHARGE_PUMP_1, WM8994_CP_ENA_MASK, + WM8994_CP_ENA); + + /* Head Phone Power Enable */ + ret |= wm8994_update_bits(WM8994_POWER_MANAGEMENT_1, + WM8994_HPOUT1L_ENA_MASK, WM8994_HPOUT1L_ENA); + + ret |= wm8994_update_bits(WM8994_POWER_MANAGEMENT_1, + WM8994_HPOUT1R_ENA_MASK, WM8994_HPOUT1R_ENA); + + /* Power enable for AIF2 and DAC1 */ + ret |= wm8994_update_bits(WM8994_POWER_MANAGEMENT_5, + WM8994_AIF2DACL_ENA_MASK | WM8994_AIF2DACR_ENA_MASK | + WM8994_DAC1L_ENA_MASK | WM8994_DAC1R_ENA_MASK, + WM8994_AIF2DACL_ENA | WM8994_AIF2DACR_ENA | WM8994_DAC1L_ENA | + WM8994_DAC1R_ENA); + + /* Head Phone Initialisation */ + ret |= wm8994_update_bits(WM8994_ANALOGUE_HP_1, + WM8994_HPOUT1L_DLY_MASK | WM8994_HPOUT1R_DLY_MASK, + WM8994_HPOUT1L_DLY | WM8994_HPOUT1R_DLY); + + ret |= wm8994_update_bits(WM8994_DC_SERVO_1, + WM8994_DCS_ENA_CHAN_0_MASK | + WM8994_DCS_ENA_CHAN_1_MASK , WM8994_DCS_ENA_CHAN_0 | + WM8994_DCS_ENA_CHAN_1); + + ret |= wm8994_update_bits(WM8994_ANALOGUE_HP_1, + WM8994_HPOUT1L_DLY_MASK | + WM8994_HPOUT1R_DLY_MASK | WM8994_HPOUT1L_OUTP_MASK | + WM8994_HPOUT1R_OUTP_MASK | + WM8994_HPOUT1L_RMV_SHORT_MASK | + WM8994_HPOUT1R_RMV_SHORT_MASK, WM8994_HPOUT1L_DLY | + WM8994_HPOUT1R_DLY | WM8994_HPOUT1L_OUTP | + WM8994_HPOUT1R_OUTP | WM8994_HPOUT1L_RMV_SHORT | + WM8994_HPOUT1R_RMV_SHORT); + + /* MIXER Config DAC1 to HP */ + ret |= wm8994_update_bits(WM8994_OUTPUT_MIXER_1, + WM8994_DAC1L_TO_HPOUT1L_MASK, WM8994_DAC1L_TO_HPOUT1L); + + ret |= wm8994_update_bits(WM8994_OUTPUT_MIXER_2, + WM8994_DAC1R_TO_HPOUT1R_MASK, WM8994_DAC1R_TO_HPOUT1R); + + /* Routing AIF2 to DAC1 */ + ret |= wm8994_update_bits(WM8994_DAC1_LEFT_MIXER_ROUTING, + WM8994_AIF2DACL_TO_DAC1L_MASK, + WM8994_AIF2DACL_TO_DAC1L); + + ret |= wm8994_update_bits(WM8994_DAC1_RIGHT_MIXER_ROUTING, + WM8994_AIF2DACR_TO_DAC1R_MASK, + WM8994_AIF2DACR_TO_DAC1R); + + /* GPIO Settings for AIF2 */ + /* B CLK */ + ret |= wm8994_update_bits(WM8994_GPIO_3, WM8994_GPIO_DIR_MASK | + WM8994_GPIO_FUNCTION_MASK , + WM8994_GPIO_DIR_OUTPUT | + WM8994_GPIO_FUNCTION_I2S_CLK); + + /* LR CLK */ + ret |= wm8994_update_bits(WM8994_GPIO_4, WM8994_GPIO_DIR_MASK | + WM8994_GPIO_FUNCTION_MASK, + WM8994_GPIO_DIR_OUTPUT | + WM8994_GPIO_FUNCTION_I2S_CLK); + + /* DATA */ + ret |= wm8994_update_bits(WM8994_GPIO_5, WM8994_GPIO_DIR_MASK | + WM8994_GPIO_FUNCTION_MASK, + WM8994_GPIO_DIR_OUTPUT | + WM8994_GPIO_FUNCTION_I2S_CLK); + + ret |= wm8994_init_volume_aif2_dac1(); + if (ret < 0) + goto err; + + debug("%s: Codec chip init ok\n", __func__); + return 0; +err: + debug("%s: Codec chip init error\n", __func__); + return -1; +} + +/*wm8994 Device Initialisation */ +int wm8994_init(struct sound_codec_info *pcodec_info, + enum en_audio_interface aif_id, + int sampling_rate, int mclk_freq, + int bits_per_sample, unsigned int channels) +{ + int ret = 0; + + /* shift the device address by 1 for 7 bit addressing */ + g_wm8994_i2c_dev_addr = pcodec_info->i2c_dev_addr >> 1; + wm8994_i2c_init(pcodec_info->i2c_bus); + + if (pcodec_info->codec_type == CODEC_WM_8994) + g_wm8994_info.type = WM8994; + else { + debug("%s: Codec id [%d] not defined\n", __func__, + pcodec_info->codec_type); + return -1; + } + + ret = wm8994_device_init(&g_wm8994_info); + if (ret < 0) { + debug("%s: wm8994 codec chip init failed\n", __func__); + return ret; + } + + ret = wm8994_set_sysclk(&g_wm8994_info, aif_id, WM8994_SYSCLK_MCLK1, + mclk_freq); + if (ret < 0) { + debug("%s: wm8994 codec set sys clock failed\n", __func__); + return ret; + } + + ret = wm8994_hw_params(&g_wm8994_info, aif_id, sampling_rate, + bits_per_sample, channels); + + if (ret == 0) { + ret = wm8994_set_fmt(aif_id, SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS); + } + return ret; +} diff --git a/drivers/sound/wm8994.h b/drivers/sound/wm8994.h new file mode 100644 index 0000000..3c12cbb --- /dev/null +++ b/drivers/sound/wm8994.h @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2012 Samsung Electronics + * R. Chadrasekar <rcsekar@samsung.com> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#ifndef __WM8994_H__ +#define __WM8994_H__ + +/* Sources for AIF1/2 SYSCLK - use with set_dai_sysclk() */ +#define WM8994_SYSCLK_MCLK1 1 +#define WM8994_SYSCLK_MCLK2 2 +#define WM8994_SYSCLK_FLL1 3 +#define WM8994_SYSCLK_FLL2 4 + +/* Avilable audi interface ports in wm8994 codec */ +enum en_audio_interface { + WM8994_AIF1 = 1, + WM8994_AIF2, + WM8994_AIF3 +}; + +/* OPCLK is also configured with set_dai_sysclk, specify division*10 as rate. */ +#define WM8994_SYSCLK_OPCLK 5 + +#define WM8994_FLL1 1 +#define WM8994_FLL2 2 + +#define WM8994_FLL_SRC_MCLK1 1 +#define WM8994_FLL_SRC_MCLK2 2 +#define WM8994_FLL_SRC_LRCLK 3 +#define WM8994_FLL_SRC_BCLK 4 + +/* maximum available digital interfac in the dac to configure */ +#define WM8994_MAX_AIF 2 + +#define WM8994_MAX_INPUT_CLK_FREQ 13500000 +#define WM8994_ID 0x8994 + +enum wm8994_vmid_mode { + WM8994_VMID_NORMAL, + WM8994_VMID_FORCE, +}; + +/* wm 8994 family devices */ +enum wm8994_type { + WM8994 = 0, + WM8958 = 1, + WM1811 = 2, +}; + +/* + * intialise wm8994 sound codec device for the given configuration + * + * @param pcodec_info pointer value of the sound codec info structure + * parsed from device tree + * @param aif_id enum value of codec interface port in which + * soc i2s is connected + * @param sampling_rate Sampling rate + * @param mclk_freq MCLK Frequency + * @param bits_per_sample bits per Sample + * @param channels Number of channnels + * + * @returns -1 for error and 0 Success. + */ +int wm8994_init(struct sound_codec_info *pcodec_info, + enum en_audio_interface aif_id, + int sampling_rate, int mclk_freq, + int bits_per_sample, unsigned int channels); +#endif /*__WM8994_H__ */ diff --git a/drivers/sound/wm8994_registers.h b/drivers/sound/wm8994_registers.h new file mode 100644 index 0000000..dacf6b6 --- /dev/null +++ b/drivers/sound/wm8994_registers.h @@ -0,0 +1,299 @@ +/* + * (C) Copyright 2012 Samsung Electronics + * + * 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 __MFD_WM8994_REGISTERS_H__ +#define __MFD_WM8994_REGISTERS_H__ + +/* + * Register values. + */ +#define WM8994_SOFTWARE_RESET 0x00 +#define WM8994_POWER_MANAGEMENT_1 0x01 +#define WM8994_POWER_MANAGEMENT_2 0x02 +#define WM8994_POWER_MANAGEMENT_5 0x05 +#define WM8994_LEFT_OUTPUT_VOLUME 0x1C +#define WM8994_RIGHT_OUTPUT_VOLUME 0x1D +#define WM8994_OUTPUT_MIXER_1 0x2D +#define WM8994_OUTPUT_MIXER_2 0x2E +#define WM8994_CHARGE_PUMP_1 0x4C +#define WM8994_DC_SERVO_1 0x54 +#define WM8994_ANALOGUE_HP_1 0x60 +#define WM8994_CHIP_REVISION 0x100 +#define WM8994_AIF1_CLOCKING_1 0x200 +#define WM8994_AIF1_CLOCKING_2 0x201 +#define WM8994_AIF2_CLOCKING_1 0x204 +#define WM8994_CLOCKING_1 0x208 +#define WM8994_CLOCKING_2 0x209 +#define WM8994_AIF1_RATE 0x210 +#define WM8994_AIF2_RATE 0x211 +#define WM8994_RATE_STATUS 0x212 +#define WM8994_AIF1_CONTROL_1 0x300 +#define WM8994_AIF1_CONTROL_2 0x301 +#define WM8994_AIF1_MASTER_SLAVE 0x302 +#define WM8994_AIF1_BCLK 0x303 +#define WM8994_AIF2_CONTROL_1 0x310 +#define WM8994_AIF2_CONTROL_2 0x311 +#define WM8994_AIF2_MASTER_SLAVE 0x312 +#define WM8994_AIF2_BCLK 0x313 +#define WM8994_AIF2_DAC_LEFT_VOLUME 0x502 +#define WM8994_AIF2_DAC_RIGHT_VOLUME 0x503 +#define WM8994_AIF2_DAC_FILTERS_1 0x520 +#define WM8994_DAC1_LEFT_MIXER_ROUTING 0x601 +#define WM8994_DAC1_RIGHT_MIXER_ROUTING 0x602 +#define WM8994_DAC1_LEFT_VOLUME 0x610 +#define WM8994_DAC1_RIGHT_VOLUME 0x611 +#define WM8994_GPIO_3 0x702 +#define WM8994_GPIO_4 0x703 +#define WM8994_GPIO_5 0x704 + +/* + * Field Definitions. + */ + +/* + * R0 (0x00) - Software Reset + */ +/* SW_RESET */ +#define WM8994_SW_RESET 1 +/* + * R1 (0x01) - Power Management (1) + */ +/* HPOUT1L_ENA */ +#define WM8994_HPOUT1L_ENA 0x0200 +/* HPOUT1L_ENA */ +#define WM8994_HPOUT1L_ENA_MASK 0x0200 +/* HPOUT1R_ENA */ +#define WM8994_HPOUT1R_ENA 0x0100 +/* HPOUT1R_ENA */ +#define WM8994_HPOUT1R_ENA_MASK 0x0100 +/* VMID_SEL - [2:1] */ +#define WM8994_VMID_SEL_MASK 0x0006 +/* BIAS_ENA */ +#define WM8994_BIAS_ENA 0x0001 +/* BIAS_ENA */ +#define WM8994_BIAS_ENA_MASK 0x0001 + +/* + * R2 (0x02) - Power Management (2) + */ +/* OPCLK_ENA */ +#define WM8994_OPCLK_ENA 0x0800 + +/* + * R5 (0x05) - Power Management (5) + */ +/* AIF2DACL_ENA */ +#define WM8994_AIF2DACL_ENA 0x2000 +#define WM8994_AIF2DACL_ENA_MASK 0x2000 +/* AIF2DACR_ENA */ +#define WM8994_AIF2DACR_ENA 0x1000 +#define WM8994_AIF2DACR_ENA_MASK 0x1000 +/* DAC1L_ENA */ +#define WM8994_DAC1L_ENA 0x0002 +#define WM8994_DAC1L_ENA_MASK 0x0002 +/* DAC1R_ENA */ +#define WM8994_DAC1R_ENA 0x0001 +#define WM8994_DAC1R_ENA_MASK 0x0001 + +/* + * R45 (0x2D) - Output Mixer (1) + */ +/* DAC1L_TO_HPOUT1L */ +#define WM8994_DAC1L_TO_HPOUT1L 0x0100 +#define WM8994_DAC1L_TO_HPOUT1L_MASK 0x0100 + +/* + * R46 (0x2E) - Output Mixer (2) + */ +/* DAC1R_TO_HPOUT1R */ +#define WM8994_DAC1R_TO_HPOUT1R 0x0100 +#define WM8994_DAC1R_TO_HPOUT1R_MASK 0x0100 + +/* + * R76 (0x4C) - Charge Pump (1) + */ +/* CP_ENA */ +#define WM8994_CP_ENA 0x8000 +#define WM8994_CP_ENA_MASK 0x8000 +/* + * R84 (0x54) - DC Servo (1) + */ +/* DCS_ENA_CHAN_1 */ +#define WM8994_DCS_ENA_CHAN_1 0x0002 +#define WM8994_DCS_ENA_CHAN_1_MASK 0x0002 +/* DCS_ENA_CHAN_0 */ +#define WM8994_DCS_ENA_CHAN_0 0x0001 +#define WM8994_DCS_ENA_CHAN_0_MASK 0x0001 + +/* + * R96 (0x60) - Analogue HP (1) + */ +/* HPOUT1L_RMV_SHORT */ +#define WM8994_HPOUT1L_RMV_SHORT 0x0080 +#define WM8994_HPOUT1L_RMV_SHORT_MASK 0x0080 +/* HPOUT1L_OUTP */ +#define WM8994_HPOUT1L_OUTP 0x0040 +#define WM8994_HPOUT1L_OUTP_MASK 0x0040 +/* HPOUT1L_DLY */ +#define WM8994_HPOUT1L_DLY 0x0020 +#define WM8994_HPOUT1L_DLY_MASK 0x0020 +/* HPOUT1R_RMV_SHORT */ +#define WM8994_HPOUT1R_RMV_SHORT 0x0008 +#define WM8994_HPOUT1R_RMV_SHORT_MASK 0x0008 +/* HPOUT1R_OUTP */ +#define WM8994_HPOUT1R_OUTP 0x0004 +#define WM8994_HPOUT1R_OUTP_MASK 0x0004 +/* HPOUT1R_DLY */ +#define WM8994_HPOUT1R_DLY 0x0002 +#define WM8994_HPOUT1R_DLY_MASK 0x0002 + +/* + * R512 (0x200) - AIF1 Clocking (1) + */ +/* AIF1CLK_SRC - [4:3] */ +#define WM8994_AIF1CLK_SRC_MASK 0x0018 +/* AIF1CLK_DIV */ +#define WM8994_AIF1CLK_DIV 0x0002 +/* AIF1CLK_ENA */ +#define WM8994_AIF1CLK_ENA 0x0001 +#define WM8994_AIF1CLK_ENA_MASK 0x0001 + +/* + * R517 (0x205) - AIF2 Clocking (2) + */ +/* AIF2DAC_DIV - [5:3] */ +#define WM8994_AIF2DAC_DIV_MASK 0x0038 + +/* + * R520 (0x208) - Clocking (1) + */ +/* AIF2DSPCLK_ENA */ +#define WM8994_AIF2DSPCLK_ENA 0x0004 +#define WM8994_AIF2DSPCLK_ENA_MASK 0x0004 +/* SYSDSPCLK_ENA */ +#define WM8994_SYSDSPCLK_ENA 0x0002 +#define WM8994_SYSDSPCLK_ENA_MASK 0x0002 +/* SYSCLK_SRC */ +#define WM8994_SYSCLK_SRC 0x0001 + +/* + * R521 (0x209) - Clocking (2) + */ +/* OPCLK_DIV - [2:0] */ +#define WM8994_OPCLK_DIV_MASK 0x0007 + +/* + * R528 (0x210) - AIF1 Rate + */ +/* AIF1_SR - [7:4] */ +#define WM8994_AIF1_SR_MASK 0x00F0 +#define WM8994_AIF1_SR_SHIFT 4 +/* AIF1CLK_RATE - [3:0] */ +#define WM8994_AIF1CLK_RATE_MASK 0x000F + +/* + * R768 (0x300) - AIF1 Control (1) + */ +/* AIF1_BCLK_INV */ +#define WM8994_AIF1_BCLK_INV 0x0100 +/* AIF1_LRCLK_INV */ +#define WM8994_AIF1_LRCLK_INV 0x0080 +#define WM8994_AIF1_LRCLK_INV_MASK 0x0080 +/* AIF1_WL - [6:5] */ +#define WM8994_AIF1_WL_MASK 0x0060 +/* AIF1_FMT - [4:3] */ +#define WM8994_AIF1_FMT_MASK 0x0018 + +/* + * R769 (0x301) - AIF1 Control (2) + */ +/* AIF1_MONO */ +#define WM8994_AIF1_MONO 0x0100 + +/* + * R770 (0x302) - AIF1 Master/Slave + */ +/* AIF1_MSTR */ +#define WM8994_AIF1_MSTR 0x4000 +#define WM8994_AIF1_MSTR_MASK 0x4000 + +/* + * R771 (0x303) - AIF1 BCLK + */ +/* AIF1_BCLK_DIV - [8:4] */ +#define WM8994_AIF1_BCLK_DIV_MASK 0x01F0 +#define WM8994_AIF1_BCLK_DIV_SHIFT 4 + +/* + * R1282 (0x502) - AIF2 DAC Left Volume + */ +/* AIF2DAC_VU */ +#define WM8994_AIF2DAC_VU 0x0100 +#define WM8994_AIF2DAC_VU_MASK 0x0100 +/* AIF2DACL_VOL - [7:0] */ +#define WM8994_AIF2DACL_VOL_MASK 0x00FF + +/* + * R1283 (0x503) - AIF2 DAC Right Volume + */ +/* AIF2DACR_VOL - [7:0] */ +#define WM8994_AIF2DACR_VOL_MASK 0x00FF + +/* + * R1312 (0x520) - AIF2 DAC Filters (1) + */ +/* AIF2DAC_MUTE */ +#define WM8994_AIF2DAC_MUTE_MASK 0x0200 + +/* + * R1537 (0x601) - DAC1 Left Mixer Routing + */ +/* AIF2DACL_TO_DAC1L */ +#define WM8994_AIF2DACL_TO_DAC1L 0x0004 +#define WM8994_AIF2DACL_TO_DAC1L_MASK 0x0004 + +/* + * R1538 (0x602) - DAC1 Right Mixer Routing + */ +/* AIF2DACR_TO_DAC1R */ +#define WM8994_AIF2DACR_TO_DAC1R 0x0004 +#define WM8994_AIF2DACR_TO_DAC1R_MASK 0x0004 + +/* + * R1552 (0x610) - DAC1 Left Volume + */ +/* DAC1L_MUTE */ +#define WM8994_DAC1L_MUTE_MASK 0x0200 +/* DAC1_VU */ +#define WM8994_DAC1_VU 0x0100 +#define WM8994_DAC1_VU_MASK 0x0100 +/* DAC1L_VOL - [7:0] */ +#define WM8994_DAC1L_VOL_MASK 0x00FF + +/* + * R1553 (0x611) - DAC1 Right Volume + */ +/* DAC1R_MUTE */ +#define WM8994_DAC1R_MUTE_MASK 0x0200 +/* DAC1R_VOL - [7:0] */ +#define WM8994_DAC1R_VOL_MASK 0x00FF + +/* + * GPIO + */ +/* OUTPUT PIN */ +#define WM8994_GPIO_DIR_OUTPUT 0x8000 +/* GPIO PIN MASK */ +#define WM8994_GPIO_DIR_MASK 0xFFE0 +/* I2S CLK */ +#define WM8994_GPIO_FUNCTION_I2S_CLK 0x0000 +/* GPn FN */ +#define WM8994_GPIO_FUNCTION_MASK 0x001F +#endif