Merge branch 'topic/asoc' into for-linus

This commit is contained in:
Takashi Iwai 2011-03-16 17:38:41 +01:00
commit 27b92d4ff2
138 changed files with 14106 additions and 2608 deletions

View file

@ -119,13 +119,6 @@ static struct platform_device keysc_device = {
};
/* FSI A */
static struct sh_fsi_platform_info fsi_info = {
.porta_flags = SH_FSI_OUT_SLAVE_MODE |
SH_FSI_IN_SLAVE_MODE |
SH_FSI_OFMT(I2S) |
SH_FSI_IFMT(I2S),
};
static struct resource fsi_resources[] = {
[0] = {
.name = "FSI",
@ -144,9 +137,6 @@ static struct platform_device fsi_device = {
.id = -1,
.num_resources = ARRAY_SIZE(fsi_resources),
.resource = fsi_resources,
.dev = {
.platform_data = &fsi_info,
},
};
static struct resource sh_mmcif_resources[] = {

View file

@ -673,16 +673,12 @@ static int fsi_set_rate(struct device *dev, int is_porta, int rate, int enable)
}
static struct sh_fsi_platform_info fsi_info = {
.porta_flags = SH_FSI_BRS_INV |
SH_FSI_OUT_SLAVE_MODE |
SH_FSI_IN_SLAVE_MODE |
SH_FSI_OFMT(PCM) |
SH_FSI_IFMT(PCM),
.porta_flags = SH_FSI_BRS_INV,
.portb_flags = SH_FSI_BRS_INV |
SH_FSI_BRM_INV |
SH_FSI_LRS_INV |
SH_FSI_OFMT(SPDIF),
SH_FSI_FMT_SPDIF,
.set_rate = fsi_set_rate,
};
@ -783,6 +779,10 @@ static struct platform_device hdmi_device = {
},
};
static struct platform_device fsi_hdmi_device = {
.name = "sh_fsi2_b_hdmi",
};
static long ap4evb_clk_optimize(unsigned long target, unsigned long *best_freq,
unsigned long *parent_freq)
{
@ -936,6 +936,7 @@ static struct platform_device *ap4evb_devices[] __initdata = {
&usb1_host_device,
&fsi_device,
&fsi_ak4643_device,
&fsi_hdmi_device,
&sh_mmcif_device,
&lcdc1_device,
&lcdc_device,

View file

@ -399,6 +399,10 @@ static struct platform_device hdmi_device = {
},
};
static struct platform_device fsi_hdmi_device = {
.name = "sh_fsi2_b_hdmi",
};
static int __init hdmi_init_pm_clock(void)
{
struct clk *hdmi_ick = clk_get(&hdmi_device.dev, "ick");
@ -609,16 +613,12 @@ static int fsi_set_rate(struct device *dev, int is_porta, int rate, int enable)
}
static struct sh_fsi_platform_info fsi_info = {
.porta_flags = SH_FSI_BRS_INV |
SH_FSI_OUT_SLAVE_MODE |
SH_FSI_IN_SLAVE_MODE |
SH_FSI_OFMT(PCM) |
SH_FSI_IFMT(PCM),
.porta_flags = SH_FSI_BRS_INV,
.portb_flags = SH_FSI_BRS_INV |
SH_FSI_BRM_INV |
SH_FSI_LRS_INV |
SH_FSI_OFMT(SPDIF),
SH_FSI_FMT_SPDIF,
.set_rate = fsi_set_rate,
};
@ -921,6 +921,7 @@ static struct platform_device *mackerel_devices[] __initdata = {
&leds_device,
&fsi_device,
&fsi_ak4643_device,
&fsi_hdmi_device,
&sdhi0_device,
#if !defined(CONFIG_MMC_SH_MMCIF)
&sdhi1_device,

View file

@ -0,0 +1,22 @@
/*
* arch/arm/mach-tegra/include/mach/harmony_audio.h
*
* Copyright 2011 NVIDIA, Inc.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* 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.
*
*/
struct harmony_audio_platform_data {
int gpio_spkr_en;
int gpio_hp_det;
int gpio_int_mic_en;
int gpio_ext_mic_en;
};

View file

@ -723,11 +723,7 @@ static struct platform_device camera_devices[] = {
/* FSI */
static struct sh_fsi_platform_info fsi_info = {
.portb_flags = SH_FSI_BRS_INV |
SH_FSI_OUT_SLAVE_MODE |
SH_FSI_IN_SLAVE_MODE |
SH_FSI_OFMT(I2S) |
SH_FSI_IFMT(I2S),
.portb_flags = SH_FSI_BRS_INV,
};
static struct resource fsi_resources[] = {

View file

@ -286,11 +286,7 @@ static struct platform_device ceu1_device = {
/* FSI */
/* change J20, J21, J22 pin to 1-2 connection to use slave mode */
static struct sh_fsi_platform_info fsi_info = {
.porta_flags = SH_FSI_BRS_INV |
SH_FSI_OUT_SLAVE_MODE |
SH_FSI_IN_SLAVE_MODE |
SH_FSI_OFMT(PCM) |
SH_FSI_IFMT(PCM),
.porta_flags = SH_FSI_BRS_INV,
};
static struct resource fsi_resources[] = {

View file

@ -103,13 +103,21 @@ struct wm8994_pdata {
unsigned int lineout1fb:1;
unsigned int lineout2fb:1;
/* Microphone biases: 0=0.9*AVDD1 1=0.65*AVVD1 */
/* IRQ for microphone detection if brought out directly as a
* signal.
*/
int micdet_irq;
/* WM8994 microphone biases: 0=0.9*AVDD1 1=0.65*AVVD1 */
unsigned int micbias1_lvl:1;
unsigned int micbias2_lvl:1;
/* Jack detect threashold levels, see datasheet for values */
/* WM8994 jack detect threashold levels, see datasheet for values */
unsigned int jd_scthr:2;
unsigned int jd_thr:2;
/* WM8958 microphone bias configuration */
int micbias[2];
};
#endif

View file

@ -63,6 +63,8 @@
#define WM8994_MICBIAS 0x3A
#define WM8994_LDO_1 0x3B
#define WM8994_LDO_2 0x3C
#define WM8958_MICBIAS1 0x3D
#define WM8958_MICBIAS2 0x3E
#define WM8994_CHARGE_PUMP_1 0x4C
#define WM8958_CHARGE_PUMP_2 0x4D
#define WM8994_CLASS_W_1 0x51

View file

@ -115,6 +115,8 @@ int snd_ctl_add(struct snd_card * card, struct snd_kcontrol * kcontrol);
int snd_ctl_remove(struct snd_card * card, struct snd_kcontrol * kcontrol);
int snd_ctl_remove_id(struct snd_card * card, struct snd_ctl_elem_id *id);
int snd_ctl_rename_id(struct snd_card * card, struct snd_ctl_elem_id *src_id, struct snd_ctl_elem_id *dst_id);
int snd_ctl_activate_id(struct snd_card *card, struct snd_ctl_elem_id *id,
int active);
struct snd_kcontrol *snd_ctl_find_numid(struct snd_card * card, unsigned int numid);
struct snd_kcontrol *snd_ctl_find_id(struct snd_card * card, struct snd_ctl_elem_id *id);

24
include/sound/cs4271.h Normal file
View file

@ -0,0 +1,24 @@
/*
* Definitions for CS4271 ASoC codec driver
*
* Copyright (c) 2010 Alexander Sverdlin <subaparts@yandex.ru>
*
* 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.
*/
#ifndef __CS4271_H
#define __CS4271_H
struct cs4271_platform_data {
int gpio_nreset; /* GPIO driving Reset pin, if any */
};
#endif /* __CS4271_H */

View file

@ -15,67 +15,29 @@
#define FSI_PORT_A 0
#define FSI_PORT_B 1
/* flags format
* 0xABCDEEFF
*
* A: channel size for TDM (input)
* B: channel size for TDM (ooutput)
* C: inversion
* D: mode
* E: input format
* F: output format
*/
#include <linux/clk.h>
#include <sound/soc.h>
/* TDM channel */
#define SH_FSI_SET_CH_I(x) ((x & 0xF) << 28)
#define SH_FSI_SET_CH_O(x) ((x & 0xF) << 24)
/*
* flags format
*
* 0x000000BA
*
* A: inversion
* B: format mode
*/
#define SH_FSI_CH_IMASK 0xF0000000
#define SH_FSI_CH_OMASK 0x0F000000
#define SH_FSI_GET_CH_I(x) ((x & SH_FSI_CH_IMASK) >> 28)
#define SH_FSI_GET_CH_O(x) ((x & SH_FSI_CH_OMASK) >> 24)
/* A: clock inversion */
#define SH_FSI_INVERSION_MASK 0x0000000F
#define SH_FSI_LRM_INV (1 << 0)
#define SH_FSI_BRM_INV (1 << 1)
#define SH_FSI_LRS_INV (1 << 2)
#define SH_FSI_BRS_INV (1 << 3)
/* clock inversion */
#define SH_FSI_INVERSION_MASK 0x00F00000
#define SH_FSI_LRM_INV (1 << 20)
#define SH_FSI_BRM_INV (1 << 21)
#define SH_FSI_LRS_INV (1 << 22)
#define SH_FSI_BRS_INV (1 << 23)
/* mode */
#define SH_FSI_MODE_MASK 0x000F0000
#define SH_FSI_IN_SLAVE_MODE (1 << 16) /* default master mode */
#define SH_FSI_OUT_SLAVE_MODE (1 << 17) /* default master mode */
/* DI format */
#define SH_FSI_FMT_MASK 0x000000FF
#define SH_FSI_IFMT(x) (((SH_FSI_FMT_ ## x) & SH_FSI_FMT_MASK) << 8)
#define SH_FSI_OFMT(x) (((SH_FSI_FMT_ ## x) & SH_FSI_FMT_MASK) << 0)
#define SH_FSI_GET_IFMT(x) ((x >> 8) & SH_FSI_FMT_MASK)
#define SH_FSI_GET_OFMT(x) ((x >> 0) & SH_FSI_FMT_MASK)
#define SH_FSI_FMT_MONO 0
#define SH_FSI_FMT_MONO_DELAY 1
#define SH_FSI_FMT_PCM 2
#define SH_FSI_FMT_I2S 3
#define SH_FSI_FMT_TDM 4
#define SH_FSI_FMT_TDM_DELAY 5
#define SH_FSI_FMT_SPDIF 6
#define SH_FSI_IFMT_TDM_CH(x) \
(SH_FSI_IFMT(TDM) | SH_FSI_SET_CH_I(x))
#define SH_FSI_IFMT_TDM_DELAY_CH(x) \
(SH_FSI_IFMT(TDM_DELAY) | SH_FSI_SET_CH_I(x))
#define SH_FSI_OFMT_TDM_CH(x) \
(SH_FSI_OFMT(TDM) | SH_FSI_SET_CH_O(x))
#define SH_FSI_OFMT_TDM_DELAY_CH(x) \
(SH_FSI_OFMT(TDM_DELAY) | SH_FSI_SET_CH_O(x))
/* B: format mode */
#define SH_FSI_FMT_MASK 0x000000F0
#define SH_FSI_FMT_DAI (0 << 4)
#define SH_FSI_FMT_SPDIF (1 << 4)
/*

View file

@ -157,6 +157,18 @@
.invert = winvert, .kcontrols = wcontrols, .num_kcontrols = 1, \
.event = wevent, .event_flags = wflags}
/* additional sequencing control within an event type */
#define SND_SOC_DAPM_PGA_S(wname, wsubseq, wreg, wshift, winvert, \
wevent, wflags) \
{ .id = snd_soc_dapm_pga, .name = wname, .reg = wreg, .shift = wshift, \
.invert = winvert, .event = wevent, .event_flags = wflags, \
.subseq = wsubseq}
#define SND_SOC_DAPM_SUPPLY_S(wname, wsubseq, wreg, wshift, winvert, wevent, \
wflags) \
{ .id = snd_soc_dapm_supply, .name = wname, .reg = wreg, \
.shift = wshift, .invert = winvert, .event = wevent, \
.event_flags = wflags, .subseq = wsubseq}
/* Simplified versions of above macros, assuming wncontrols = ARRAY_SIZE(wcontrols) */
#define SOC_PGA_E_ARRAY(wname, wreg, wshift, winvert, wcontrols, \
wevent, wflags) \
@ -450,6 +462,7 @@ struct snd_soc_dapm_widget {
unsigned char ext:1; /* has external widgets */
unsigned char force:1; /* force state */
unsigned char ignore_suspend:1; /* kept enabled over suspend */
int subseq; /* sort within widget type */
int (*power_check)(struct snd_soc_dapm_widget *w);
@ -487,6 +500,9 @@ struct snd_soc_dapm_context {
struct snd_soc_dapm_update *update;
void (*seq_notifier)(struct snd_soc_dapm_context *,
enum snd_soc_dapm_type, int);
struct device *dev; /* from parent - for debug */
struct snd_soc_codec *codec; /* parent codec */
struct snd_soc_card *card; /* parent card */

View file

@ -234,6 +234,7 @@ struct snd_soc_codec;
struct snd_soc_codec_driver;
struct soc_enum;
struct snd_soc_jack;
struct snd_soc_jack_zone;
struct snd_soc_jack_pin;
struct snd_soc_cache_ops;
#include <sound/soc-dapm.h>
@ -258,6 +259,16 @@ enum snd_soc_compress_type {
SND_SOC_RBTREE_COMPRESSION
};
int snd_soc_codec_set_sysclk(struct snd_soc_codec *codec, int clk_id,
unsigned int freq, int dir);
int snd_soc_codec_set_pll(struct snd_soc_codec *codec, int pll_id, int source,
unsigned int freq_in, unsigned int freq_out);
int snd_soc_register_card(struct snd_soc_card *card);
int snd_soc_unregister_card(struct snd_soc_card *card);
int snd_soc_suspend(struct device *dev);
int snd_soc_resume(struct device *dev);
int snd_soc_poweroff(struct device *dev);
int snd_soc_register_platform(struct device *dev,
struct snd_soc_platform_driver *platform_drv);
void snd_soc_unregister_platform(struct device *dev);
@ -265,7 +276,8 @@ int snd_soc_register_codec(struct device *dev,
const struct snd_soc_codec_driver *codec_drv,
struct snd_soc_dai_driver *dai_drv, int num_dai);
void snd_soc_unregister_codec(struct device *dev);
int snd_soc_codec_volatile_register(struct snd_soc_codec *codec, int reg);
int snd_soc_codec_volatile_register(struct snd_soc_codec *codec,
unsigned int reg);
int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec,
int addr_bits, int data_bits,
enum snd_soc_control_type control);
@ -276,6 +288,10 @@ int snd_soc_cache_write(struct snd_soc_codec *codec,
unsigned int reg, unsigned int value);
int snd_soc_cache_read(struct snd_soc_codec *codec,
unsigned int reg, unsigned int *value);
int snd_soc_default_volatile_register(struct snd_soc_codec *codec,
unsigned int reg);
int snd_soc_default_readable_register(struct snd_soc_codec *codec,
unsigned int reg);
/* Utility functions to get clock rates from various things */
int snd_soc_calc_frame_size(int sample_size, int channels, int tdm_slots);
@ -297,6 +313,9 @@ void snd_soc_jack_notifier_register(struct snd_soc_jack *jack,
struct notifier_block *nb);
void snd_soc_jack_notifier_unregister(struct snd_soc_jack *jack,
struct notifier_block *nb);
int snd_soc_jack_add_zones(struct snd_soc_jack *jack, int count,
struct snd_soc_jack_zone *zones);
int snd_soc_jack_get_type(struct snd_soc_jack *jack, int micbias_voltage);
#ifdef CONFIG_GPIOLIB
int snd_soc_jack_add_gpios(struct snd_soc_jack *jack, int count,
struct snd_soc_jack_gpio *gpios);
@ -321,7 +340,8 @@ void snd_soc_free_ac97_codec(struct snd_soc_codec *codec);
*Controls
*/
struct snd_kcontrol *snd_soc_cnew(const struct snd_kcontrol_new *_template,
void *data, char *long_name);
void *data, char *long_name,
const char *prefix);
int snd_soc_add_controls(struct snd_soc_codec *codec,
const struct snd_kcontrol_new *controls, int num_controls);
int snd_soc_info_enum_double(struct snd_kcontrol *kcontrol,
@ -366,6 +386,22 @@ int snd_soc_get_volsw_2r_sx(struct snd_kcontrol *kcontrol,
int snd_soc_put_volsw_2r_sx(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
/**
* struct snd_soc_reg_access - Describes whether a given register is
* readable, writable or volatile.
*
* @reg: the register number
* @read: whether this register is readable
* @write: whether this register is writable
* @vol: whether this register is volatile
*/
struct snd_soc_reg_access {
u16 reg;
u16 read;
u16 write;
u16 vol;
};
/**
* struct snd_soc_jack_pin - Describes a pin to update based on jack detection
*
@ -380,6 +416,24 @@ struct snd_soc_jack_pin {
bool invert;
};
/**
* struct snd_soc_jack_zone - Describes voltage zones of jack detection
*
* @min_mv: start voltage in mv
* @max_mv: end voltage in mv
* @jack_type: type of jack that is expected for this voltage
* @debounce_time: debounce_time for jack, codec driver should wait for this
* duration before reading the adc for voltages
* @:list: list container
*/
struct snd_soc_jack_zone {
unsigned int min_mv;
unsigned int max_mv;
unsigned int jack_type;
unsigned int debounce_time;
struct list_head list;
};
/**
* struct snd_soc_jack_gpio - Describes a gpio pin for jack detection
*
@ -388,6 +442,10 @@ struct snd_soc_jack_pin {
* @report: value to report when jack detected
* @invert: report presence in low state
* @debouce_time: debouce time in ms
* @wake: enable as wake source
* @jack_status_check: callback function which overrides the detection
* to provide more complex checks (eg, reading an
* ADC).
*/
#ifdef CONFIG_GPIOLIB
struct snd_soc_jack_gpio {
@ -396,6 +454,8 @@ struct snd_soc_jack_gpio {
int report;
int invert;
int debounce_time;
bool wake;
struct snd_soc_jack *jack;
struct delayed_work work;
@ -409,6 +469,7 @@ struct snd_soc_jack {
struct list_head pins;
int status;
struct blocking_notifier_head notifier;
struct list_head jack_zones;
};
/* SoC PCM stream information */
@ -459,18 +520,22 @@ struct snd_soc_codec {
struct list_head card_list;
int num_dai;
enum snd_soc_compress_type compress_type;
size_t reg_size; /* reg_cache_size * reg_word_size */
int (*volatile_register)(struct snd_soc_codec *, unsigned int);
int (*readable_register)(struct snd_soc_codec *, unsigned int);
/* runtime */
struct snd_ac97 *ac97; /* for ad-hoc ac97 devices */
unsigned int active;
unsigned int cache_only:1; /* Suppress writes to hardware */
unsigned int cache_sync:1; /* Cache needs to be synced to hardware */
unsigned int cache_bypass:1; /* Suppress access to the cache */
unsigned int suspended:1; /* Codec is in suspend PM state */
unsigned int probed:1; /* Codec has been probed */
unsigned int ac97_registered:1; /* Codec has been AC97 registered */
unsigned int ac97_created:1; /* Codec has been created by SoC */
unsigned int sysfs_registered:1; /* codec has been sysfs registered */
unsigned int cache_init:1; /* codec cache has been initialized */
u32 cache_only; /* Suppress writes to hardware */
u32 cache_sync; /* Cache needs to be synced to hardware */
/* codec IO */
void *control_data; /* codec control (i2c/3wire) data */
@ -503,22 +568,39 @@ struct snd_soc_codec_driver {
pm_message_t state);
int (*resume)(struct snd_soc_codec *);
/* Default DAPM setup, added after probe() is run */
const struct snd_soc_dapm_widget *dapm_widgets;
int num_dapm_widgets;
const struct snd_soc_dapm_route *dapm_routes;
int num_dapm_routes;
/* codec wide operations */
int (*set_sysclk)(struct snd_soc_codec *codec,
int clk_id, unsigned int freq, int dir);
int (*set_pll)(struct snd_soc_codec *codec, int pll_id, int source,
unsigned int freq_in, unsigned int freq_out);
/* codec IO */
unsigned int (*read)(struct snd_soc_codec *, unsigned int);
int (*write)(struct snd_soc_codec *, unsigned int, unsigned int);
int (*display_register)(struct snd_soc_codec *, char *,
size_t, unsigned int);
int (*volatile_register)(unsigned int);
int (*readable_register)(unsigned int);
int (*volatile_register)(struct snd_soc_codec *, unsigned int);
int (*readable_register)(struct snd_soc_codec *, unsigned int);
short reg_cache_size;
short reg_cache_step;
short reg_word_size;
const void *reg_cache_default;
short reg_access_size;
const struct snd_soc_reg_access *reg_access_default;
enum snd_soc_compress_type compress_type;
/* codec bias level */
int (*set_bias_level)(struct snd_soc_codec *,
enum snd_soc_bias_level level);
void (*seq_notifier)(struct snd_soc_dapm_context *,
enum snd_soc_dapm_type, int);
};
/* SoC platform interface */
@ -617,15 +699,16 @@ struct snd_soc_card {
bool instantiated;
int (*probe)(struct platform_device *pdev);
int (*remove)(struct platform_device *pdev);
int (*probe)(struct snd_soc_card *card);
int (*late_probe)(struct snd_soc_card *card);
int (*remove)(struct snd_soc_card *card);
/* the pre and post PM functions are used to do any PM work before and
* after the codec and DAI's do any PM work. */
int (*suspend_pre)(struct platform_device *pdev, pm_message_t state);
int (*suspend_post)(struct platform_device *pdev, pm_message_t state);
int (*resume_pre)(struct platform_device *pdev);
int (*resume_post)(struct platform_device *pdev);
int (*suspend_pre)(struct snd_soc_card *card);
int (*suspend_post)(struct snd_soc_card *card);
int (*resume_pre)(struct snd_soc_card *card);
int (*resume_post)(struct snd_soc_card *card);
/* callbacks */
int (*set_bias_level)(struct snd_soc_card *,
@ -654,6 +737,14 @@ struct snd_soc_card {
struct snd_soc_pcm_runtime *rtd_aux;
int num_aux_rtd;
/*
* Card-specific routes and widgets.
*/
struct snd_soc_dapm_widget *dapm_widgets;
int num_dapm_widgets;
struct snd_soc_dapm_route *dapm_routes;
int num_dapm_routes;
struct work_struct deferred_resume_work;
/* lists of probed devices belonging to this card */
@ -665,11 +756,16 @@ struct snd_soc_card {
struct list_head paths;
struct list_head dapm_list;
/* Generic DAPM context for the card */
struct snd_soc_dapm_context dapm;
#ifdef CONFIG_DEBUG_FS
struct dentry *debugfs_card_root;
struct dentry *debugfs_pop_time;
#endif
u32 pop_time;
void *drvdata;
};
/* SoC machine DAI configuration, glues a codec and cpu DAI together */
@ -721,6 +817,17 @@ unsigned int snd_soc_write(struct snd_soc_codec *codec,
/* device driver data */
static inline void snd_soc_card_set_drvdata(struct snd_soc_card *card,
void *data)
{
card->drvdata = data;
}
static inline void *snd_soc_card_get_drvdata(struct snd_soc_card *card)
{
return card->drvdata;
}
static inline void snd_soc_codec_set_drvdata(struct snd_soc_codec *codec,
void *data)
{
@ -754,6 +861,22 @@ static inline void *snd_soc_pcm_get_drvdata(struct snd_soc_pcm_runtime *rtd)
return dev_get_drvdata(&rtd->dev);
}
static inline void snd_soc_initialize_card_lists(struct snd_soc_card *card)
{
INIT_LIST_HEAD(&card->dai_dev_list);
INIT_LIST_HEAD(&card->codec_dev_list);
INIT_LIST_HEAD(&card->platform_dev_list);
INIT_LIST_HEAD(&card->widgets);
INIT_LIST_HEAD(&card->paths);
INIT_LIST_HEAD(&card->dapm_list);
}
#include <sound/soc-dai.h>
#ifdef CONFIG_DEBUG_FS
extern struct dentry *snd_soc_debugfs_root;
#endif
extern const struct dev_pm_ops snd_soc_pm_ops;
#endif

View file

@ -0,0 +1,31 @@
/*
* tlv320aic32x4.h -- TLV320AIC32X4 Soc Audio driver platform data
*
* Copyright 2011 Vista Silicon S.L.
*
* Author: Javier Martin <javier.martin@vista-silicon.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef _AIC32X4_PDATA_H
#define _AIC32X4_PDATA_H
#define AIC32X4_PWR_MICBIAS_2075_LDOIN 0x00000001
#define AIC32X4_PWR_AVDD_DVDD_WEAK_DISABLE 0x00000002
#define AIC32X4_PWR_AIC32X4_LDO_ENABLE 0x00000004
#define AIC32X4_PWR_CMMODE_LDOIN_RANGE_18_36 0x00000008
#define AIC32X4_PWR_CMMODE_HP_LDOIN_POWERED 0x00000010
#define AIC32X4_MICPGA_ROUTE_LMIC_IN2R_10K 0x00000001
#define AIC32X4_MICPGA_ROUTE_RMIC_IN1L_10K 0x00000002
struct aic32x4_pdata {
u32 power_cfg;
u32 micpga_routing;
bool swapdacs;
};
#endif

View file

@ -32,6 +32,21 @@
#define WM8903_MICBIAS_ENA_SHIFT 0 /* MICBIAS_ENA */
#define WM8903_MICBIAS_ENA_WIDTH 1 /* MICBIAS_ENA */
/*
* WM8903_GPn_FN values
*
* See datasheets for list of valid values per pin
*/
#define WM8903_GPn_FN_GPIO_OUTPUT 0
#define WM8903_GPn_FN_BCLK 1
#define WM8903_GPn_FN_IRQ_OUTPT 2
#define WM8903_GPn_FN_GPIO_INPUT 3
#define WM8903_GPn_FN_MICBIAS_CURRENT_DETECT 4
#define WM8903_GPn_FN_MICBIAS_SHORT_DETECT 5
#define WM8903_GPn_FN_DMIC_LR_CLK_OUTPUT 6
#define WM8903_GPn_FN_FLL_LOCK_OUTPUT 8
#define WM8903_GPn_FN_FLL_CLOCK_OUTPUT 9
/*
* R116 (0x74) - GPIO Control 1
*/
@ -227,6 +242,8 @@
#define WM8903_GP5_DB_SHIFT 0 /* GP5_DB */
#define WM8903_GP5_DB_WIDTH 1 /* GP5_DB */
#define WM8903_NUM_GPIO 5
struct wm8903_platform_data {
bool irq_active_low; /* Set if IRQ active low, default high */
@ -239,7 +256,8 @@ struct wm8903_platform_data {
int micdet_delay; /* Delay after microphone detection (ms) */
u32 gpio_cfg[5]; /* Default register values for GPIO pin mux */
int gpio_base;
u32 gpio_cfg[WM8903_NUM_GPIO]; /* Default register values for GPIO pin mux */
};
#endif

View file

@ -17,9 +17,12 @@ struct wm9081_retune_mobile_setting {
u16 config[20];
};
struct wm9081_retune_mobile_config {
struct wm9081_retune_mobile_setting *configs;
int num_configs;
struct wm9081_pdata {
bool irq_high; /* IRQ is active high */
bool irq_cmos; /* IRQ is in CMOS mode */
struct wm9081_retune_mobile_setting *retune_configs;
int num_retune_configs;
};
#endif

View file

@ -229,6 +229,31 @@ TRACE_EVENT(snd_soc_jack_notify,
TP_printk("jack=%s %x", __get_str(name), (int)__entry->val)
);
TRACE_EVENT(snd_soc_cache_sync,
TP_PROTO(struct snd_soc_codec *codec, const char *type,
const char *status),
TP_ARGS(codec, type, status),
TP_STRUCT__entry(
__string( name, codec->name )
__string( status, status )
__string( type, type )
__field( int, id )
),
TP_fast_assign(
__assign_str(name, codec->name);
__assign_str(status, status);
__assign_str(type, type);
__entry->id = codec->id;
),
TP_printk("codec=%s.%d type=%s status=%s", __get_str(name),
(int)__entry->id, __get_str(type), __get_str(status))
);
#endif /* _TRACE_ASOC_H */
/* This part must be outside protection */

View file

@ -465,6 +465,52 @@ static int snd_ctl_remove_user_ctl(struct snd_ctl_file * file,
return ret;
}
/**
* snd_ctl_activate_id - activate/inactivate the control of the given id
* @card: the card instance
* @id: the control id to activate/inactivate
* @active: non-zero to activate
*
* Finds the control instance with the given id, and activate or
* inactivate the control together with notification, if changed.
*
* Returns 0 if unchanged, 1 if changed, or a negative error code on failure.
*/
int snd_ctl_activate_id(struct snd_card *card, struct snd_ctl_elem_id *id,
int active)
{
struct snd_kcontrol *kctl;
struct snd_kcontrol_volatile *vd;
unsigned int index_offset;
int ret;
down_write(&card->controls_rwsem);
kctl = snd_ctl_find_id(card, id);
if (kctl == NULL) {
ret = -ENOENT;
goto unlock;
}
index_offset = snd_ctl_get_ioff(kctl, &kctl->id);
vd = &kctl->vd[index_offset];
ret = 0;
if (active) {
if (!(vd->access & SNDRV_CTL_ELEM_ACCESS_INACTIVE))
goto unlock;
vd->access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
} else {
if (vd->access & SNDRV_CTL_ELEM_ACCESS_INACTIVE)
goto unlock;
vd->access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
}
ret = 1;
unlock:
up_write(&card->controls_rwsem);
if (ret > 0)
snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO, id);
return ret;
}
EXPORT_SYMBOL_GPL(snd_ctl_activate_id);
/**
* snd_ctl_rename_id - replace the id of a control on the card
* @card: the card instance

View file

@ -50,10 +50,12 @@ source "sound/soc/jz4740/Kconfig"
source "sound/soc/nuc900/Kconfig"
source "sound/soc/omap/Kconfig"
source "sound/soc/kirkwood/Kconfig"
source "sound/soc/mid-x86/Kconfig"
source "sound/soc/pxa/Kconfig"
source "sound/soc/samsung/Kconfig"
source "sound/soc/s6000/Kconfig"
source "sound/soc/sh/Kconfig"
source "sound/soc/tegra/Kconfig"
source "sound/soc/txx9/Kconfig"
# Supported codecs

View file

@ -10,6 +10,7 @@ obj-$(CONFIG_SND_SOC) += ep93xx/
obj-$(CONFIG_SND_SOC) += fsl/
obj-$(CONFIG_SND_SOC) += imx/
obj-$(CONFIG_SND_SOC) += jz4740/
obj-$(CONFIG_SND_SOC) += mid-x86/
obj-$(CONFIG_SND_SOC) += nuc900/
obj-$(CONFIG_SND_SOC) += omap/
obj-$(CONFIG_SND_SOC) += kirkwood/
@ -17,4 +18,5 @@ obj-$(CONFIG_SND_SOC) += pxa/
obj-$(CONFIG_SND_SOC) += samsung/
obj-$(CONFIG_SND_SOC) += s6000/
obj-$(CONFIG_SND_SOC) += sh/
obj-$(CONFIG_SND_SOC) += tegra/
obj-$(CONFIG_SND_SOC) += txx9/

View file

@ -26,17 +26,24 @@ config SND_SOC_ALL_CODECS
select SND_SOC_CQ0093VC if MFD_DAVINCI_VOICECODEC
select SND_SOC_CS42L51 if I2C
select SND_SOC_CS4270 if I2C
select SND_SOC_CS4271 if SND_SOC_I2C_AND_SPI
select SND_SOC_CX20442
select SND_SOC_DA7210 if I2C
select SND_SOC_DFBMCS320
select SND_SOC_JZ4740_CODEC if SOC_JZ4740
select SND_SOC_LM4857 if I2C
select SND_SOC_MAX98088 if I2C
select SND_SOC_MAX9850 if I2C
select SND_SOC_MAX9877 if I2C
select SND_SOC_PCM3008
select SND_SOC_SGTL5000 if I2C
select SND_SOC_SN95031 if INTEL_SCU_IPC
select SND_SOC_SPDIF
select SND_SOC_SSM2602 if I2C
select SND_SOC_STAC9766 if SND_SOC_AC97_BUS
select SND_SOC_TLV320AIC23 if I2C
select SND_SOC_TLV320AIC26 if SPI_MASTER
select SND_SOC_TVL320AIC32X4 if I2C
select SND_SOC_TLV320AIC3X if I2C
select SND_SOC_TPA6130A2 if I2C
select SND_SOC_TLV320DAC33 if I2C
@ -76,6 +83,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_WM8985 if SND_SOC_I2C_AND_SPI
select SND_SOC_WM8988 if SND_SOC_I2C_AND_SPI
select SND_SOC_WM8990 if I2C
select SND_SOC_WM8991 if I2C
select SND_SOC_WM8993 if I2C
select SND_SOC_WM8994 if MFD_WM8994
select SND_SOC_WM8995 if SND_SOC_I2C_AND_SPI
@ -155,6 +163,9 @@ config SND_SOC_CS4270_VD33_ERRATA
bool
depends on SND_SOC_CS4270
config SND_SOC_CS4271
tristate
config SND_SOC_CX20442
tristate
@ -167,15 +178,28 @@ config SND_SOC_L3
config SND_SOC_DA7210
tristate
config SND_SOC_DFBMCS320
tristate
config SND_SOC_DMIC
tristate
config SND_SOC_MAX98088
tristate
config SND_SOC_MAX9850
tristate
config SND_SOC_PCM3008
tristate
#Freescale sgtl5000 codec
config SND_SOC_SGTL5000
tristate
config SND_SOC_SN95031
tristate
config SND_SOC_SPDIF
tristate
@ -192,6 +216,9 @@ config SND_SOC_TLV320AIC26
tristate "TI TLV320AIC26 Codec support" if SND_SOC_OF_SIMPLE
depends on SPI
config SND_SOC_TVL320AIC32X4
tristate
config SND_SOC_TLV320AIC3X
tristate
@ -304,6 +331,9 @@ config SND_SOC_WM8988
config SND_SOC_WM8990
tristate
config SND_SOC_WM8991
tristate
config SND_SOC_WM8993
tristate
@ -326,6 +356,9 @@ config SND_SOC_WM9713
tristate
# Amp
config SND_SOC_LM4857
tristate
config SND_SOC_MAX9877
tristate
@ -337,4 +370,3 @@ config SND_SOC_WM2000
config SND_SOC_WM9090
tristate

View file

@ -12,19 +12,25 @@ snd-soc-ak4671-objs := ak4671.o
snd-soc-cq93vc-objs := cq93vc.o
snd-soc-cs42l51-objs := cs42l51.o
snd-soc-cs4270-objs := cs4270.o
snd-soc-cs4271-objs := cs4271.o
snd-soc-cx20442-objs := cx20442.o
snd-soc-da7210-objs := da7210.o
snd-soc-dfbmcs320-objs := dfbmcs320.o
snd-soc-dmic-objs := dmic.o
snd-soc-l3-objs := l3.o
snd-soc-max98088-objs := max98088.o
snd-soc-max9850-objs := max9850.o
snd-soc-pcm3008-objs := pcm3008.o
snd-soc-sgtl5000-objs := sgtl5000.o
snd-soc-alc5623-objs := alc5623.o
snd-soc-sn95031-objs := sn95031.o
snd-soc-spdif-objs := spdif_transciever.o
snd-soc-ssm2602-objs := ssm2602.o
snd-soc-stac9766-objs := stac9766.o
snd-soc-tlv320aic23-objs := tlv320aic23.o
snd-soc-tlv320aic26-objs := tlv320aic26.o
snd-soc-tlv320aic3x-objs := tlv320aic3x.o
snd-soc-tlv320aic32x4-objs := tlv320aic32x4.o
snd-soc-tlv320dac33-objs := tlv320dac33.o
snd-soc-twl4030-objs := twl4030.o
snd-soc-twl6040-objs := twl6040.o
@ -61,6 +67,7 @@ snd-soc-wm8978-objs := wm8978.o
snd-soc-wm8985-objs := wm8985.o
snd-soc-wm8988-objs := wm8988.o
snd-soc-wm8990-objs := wm8990.o
snd-soc-wm8991-objs := wm8991.o
snd-soc-wm8993-objs := wm8993.o
snd-soc-wm8994-objs := wm8994.o wm8994-tables.o
snd-soc-wm8995-objs := wm8995.o
@ -72,6 +79,7 @@ snd-soc-wm-hubs-objs := wm_hubs.o
snd-soc-jz4740-codec-objs := jz4740.o
# Amp
snd-soc-lm4857-objs := lm4857.o
snd-soc-max9877-objs := max9877.o
snd-soc-tpa6130a2-objs := tpa6130a2.o
snd-soc-wm2000-objs := wm2000.o
@ -88,23 +96,29 @@ obj-$(CONFIG_SND_SOC_AK4104) += snd-soc-ak4104.o
obj-$(CONFIG_SND_SOC_AK4535) += snd-soc-ak4535.o
obj-$(CONFIG_SND_SOC_AK4642) += snd-soc-ak4642.o
obj-$(CONFIG_SND_SOC_AK4671) += snd-soc-ak4671.o
obj-$(CONFIG_SND_SOC_ALC5623) += snd-soc-alc5623.o
obj-$(CONFIG_SND_SOC_CQ0093VC) += snd-soc-cq93vc.o
obj-$(CONFIG_SND_SOC_CS42L51) += snd-soc-cs42l51.o
obj-$(CONFIG_SND_SOC_CS4270) += snd-soc-cs4270.o
obj-$(CONFIG_SND_SOC_CS4271) += snd-soc-cs4271.o
obj-$(CONFIG_SND_SOC_CX20442) += snd-soc-cx20442.o
obj-$(CONFIG_SND_SOC_DA7210) += snd-soc-da7210.o
obj-$(CONFIG_SND_SOC_DFBMCS320) += snd-soc-dfbmcs320.o
obj-$(CONFIG_SND_SOC_DMIC) += snd-soc-dmic.o
obj-$(CONFIG_SND_SOC_L3) += snd-soc-l3.o
obj-$(CONFIG_SND_SOC_JZ4740_CODEC) += snd-soc-jz4740-codec.o
obj-$(CONFIG_SND_SOC_MAX98088) += snd-soc-max98088.o
obj-$(CONFIG_SND_SOC_MAX9850) += snd-soc-max9850.o
obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o
obj-$(CONFIG_SND_SOC_ALC5623) += snd-soc-alc5623.o
obj-$(CONFIG_SND_SOC_SGTL5000) += snd-soc-sgtl5000.o
obj-$(CONFIG_SND_SOC_SN95031) +=snd-soc-sn95031.o
obj-$(CONFIG_SND_SOC_SPDIF) += snd-soc-spdif.o
obj-$(CONFIG_SND_SOC_SSM2602) += snd-soc-ssm2602.o
obj-$(CONFIG_SND_SOC_STAC9766) += snd-soc-stac9766.o
obj-$(CONFIG_SND_SOC_TLV320AIC23) += snd-soc-tlv320aic23.o
obj-$(CONFIG_SND_SOC_TLV320AIC26) += snd-soc-tlv320aic26.o
obj-$(CONFIG_SND_SOC_TLV320AIC3X) += snd-soc-tlv320aic3x.o
obj-$(CONFIG_SND_SOC_TVL320AIC32X4) += snd-soc-tlv320aic32x4.o
obj-$(CONFIG_SND_SOC_TLV320DAC33) += snd-soc-tlv320dac33.o
obj-$(CONFIG_SND_SOC_TWL4030) += snd-soc-twl4030.o
obj-$(CONFIG_SND_SOC_TWL6040) += snd-soc-twl6040.o
@ -141,6 +155,7 @@ obj-$(CONFIG_SND_SOC_WM8978) += snd-soc-wm8978.o
obj-$(CONFIG_SND_SOC_WM8985) += snd-soc-wm8985.o
obj-$(CONFIG_SND_SOC_WM8988) += snd-soc-wm8988.o
obj-$(CONFIG_SND_SOC_WM8990) += snd-soc-wm8990.o
obj-$(CONFIG_SND_SOC_WM8991) += snd-soc-wm8991.o
obj-$(CONFIG_SND_SOC_WM8993) += snd-soc-wm8993.o
obj-$(CONFIG_SND_SOC_WM8994) += snd-soc-wm8994.o
obj-$(CONFIG_SND_SOC_WM8995) += snd-soc-wm8995.o
@ -151,6 +166,7 @@ obj-$(CONFIG_SND_SOC_WM9713) += snd-soc-wm9713.o
obj-$(CONFIG_SND_SOC_WM_HUBS) += snd-soc-wm-hubs.o
# Amp
obj-$(CONFIG_SND_SOC_LM4857) += snd-soc-lm4857.o
obj-$(CONFIG_SND_SOC_MAX9877) += snd-soc-max9877.o
obj-$(CONFIG_SND_SOC_TPA6130A2) += snd-soc-tpa6130a2.o
obj-$(CONFIG_SND_SOC_WM2000) += snd-soc-wm2000.o

View file

@ -294,7 +294,6 @@ static struct spi_driver ak4104_spi_driver = {
static int __init ak4104_init(void)
{
pr_info("Asahi Kasei AK4104 ALSA SoC Codec Driver\n");
return spi_register_driver(&ak4104_spi_driver);
}
module_init(ak4104_init);

View file

@ -116,6 +116,12 @@
#define BCKO_MASK (1 << 3)
#define BCKO_64 BCKO_MASK
#define DIF_MASK (3 << 0)
#define DSP (0 << 0)
#define RIGHT_J (1 << 0)
#define LEFT_J (2 << 0)
#define I2S (3 << 0)
/* MD_CTL2 */
#define FS0 (1 << 0)
#define FS1 (1 << 1)
@ -354,6 +360,24 @@ static int ak4642_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
snd_soc_update_bits(codec, PW_MGMT2, MS, data);
snd_soc_update_bits(codec, MD_CTL1, BCKO_MASK, bcko);
/* format type */
data = 0;
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_LEFT_J:
data = LEFT_J;
break;
case SND_SOC_DAIFMT_I2S:
data = I2S;
break;
/* FIXME
* Please add RIGHT_J / DSP support here
*/
default:
return -EINVAL;
break;
}
snd_soc_update_bits(codec, MD_CTL1, DIF_MASK, data);
return 0;
}

View file

@ -193,12 +193,12 @@ static struct cs4270_mode_ratios cs4270_mode_ratios[] = {
/* The number of MCLK/LRCK ratios supported by the CS4270 */
#define NUM_MCLK_RATIOS ARRAY_SIZE(cs4270_mode_ratios)
static int cs4270_reg_is_readable(unsigned int reg)
static int cs4270_reg_is_readable(struct snd_soc_codec *codec, unsigned int reg)
{
return (reg >= CS4270_FIRSTREG) && (reg <= CS4270_LASTREG);
}
static int cs4270_reg_is_volatile(unsigned int reg)
static int cs4270_reg_is_volatile(struct snd_soc_codec *codec, unsigned int reg)
{
/* Unreadable registers are considered volatile */
if ((reg < CS4270_FIRSTREG) || (reg > CS4270_LASTREG))
@ -719,7 +719,7 @@ static int cs4270_i2c_remove(struct i2c_client *i2c_client)
/*
* cs4270_id - I2C device IDs supported by this driver
*/
static struct i2c_device_id cs4270_id[] = {
static const struct i2c_device_id cs4270_id[] = {
{"cs4270", 0},
{}
};
@ -743,8 +743,6 @@ static struct i2c_driver cs4270_i2c_driver = {
static int __init cs4270_init(void)
{
pr_info("Cirrus Logic CS4270 ALSA SoC Codec Driver\n");
return i2c_add_driver(&cs4270_i2c_driver);
}
module_init(cs4270_init);

667
sound/soc/codecs/cs4271.c Normal file
View file

@ -0,0 +1,667 @@
/*
* CS4271 ASoC codec driver
*
* Copyright (c) 2010 Alexander Sverdlin <subaparts@yandex.ru>
*
* 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.
*
* This driver support CS4271 codec being master or slave, working
* in control port mode, connected either via SPI or I2C.
* The data format accepted is I2S or left-justified.
* DAPM support not implemented.
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <sound/pcm.h>
#include <sound/soc.h>
#include <sound/tlv.h>
#include <linux/gpio.h>
#include <linux/i2c.h>
#include <linux/spi/spi.h>
#include <sound/cs4271.h>
#define CS4271_PCM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S24_LE | \
SNDRV_PCM_FMTBIT_S32_LE)
#define CS4271_PCM_RATES SNDRV_PCM_RATE_8000_192000
/*
* CS4271 registers
* High byte represents SPI chip address (0x10) + write command (0)
* Low byte - codec register address
*/
#define CS4271_MODE1 0x2001 /* Mode Control 1 */
#define CS4271_DACCTL 0x2002 /* DAC Control */
#define CS4271_DACVOL 0x2003 /* DAC Volume & Mixing Control */
#define CS4271_VOLA 0x2004 /* DAC Channel A Volume Control */
#define CS4271_VOLB 0x2005 /* DAC Channel B Volume Control */
#define CS4271_ADCCTL 0x2006 /* ADC Control */
#define CS4271_MODE2 0x2007 /* Mode Control 2 */
#define CS4271_CHIPID 0x2008 /* Chip ID */
#define CS4271_FIRSTREG CS4271_MODE1
#define CS4271_LASTREG CS4271_MODE2
#define CS4271_NR_REGS ((CS4271_LASTREG & 0xFF) + 1)
/* Bit masks for the CS4271 registers */
#define CS4271_MODE1_MODE_MASK 0xC0
#define CS4271_MODE1_MODE_1X 0x00
#define CS4271_MODE1_MODE_2X 0x80
#define CS4271_MODE1_MODE_4X 0xC0
#define CS4271_MODE1_DIV_MASK 0x30
#define CS4271_MODE1_DIV_1 0x00
#define CS4271_MODE1_DIV_15 0x10
#define CS4271_MODE1_DIV_2 0x20
#define CS4271_MODE1_DIV_3 0x30
#define CS4271_MODE1_MASTER 0x08
#define CS4271_MODE1_DAC_DIF_MASK 0x07
#define CS4271_MODE1_DAC_DIF_LJ 0x00
#define CS4271_MODE1_DAC_DIF_I2S 0x01
#define CS4271_MODE1_DAC_DIF_RJ16 0x02
#define CS4271_MODE1_DAC_DIF_RJ24 0x03
#define CS4271_MODE1_DAC_DIF_RJ20 0x04
#define CS4271_MODE1_DAC_DIF_RJ18 0x05
#define CS4271_DACCTL_AMUTE 0x80
#define CS4271_DACCTL_IF_SLOW 0x40
#define CS4271_DACCTL_DEM_MASK 0x30
#define CS4271_DACCTL_DEM_DIS 0x00
#define CS4271_DACCTL_DEM_441 0x10
#define CS4271_DACCTL_DEM_48 0x20
#define CS4271_DACCTL_DEM_32 0x30
#define CS4271_DACCTL_SVRU 0x08
#define CS4271_DACCTL_SRD 0x04
#define CS4271_DACCTL_INVA 0x02
#define CS4271_DACCTL_INVB 0x01
#define CS4271_DACVOL_BEQUA 0x40
#define CS4271_DACVOL_SOFT 0x20
#define CS4271_DACVOL_ZEROC 0x10
#define CS4271_DACVOL_ATAPI_MASK 0x0F
#define CS4271_DACVOL_ATAPI_M_M 0x00
#define CS4271_DACVOL_ATAPI_M_BR 0x01
#define CS4271_DACVOL_ATAPI_M_BL 0x02
#define CS4271_DACVOL_ATAPI_M_BLR2 0x03
#define CS4271_DACVOL_ATAPI_AR_M 0x04
#define CS4271_DACVOL_ATAPI_AR_BR 0x05
#define CS4271_DACVOL_ATAPI_AR_BL 0x06
#define CS4271_DACVOL_ATAPI_AR_BLR2 0x07
#define CS4271_DACVOL_ATAPI_AL_M 0x08
#define CS4271_DACVOL_ATAPI_AL_BR 0x09
#define CS4271_DACVOL_ATAPI_AL_BL 0x0A
#define CS4271_DACVOL_ATAPI_AL_BLR2 0x0B
#define CS4271_DACVOL_ATAPI_ALR2_M 0x0C
#define CS4271_DACVOL_ATAPI_ALR2_BR 0x0D
#define CS4271_DACVOL_ATAPI_ALR2_BL 0x0E
#define CS4271_DACVOL_ATAPI_ALR2_BLR2 0x0F
#define CS4271_VOLA_MUTE 0x80
#define CS4271_VOLA_VOL_MASK 0x7F
#define CS4271_VOLB_MUTE 0x80
#define CS4271_VOLB_VOL_MASK 0x7F
#define CS4271_ADCCTL_DITHER16 0x20
#define CS4271_ADCCTL_ADC_DIF_MASK 0x10
#define CS4271_ADCCTL_ADC_DIF_LJ 0x00
#define CS4271_ADCCTL_ADC_DIF_I2S 0x10
#define CS4271_ADCCTL_MUTEA 0x08
#define CS4271_ADCCTL_MUTEB 0x04
#define CS4271_ADCCTL_HPFDA 0x02
#define CS4271_ADCCTL_HPFDB 0x01
#define CS4271_MODE2_LOOP 0x10
#define CS4271_MODE2_MUTECAEQUB 0x08
#define CS4271_MODE2_FREEZE 0x04
#define CS4271_MODE2_CPEN 0x02
#define CS4271_MODE2_PDN 0x01
#define CS4271_CHIPID_PART_MASK 0xF0
#define CS4271_CHIPID_REV_MASK 0x0F
/*
* Default CS4271 power-up configuration
* Array contains non-existing in hw register at address 0
* Array do not include Chip ID, as codec driver does not use
* registers read operations at all
*/
static const u8 cs4271_dflt_reg[CS4271_NR_REGS] = {
0,
0,
CS4271_DACCTL_AMUTE,
CS4271_DACVOL_SOFT | CS4271_DACVOL_ATAPI_AL_BR,
0,
0,
0,
0,
};
struct cs4271_private {
/* SND_SOC_I2C or SND_SOC_SPI */
enum snd_soc_control_type bus_type;
void *control_data;
unsigned int mclk;
bool master;
bool deemph;
/* Current sample rate for de-emphasis control */
int rate;
/* GPIO driving Reset pin, if any */
int gpio_nreset;
/* GPIO that disable serial bus, if any */
int gpio_disable;
};
/*
* @freq is the desired MCLK rate
* MCLK rate should (c) be the sample rate, multiplied by one of the
* ratios listed in cs4271_mclk_fs_ratios table
*/
static int cs4271_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
struct snd_soc_codec *codec = codec_dai->codec;
struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
cs4271->mclk = freq;
return 0;
}
static int cs4271_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int format)
{
struct snd_soc_codec *codec = codec_dai->codec;
struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
unsigned int val = 0;
int ret;
switch (format & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBS_CFS:
cs4271->master = 0;
break;
case SND_SOC_DAIFMT_CBM_CFM:
cs4271->master = 1;
val |= CS4271_MODE1_MASTER;
break;
default:
dev_err(codec->dev, "Invalid DAI format\n");
return -EINVAL;
}
switch (format & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_LEFT_J:
val |= CS4271_MODE1_DAC_DIF_LJ;
ret = snd_soc_update_bits(codec, CS4271_ADCCTL,
CS4271_ADCCTL_ADC_DIF_MASK, CS4271_ADCCTL_ADC_DIF_LJ);
if (ret < 0)
return ret;
break;
case SND_SOC_DAIFMT_I2S:
val |= CS4271_MODE1_DAC_DIF_I2S;
ret = snd_soc_update_bits(codec, CS4271_ADCCTL,
CS4271_ADCCTL_ADC_DIF_MASK, CS4271_ADCCTL_ADC_DIF_I2S);
if (ret < 0)
return ret;
break;
default:
dev_err(codec->dev, "Invalid DAI format\n");
return -EINVAL;
}
ret = snd_soc_update_bits(codec, CS4271_MODE1,
CS4271_MODE1_DAC_DIF_MASK | CS4271_MODE1_MASTER, val);
if (ret < 0)
return ret;
return 0;
}
static int cs4271_deemph[] = {0, 44100, 48000, 32000};
static int cs4271_set_deemph(struct snd_soc_codec *codec)
{
struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
int i, ret;
int val = CS4271_DACCTL_DEM_DIS;
if (cs4271->deemph) {
/* Find closest de-emphasis freq */
val = 1;
for (i = 2; i < ARRAY_SIZE(cs4271_deemph); i++)
if (abs(cs4271_deemph[i] - cs4271->rate) <
abs(cs4271_deemph[val] - cs4271->rate))
val = i;
val <<= 4;
}
ret = snd_soc_update_bits(codec, CS4271_DACCTL,
CS4271_DACCTL_DEM_MASK, val);
if (ret < 0)
return ret;
return 0;
}
static int cs4271_get_deemph(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
ucontrol->value.enumerated.item[0] = cs4271->deemph;
return 0;
}
static int cs4271_put_deemph(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
cs4271->deemph = ucontrol->value.enumerated.item[0];
return cs4271_set_deemph(codec);
}
struct cs4271_clk_cfg {
bool master; /* codec mode */
u8 speed_mode; /* codec speed mode: 1x, 2x, 4x */
unsigned short ratio; /* MCLK / sample rate */
u8 ratio_mask; /* ratio bit mask for Master mode */
};
static struct cs4271_clk_cfg cs4271_clk_tab[] = {
{1, CS4271_MODE1_MODE_1X, 256, CS4271_MODE1_DIV_1},
{1, CS4271_MODE1_MODE_1X, 384, CS4271_MODE1_DIV_15},
{1, CS4271_MODE1_MODE_1X, 512, CS4271_MODE1_DIV_2},
{1, CS4271_MODE1_MODE_1X, 768, CS4271_MODE1_DIV_3},
{1, CS4271_MODE1_MODE_2X, 128, CS4271_MODE1_DIV_1},
{1, CS4271_MODE1_MODE_2X, 192, CS4271_MODE1_DIV_15},
{1, CS4271_MODE1_MODE_2X, 256, CS4271_MODE1_DIV_2},
{1, CS4271_MODE1_MODE_2X, 384, CS4271_MODE1_DIV_3},
{1, CS4271_MODE1_MODE_4X, 64, CS4271_MODE1_DIV_1},
{1, CS4271_MODE1_MODE_4X, 96, CS4271_MODE1_DIV_15},
{1, CS4271_MODE1_MODE_4X, 128, CS4271_MODE1_DIV_2},
{1, CS4271_MODE1_MODE_4X, 192, CS4271_MODE1_DIV_3},
{0, CS4271_MODE1_MODE_1X, 256, CS4271_MODE1_DIV_1},
{0, CS4271_MODE1_MODE_1X, 384, CS4271_MODE1_DIV_1},
{0, CS4271_MODE1_MODE_1X, 512, CS4271_MODE1_DIV_1},
{0, CS4271_MODE1_MODE_1X, 768, CS4271_MODE1_DIV_2},
{0, CS4271_MODE1_MODE_1X, 1024, CS4271_MODE1_DIV_2},
{0, CS4271_MODE1_MODE_2X, 128, CS4271_MODE1_DIV_1},
{0, CS4271_MODE1_MODE_2X, 192, CS4271_MODE1_DIV_1},
{0, CS4271_MODE1_MODE_2X, 256, CS4271_MODE1_DIV_1},
{0, CS4271_MODE1_MODE_2X, 384, CS4271_MODE1_DIV_2},
{0, CS4271_MODE1_MODE_2X, 512, CS4271_MODE1_DIV_2},
{0, CS4271_MODE1_MODE_4X, 64, CS4271_MODE1_DIV_1},
{0, CS4271_MODE1_MODE_4X, 96, CS4271_MODE1_DIV_1},
{0, CS4271_MODE1_MODE_4X, 128, CS4271_MODE1_DIV_1},
{0, CS4271_MODE1_MODE_4X, 192, CS4271_MODE1_DIV_2},
{0, CS4271_MODE1_MODE_4X, 256, CS4271_MODE1_DIV_2},
};
#define CS4171_NR_RATIOS ARRAY_SIZE(cs4271_clk_tab)
static int cs4271_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_codec *codec = rtd->codec;
struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
int i, ret;
unsigned int ratio, val;
cs4271->rate = params_rate(params);
/* Configure DAC */
if (cs4271->rate < 50000)
val = CS4271_MODE1_MODE_1X;
else if (cs4271->rate < 100000)
val = CS4271_MODE1_MODE_2X;
else
val = CS4271_MODE1_MODE_4X;
ratio = cs4271->mclk / cs4271->rate;
for (i = 0; i < CS4171_NR_RATIOS; i++)
if ((cs4271_clk_tab[i].master == cs4271->master) &&
(cs4271_clk_tab[i].speed_mode == val) &&
(cs4271_clk_tab[i].ratio == ratio))
break;
if (i == CS4171_NR_RATIOS) {
dev_err(codec->dev, "Invalid sample rate\n");
return -EINVAL;
}
val |= cs4271_clk_tab[i].ratio_mask;
ret = snd_soc_update_bits(codec, CS4271_MODE1,
CS4271_MODE1_MODE_MASK | CS4271_MODE1_DIV_MASK, val);
if (ret < 0)
return ret;
return cs4271_set_deemph(codec);
}
static int cs4271_digital_mute(struct snd_soc_dai *dai, int mute)
{
struct snd_soc_codec *codec = dai->codec;
int ret;
int val_a = 0;
int val_b = 0;
if (mute) {
val_a = CS4271_VOLA_MUTE;
val_b = CS4271_VOLB_MUTE;
}
ret = snd_soc_update_bits(codec, CS4271_VOLA, CS4271_VOLA_MUTE, val_a);
if (ret < 0)
return ret;
ret = snd_soc_update_bits(codec, CS4271_VOLB, CS4271_VOLB_MUTE, val_b);
if (ret < 0)
return ret;
return 0;
}
/* CS4271 controls */
static DECLARE_TLV_DB_SCALE(cs4271_dac_tlv, -12700, 100, 0);
static const struct snd_kcontrol_new cs4271_snd_controls[] = {
SOC_DOUBLE_R_TLV("Master Playback Volume", CS4271_VOLA, CS4271_VOLB,
0, 0x7F, 1, cs4271_dac_tlv),
SOC_SINGLE("Digital Loopback Switch", CS4271_MODE2, 4, 1, 0),
SOC_SINGLE("Soft Ramp Switch", CS4271_DACVOL, 5, 1, 0),
SOC_SINGLE("Zero Cross Switch", CS4271_DACVOL, 4, 1, 0),
SOC_SINGLE_BOOL_EXT("De-emphasis Switch", 0,
cs4271_get_deemph, cs4271_put_deemph),
SOC_SINGLE("Auto-Mute Switch", CS4271_DACCTL, 7, 1, 0),
SOC_SINGLE("Slow Roll Off Filter Switch", CS4271_DACCTL, 6, 1, 0),
SOC_SINGLE("Soft Volume Ramp-Up Switch", CS4271_DACCTL, 3, 1, 0),
SOC_SINGLE("Soft Ramp-Down Switch", CS4271_DACCTL, 2, 1, 0),
SOC_SINGLE("Left Channel Inversion Switch", CS4271_DACCTL, 1, 1, 0),
SOC_SINGLE("Right Channel Inversion Switch", CS4271_DACCTL, 0, 1, 0),
SOC_DOUBLE("Master Capture Switch", CS4271_ADCCTL, 3, 2, 1, 1),
SOC_SINGLE("Dither 16-Bit Data Switch", CS4271_ADCCTL, 5, 1, 0),
SOC_DOUBLE("High Pass Filter Switch", CS4271_ADCCTL, 1, 0, 1, 1),
SOC_DOUBLE_R("Master Playback Switch", CS4271_VOLA, CS4271_VOLB,
7, 1, 1),
};
static struct snd_soc_dai_ops cs4271_dai_ops = {
.hw_params = cs4271_hw_params,
.set_sysclk = cs4271_set_dai_sysclk,
.set_fmt = cs4271_set_dai_fmt,
.digital_mute = cs4271_digital_mute,
};
static struct snd_soc_dai_driver cs4271_dai = {
.name = "cs4271-hifi",
.playback = {
.stream_name = "Playback",
.channels_min = 2,
.channels_max = 2,
.rates = CS4271_PCM_RATES,
.formats = CS4271_PCM_FORMATS,
},
.capture = {
.stream_name = "Capture",
.channels_min = 2,
.channels_max = 2,
.rates = CS4271_PCM_RATES,
.formats = CS4271_PCM_FORMATS,
},
.ops = &cs4271_dai_ops,
.symmetric_rates = 1,
};
#ifdef CONFIG_PM
static int cs4271_soc_suspend(struct snd_soc_codec *codec, pm_message_t mesg)
{
int ret;
/* Set power-down bit */
ret = snd_soc_update_bits(codec, CS4271_MODE2, 0, CS4271_MODE2_PDN);
if (ret < 0)
return ret;
return 0;
}
static int cs4271_soc_resume(struct snd_soc_codec *codec)
{
int ret;
/* Restore codec state */
ret = snd_soc_cache_sync(codec);
if (ret < 0)
return ret;
/* then disable the power-down bit */
ret = snd_soc_update_bits(codec, CS4271_MODE2, CS4271_MODE2_PDN, 0);
if (ret < 0)
return ret;
return 0;
}
#else
#define cs4271_soc_suspend NULL
#define cs4271_soc_resume NULL
#endif /* CONFIG_PM */
static int cs4271_probe(struct snd_soc_codec *codec)
{
struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
struct cs4271_platform_data *cs4271plat = codec->dev->platform_data;
int ret;
int gpio_nreset = -EINVAL;
codec->control_data = cs4271->control_data;
if (cs4271plat && gpio_is_valid(cs4271plat->gpio_nreset))
gpio_nreset = cs4271plat->gpio_nreset;
if (gpio_nreset >= 0)
if (gpio_request(gpio_nreset, "CS4271 Reset"))
gpio_nreset = -EINVAL;
if (gpio_nreset >= 0) {
/* Reset codec */
gpio_direction_output(gpio_nreset, 0);
udelay(1);
gpio_set_value(gpio_nreset, 1);
/* Give the codec time to wake up */
udelay(1);
}
cs4271->gpio_nreset = gpio_nreset;
/*
* In case of I2C, chip address specified in board data.
* So cache IO operations use 8 bit codec register address.
* In case of SPI, chip address and register address
* passed together as 16 bit value.
* Anyway, register address is masked with 0xFF inside
* soc-cache code.
*/
if (cs4271->bus_type == SND_SOC_SPI)
ret = snd_soc_codec_set_cache_io(codec, 16, 8,
cs4271->bus_type);
else
ret = snd_soc_codec_set_cache_io(codec, 8, 8,
cs4271->bus_type);
if (ret) {
dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
return ret;
}
ret = snd_soc_update_bits(codec, CS4271_MODE2, 0,
CS4271_MODE2_PDN | CS4271_MODE2_CPEN);
if (ret < 0)
return ret;
ret = snd_soc_update_bits(codec, CS4271_MODE2, CS4271_MODE2_PDN, 0);
if (ret < 0)
return ret;
/* Power-up sequence requires 85 uS */
udelay(85);
return snd_soc_add_controls(codec, cs4271_snd_controls,
ARRAY_SIZE(cs4271_snd_controls));
}
static int cs4271_remove(struct snd_soc_codec *codec)
{
struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
int gpio_nreset;
gpio_nreset = cs4271->gpio_nreset;
if (gpio_is_valid(gpio_nreset)) {
/* Set codec to the reset state */
gpio_set_value(gpio_nreset, 0);
gpio_free(gpio_nreset);
}
return 0;
};
static struct snd_soc_codec_driver soc_codec_dev_cs4271 = {
.probe = cs4271_probe,
.remove = cs4271_remove,
.suspend = cs4271_soc_suspend,
.resume = cs4271_soc_resume,
.reg_cache_default = cs4271_dflt_reg,
.reg_cache_size = ARRAY_SIZE(cs4271_dflt_reg),
.reg_word_size = sizeof(cs4271_dflt_reg[0]),
.compress_type = SND_SOC_FLAT_COMPRESSION,
};
#if defined(CONFIG_SPI_MASTER)
static int __devinit cs4271_spi_probe(struct spi_device *spi)
{
struct cs4271_private *cs4271;
cs4271 = devm_kzalloc(&spi->dev, sizeof(*cs4271), GFP_KERNEL);
if (!cs4271)
return -ENOMEM;
spi_set_drvdata(spi, cs4271);
cs4271->control_data = spi;
cs4271->bus_type = SND_SOC_SPI;
return snd_soc_register_codec(&spi->dev, &soc_codec_dev_cs4271,
&cs4271_dai, 1);
}
static int __devexit cs4271_spi_remove(struct spi_device *spi)
{
snd_soc_unregister_codec(&spi->dev);
return 0;
}
static struct spi_driver cs4271_spi_driver = {
.driver = {
.name = "cs4271",
.owner = THIS_MODULE,
},
.probe = cs4271_spi_probe,
.remove = __devexit_p(cs4271_spi_remove),
};
#endif /* defined(CONFIG_SPI_MASTER) */
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
static const struct i2c_device_id cs4271_i2c_id[] = {
{"cs4271", 0},
{}
};
MODULE_DEVICE_TABLE(i2c, cs4271_i2c_id);
static int __devinit cs4271_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct cs4271_private *cs4271;
cs4271 = devm_kzalloc(&client->dev, sizeof(*cs4271), GFP_KERNEL);
if (!cs4271)
return -ENOMEM;
i2c_set_clientdata(client, cs4271);
cs4271->control_data = client;
cs4271->bus_type = SND_SOC_I2C;
return snd_soc_register_codec(&client->dev, &soc_codec_dev_cs4271,
&cs4271_dai, 1);
}
static int __devexit cs4271_i2c_remove(struct i2c_client *client)
{
snd_soc_unregister_codec(&client->dev);
return 0;
}
static struct i2c_driver cs4271_i2c_driver = {
.driver = {
.name = "cs4271",
.owner = THIS_MODULE,
},
.id_table = cs4271_i2c_id,
.probe = cs4271_i2c_probe,
.remove = __devexit_p(cs4271_i2c_remove),
};
#endif /* defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) */
/*
* We only register our serial bus driver here without
* assignment to particular chip. So if any of the below
* fails, there is some problem with I2C or SPI subsystem.
* In most cases this module will be compiled with support
* of only one serial bus.
*/
static int __init cs4271_modinit(void)
{
int ret;
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
ret = i2c_add_driver(&cs4271_i2c_driver);
if (ret) {
pr_err("Failed to register CS4271 I2C driver: %d\n", ret);
return ret;
}
#endif
#if defined(CONFIG_SPI_MASTER)
ret = spi_register_driver(&cs4271_spi_driver);
if (ret) {
pr_err("Failed to register CS4271 SPI driver: %d\n", ret);
return ret;
}
#endif
return 0;
}
module_init(cs4271_modinit);
static void __exit cs4271_modexit(void)
{
#if defined(CONFIG_SPI_MASTER)
spi_unregister_driver(&cs4271_spi_driver);
#endif
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
i2c_del_driver(&cs4271_i2c_driver);
#endif
}
module_exit(cs4271_modexit);
MODULE_AUTHOR("Alexander Sverdlin <subaparts@yandex.ru>");
MODULE_DESCRIPTION("Cirrus Logic CS4271 ALSA SoC Codec Driver");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,72 @@
/*
* Driver for the DFBM-CS320 bluetooth module
* Copyright 2011 Lars-Peter Clausen <lars@metafoo.de>
*
* 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/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <sound/soc.h>
static struct snd_soc_dai_driver dfbmcs320_dai = {
.name = "dfbmcs320-pcm",
.playback = {
.channels_min = 1,
.channels_max = 1,
.rates = SNDRV_PCM_RATE_8000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
},
.capture = {
.channels_min = 1,
.channels_max = 1,
.rates = SNDRV_PCM_RATE_8000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
},
};
static struct snd_soc_codec_driver soc_codec_dev_dfbmcs320;
static int __devinit dfbmcs320_probe(struct platform_device *pdev)
{
return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_dfbmcs320,
&dfbmcs320_dai, 1);
}
static int __devexit dfbmcs320_remove(struct platform_device *pdev)
{
snd_soc_unregister_codec(&pdev->dev);
return 0;
}
static struct platform_driver dfmcs320_driver = {
.driver = {
.name = "dfbmcs320",
.owner = THIS_MODULE,
},
.probe = dfbmcs320_probe,
.remove = __devexit_p(dfbmcs320_remove),
};
static int __init dfbmcs320_init(void)
{
return platform_driver_register(&dfmcs320_driver);
}
module_init(dfbmcs320_init);
static void __exit dfbmcs320_exit(void)
{
platform_driver_unregister(&dfmcs320_driver);
}
module_exit(dfbmcs320_exit);
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
MODULE_DESCRIPTION("ASoC DFBM-CS320 bluethooth module driver");
MODULE_LICENSE("GPL");

276
sound/soc/codecs/lm4857.c Normal file
View file

@ -0,0 +1,276 @@
/*
* LM4857 AMP driver
*
* Copyright 2007 Wolfson Microelectronics PLC.
* Author: Graeme Gregory
* graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com
* Copyright 2011 Lars-Peter Clausen <lars@metafoo.de>
*
* 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/init.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/soc.h>
#include <sound/tlv.h>
struct lm4857 {
struct i2c_client *i2c;
uint8_t mode;
};
static const uint8_t lm4857_default_regs[] = {
0x00, 0x00, 0x00, 0x00,
};
/* The register offsets in the cache array */
#define LM4857_MVOL 0
#define LM4857_LVOL 1
#define LM4857_RVOL 2
#define LM4857_CTRL 3
/* the shifts required to set these bits */
#define LM4857_3D 5
#define LM4857_WAKEUP 5
#define LM4857_EPGAIN 4
static int lm4857_write(struct snd_soc_codec *codec, unsigned int reg,
unsigned int value)
{
uint8_t data;
int ret;
ret = snd_soc_cache_write(codec, reg, value);
if (ret < 0)
return ret;
data = (reg << 6) | value;
ret = i2c_master_send(codec->control_data, &data, 1);
if (ret != 1) {
dev_err(codec->dev, "Failed to write register: %d\n", ret);
return ret;
}
return 0;
}
static unsigned int lm4857_read(struct snd_soc_codec *codec,
unsigned int reg)
{
unsigned int val;
int ret;
ret = snd_soc_cache_read(codec, reg, &val);
if (ret)
return -1;
return val;
}
static int lm4857_get_mode(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
struct lm4857 *lm4857 = snd_soc_codec_get_drvdata(codec);
ucontrol->value.integer.value[0] = lm4857->mode;
return 0;
}
static int lm4857_set_mode(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
struct lm4857 *lm4857 = snd_soc_codec_get_drvdata(codec);
uint8_t value = ucontrol->value.integer.value[0];
lm4857->mode = value;
if (codec->dapm.bias_level == SND_SOC_BIAS_ON)
snd_soc_update_bits(codec, LM4857_CTRL, 0x0F, value + 6);
return 1;
}
static int lm4857_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
struct lm4857 *lm4857 = snd_soc_codec_get_drvdata(codec);
switch (level) {
case SND_SOC_BIAS_ON:
snd_soc_update_bits(codec, LM4857_CTRL, 0x0F, lm4857->mode + 6);
break;
case SND_SOC_BIAS_STANDBY:
snd_soc_update_bits(codec, LM4857_CTRL, 0x0F, 0);
break;
default:
break;
}
codec->dapm.bias_level = level;
return 0;
}
static const char *lm4857_mode[] = {
"Earpiece",
"Loudspeaker",
"Loudspeaker + Headphone",
"Headphone",
};
static const struct soc_enum lm4857_mode_enum =
SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(lm4857_mode), lm4857_mode);
static const struct snd_soc_dapm_widget lm4857_dapm_widgets[] = {
SND_SOC_DAPM_INPUT("IN"),
SND_SOC_DAPM_OUTPUT("LS"),
SND_SOC_DAPM_OUTPUT("HP"),
SND_SOC_DAPM_OUTPUT("EP"),
};
static const DECLARE_TLV_DB_SCALE(stereo_tlv, -4050, 150, 0);
static const DECLARE_TLV_DB_SCALE(mono_tlv, -3450, 150, 0);
static const struct snd_kcontrol_new lm4857_controls[] = {
SOC_SINGLE_TLV("Left Playback Volume", LM4857_LVOL, 0, 31, 0,
stereo_tlv),
SOC_SINGLE_TLV("Right Playback Volume", LM4857_RVOL, 0, 31, 0,
stereo_tlv),
SOC_SINGLE_TLV("Mono Playback Volume", LM4857_MVOL, 0, 31, 0,
mono_tlv),
SOC_SINGLE("Spk 3D Playback Switch", LM4857_LVOL, LM4857_3D, 1, 0),
SOC_SINGLE("HP 3D Playback Switch", LM4857_RVOL, LM4857_3D, 1, 0),
SOC_SINGLE("Fast Wakeup Playback Switch", LM4857_CTRL,
LM4857_WAKEUP, 1, 0),
SOC_SINGLE("Earpiece 6dB Playback Switch", LM4857_CTRL,
LM4857_EPGAIN, 1, 0),
SOC_ENUM_EXT("Mode", lm4857_mode_enum,
lm4857_get_mode, lm4857_set_mode),
};
/* There is a demux inbetween the the input signal and the output signals.
* Currently there is no easy way to model it in ASoC and since it does not make
* much of a difference in practice simply connect the input direclty to the
* outputs. */
static const struct snd_soc_dapm_route lm4857_routes[] = {
{"LS", NULL, "IN"},
{"HP", NULL, "IN"},
{"EP", NULL, "IN"},
};
static int lm4857_probe(struct snd_soc_codec *codec)
{
struct lm4857 *lm4857 = snd_soc_codec_get_drvdata(codec);
struct snd_soc_dapm_context *dapm = &codec->dapm;
int ret;
codec->control_data = lm4857->i2c;
ret = snd_soc_add_controls(codec, lm4857_controls,
ARRAY_SIZE(lm4857_controls));
if (ret)
return ret;
ret = snd_soc_dapm_new_controls(dapm, lm4857_dapm_widgets,
ARRAY_SIZE(lm4857_dapm_widgets));
if (ret)
return ret;
ret = snd_soc_dapm_add_routes(dapm, lm4857_routes,
ARRAY_SIZE(lm4857_routes));
if (ret)
return ret;
snd_soc_dapm_new_widgets(dapm);
return 0;
}
static struct snd_soc_codec_driver soc_codec_dev_lm4857 = {
.write = lm4857_write,
.read = lm4857_read,
.probe = lm4857_probe,
.reg_cache_size = ARRAY_SIZE(lm4857_default_regs),
.reg_word_size = sizeof(uint8_t),
.reg_cache_default = lm4857_default_regs,
.set_bias_level = lm4857_set_bias_level,
};
static int __devinit lm4857_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct lm4857 *lm4857;
int ret;
lm4857 = kzalloc(sizeof(*lm4857), GFP_KERNEL);
if (!lm4857)
return -ENOMEM;
i2c_set_clientdata(i2c, lm4857);
lm4857->i2c = i2c;
ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_lm4857, NULL, 0);
if (ret) {
kfree(lm4857);
return ret;
}
return 0;
}
static int __devexit lm4857_i2c_remove(struct i2c_client *i2c)
{
struct lm4857 *lm4857 = i2c_get_clientdata(i2c);
snd_soc_unregister_codec(&i2c->dev);
kfree(lm4857);
return 0;
}
static const struct i2c_device_id lm4857_i2c_id[] = {
{ "lm4857", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, lm4857_i2c_id);
static struct i2c_driver lm4857_i2c_driver = {
.driver = {
.name = "lm4857",
.owner = THIS_MODULE,
},
.probe = lm4857_i2c_probe,
.remove = __devexit_p(lm4857_i2c_remove),
.id_table = lm4857_i2c_id,
};
static int __init lm4857_init(void)
{
return i2c_add_driver(&lm4857_i2c_driver);
}
module_init(lm4857_init);
static void __exit lm4857_exit(void)
{
i2c_del_driver(&lm4857_i2c_driver);
}
module_exit(lm4857_exit);
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
MODULE_DESCRIPTION("LM4857 amplifier driver");
MODULE_LICENSE("GPL");

View file

@ -608,7 +608,7 @@ static struct {
{ 0xFF, 0x00, 1 }, /* FF */
};
static int max98088_volatile_register(unsigned int reg)
static int max98088_volatile_register(struct snd_soc_codec *codec, unsigned int reg)
{
return max98088_access[reg].vol;
}

389
sound/soc/codecs/max9850.c Normal file
View file

@ -0,0 +1,389 @@
/*
* max9850.c -- codec driver for max9850
*
* Copyright (C) 2011 taskit GmbH
*
* Author: Christian Glindkamp <christian.glindkamp@taskit.de>
*
* Initial development of this code was funded by
* MICRONIC Computer Systeme GmbH, http://www.mcsberlin.de/
*
* 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/module.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/slab.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/tlv.h>
#include "max9850.h"
struct max9850_priv {
unsigned int sysclk;
};
/* max9850 register cache */
static const u8 max9850_reg[MAX9850_CACHEREGNUM] = {
0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
/* these registers are not used at the moment but provided for the sake of
* completeness */
static int max9850_volatile_register(struct snd_soc_codec *codec,
unsigned int reg)
{
switch (reg) {
case MAX9850_STATUSA:
case MAX9850_STATUSB:
return 1;
default:
return 0;
}
}
static const unsigned int max9850_tlv[] = {
TLV_DB_RANGE_HEAD(4),
0x18, 0x1f, TLV_DB_SCALE_ITEM(-7450, 400, 0),
0x20, 0x33, TLV_DB_SCALE_ITEM(-4150, 200, 0),
0x34, 0x37, TLV_DB_SCALE_ITEM(-150, 100, 0),
0x38, 0x3f, TLV_DB_SCALE_ITEM(250, 50, 0),
};
static const struct snd_kcontrol_new max9850_controls[] = {
SOC_SINGLE_TLV("Headphone Volume", MAX9850_VOLUME, 0, 0x3f, 1, max9850_tlv),
SOC_SINGLE("Headphone Switch", MAX9850_VOLUME, 7, 1, 1),
SOC_SINGLE("Mono Switch", MAX9850_GENERAL_PURPOSE, 2, 1, 0),
};
static const struct snd_kcontrol_new max9850_mixer_controls[] = {
SOC_DAPM_SINGLE("Line In Switch", MAX9850_ENABLE, 1, 1, 0),
};
static const struct snd_soc_dapm_widget max9850_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY("Charge Pump 1", MAX9850_ENABLE, 4, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("Charge Pump 2", MAX9850_ENABLE, 5, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("MCLK", MAX9850_ENABLE, 6, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("SHDN", MAX9850_ENABLE, 7, 0, NULL, 0),
SND_SOC_DAPM_MIXER_NAMED_CTL("Output Mixer", MAX9850_ENABLE, 2, 0,
&max9850_mixer_controls[0],
ARRAY_SIZE(max9850_mixer_controls)),
SND_SOC_DAPM_PGA("Headphone Output", MAX9850_ENABLE, 3, 0, NULL, 0),
SND_SOC_DAPM_DAC("DAC", "HiFi Playback", MAX9850_ENABLE, 0, 0),
SND_SOC_DAPM_OUTPUT("OUTL"),
SND_SOC_DAPM_OUTPUT("HPL"),
SND_SOC_DAPM_OUTPUT("OUTR"),
SND_SOC_DAPM_OUTPUT("HPR"),
SND_SOC_DAPM_MIXER("Line Input", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_INPUT("INL"),
SND_SOC_DAPM_INPUT("INR"),
};
static const struct snd_soc_dapm_route intercon[] = {
/* output mixer */
{"Output Mixer", NULL, "DAC"},
{"Output Mixer", "Line In Switch", "Line Input"},
/* outputs */
{"Headphone Output", NULL, "Output Mixer"},
{"HPL", NULL, "Headphone Output"},
{"HPR", NULL, "Headphone Output"},
{"OUTL", NULL, "Output Mixer"},
{"OUTR", NULL, "Output Mixer"},
/* inputs */
{"Line Input", NULL, "INL"},
{"Line Input", NULL, "INR"},
/* supplies */
{"Output Mixer", NULL, "Charge Pump 1"},
{"Output Mixer", NULL, "Charge Pump 2"},
{"Output Mixer", NULL, "SHDN"},
{"DAC", NULL, "MCLK"},
};
static int max9850_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
struct max9850_priv *max9850 = snd_soc_codec_get_drvdata(codec);
u64 lrclk_div;
u8 sf, da;
if (!max9850->sysclk)
return -EINVAL;
/* lrclk_div = 2^22 * rate / iclk with iclk = mclk / sf */
sf = (snd_soc_read(codec, MAX9850_CLOCK) >> 2) + 1;
lrclk_div = (1 << 22);
lrclk_div *= params_rate(params);
lrclk_div *= sf;
do_div(lrclk_div, max9850->sysclk);
snd_soc_write(codec, MAX9850_LRCLK_MSB, (lrclk_div >> 8) & 0x7f);
snd_soc_write(codec, MAX9850_LRCLK_LSB, lrclk_div & 0xff);
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE:
da = 0;
break;
case SNDRV_PCM_FORMAT_S20_3LE:
da = 0x2;
break;
case SNDRV_PCM_FORMAT_S24_LE:
da = 0x3;
break;
default:
return -EINVAL;
}
snd_soc_update_bits(codec, MAX9850_DIGITAL_AUDIO, 0x3, da);
return 0;
}
static int max9850_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
struct snd_soc_codec *codec = codec_dai->codec;
struct max9850_priv *max9850 = snd_soc_codec_get_drvdata(codec);
/* calculate mclk -> iclk divider */
if (freq <= 13000000)
snd_soc_write(codec, MAX9850_CLOCK, 0x0);
else if (freq <= 26000000)
snd_soc_write(codec, MAX9850_CLOCK, 0x4);
else if (freq <= 40000000)
snd_soc_write(codec, MAX9850_CLOCK, 0x8);
else
return -EINVAL;
max9850->sysclk = freq;
return 0;
}
static int max9850_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
{
struct snd_soc_codec *codec = codec_dai->codec;
u8 da = 0;
/* set master/slave audio interface */
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBM_CFM:
da |= MAX9850_MASTER;
break;
case SND_SOC_DAIFMT_CBS_CFS:
break;
default:
return -EINVAL;
}
/* interface format */
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
da |= MAX9850_DLY;
break;
case SND_SOC_DAIFMT_RIGHT_J:
da |= MAX9850_RTJ;
break;
case SND_SOC_DAIFMT_LEFT_J:
break;
default:
return -EINVAL;
}
/* clock inversion */
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_NB_NF:
break;
case SND_SOC_DAIFMT_IB_IF:
da |= MAX9850_BCINV | MAX9850_INV;
break;
case SND_SOC_DAIFMT_IB_NF:
da |= MAX9850_BCINV;
break;
case SND_SOC_DAIFMT_NB_IF:
da |= MAX9850_INV;
break;
default:
return -EINVAL;
}
/* set da */
snd_soc_write(codec, MAX9850_DIGITAL_AUDIO, da);
return 0;
}
static int max9850_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
int ret;
switch (level) {
case SND_SOC_BIAS_ON:
break;
case SND_SOC_BIAS_PREPARE:
break;
case SND_SOC_BIAS_STANDBY:
if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
ret = snd_soc_cache_sync(codec);
if (ret) {
dev_err(codec->dev,
"Failed to sync cache: %d\n", ret);
return ret;
}
}
break;
case SND_SOC_BIAS_OFF:
break;
}
codec->dapm.bias_level = level;
return 0;
}
#define MAX9850_RATES SNDRV_PCM_RATE_8000_48000
#define MAX9850_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE)
static struct snd_soc_dai_ops max9850_dai_ops = {
.hw_params = max9850_hw_params,
.set_sysclk = max9850_set_dai_sysclk,
.set_fmt = max9850_set_dai_fmt,
};
static struct snd_soc_dai_driver max9850_dai = {
.name = "max9850-hifi",
.playback = {
.stream_name = "Playback",
.channels_min = 1,
.channels_max = 2,
.rates = MAX9850_RATES,
.formats = MAX9850_FORMATS
},
.ops = &max9850_dai_ops,
};
#ifdef CONFIG_PM
static int max9850_suspend(struct snd_soc_codec *codec, pm_message_t state)
{
max9850_set_bias_level(codec, SND_SOC_BIAS_OFF);
return 0;
}
static int max9850_resume(struct snd_soc_codec *codec)
{
max9850_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
return 0;
}
#else
#define max9850_suspend NULL
#define max9850_resume NULL
#endif
static int max9850_probe(struct snd_soc_codec *codec)
{
struct snd_soc_dapm_context *dapm = &codec->dapm;
int ret;
ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_I2C);
if (ret < 0) {
dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
return ret;
}
/* enable zero-detect */
snd_soc_update_bits(codec, MAX9850_GENERAL_PURPOSE, 1, 1);
/* enable slew-rate control */
snd_soc_update_bits(codec, MAX9850_VOLUME, 0x40, 0x40);
/* set slew-rate 125ms */
snd_soc_update_bits(codec, MAX9850_CHARGE_PUMP, 0xff, 0xc0);
snd_soc_dapm_new_controls(dapm, max9850_dapm_widgets,
ARRAY_SIZE(max9850_dapm_widgets));
snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon));
snd_soc_add_controls(codec, max9850_controls,
ARRAY_SIZE(max9850_controls));
return 0;
}
static struct snd_soc_codec_driver soc_codec_dev_max9850 = {
.probe = max9850_probe,
.suspend = max9850_suspend,
.resume = max9850_resume,
.set_bias_level = max9850_set_bias_level,
.reg_cache_size = ARRAY_SIZE(max9850_reg),
.reg_word_size = sizeof(u8),
.reg_cache_default = max9850_reg,
.volatile_register = max9850_volatile_register,
};
static int __devinit max9850_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct max9850_priv *max9850;
int ret;
max9850 = kzalloc(sizeof(struct max9850_priv), GFP_KERNEL);
if (max9850 == NULL)
return -ENOMEM;
i2c_set_clientdata(i2c, max9850);
ret = snd_soc_register_codec(&i2c->dev,
&soc_codec_dev_max9850, &max9850_dai, 1);
if (ret < 0)
kfree(max9850);
return ret;
}
static __devexit int max9850_i2c_remove(struct i2c_client *client)
{
snd_soc_unregister_codec(&client->dev);
kfree(i2c_get_clientdata(client));
return 0;
}
static const struct i2c_device_id max9850_i2c_id[] = {
{ "max9850", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, max9850_i2c_id);
static struct i2c_driver max9850_i2c_driver = {
.driver = {
.name = "max9850",
.owner = THIS_MODULE,
},
.probe = max9850_i2c_probe,
.remove = __devexit_p(max9850_i2c_remove),
.id_table = max9850_i2c_id,
};
static int __init max9850_init(void)
{
return i2c_add_driver(&max9850_i2c_driver);
}
module_init(max9850_init);
static void __exit max9850_exit(void)
{
i2c_del_driver(&max9850_i2c_driver);
}
module_exit(max9850_exit);
MODULE_AUTHOR("Christian Glindkamp <christian.glindkamp@taskit.de>");
MODULE_DESCRIPTION("ASoC MAX9850 codec driver");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,38 @@
/*
* max9850.h -- codec driver for max9850
*
* Copyright (C) 2011 taskit GmbH
* Author: Christian Glindkamp <christian.glindkamp@taskit.de>
*
* 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 _MAX9850_H
#define _MAX9850_H
#define MAX9850_STATUSA 0x00
#define MAX9850_STATUSB 0x01
#define MAX9850_VOLUME 0x02
#define MAX9850_GENERAL_PURPOSE 0x03
#define MAX9850_INTERRUPT 0x04
#define MAX9850_ENABLE 0x05
#define MAX9850_CLOCK 0x06
#define MAX9850_CHARGE_PUMP 0x07
#define MAX9850_LRCLK_MSB 0x08
#define MAX9850_LRCLK_LSB 0x09
#define MAX9850_DIGITAL_AUDIO 0x0a
#define MAX9850_CACHEREGNUM 11
/* MAX9850_DIGITAL_AUDIO */
#define MAX9850_MASTER (1<<7)
#define MAX9850_INV (1<<6)
#define MAX9850_BCINV (1<<5)
#define MAX9850_DLY (1<<3)
#define MAX9850_RTJ (1<<2)
#endif

1513
sound/soc/codecs/sgtl5000.c Normal file

File diff suppressed because it is too large Load diff

400
sound/soc/codecs/sgtl5000.h Normal file
View file

@ -0,0 +1,400 @@
/*
* sgtl5000.h - SGTL5000 audio codec interface
*
* Copyright 2010-2011 Freescale Semiconductor, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef _SGTL5000_H
#define _SGTL5000_H
/*
* Register values.
*/
#define SGTL5000_CHIP_ID 0x0000
#define SGTL5000_CHIP_DIG_POWER 0x0002
#define SGTL5000_CHIP_CLK_CTRL 0x0004
#define SGTL5000_CHIP_I2S_CTRL 0x0006
#define SGTL5000_CHIP_SSS_CTRL 0x000a
#define SGTL5000_CHIP_ADCDAC_CTRL 0x000e
#define SGTL5000_CHIP_DAC_VOL 0x0010
#define SGTL5000_CHIP_PAD_STRENGTH 0x0014
#define SGTL5000_CHIP_ANA_ADC_CTRL 0x0020
#define SGTL5000_CHIP_ANA_HP_CTRL 0x0022
#define SGTL5000_CHIP_ANA_CTRL 0x0024
#define SGTL5000_CHIP_LINREG_CTRL 0x0026
#define SGTL5000_CHIP_REF_CTRL 0x0028
#define SGTL5000_CHIP_MIC_CTRL 0x002a
#define SGTL5000_CHIP_LINE_OUT_CTRL 0x002c
#define SGTL5000_CHIP_LINE_OUT_VOL 0x002e
#define SGTL5000_CHIP_ANA_POWER 0x0030
#define SGTL5000_CHIP_PLL_CTRL 0x0032
#define SGTL5000_CHIP_CLK_TOP_CTRL 0x0034
#define SGTL5000_CHIP_ANA_STATUS 0x0036
#define SGTL5000_CHIP_SHORT_CTRL 0x003c
#define SGTL5000_CHIP_ANA_TEST2 0x003a
#define SGTL5000_DAP_CTRL 0x0100
#define SGTL5000_DAP_PEQ 0x0102
#define SGTL5000_DAP_BASS_ENHANCE 0x0104
#define SGTL5000_DAP_BASS_ENHANCE_CTRL 0x0106
#define SGTL5000_DAP_AUDIO_EQ 0x0108
#define SGTL5000_DAP_SURROUND 0x010a
#define SGTL5000_DAP_FLT_COEF_ACCESS 0x010c
#define SGTL5000_DAP_COEF_WR_B0_MSB 0x010e
#define SGTL5000_DAP_COEF_WR_B0_LSB 0x0110
#define SGTL5000_DAP_EQ_BASS_BAND0 0x0116
#define SGTL5000_DAP_EQ_BASS_BAND1 0x0118
#define SGTL5000_DAP_EQ_BASS_BAND2 0x011a
#define SGTL5000_DAP_EQ_BASS_BAND3 0x011c
#define SGTL5000_DAP_EQ_BASS_BAND4 0x011e
#define SGTL5000_DAP_MAIN_CHAN 0x0120
#define SGTL5000_DAP_MIX_CHAN 0x0122
#define SGTL5000_DAP_AVC_CTRL 0x0124
#define SGTL5000_DAP_AVC_THRESHOLD 0x0126
#define SGTL5000_DAP_AVC_ATTACK 0x0128
#define SGTL5000_DAP_AVC_DECAY 0x012a
#define SGTL5000_DAP_COEF_WR_B1_MSB 0x012c
#define SGTL5000_DAP_COEF_WR_B1_LSB 0x012e
#define SGTL5000_DAP_COEF_WR_B2_MSB 0x0130
#define SGTL5000_DAP_COEF_WR_B2_LSB 0x0132
#define SGTL5000_DAP_COEF_WR_A1_MSB 0x0134
#define SGTL5000_DAP_COEF_WR_A1_LSB 0x0136
#define SGTL5000_DAP_COEF_WR_A2_MSB 0x0138
#define SGTL5000_DAP_COEF_WR_A2_LSB 0x013a
/*
* Field Definitions.
*/
/*
* SGTL5000_CHIP_ID
*/
#define SGTL5000_PARTID_MASK 0xff00
#define SGTL5000_PARTID_SHIFT 8
#define SGTL5000_PARTID_WIDTH 8
#define SGTL5000_PARTID_PART_ID 0xa0
#define SGTL5000_REVID_MASK 0x00ff
#define SGTL5000_REVID_SHIFT 0
#define SGTL5000_REVID_WIDTH 8
/*
* SGTL5000_CHIP_DIG_POWER
*/
#define SGTL5000_ADC_EN 0x0040
#define SGTL5000_DAC_EN 0x0020
#define SGTL5000_DAP_POWERUP 0x0010
#define SGTL5000_I2S_OUT_POWERUP 0x0002
#define SGTL5000_I2S_IN_POWERUP 0x0001
/*
* SGTL5000_CHIP_CLK_CTRL
*/
#define SGTL5000_RATE_MODE_MASK 0x0030
#define SGTL5000_RATE_MODE_SHIFT 4
#define SGTL5000_RATE_MODE_WIDTH 2
#define SGTL5000_RATE_MODE_DIV_1 0
#define SGTL5000_RATE_MODE_DIV_2 1
#define SGTL5000_RATE_MODE_DIV_4 2
#define SGTL5000_RATE_MODE_DIV_6 3
#define SGTL5000_SYS_FS_MASK 0x000c
#define SGTL5000_SYS_FS_SHIFT 2
#define SGTL5000_SYS_FS_WIDTH 2
#define SGTL5000_SYS_FS_32k 0x0
#define SGTL5000_SYS_FS_44_1k 0x1
#define SGTL5000_SYS_FS_48k 0x2
#define SGTL5000_SYS_FS_96k 0x3
#define SGTL5000_MCLK_FREQ_MASK 0x0003
#define SGTL5000_MCLK_FREQ_SHIFT 0
#define SGTL5000_MCLK_FREQ_WIDTH 2
#define SGTL5000_MCLK_FREQ_256FS 0x0
#define SGTL5000_MCLK_FREQ_384FS 0x1
#define SGTL5000_MCLK_FREQ_512FS 0x2
#define SGTL5000_MCLK_FREQ_PLL 0x3
/*
* SGTL5000_CHIP_I2S_CTRL
*/
#define SGTL5000_I2S_SCLKFREQ_MASK 0x0100
#define SGTL5000_I2S_SCLKFREQ_SHIFT 8
#define SGTL5000_I2S_SCLKFREQ_WIDTH 1
#define SGTL5000_I2S_SCLKFREQ_64FS 0x0
#define SGTL5000_I2S_SCLKFREQ_32FS 0x1 /* Not for RJ mode */
#define SGTL5000_I2S_MASTER 0x0080
#define SGTL5000_I2S_SCLK_INV 0x0040
#define SGTL5000_I2S_DLEN_MASK 0x0030
#define SGTL5000_I2S_DLEN_SHIFT 4
#define SGTL5000_I2S_DLEN_WIDTH 2
#define SGTL5000_I2S_DLEN_32 0x0
#define SGTL5000_I2S_DLEN_24 0x1
#define SGTL5000_I2S_DLEN_20 0x2
#define SGTL5000_I2S_DLEN_16 0x3
#define SGTL5000_I2S_MODE_MASK 0x000c
#define SGTL5000_I2S_MODE_SHIFT 2
#define SGTL5000_I2S_MODE_WIDTH 2
#define SGTL5000_I2S_MODE_I2S_LJ 0x0
#define SGTL5000_I2S_MODE_RJ 0x1
#define SGTL5000_I2S_MODE_PCM 0x2
#define SGTL5000_I2S_LRALIGN 0x0002
#define SGTL5000_I2S_LRPOL 0x0001 /* set for which mode */
/*
* SGTL5000_CHIP_SSS_CTRL
*/
#define SGTL5000_DAP_MIX_LRSWAP 0x4000
#define SGTL5000_DAP_LRSWAP 0x2000
#define SGTL5000_DAC_LRSWAP 0x1000
#define SGTL5000_I2S_OUT_LRSWAP 0x0400
#define SGTL5000_DAP_MIX_SEL_MASK 0x0300
#define SGTL5000_DAP_MIX_SEL_SHIFT 8
#define SGTL5000_DAP_MIX_SEL_WIDTH 2
#define SGTL5000_DAP_MIX_SEL_ADC 0x0
#define SGTL5000_DAP_MIX_SEL_I2S_IN 0x1
#define SGTL5000_DAP_SEL_MASK 0x00c0
#define SGTL5000_DAP_SEL_SHIFT 6
#define SGTL5000_DAP_SEL_WIDTH 2
#define SGTL5000_DAP_SEL_ADC 0x0
#define SGTL5000_DAP_SEL_I2S_IN 0x1
#define SGTL5000_DAC_SEL_MASK 0x0030
#define SGTL5000_DAC_SEL_SHIFT 4
#define SGTL5000_DAC_SEL_WIDTH 2
#define SGTL5000_DAC_SEL_ADC 0x0
#define SGTL5000_DAC_SEL_I2S_IN 0x1
#define SGTL5000_DAC_SEL_DAP 0x3
#define SGTL5000_I2S_OUT_SEL_MASK 0x0003
#define SGTL5000_I2S_OUT_SEL_SHIFT 0
#define SGTL5000_I2S_OUT_SEL_WIDTH 2
#define SGTL5000_I2S_OUT_SEL_ADC 0x0
#define SGTL5000_I2S_OUT_SEL_I2S_IN 0x1
#define SGTL5000_I2S_OUT_SEL_DAP 0x3
/*
* SGTL5000_CHIP_ADCDAC_CTRL
*/
#define SGTL5000_VOL_BUSY_DAC_RIGHT 0x2000
#define SGTL5000_VOL_BUSY_DAC_LEFT 0x1000
#define SGTL5000_DAC_VOL_RAMP_EN 0x0200
#define SGTL5000_DAC_VOL_RAMP_EXPO 0x0100
#define SGTL5000_DAC_MUTE_RIGHT 0x0008
#define SGTL5000_DAC_MUTE_LEFT 0x0004
#define SGTL5000_ADC_HPF_FREEZE 0x0002
#define SGTL5000_ADC_HPF_BYPASS 0x0001
/*
* SGTL5000_CHIP_DAC_VOL
*/
#define SGTL5000_DAC_VOL_RIGHT_MASK 0xff00
#define SGTL5000_DAC_VOL_RIGHT_SHIFT 8
#define SGTL5000_DAC_VOL_RIGHT_WIDTH 8
#define SGTL5000_DAC_VOL_LEFT_MASK 0x00ff
#define SGTL5000_DAC_VOL_LEFT_SHIFT 0
#define SGTL5000_DAC_VOL_LEFT_WIDTH 8
/*
* SGTL5000_CHIP_PAD_STRENGTH
*/
#define SGTL5000_PAD_I2S_LRCLK_MASK 0x0300
#define SGTL5000_PAD_I2S_LRCLK_SHIFT 8
#define SGTL5000_PAD_I2S_LRCLK_WIDTH 2
#define SGTL5000_PAD_I2S_SCLK_MASK 0x00c0
#define SGTL5000_PAD_I2S_SCLK_SHIFT 6
#define SGTL5000_PAD_I2S_SCLK_WIDTH 2
#define SGTL5000_PAD_I2S_DOUT_MASK 0x0030
#define SGTL5000_PAD_I2S_DOUT_SHIFT 4
#define SGTL5000_PAD_I2S_DOUT_WIDTH 2
#define SGTL5000_PAD_I2C_SDA_MASK 0x000c
#define SGTL5000_PAD_I2C_SDA_SHIFT 2
#define SGTL5000_PAD_I2C_SDA_WIDTH 2
#define SGTL5000_PAD_I2C_SCL_MASK 0x0003
#define SGTL5000_PAD_I2C_SCL_SHIFT 0
#define SGTL5000_PAD_I2C_SCL_WIDTH 2
/*
* SGTL5000_CHIP_ANA_ADC_CTRL
*/
#define SGTL5000_ADC_VOL_M6DB 0x0100
#define SGTL5000_ADC_VOL_RIGHT_MASK 0x00f0
#define SGTL5000_ADC_VOL_RIGHT_SHIFT 4
#define SGTL5000_ADC_VOL_RIGHT_WIDTH 4
#define SGTL5000_ADC_VOL_LEFT_MASK 0x000f
#define SGTL5000_ADC_VOL_LEFT_SHIFT 0
#define SGTL5000_ADC_VOL_LEFT_WIDTH 4
/*
* SGTL5000_CHIP_ANA_HP_CTRL
*/
#define SGTL5000_HP_VOL_RIGHT_MASK 0x7f00
#define SGTL5000_HP_VOL_RIGHT_SHIFT 8
#define SGTL5000_HP_VOL_RIGHT_WIDTH 7
#define SGTL5000_HP_VOL_LEFT_MASK 0x007f
#define SGTL5000_HP_VOL_LEFT_SHIFT 0
#define SGTL5000_HP_VOL_LEFT_WIDTH 7
/*
* SGTL5000_CHIP_ANA_CTRL
*/
#define SGTL5000_LINE_OUT_MUTE 0x0100
#define SGTL5000_HP_SEL_MASK 0x0040
#define SGTL5000_HP_SEL_SHIFT 6
#define SGTL5000_HP_SEL_WIDTH 1
#define SGTL5000_HP_SEL_DAC 0x0
#define SGTL5000_HP_SEL_LINE_IN 0x1
#define SGTL5000_HP_ZCD_EN 0x0020
#define SGTL5000_HP_MUTE 0x0010
#define SGTL5000_ADC_SEL_MASK 0x0004
#define SGTL5000_ADC_SEL_SHIFT 2
#define SGTL5000_ADC_SEL_WIDTH 1
#define SGTL5000_ADC_SEL_MIC 0x0
#define SGTL5000_ADC_SEL_LINE_IN 0x1
#define SGTL5000_ADC_ZCD_EN 0x0002
#define SGTL5000_ADC_MUTE 0x0001
/*
* SGTL5000_CHIP_LINREG_CTRL
*/
#define SGTL5000_VDDC_MAN_ASSN_MASK 0x0040
#define SGTL5000_VDDC_MAN_ASSN_SHIFT 6
#define SGTL5000_VDDC_MAN_ASSN_WIDTH 1
#define SGTL5000_VDDC_MAN_ASSN_VDDA 0x0
#define SGTL5000_VDDC_MAN_ASSN_VDDIO 0x1
#define SGTL5000_VDDC_ASSN_OVRD 0x0020
#define SGTL5000_LINREG_VDDD_MASK 0x000f
#define SGTL5000_LINREG_VDDD_SHIFT 0
#define SGTL5000_LINREG_VDDD_WIDTH 4
/*
* SGTL5000_CHIP_REF_CTRL
*/
#define SGTL5000_ANA_GND_MASK 0x01f0
#define SGTL5000_ANA_GND_SHIFT 4
#define SGTL5000_ANA_GND_WIDTH 5
#define SGTL5000_ANA_GND_BASE 800 /* mv */
#define SGTL5000_ANA_GND_STP 25 /*mv */
#define SGTL5000_BIAS_CTRL_MASK 0x000e
#define SGTL5000_BIAS_CTRL_SHIFT 1
#define SGTL5000_BIAS_CTRL_WIDTH 3
#define SGTL5000_SMALL_POP 0x0001
/*
* SGTL5000_CHIP_MIC_CTRL
*/
#define SGTL5000_BIAS_R_MASK 0x0200
#define SGTL5000_BIAS_R_SHIFT 8
#define SGTL5000_BIAS_R_WIDTH 2
#define SGTL5000_BIAS_R_off 0x0
#define SGTL5000_BIAS_R_2K 0x1
#define SGTL5000_BIAS_R_4k 0x2
#define SGTL5000_BIAS_R_8k 0x3
#define SGTL5000_BIAS_VOLT_MASK 0x0070
#define SGTL5000_BIAS_VOLT_SHIFT 4
#define SGTL5000_BIAS_VOLT_WIDTH 3
#define SGTL5000_MIC_GAIN_MASK 0x0003
#define SGTL5000_MIC_GAIN_SHIFT 0
#define SGTL5000_MIC_GAIN_WIDTH 2
/*
* SGTL5000_CHIP_LINE_OUT_CTRL
*/
#define SGTL5000_LINE_OUT_CURRENT_MASK 0x0f00
#define SGTL5000_LINE_OUT_CURRENT_SHIFT 8
#define SGTL5000_LINE_OUT_CURRENT_WIDTH 4
#define SGTL5000_LINE_OUT_CURRENT_180u 0x0
#define SGTL5000_LINE_OUT_CURRENT_270u 0x1
#define SGTL5000_LINE_OUT_CURRENT_360u 0x3
#define SGTL5000_LINE_OUT_CURRENT_450u 0x7
#define SGTL5000_LINE_OUT_CURRENT_540u 0xf
#define SGTL5000_LINE_OUT_GND_MASK 0x003f
#define SGTL5000_LINE_OUT_GND_SHIFT 0
#define SGTL5000_LINE_OUT_GND_WIDTH 6
#define SGTL5000_LINE_OUT_GND_BASE 800 /* mv */
#define SGTL5000_LINE_OUT_GND_STP 25
#define SGTL5000_LINE_OUT_GND_MAX 0x23
/*
* SGTL5000_CHIP_LINE_OUT_VOL
*/
#define SGTL5000_LINE_OUT_VOL_RIGHT_MASK 0x1f00
#define SGTL5000_LINE_OUT_VOL_RIGHT_SHIFT 8
#define SGTL5000_LINE_OUT_VOL_RIGHT_WIDTH 5
#define SGTL5000_LINE_OUT_VOL_LEFT_MASK 0x001f
#define SGTL5000_LINE_OUT_VOL_LEFT_SHIFT 0
#define SGTL5000_LINE_OUT_VOL_LEFT_WIDTH 5
/*
* SGTL5000_CHIP_ANA_POWER
*/
#define SGTL5000_DAC_STEREO 0x4000
#define SGTL5000_LINREG_SIMPLE_POWERUP 0x2000
#define SGTL5000_STARTUP_POWERUP 0x1000
#define SGTL5000_VDDC_CHRGPMP_POWERUP 0x0800
#define SGTL5000_PLL_POWERUP 0x0400
#define SGTL5000_LINEREG_D_POWERUP 0x0200
#define SGTL5000_VCOAMP_POWERUP 0x0100
#define SGTL5000_VAG_POWERUP 0x0080
#define SGTL5000_ADC_STEREO 0x0040
#define SGTL5000_REFTOP_POWERUP 0x0020
#define SGTL5000_HP_POWERUP 0x0010
#define SGTL5000_DAC_POWERUP 0x0008
#define SGTL5000_CAPLESS_HP_POWERUP 0x0004
#define SGTL5000_ADC_POWERUP 0x0002
#define SGTL5000_LINE_OUT_POWERUP 0x0001
/*
* SGTL5000_CHIP_PLL_CTRL
*/
#define SGTL5000_PLL_INT_DIV_MASK 0xf800
#define SGTL5000_PLL_INT_DIV_SHIFT 11
#define SGTL5000_PLL_INT_DIV_WIDTH 5
#define SGTL5000_PLL_FRAC_DIV_MASK 0x0700
#define SGTL5000_PLL_FRAC_DIV_SHIFT 0
#define SGTL5000_PLL_FRAC_DIV_WIDTH 11
/*
* SGTL5000_CHIP_CLK_TOP_CTRL
*/
#define SGTL5000_INT_OSC_EN 0x0800
#define SGTL5000_INPUT_FREQ_DIV2 0x0008
/*
* SGTL5000_CHIP_ANA_STATUS
*/
#define SGTL5000_HP_LRSHORT 0x0200
#define SGTL5000_CAPLESS_SHORT 0x0100
#define SGTL5000_PLL_LOCKED 0x0010
/*
* SGTL5000_CHIP_SHORT_CTRL
*/
#define SGTL5000_LVLADJR_MASK 0x7000
#define SGTL5000_LVLADJR_SHIFT 12
#define SGTL5000_LVLADJR_WIDTH 3
#define SGTL5000_LVLADJL_MASK 0x0700
#define SGTL5000_LVLADJL_SHIFT 8
#define SGTL5000_LVLADJL_WIDTH 3
#define SGTL5000_LVLADJC_MASK 0x0070
#define SGTL5000_LVLADJC_SHIFT 4
#define SGTL5000_LVLADJC_WIDTH 3
#define SGTL5000_LR_SHORT_MOD_MASK 0x000c
#define SGTL5000_LR_SHORT_MOD_SHIFT 2
#define SGTL5000_LR_SHORT_MOD_WIDTH 2
#define SGTL5000_CM_SHORT_MOD_MASK 0x0003
#define SGTL5000_CM_SHORT_MOD_SHIFT 0
#define SGTL5000_CM_SHORT_MOD_WIDTH 2
/*
*SGTL5000_CHIP_ANA_TEST2
*/
#define SGTL5000_MONO_DAC 0x1000
/*
* SGTL5000_DAP_CTRL
*/
#define SGTL5000_DAP_MIX_EN 0x0010
#define SGTL5000_DAP_EN 0x0001
#define SGTL5000_SYSCLK 0x00
#define SGTL5000_LRCLK 0x01
#endif

949
sound/soc/codecs/sn95031.c Normal file
View file

@ -0,0 +1,949 @@
/*
* sn95031.c - TI sn95031 Codec driver
*
* Copyright (C) 2010 Intel Corp
* Author: Vinod Koul <vinod.koul@intel.com>
* Author: Harsha Priya <priya.harsha@intel.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; version 2 of the License.
*
* 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 pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <asm/intel_scu_ipc.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <sound/initval.h>
#include <sound/tlv.h>
#include <sound/jack.h>
#include "sn95031.h"
#define SN95031_RATES (SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_44100)
#define SN95031_FORMATS (SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE)
/* adc helper functions */
/* enables mic bias voltage */
static void sn95031_enable_mic_bias(struct snd_soc_codec *codec)
{
snd_soc_write(codec, SN95031_VAUD, BIT(2)|BIT(1)|BIT(0));
snd_soc_update_bits(codec, SN95031_MICBIAS, BIT(2), BIT(2));
}
/* Enable/Disable the ADC depending on the argument */
static void configure_adc(struct snd_soc_codec *sn95031_codec, int val)
{
int value = snd_soc_read(sn95031_codec, SN95031_ADC1CNTL1);
if (val) {
/* Enable and start the ADC */
value |= (SN95031_ADC_ENBL | SN95031_ADC_START);
value &= (~SN95031_ADC_NO_LOOP);
} else {
/* Just stop the ADC */
value &= (~SN95031_ADC_START);
}
snd_soc_write(sn95031_codec, SN95031_ADC1CNTL1, value);
}
/*
* finds an empty channel for conversion
* If the ADC is not enabled then start using 0th channel
* itself. Otherwise find an empty channel by looking for a
* channel in which the stopbit is set to 1. returns the index
* of the first free channel if succeeds or an error code.
*
* Context: can sleep
*
*/
static int find_free_channel(struct snd_soc_codec *sn95031_codec)
{
int ret = 0, i, value;
/* check whether ADC is enabled */
value = snd_soc_read(sn95031_codec, SN95031_ADC1CNTL1);
if ((value & SN95031_ADC_ENBL) == 0)
return 0;
/* ADC is already enabled; Looking for an empty channel */
for (i = 0; i < SN95031_ADC_CHANLS_MAX; i++) {
value = snd_soc_read(sn95031_codec,
SN95031_ADC_CHNL_START_ADDR + i);
if (value & SN95031_STOPBIT_MASK) {
ret = i;
break;
}
}
return (ret > SN95031_ADC_LOOP_MAX) ? (-EINVAL) : ret;
}
/* Initialize the ADC for reading micbias values. Can sleep. */
static int sn95031_initialize_adc(struct snd_soc_codec *sn95031_codec)
{
int base_addr, chnl_addr;
int value;
static int channel_index;
/* Index of the first channel in which the stop bit is set */
channel_index = find_free_channel(sn95031_codec);
if (channel_index < 0) {
pr_err("No free ADC channels");
return channel_index;
}
base_addr = SN95031_ADC_CHNL_START_ADDR + channel_index;
if (!(channel_index == 0 || channel_index == SN95031_ADC_LOOP_MAX)) {
/* Reset stop bit for channels other than 0 and 12 */
value = snd_soc_read(sn95031_codec, base_addr);
/* Set the stop bit to zero */
snd_soc_write(sn95031_codec, base_addr, value & 0xEF);
/* Index of the first free channel */
base_addr++;
channel_index++;
}
/* Since this is the last channel, set the stop bit
to 1 by ORing the DIE_SENSOR_CODE with 0x10 */
snd_soc_write(sn95031_codec, base_addr,
SN95031_AUDIO_DETECT_CODE | 0x10);
chnl_addr = SN95031_ADC_DATA_START_ADDR + 2 * channel_index;
pr_debug("mid_initialize : %x", chnl_addr);
configure_adc(sn95031_codec, 1);
return chnl_addr;
}
/* reads the ADC registers and gets the mic bias value in mV. */
static unsigned int sn95031_get_mic_bias(struct snd_soc_codec *codec)
{
u16 adc_adr = sn95031_initialize_adc(codec);
u16 adc_val1, adc_val2;
unsigned int mic_bias;
sn95031_enable_mic_bias(codec);
/* Enable the sound card for conversion before reading */
snd_soc_write(codec, SN95031_ADC1CNTL3, 0x05);
/* Re-toggle the RRDATARD bit */
snd_soc_write(codec, SN95031_ADC1CNTL3, 0x04);
/* Read the higher bits of data */
msleep(1000);
adc_val1 = snd_soc_read(codec, adc_adr);
adc_adr++;
adc_val2 = snd_soc_read(codec, adc_adr);
/* Adding lower two bits to the higher bits */
mic_bias = (adc_val1 << 2) + (adc_val2 & 3);
mic_bias = (mic_bias * SN95031_ADC_ONE_LSB_MULTIPLIER) / 1000;
pr_debug("mic bias = %dmV\n", mic_bias);
return mic_bias;
}
EXPORT_SYMBOL_GPL(sn95031_get_mic_bias);
/*end - adc helper functions */
static inline unsigned int sn95031_read(struct snd_soc_codec *codec,
unsigned int reg)
{
u8 value = 0;
int ret;
ret = intel_scu_ipc_ioread8(reg, &value);
if (ret)
pr_err("read of %x failed, err %d\n", reg, ret);
return value;
}
static inline int sn95031_write(struct snd_soc_codec *codec,
unsigned int reg, unsigned int value)
{
int ret;
ret = intel_scu_ipc_iowrite8(reg, value);
if (ret)
pr_err("write of %x failed, err %d\n", reg, ret);
return ret;
}
static int sn95031_set_vaud_bias(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
switch (level) {
case SND_SOC_BIAS_ON:
break;
case SND_SOC_BIAS_PREPARE:
if (codec->dapm.bias_level == SND_SOC_BIAS_STANDBY) {
pr_debug("vaud_bias powering up pll\n");
/* power up the pll */
snd_soc_write(codec, SN95031_AUDPLLCTRL, BIT(5));
/* enable pcm 2 */
snd_soc_update_bits(codec, SN95031_PCM2C2,
BIT(0), BIT(0));
}
break;
case SND_SOC_BIAS_STANDBY:
if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
pr_debug("vaud_bias power up rail\n");
/* power up the rail */
snd_soc_write(codec, SN95031_VAUD,
BIT(2)|BIT(1)|BIT(0));
msleep(1);
} else if (codec->dapm.bias_level == SND_SOC_BIAS_PREPARE) {
/* turn off pcm */
pr_debug("vaud_bias power dn pcm\n");
snd_soc_update_bits(codec, SN95031_PCM2C2, BIT(0), 0);
snd_soc_write(codec, SN95031_AUDPLLCTRL, 0);
}
break;
case SND_SOC_BIAS_OFF:
pr_debug("vaud_bias _OFF doing rail shutdown\n");
snd_soc_write(codec, SN95031_VAUD, BIT(3));
break;
}
codec->dapm.bias_level = level;
return 0;
}
static int sn95031_vhs_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
if (SND_SOC_DAPM_EVENT_ON(event)) {
pr_debug("VHS SND_SOC_DAPM_EVENT_ON doing rail startup now\n");
/* power up the rail */
snd_soc_write(w->codec, SN95031_VHSP, 0x3D);
snd_soc_write(w->codec, SN95031_VHSN, 0x3F);
msleep(1);
} else if (SND_SOC_DAPM_EVENT_OFF(event)) {
pr_debug("VHS SND_SOC_DAPM_EVENT_OFF doing rail shutdown\n");
snd_soc_write(w->codec, SN95031_VHSP, 0xC4);
snd_soc_write(w->codec, SN95031_VHSN, 0x04);
}
return 0;
}
static int sn95031_vihf_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
if (SND_SOC_DAPM_EVENT_ON(event)) {
pr_debug("VIHF SND_SOC_DAPM_EVENT_ON doing rail startup now\n");
/* power up the rail */
snd_soc_write(w->codec, SN95031_VIHF, 0x27);
msleep(1);
} else if (SND_SOC_DAPM_EVENT_OFF(event)) {
pr_debug("VIHF SND_SOC_DAPM_EVENT_OFF doing rail shutdown\n");
snd_soc_write(w->codec, SN95031_VIHF, 0x24);
}
return 0;
}
static int sn95031_dmic12_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *k, int event)
{
unsigned int ldo = 0, clk_dir = 0, data_dir = 0;
if (SND_SOC_DAPM_EVENT_ON(event)) {
ldo = BIT(5)|BIT(4);
clk_dir = BIT(0);
data_dir = BIT(7);
}
/* program DMIC LDO, clock and set clock */
snd_soc_update_bits(w->codec, SN95031_MICBIAS, BIT(5)|BIT(4), ldo);
snd_soc_update_bits(w->codec, SN95031_DMICBUF0123, BIT(0), clk_dir);
snd_soc_update_bits(w->codec, SN95031_DMICBUF0123, BIT(7), data_dir);
return 0;
}
static int sn95031_dmic34_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *k, int event)
{
unsigned int ldo = 0, clk_dir = 0, data_dir = 0;
if (SND_SOC_DAPM_EVENT_ON(event)) {
ldo = BIT(5)|BIT(4);
clk_dir = BIT(2);
data_dir = BIT(1);
}
/* program DMIC LDO, clock and set clock */
snd_soc_update_bits(w->codec, SN95031_MICBIAS, BIT(5)|BIT(4), ldo);
snd_soc_update_bits(w->codec, SN95031_DMICBUF0123, BIT(2), clk_dir);
snd_soc_update_bits(w->codec, SN95031_DMICBUF45, BIT(1), data_dir);
return 0;
}
static int sn95031_dmic56_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *k, int event)
{
unsigned int ldo = 0;
if (SND_SOC_DAPM_EVENT_ON(event))
ldo = BIT(7)|BIT(6);
/* program DMIC LDO */
snd_soc_update_bits(w->codec, SN95031_MICBIAS, BIT(7)|BIT(6), ldo);
return 0;
}
/* mux controls */
static const char *sn95031_mic_texts[] = { "AMIC", "LineIn" };
static const struct soc_enum sn95031_micl_enum =
SOC_ENUM_SINGLE(SN95031_ADCCONFIG, 1, 2, sn95031_mic_texts);
static const struct snd_kcontrol_new sn95031_micl_mux_control =
SOC_DAPM_ENUM("Route", sn95031_micl_enum);
static const struct soc_enum sn95031_micr_enum =
SOC_ENUM_SINGLE(SN95031_ADCCONFIG, 3, 2, sn95031_mic_texts);
static const struct snd_kcontrol_new sn95031_micr_mux_control =
SOC_DAPM_ENUM("Route", sn95031_micr_enum);
static const char *sn95031_input_texts[] = { "DMIC1", "DMIC2", "DMIC3",
"DMIC4", "DMIC5", "DMIC6",
"ADC Left", "ADC Right" };
static const struct soc_enum sn95031_input1_enum =
SOC_ENUM_SINGLE(SN95031_AUDIOMUX12, 0, 8, sn95031_input_texts);
static const struct snd_kcontrol_new sn95031_input1_mux_control =
SOC_DAPM_ENUM("Route", sn95031_input1_enum);
static const struct soc_enum sn95031_input2_enum =
SOC_ENUM_SINGLE(SN95031_AUDIOMUX12, 4, 8, sn95031_input_texts);
static const struct snd_kcontrol_new sn95031_input2_mux_control =
SOC_DAPM_ENUM("Route", sn95031_input2_enum);
static const struct soc_enum sn95031_input3_enum =
SOC_ENUM_SINGLE(SN95031_AUDIOMUX34, 0, 8, sn95031_input_texts);
static const struct snd_kcontrol_new sn95031_input3_mux_control =
SOC_DAPM_ENUM("Route", sn95031_input3_enum);
static const struct soc_enum sn95031_input4_enum =
SOC_ENUM_SINGLE(SN95031_AUDIOMUX34, 4, 8, sn95031_input_texts);
static const struct snd_kcontrol_new sn95031_input4_mux_control =
SOC_DAPM_ENUM("Route", sn95031_input4_enum);
/* capture path controls */
static const char *sn95031_micmode_text[] = {"Single Ended", "Differential"};
/* 0dB to 30dB in 10dB steps */
static const DECLARE_TLV_DB_SCALE(mic_tlv, 0, 10, 0);
static const struct soc_enum sn95031_micmode1_enum =
SOC_ENUM_SINGLE(SN95031_MICAMP1, 1, 2, sn95031_micmode_text);
static const struct soc_enum sn95031_micmode2_enum =
SOC_ENUM_SINGLE(SN95031_MICAMP2, 1, 2, sn95031_micmode_text);
static const char *sn95031_dmic_cfg_text[] = {"GPO", "DMIC"};
static const struct soc_enum sn95031_dmic12_cfg_enum =
SOC_ENUM_SINGLE(SN95031_DMICMUX, 0, 2, sn95031_dmic_cfg_text);
static const struct soc_enum sn95031_dmic34_cfg_enum =
SOC_ENUM_SINGLE(SN95031_DMICMUX, 1, 2, sn95031_dmic_cfg_text);
static const struct soc_enum sn95031_dmic56_cfg_enum =
SOC_ENUM_SINGLE(SN95031_DMICMUX, 2, 2, sn95031_dmic_cfg_text);
static const struct snd_kcontrol_new sn95031_snd_controls[] = {
SOC_ENUM("Mic1Mode Capture Route", sn95031_micmode1_enum),
SOC_ENUM("Mic2Mode Capture Route", sn95031_micmode2_enum),
SOC_ENUM("DMIC12 Capture Route", sn95031_dmic12_cfg_enum),
SOC_ENUM("DMIC34 Capture Route", sn95031_dmic34_cfg_enum),
SOC_ENUM("DMIC56 Capture Route", sn95031_dmic56_cfg_enum),
SOC_SINGLE_TLV("Mic1 Capture Volume", SN95031_MICAMP1,
2, 4, 0, mic_tlv),
SOC_SINGLE_TLV("Mic2 Capture Volume", SN95031_MICAMP2,
2, 4, 0, mic_tlv),
};
/* DAPM widgets */
static const struct snd_soc_dapm_widget sn95031_dapm_widgets[] = {
/* all end points mic, hs etc */
SND_SOC_DAPM_OUTPUT("HPOUTL"),
SND_SOC_DAPM_OUTPUT("HPOUTR"),
SND_SOC_DAPM_OUTPUT("EPOUT"),
SND_SOC_DAPM_OUTPUT("IHFOUTL"),
SND_SOC_DAPM_OUTPUT("IHFOUTR"),
SND_SOC_DAPM_OUTPUT("LINEOUTL"),
SND_SOC_DAPM_OUTPUT("LINEOUTR"),
SND_SOC_DAPM_OUTPUT("VIB1OUT"),
SND_SOC_DAPM_OUTPUT("VIB2OUT"),
SND_SOC_DAPM_INPUT("AMIC1"), /* headset mic */
SND_SOC_DAPM_INPUT("AMIC2"),
SND_SOC_DAPM_INPUT("DMIC1"),
SND_SOC_DAPM_INPUT("DMIC2"),
SND_SOC_DAPM_INPUT("DMIC3"),
SND_SOC_DAPM_INPUT("DMIC4"),
SND_SOC_DAPM_INPUT("DMIC5"),
SND_SOC_DAPM_INPUT("DMIC6"),
SND_SOC_DAPM_INPUT("LINEINL"),
SND_SOC_DAPM_INPUT("LINEINR"),
SND_SOC_DAPM_MICBIAS("AMIC1Bias", SN95031_MICBIAS, 2, 0),
SND_SOC_DAPM_MICBIAS("AMIC2Bias", SN95031_MICBIAS, 3, 0),
SND_SOC_DAPM_MICBIAS("DMIC12Bias", SN95031_DMICMUX, 3, 0),
SND_SOC_DAPM_MICBIAS("DMIC34Bias", SN95031_DMICMUX, 4, 0),
SND_SOC_DAPM_MICBIAS("DMIC56Bias", SN95031_DMICMUX, 5, 0),
SND_SOC_DAPM_SUPPLY("DMIC12supply", SN95031_DMICLK, 0, 0,
sn95031_dmic12_event,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_SUPPLY("DMIC34supply", SN95031_DMICLK, 1, 0,
sn95031_dmic34_event,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_SUPPLY("DMIC56supply", SN95031_DMICLK, 2, 0,
sn95031_dmic56_event,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_AIF_OUT("PCM_Out", "Capture", 0,
SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_SUPPLY("Headset Rail", SND_SOC_NOPM, 0, 0,
sn95031_vhs_event,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_SUPPLY("Speaker Rail", SND_SOC_NOPM, 0, 0,
sn95031_vihf_event,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
/* playback path driver enables */
SND_SOC_DAPM_PGA("Headset Left Playback",
SN95031_DRIVEREN, 0, 0, NULL, 0),
SND_SOC_DAPM_PGA("Headset Right Playback",
SN95031_DRIVEREN, 1, 0, NULL, 0),
SND_SOC_DAPM_PGA("Speaker Left Playback",
SN95031_DRIVEREN, 2, 0, NULL, 0),
SND_SOC_DAPM_PGA("Speaker Right Playback",
SN95031_DRIVEREN, 3, 0, NULL, 0),
SND_SOC_DAPM_PGA("Vibra1 Playback",
SN95031_DRIVEREN, 4, 0, NULL, 0),
SND_SOC_DAPM_PGA("Vibra2 Playback",
SN95031_DRIVEREN, 5, 0, NULL, 0),
SND_SOC_DAPM_PGA("Earpiece Playback",
SN95031_DRIVEREN, 6, 0, NULL, 0),
SND_SOC_DAPM_PGA("Lineout Left Playback",
SN95031_LOCTL, 0, 0, NULL, 0),
SND_SOC_DAPM_PGA("Lineout Right Playback",
SN95031_LOCTL, 4, 0, NULL, 0),
/* playback path filter enable */
SND_SOC_DAPM_PGA("Headset Left Filter",
SN95031_HSEPRXCTRL, 4, 0, NULL, 0),
SND_SOC_DAPM_PGA("Headset Right Filter",
SN95031_HSEPRXCTRL, 5, 0, NULL, 0),
SND_SOC_DAPM_PGA("Speaker Left Filter",
SN95031_IHFRXCTRL, 0, 0, NULL, 0),
SND_SOC_DAPM_PGA("Speaker Right Filter",
SN95031_IHFRXCTRL, 1, 0, NULL, 0),
/* DACs */
SND_SOC_DAPM_DAC("HSDAC Left", "Headset",
SN95031_DACCONFIG, 0, 0),
SND_SOC_DAPM_DAC("HSDAC Right", "Headset",
SN95031_DACCONFIG, 1, 0),
SND_SOC_DAPM_DAC("IHFDAC Left", "Speaker",
SN95031_DACCONFIG, 2, 0),
SND_SOC_DAPM_DAC("IHFDAC Right", "Speaker",
SN95031_DACCONFIG, 3, 0),
SND_SOC_DAPM_DAC("Vibra1 DAC", "Vibra1",
SN95031_VIB1C5, 1, 0),
SND_SOC_DAPM_DAC("Vibra2 DAC", "Vibra2",
SN95031_VIB2C5, 1, 0),
/* capture widgets */
SND_SOC_DAPM_PGA("LineIn Enable Left", SN95031_MICAMP1,
7, 0, NULL, 0),
SND_SOC_DAPM_PGA("LineIn Enable Right", SN95031_MICAMP2,
7, 0, NULL, 0),
SND_SOC_DAPM_PGA("MIC1 Enable", SN95031_MICAMP1, 0, 0, NULL, 0),
SND_SOC_DAPM_PGA("MIC2 Enable", SN95031_MICAMP2, 0, 0, NULL, 0),
SND_SOC_DAPM_PGA("TX1 Enable", SN95031_AUDIOTXEN, 2, 0, NULL, 0),
SND_SOC_DAPM_PGA("TX2 Enable", SN95031_AUDIOTXEN, 3, 0, NULL, 0),
SND_SOC_DAPM_PGA("TX3 Enable", SN95031_AUDIOTXEN, 4, 0, NULL, 0),
SND_SOC_DAPM_PGA("TX4 Enable", SN95031_AUDIOTXEN, 5, 0, NULL, 0),
/* ADC have null stream as they will be turned ON by TX path */
SND_SOC_DAPM_ADC("ADC Left", NULL,
SN95031_ADCCONFIG, 0, 0),
SND_SOC_DAPM_ADC("ADC Right", NULL,
SN95031_ADCCONFIG, 2, 0),
SND_SOC_DAPM_MUX("Mic_InputL Capture Route",
SND_SOC_NOPM, 0, 0, &sn95031_micl_mux_control),
SND_SOC_DAPM_MUX("Mic_InputR Capture Route",
SND_SOC_NOPM, 0, 0, &sn95031_micr_mux_control),
SND_SOC_DAPM_MUX("Txpath1 Capture Route",
SND_SOC_NOPM, 0, 0, &sn95031_input1_mux_control),
SND_SOC_DAPM_MUX("Txpath2 Capture Route",
SND_SOC_NOPM, 0, 0, &sn95031_input2_mux_control),
SND_SOC_DAPM_MUX("Txpath3 Capture Route",
SND_SOC_NOPM, 0, 0, &sn95031_input3_mux_control),
SND_SOC_DAPM_MUX("Txpath4 Capture Route",
SND_SOC_NOPM, 0, 0, &sn95031_input4_mux_control),
};
static const struct snd_soc_dapm_route sn95031_audio_map[] = {
/* headset and earpiece map */
{ "HPOUTL", NULL, "Headset Rail"},
{ "HPOUTR", NULL, "Headset Rail"},
{ "HPOUTL", NULL, "Headset Left Playback" },
{ "HPOUTR", NULL, "Headset Right Playback" },
{ "EPOUT", NULL, "Earpiece Playback" },
{ "Headset Left Playback", NULL, "Headset Left Filter"},
{ "Headset Right Playback", NULL, "Headset Right Filter"},
{ "Earpiece Playback", NULL, "Headset Left Filter"},
{ "Headset Left Filter", NULL, "HSDAC Left"},
{ "Headset Right Filter", NULL, "HSDAC Right"},
/* speaker map */
{ "IHFOUTL", NULL, "Speaker Rail"},
{ "IHFOUTR", NULL, "Speaker Rail"},
{ "IHFOUTL", "NULL", "Speaker Left Playback"},
{ "IHFOUTR", "NULL", "Speaker Right Playback"},
{ "Speaker Left Playback", NULL, "Speaker Left Filter"},
{ "Speaker Right Playback", NULL, "Speaker Right Filter"},
{ "Speaker Left Filter", NULL, "IHFDAC Left"},
{ "Speaker Right Filter", NULL, "IHFDAC Right"},
/* vibra map */
{ "VIB1OUT", NULL, "Vibra1 Playback"},
{ "Vibra1 Playback", NULL, "Vibra1 DAC"},
{ "VIB2OUT", NULL, "Vibra2 Playback"},
{ "Vibra2 Playback", NULL, "Vibra2 DAC"},
/* lineout */
{ "LINEOUTL", NULL, "Lineout Left Playback"},
{ "LINEOUTR", NULL, "Lineout Right Playback"},
{ "Lineout Left Playback", NULL, "Headset Left Filter"},
{ "Lineout Left Playback", NULL, "Speaker Left Filter"},
{ "Lineout Left Playback", NULL, "Vibra1 DAC"},
{ "Lineout Right Playback", NULL, "Headset Right Filter"},
{ "Lineout Right Playback", NULL, "Speaker Right Filter"},
{ "Lineout Right Playback", NULL, "Vibra2 DAC"},
/* Headset (AMIC1) mic */
{ "AMIC1Bias", NULL, "AMIC1"},
{ "MIC1 Enable", NULL, "AMIC1Bias"},
{ "Mic_InputL Capture Route", "AMIC", "MIC1 Enable"},
/* AMIC2 */
{ "AMIC2Bias", NULL, "AMIC2"},
{ "MIC2 Enable", NULL, "AMIC2Bias"},
{ "Mic_InputR Capture Route", "AMIC", "MIC2 Enable"},
/* Linein */
{ "LineIn Enable Left", NULL, "LINEINL"},
{ "LineIn Enable Right", NULL, "LINEINR"},
{ "Mic_InputL Capture Route", "LineIn", "LineIn Enable Left"},
{ "Mic_InputR Capture Route", "LineIn", "LineIn Enable Right"},
/* ADC connection */
{ "ADC Left", NULL, "Mic_InputL Capture Route"},
{ "ADC Right", NULL, "Mic_InputR Capture Route"},
/*DMIC connections */
{ "DMIC1", NULL, "DMIC12supply"},
{ "DMIC2", NULL, "DMIC12supply"},
{ "DMIC3", NULL, "DMIC34supply"},
{ "DMIC4", NULL, "DMIC34supply"},
{ "DMIC5", NULL, "DMIC56supply"},
{ "DMIC6", NULL, "DMIC56supply"},
{ "DMIC12Bias", NULL, "DMIC1"},
{ "DMIC12Bias", NULL, "DMIC2"},
{ "DMIC34Bias", NULL, "DMIC3"},
{ "DMIC34Bias", NULL, "DMIC4"},
{ "DMIC56Bias", NULL, "DMIC5"},
{ "DMIC56Bias", NULL, "DMIC6"},
/*TX path inputs*/
{ "Txpath1 Capture Route", "ADC Left", "ADC Left"},
{ "Txpath2 Capture Route", "ADC Left", "ADC Left"},
{ "Txpath3 Capture Route", "ADC Left", "ADC Left"},
{ "Txpath4 Capture Route", "ADC Left", "ADC Left"},
{ "Txpath1 Capture Route", "ADC Right", "ADC Right"},
{ "Txpath2 Capture Route", "ADC Right", "ADC Right"},
{ "Txpath3 Capture Route", "ADC Right", "ADC Right"},
{ "Txpath4 Capture Route", "ADC Right", "ADC Right"},
{ "Txpath1 Capture Route", "DMIC1", "DMIC1"},
{ "Txpath2 Capture Route", "DMIC1", "DMIC1"},
{ "Txpath3 Capture Route", "DMIC1", "DMIC1"},
{ "Txpath4 Capture Route", "DMIC1", "DMIC1"},
{ "Txpath1 Capture Route", "DMIC2", "DMIC2"},
{ "Txpath2 Capture Route", "DMIC2", "DMIC2"},
{ "Txpath3 Capture Route", "DMIC2", "DMIC2"},
{ "Txpath4 Capture Route", "DMIC2", "DMIC2"},
{ "Txpath1 Capture Route", "DMIC3", "DMIC3"},
{ "Txpath2 Capture Route", "DMIC3", "DMIC3"},
{ "Txpath3 Capture Route", "DMIC3", "DMIC3"},
{ "Txpath4 Capture Route", "DMIC3", "DMIC3"},
{ "Txpath1 Capture Route", "DMIC4", "DMIC4"},
{ "Txpath2 Capture Route", "DMIC4", "DMIC4"},
{ "Txpath3 Capture Route", "DMIC4", "DMIC4"},
{ "Txpath4 Capture Route", "DMIC4", "DMIC4"},
{ "Txpath1 Capture Route", "DMIC5", "DMIC5"},
{ "Txpath2 Capture Route", "DMIC5", "DMIC5"},
{ "Txpath3 Capture Route", "DMIC5", "DMIC5"},
{ "Txpath4 Capture Route", "DMIC5", "DMIC5"},
{ "Txpath1 Capture Route", "DMIC6", "DMIC6"},
{ "Txpath2 Capture Route", "DMIC6", "DMIC6"},
{ "Txpath3 Capture Route", "DMIC6", "DMIC6"},
{ "Txpath4 Capture Route", "DMIC6", "DMIC6"},
/* tx path */
{ "TX1 Enable", NULL, "Txpath1 Capture Route"},
{ "TX2 Enable", NULL, "Txpath2 Capture Route"},
{ "TX3 Enable", NULL, "Txpath3 Capture Route"},
{ "TX4 Enable", NULL, "Txpath4 Capture Route"},
{ "PCM_Out", NULL, "TX1 Enable"},
{ "PCM_Out", NULL, "TX2 Enable"},
{ "PCM_Out", NULL, "TX3 Enable"},
{ "PCM_Out", NULL, "TX4 Enable"},
};
/* speaker and headset mutes, for audio pops and clicks */
static int sn95031_pcm_hs_mute(struct snd_soc_dai *dai, int mute)
{
snd_soc_update_bits(dai->codec,
SN95031_HSLVOLCTRL, BIT(7), (!mute << 7));
snd_soc_update_bits(dai->codec,
SN95031_HSRVOLCTRL, BIT(7), (!mute << 7));
return 0;
}
static int sn95031_pcm_spkr_mute(struct snd_soc_dai *dai, int mute)
{
snd_soc_update_bits(dai->codec,
SN95031_IHFLVOLCTRL, BIT(7), (!mute << 7));
snd_soc_update_bits(dai->codec,
SN95031_IHFRVOLCTRL, BIT(7), (!mute << 7));
return 0;
}
int sn95031_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
{
unsigned int format, rate;
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE:
format = BIT(4)|BIT(5);
break;
case SNDRV_PCM_FORMAT_S24_LE:
format = 0;
break;
default:
return -EINVAL;
}
snd_soc_update_bits(dai->codec, SN95031_PCM2C2,
BIT(4)|BIT(5), format);
switch (params_rate(params)) {
case 48000:
pr_debug("RATE_48000\n");
rate = 0;
break;
case 44100:
pr_debug("RATE_44100\n");
rate = BIT(7);
break;
default:
pr_err("ERR rate %d\n", params_rate(params));
return -EINVAL;
}
snd_soc_update_bits(dai->codec, SN95031_PCM1C1, BIT(7), rate);
return 0;
}
/* Codec DAI section */
static struct snd_soc_dai_ops sn95031_headset_dai_ops = {
.digital_mute = sn95031_pcm_hs_mute,
.hw_params = sn95031_pcm_hw_params,
};
static struct snd_soc_dai_ops sn95031_speaker_dai_ops = {
.digital_mute = sn95031_pcm_spkr_mute,
.hw_params = sn95031_pcm_hw_params,
};
static struct snd_soc_dai_ops sn95031_vib1_dai_ops = {
.hw_params = sn95031_pcm_hw_params,
};
static struct snd_soc_dai_ops sn95031_vib2_dai_ops = {
.hw_params = sn95031_pcm_hw_params,
};
struct snd_soc_dai_driver sn95031_dais[] = {
{
.name = "SN95031 Headset",
.playback = {
.stream_name = "Headset",
.channels_min = 2,
.channels_max = 2,
.rates = SN95031_RATES,
.formats = SN95031_FORMATS,
},
.capture = {
.stream_name = "Capture",
.channels_min = 1,
.channels_max = 5,
.rates = SN95031_RATES,
.formats = SN95031_FORMATS,
},
.ops = &sn95031_headset_dai_ops,
},
{ .name = "SN95031 Speaker",
.playback = {
.stream_name = "Speaker",
.channels_min = 2,
.channels_max = 2,
.rates = SN95031_RATES,
.formats = SN95031_FORMATS,
},
.ops = &sn95031_speaker_dai_ops,
},
{ .name = "SN95031 Vibra1",
.playback = {
.stream_name = "Vibra1",
.channels_min = 1,
.channels_max = 1,
.rates = SN95031_RATES,
.formats = SN95031_FORMATS,
},
.ops = &sn95031_vib1_dai_ops,
},
{ .name = "SN95031 Vibra2",
.playback = {
.stream_name = "Vibra2",
.channels_min = 1,
.channels_max = 1,
.rates = SN95031_RATES,
.formats = SN95031_FORMATS,
},
.ops = &sn95031_vib2_dai_ops,
},
};
static inline void sn95031_disable_jack_btn(struct snd_soc_codec *codec)
{
snd_soc_write(codec, SN95031_BTNCTRL2, 0x00);
}
static inline void sn95031_enable_jack_btn(struct snd_soc_codec *codec)
{
snd_soc_write(codec, SN95031_BTNCTRL1, 0x77);
snd_soc_write(codec, SN95031_BTNCTRL2, 0x01);
}
static int sn95031_get_headset_state(struct snd_soc_jack *mfld_jack)
{
int micbias = sn95031_get_mic_bias(mfld_jack->codec);
int jack_type = snd_soc_jack_get_type(mfld_jack, micbias);
pr_debug("jack type detected = %d\n", jack_type);
if (jack_type == SND_JACK_HEADSET)
sn95031_enable_jack_btn(mfld_jack->codec);
return jack_type;
}
void sn95031_jack_detection(struct mfld_jack_data *jack_data)
{
unsigned int status;
unsigned int mask = SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_HEADSET;
pr_debug("interrupt id read in sram = 0x%x\n", jack_data->intr_id);
if (jack_data->intr_id & 0x1) {
pr_debug("short_push detected\n");
status = SND_JACK_HEADSET | SND_JACK_BTN_0;
} else if (jack_data->intr_id & 0x2) {
pr_debug("long_push detected\n");
status = SND_JACK_HEADSET | SND_JACK_BTN_1;
} else if (jack_data->intr_id & 0x4) {
pr_debug("headset or headphones inserted\n");
status = sn95031_get_headset_state(jack_data->mfld_jack);
} else if (jack_data->intr_id & 0x8) {
pr_debug("headset or headphones removed\n");
status = 0;
sn95031_disable_jack_btn(jack_data->mfld_jack->codec);
} else {
pr_err("unidentified interrupt\n");
return;
}
snd_soc_jack_report(jack_data->mfld_jack, status, mask);
/*button pressed and released so we send explicit button release */
if ((status & SND_JACK_BTN_0) | (status & SND_JACK_BTN_1))
snd_soc_jack_report(jack_data->mfld_jack,
SND_JACK_HEADSET, mask);
}
EXPORT_SYMBOL_GPL(sn95031_jack_detection);
/* codec registration */
static int sn95031_codec_probe(struct snd_soc_codec *codec)
{
int ret;
pr_debug("codec_probe called\n");
codec->dapm.bias_level = SND_SOC_BIAS_OFF;
codec->dapm.idle_bias_off = 1;
/* PCM interface config
* This sets the pcm rx slot conguration to max 6 slots
* for max 4 dais (2 stereo and 2 mono)
*/
snd_soc_write(codec, SN95031_PCM2RXSLOT01, 0x10);
snd_soc_write(codec, SN95031_PCM2RXSLOT23, 0x32);
snd_soc_write(codec, SN95031_PCM2RXSLOT45, 0x54);
snd_soc_write(codec, SN95031_PCM2TXSLOT01, 0x10);
snd_soc_write(codec, SN95031_PCM2TXSLOT23, 0x32);
/* pcm port setting
* This sets the pcm port to slave and clock at 19.2Mhz which
* can support 6slots, sampling rate set per stream in hw-params
*/
snd_soc_write(codec, SN95031_PCM1C1, 0x00);
snd_soc_write(codec, SN95031_PCM2C1, 0x01);
snd_soc_write(codec, SN95031_PCM2C2, 0x0A);
snd_soc_write(codec, SN95031_HSMIXER, BIT(0)|BIT(4));
/* vendor vibra workround, the vibras are muted by
* custom register so unmute them
*/
snd_soc_write(codec, SN95031_SSR5, 0x80);
snd_soc_write(codec, SN95031_SSR6, 0x80);
snd_soc_write(codec, SN95031_VIB1C5, 0x00);
snd_soc_write(codec, SN95031_VIB2C5, 0x00);
/* configure vibras for pcm port */
snd_soc_write(codec, SN95031_VIB1C3, 0x00);
snd_soc_write(codec, SN95031_VIB2C3, 0x00);
/* soft mute ramp time */
snd_soc_write(codec, SN95031_SOFTMUTE, 0x3);
/* fix the initial volume at 1dB,
* default in +9dB,
* 1dB give optimal swing on DAC, amps
*/
snd_soc_write(codec, SN95031_HSLVOLCTRL, 0x08);
snd_soc_write(codec, SN95031_HSRVOLCTRL, 0x08);
snd_soc_write(codec, SN95031_IHFLVOLCTRL, 0x08);
snd_soc_write(codec, SN95031_IHFRVOLCTRL, 0x08);
/* dac mode and lineout workaround */
snd_soc_write(codec, SN95031_SSR2, 0x10);
snd_soc_write(codec, SN95031_SSR3, 0x40);
snd_soc_add_controls(codec, sn95031_snd_controls,
ARRAY_SIZE(sn95031_snd_controls));
ret = snd_soc_dapm_new_controls(&codec->dapm, sn95031_dapm_widgets,
ARRAY_SIZE(sn95031_dapm_widgets));
if (ret)
pr_err("soc_dapm_new_control failed %d", ret);
ret = snd_soc_dapm_add_routes(&codec->dapm, sn95031_audio_map,
ARRAY_SIZE(sn95031_audio_map));
if (ret)
pr_err("soc_dapm_add_routes failed %d", ret);
return ret;
}
static int sn95031_codec_remove(struct snd_soc_codec *codec)
{
pr_debug("codec_remove called\n");
sn95031_set_vaud_bias(codec, SND_SOC_BIAS_OFF);
return 0;
}
struct snd_soc_codec_driver sn95031_codec = {
.probe = sn95031_codec_probe,
.remove = sn95031_codec_remove,
.read = sn95031_read,
.write = sn95031_write,
.set_bias_level = sn95031_set_vaud_bias,
};
static int __devinit sn95031_device_probe(struct platform_device *pdev)
{
pr_debug("codec device probe called for %s\n", dev_name(&pdev->dev));
return snd_soc_register_codec(&pdev->dev, &sn95031_codec,
sn95031_dais, ARRAY_SIZE(sn95031_dais));
}
static int __devexit sn95031_device_remove(struct platform_device *pdev)
{
pr_debug("codec device remove called\n");
snd_soc_unregister_codec(&pdev->dev);
return 0;
}
static struct platform_driver sn95031_codec_driver = {
.driver = {
.name = "sn95031",
.owner = THIS_MODULE,
},
.probe = sn95031_device_probe,
.remove = sn95031_device_remove,
};
static int __init sn95031_init(void)
{
pr_debug("driver init called\n");
return platform_driver_register(&sn95031_codec_driver);
}
module_init(sn95031_init);
static void __exit sn95031_exit(void)
{
pr_debug("driver exit called\n");
platform_driver_unregister(&sn95031_codec_driver);
}
module_exit(sn95031_exit);
MODULE_DESCRIPTION("ASoC TI SN95031 codec driver");
MODULE_AUTHOR("Vinod Koul <vinod.koul@intel.com>");
MODULE_AUTHOR("Harsha Priya <priya.harsha@intel.com>");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:sn95031");

132
sound/soc/codecs/sn95031.h Normal file
View file

@ -0,0 +1,132 @@
/*
* sn95031.h - TI sn95031 Codec driver
*
* Copyright (C) 2010 Intel Corp
* Author: Vinod Koul <vinod.koul@intel.com>
* Author: Harsha Priya <priya.harsha@intel.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; version 2 of the License.
*
* 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 _SN95031_H
#define _SN95031_H
/*register map*/
#define SN95031_VAUD 0xDB
#define SN95031_VHSP 0xDC
#define SN95031_VHSN 0xDD
#define SN95031_VIHF 0xC9
#define SN95031_AUDPLLCTRL 0x240
#define SN95031_DMICBUF0123 0x241
#define SN95031_DMICBUF45 0x242
#define SN95031_DMICGPO 0x244
#define SN95031_DMICMUX 0x245
#define SN95031_DMICLK 0x246
#define SN95031_MICBIAS 0x247
#define SN95031_ADCCONFIG 0x248
#define SN95031_MICAMP1 0x249
#define SN95031_MICAMP2 0x24A
#define SN95031_NOISEMUX 0x24B
#define SN95031_AUDIOMUX12 0x24C
#define SN95031_AUDIOMUX34 0x24D
#define SN95031_AUDIOSINC 0x24E
#define SN95031_AUDIOTXEN 0x24F
#define SN95031_HSEPRXCTRL 0x250
#define SN95031_IHFRXCTRL 0x251
#define SN95031_HSMIXER 0x256
#define SN95031_DACCONFIG 0x257
#define SN95031_SOFTMUTE 0x258
#define SN95031_HSLVOLCTRL 0x259
#define SN95031_HSRVOLCTRL 0x25A
#define SN95031_IHFLVOLCTRL 0x25B
#define SN95031_IHFRVOLCTRL 0x25C
#define SN95031_DRIVEREN 0x25D
#define SN95031_LOCTL 0x25E
#define SN95031_VIB1C1 0x25F
#define SN95031_VIB1C2 0x260
#define SN95031_VIB1C3 0x261
#define SN95031_VIB1SPIPCM1 0x262
#define SN95031_VIB1SPIPCM2 0x263
#define SN95031_VIB1C5 0x264
#define SN95031_VIB2C1 0x265
#define SN95031_VIB2C2 0x266
#define SN95031_VIB2C3 0x267
#define SN95031_VIB2SPIPCM1 0x268
#define SN95031_VIB2SPIPCM2 0x269
#define SN95031_VIB2C5 0x26A
#define SN95031_BTNCTRL1 0x26B
#define SN95031_BTNCTRL2 0x26C
#define SN95031_PCM1TXSLOT01 0x26D
#define SN95031_PCM1TXSLOT23 0x26E
#define SN95031_PCM1TXSLOT45 0x26F
#define SN95031_PCM1RXSLOT0_3 0x270
#define SN95031_PCM1RXSLOT45 0x271
#define SN95031_PCM2TXSLOT01 0x272
#define SN95031_PCM2TXSLOT23 0x273
#define SN95031_PCM2TXSLOT45 0x274
#define SN95031_PCM2RXSLOT01 0x275
#define SN95031_PCM2RXSLOT23 0x276
#define SN95031_PCM2RXSLOT45 0x277
#define SN95031_PCM1C1 0x278
#define SN95031_PCM1C2 0x279
#define SN95031_PCM1C3 0x27A
#define SN95031_PCM2C1 0x27B
#define SN95031_PCM2C2 0x27C
/*end codec register defn*/
/*vendor defn these are not part of avp*/
#define SN95031_SSR2 0x381
#define SN95031_SSR3 0x382
#define SN95031_SSR5 0x384
#define SN95031_SSR6 0x385
/* ADC registers */
#define SN95031_ADC1CNTL1 0x1C0
#define SN95031_ADC_ENBL 0x10
#define SN95031_ADC_START 0x08
#define SN95031_ADC1CNTL3 0x1C2
#define SN95031_ADCTHERM_ENBL 0x04
#define SN95031_ADCRRDATA_ENBL 0x05
#define SN95031_STOPBIT_MASK 16
#define SN95031_ADCTHERM_MASK 4
#define SN95031_ADC_CHANLS_MAX 15 /* Number of ADC channels */
#define SN95031_ADC_LOOP_MAX (SN95031_ADC_CHANLS_MAX - 1)
#define SN95031_ADC_NO_LOOP 0x07
#define SN95031_AUDIO_GPIO_CTRL 0x070
/* ADC channel code values */
#define SN95031_AUDIO_DETECT_CODE 0x06
/* ADC base addresses */
#define SN95031_ADC_CHNL_START_ADDR 0x1C5 /* increments by 1 */
#define SN95031_ADC_DATA_START_ADDR 0x1D4 /* increments by 2 */
/* multipier to convert to mV */
#define SN95031_ADC_ONE_LSB_MULTIPLIER 2346
struct mfld_jack_data {
int intr_id;
int micbias_vol;
struct snd_soc_jack *mfld_jack;
};
extern void sn95031_jack_detection(struct mfld_jack_data *jack_data);
#endif

View file

@ -0,0 +1,794 @@
/*
* linux/sound/soc/codecs/tlv320aic32x4.c
*
* Copyright 2011 Vista Silicon S.L.
*
* Author: Javier Martin <javier.martin@vista-silicon.com>
*
* Based on sound/soc/codecs/wm8974 and TI driver for kernel 2.6.27.
*
* 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., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/i2c.h>
#include <linux/platform_device.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <sound/tlv320aic32x4.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <sound/initval.h>
#include <sound/tlv.h>
#include "tlv320aic32x4.h"
struct aic32x4_rate_divs {
u32 mclk;
u32 rate;
u8 p_val;
u8 pll_j;
u16 pll_d;
u16 dosr;
u8 ndac;
u8 mdac;
u8 aosr;
u8 nadc;
u8 madc;
u8 blck_N;
};
struct aic32x4_priv {
u32 sysclk;
s32 master;
u8 page_no;
void *control_data;
u32 power_cfg;
u32 micpga_routing;
bool swapdacs;
};
/* 0dB min, 1dB steps */
static DECLARE_TLV_DB_SCALE(tlv_step_1, 0, 100, 0);
/* 0dB min, 0.5dB steps */
static DECLARE_TLV_DB_SCALE(tlv_step_0_5, 0, 50, 0);
static const struct snd_kcontrol_new aic32x4_snd_controls[] = {
SOC_DOUBLE_R_TLV("PCM Playback Volume", AIC32X4_LDACVOL,
AIC32X4_RDACVOL, 0, 0x30, 0, tlv_step_0_5),
SOC_DOUBLE_R_TLV("HP Driver Gain Volume", AIC32X4_HPLGAIN,
AIC32X4_HPRGAIN, 0, 0x1D, 0, tlv_step_1),
SOC_DOUBLE_R_TLV("LO Driver Gain Volume", AIC32X4_LOLGAIN,
AIC32X4_LORGAIN, 0, 0x1D, 0, tlv_step_1),
SOC_DOUBLE_R("HP DAC Playback Switch", AIC32X4_HPLGAIN,
AIC32X4_HPRGAIN, 6, 0x01, 1),
SOC_DOUBLE_R("LO DAC Playback Switch", AIC32X4_LOLGAIN,
AIC32X4_LORGAIN, 6, 0x01, 1),
SOC_DOUBLE_R("Mic PGA Switch", AIC32X4_LMICPGAVOL,
AIC32X4_RMICPGAVOL, 7, 0x01, 1),
SOC_SINGLE("ADCFGA Left Mute Switch", AIC32X4_ADCFGA, 7, 1, 0),
SOC_SINGLE("ADCFGA Right Mute Switch", AIC32X4_ADCFGA, 3, 1, 0),
SOC_DOUBLE_R_TLV("ADC Level Volume", AIC32X4_LADCVOL,
AIC32X4_RADCVOL, 0, 0x28, 0, tlv_step_0_5),
SOC_DOUBLE_R_TLV("PGA Level Volume", AIC32X4_LMICPGAVOL,
AIC32X4_RMICPGAVOL, 0, 0x5f, 0, tlv_step_0_5),
SOC_SINGLE("Auto-mute Switch", AIC32X4_DACMUTE, 4, 7, 0),
SOC_SINGLE("AGC Left Switch", AIC32X4_LAGC1, 7, 1, 0),
SOC_SINGLE("AGC Right Switch", AIC32X4_RAGC1, 7, 1, 0),
SOC_DOUBLE_R("AGC Target Level", AIC32X4_LAGC1, AIC32X4_RAGC1,
4, 0x07, 0),
SOC_DOUBLE_R("AGC Gain Hysteresis", AIC32X4_LAGC1, AIC32X4_RAGC1,
0, 0x03, 0),
SOC_DOUBLE_R("AGC Hysteresis", AIC32X4_LAGC2, AIC32X4_RAGC2,
6, 0x03, 0),
SOC_DOUBLE_R("AGC Noise Threshold", AIC32X4_LAGC2, AIC32X4_RAGC2,
1, 0x1F, 0),
SOC_DOUBLE_R("AGC Max PGA", AIC32X4_LAGC3, AIC32X4_RAGC3,
0, 0x7F, 0),
SOC_DOUBLE_R("AGC Attack Time", AIC32X4_LAGC4, AIC32X4_RAGC4,
3, 0x1F, 0),
SOC_DOUBLE_R("AGC Decay Time", AIC32X4_LAGC5, AIC32X4_RAGC5,
3, 0x1F, 0),
SOC_DOUBLE_R("AGC Noise Debounce", AIC32X4_LAGC6, AIC32X4_RAGC6,
0, 0x1F, 0),
SOC_DOUBLE_R("AGC Signal Debounce", AIC32X4_LAGC7, AIC32X4_RAGC7,
0, 0x0F, 0),
};
static const struct aic32x4_rate_divs aic32x4_divs[] = {
/* 8k rate */
{AIC32X4_FREQ_12000000, 8000, 1, 7, 6800, 768, 5, 3, 128, 5, 18, 24},
{AIC32X4_FREQ_24000000, 8000, 2, 7, 6800, 768, 15, 1, 64, 45, 4, 24},
{AIC32X4_FREQ_25000000, 8000, 2, 7, 3728, 768, 15, 1, 64, 45, 4, 24},
/* 11.025k rate */
{AIC32X4_FREQ_12000000, 11025, 1, 7, 5264, 512, 8, 2, 128, 8, 8, 16},
{AIC32X4_FREQ_24000000, 11025, 2, 7, 5264, 512, 16, 1, 64, 32, 4, 16},
/* 16k rate */
{AIC32X4_FREQ_12000000, 16000, 1, 7, 6800, 384, 5, 3, 128, 5, 9, 12},
{AIC32X4_FREQ_24000000, 16000, 2, 7, 6800, 384, 15, 1, 64, 18, 5, 12},
{AIC32X4_FREQ_25000000, 16000, 2, 7, 3728, 384, 15, 1, 64, 18, 5, 12},
/* 22.05k rate */
{AIC32X4_FREQ_12000000, 22050, 1, 7, 5264, 256, 4, 4, 128, 4, 8, 8},
{AIC32X4_FREQ_24000000, 22050, 2, 7, 5264, 256, 16, 1, 64, 16, 4, 8},
{AIC32X4_FREQ_25000000, 22050, 2, 7, 2253, 256, 16, 1, 64, 16, 4, 8},
/* 32k rate */
{AIC32X4_FREQ_12000000, 32000, 1, 7, 1680, 192, 2, 7, 64, 2, 21, 6},
{AIC32X4_FREQ_24000000, 32000, 2, 7, 1680, 192, 7, 2, 64, 7, 6, 6},
/* 44.1k rate */
{AIC32X4_FREQ_12000000, 44100, 1, 7, 5264, 128, 2, 8, 128, 2, 8, 4},
{AIC32X4_FREQ_24000000, 44100, 2, 7, 5264, 128, 8, 2, 64, 8, 4, 4},
{AIC32X4_FREQ_25000000, 44100, 2, 7, 2253, 128, 8, 2, 64, 8, 4, 4},
/* 48k rate */
{AIC32X4_FREQ_12000000, 48000, 1, 8, 1920, 128, 2, 8, 128, 2, 8, 4},
{AIC32X4_FREQ_24000000, 48000, 2, 8, 1920, 128, 8, 2, 64, 8, 4, 4},
{AIC32X4_FREQ_25000000, 48000, 2, 7, 8643, 128, 8, 2, 64, 8, 4, 4}
};
static const struct snd_kcontrol_new hpl_output_mixer_controls[] = {
SOC_DAPM_SINGLE("L_DAC Switch", AIC32X4_HPLROUTE, 3, 1, 0),
SOC_DAPM_SINGLE("IN1_L Switch", AIC32X4_HPLROUTE, 2, 1, 0),
};
static const struct snd_kcontrol_new hpr_output_mixer_controls[] = {
SOC_DAPM_SINGLE("R_DAC Switch", AIC32X4_HPRROUTE, 3, 1, 0),
SOC_DAPM_SINGLE("IN1_R Switch", AIC32X4_HPRROUTE, 2, 1, 0),
};
static const struct snd_kcontrol_new lol_output_mixer_controls[] = {
SOC_DAPM_SINGLE("L_DAC Switch", AIC32X4_LOLROUTE, 3, 1, 0),
};
static const struct snd_kcontrol_new lor_output_mixer_controls[] = {
SOC_DAPM_SINGLE("R_DAC Switch", AIC32X4_LORROUTE, 3, 1, 0),
};
static const struct snd_kcontrol_new left_input_mixer_controls[] = {
SOC_DAPM_SINGLE("IN1_L P Switch", AIC32X4_LMICPGAPIN, 6, 1, 0),
SOC_DAPM_SINGLE("IN2_L P Switch", AIC32X4_LMICPGAPIN, 4, 1, 0),
SOC_DAPM_SINGLE("IN3_L P Switch", AIC32X4_LMICPGAPIN, 2, 1, 0),
};
static const struct snd_kcontrol_new right_input_mixer_controls[] = {
SOC_DAPM_SINGLE("IN1_R P Switch", AIC32X4_RMICPGAPIN, 6, 1, 0),
SOC_DAPM_SINGLE("IN2_R P Switch", AIC32X4_RMICPGAPIN, 4, 1, 0),
SOC_DAPM_SINGLE("IN3_R P Switch", AIC32X4_RMICPGAPIN, 2, 1, 0),
};
static const struct snd_soc_dapm_widget aic32x4_dapm_widgets[] = {
SND_SOC_DAPM_DAC("Left DAC", "Left Playback", AIC32X4_DACSETUP, 7, 0),
SND_SOC_DAPM_MIXER("HPL Output Mixer", SND_SOC_NOPM, 0, 0,
&hpl_output_mixer_controls[0],
ARRAY_SIZE(hpl_output_mixer_controls)),
SND_SOC_DAPM_PGA("HPL Power", AIC32X4_OUTPWRCTL, 5, 0, NULL, 0),
SND_SOC_DAPM_MIXER("LOL Output Mixer", SND_SOC_NOPM, 0, 0,
&lol_output_mixer_controls[0],
ARRAY_SIZE(lol_output_mixer_controls)),
SND_SOC_DAPM_PGA("LOL Power", AIC32X4_OUTPWRCTL, 3, 0, NULL, 0),
SND_SOC_DAPM_DAC("Right DAC", "Right Playback", AIC32X4_DACSETUP, 6, 0),
SND_SOC_DAPM_MIXER("HPR Output Mixer", SND_SOC_NOPM, 0, 0,
&hpr_output_mixer_controls[0],
ARRAY_SIZE(hpr_output_mixer_controls)),
SND_SOC_DAPM_PGA("HPR Power", AIC32X4_OUTPWRCTL, 4, 0, NULL, 0),
SND_SOC_DAPM_MIXER("LOR Output Mixer", SND_SOC_NOPM, 0, 0,
&lor_output_mixer_controls[0],
ARRAY_SIZE(lor_output_mixer_controls)),
SND_SOC_DAPM_PGA("LOR Power", AIC32X4_OUTPWRCTL, 2, 0, NULL, 0),
SND_SOC_DAPM_MIXER("Left Input Mixer", SND_SOC_NOPM, 0, 0,
&left_input_mixer_controls[0],
ARRAY_SIZE(left_input_mixer_controls)),
SND_SOC_DAPM_MIXER("Right Input Mixer", SND_SOC_NOPM, 0, 0,
&right_input_mixer_controls[0],
ARRAY_SIZE(right_input_mixer_controls)),
SND_SOC_DAPM_ADC("Left ADC", "Left Capture", AIC32X4_ADCSETUP, 7, 0),
SND_SOC_DAPM_ADC("Right ADC", "Right Capture", AIC32X4_ADCSETUP, 6, 0),
SND_SOC_DAPM_MICBIAS("Mic Bias", AIC32X4_MICBIAS, 6, 0),
SND_SOC_DAPM_OUTPUT("HPL"),
SND_SOC_DAPM_OUTPUT("HPR"),
SND_SOC_DAPM_OUTPUT("LOL"),
SND_SOC_DAPM_OUTPUT("LOR"),
SND_SOC_DAPM_INPUT("IN1_L"),
SND_SOC_DAPM_INPUT("IN1_R"),
SND_SOC_DAPM_INPUT("IN2_L"),
SND_SOC_DAPM_INPUT("IN2_R"),
SND_SOC_DAPM_INPUT("IN3_L"),
SND_SOC_DAPM_INPUT("IN3_R"),
};
static const struct snd_soc_dapm_route aic32x4_dapm_routes[] = {
/* Left Output */
{"HPL Output Mixer", "L_DAC Switch", "Left DAC"},
{"HPL Output Mixer", "IN1_L Switch", "IN1_L"},
{"HPL Power", NULL, "HPL Output Mixer"},
{"HPL", NULL, "HPL Power"},
{"LOL Output Mixer", "L_DAC Switch", "Left DAC"},
{"LOL Power", NULL, "LOL Output Mixer"},
{"LOL", NULL, "LOL Power"},
/* Right Output */
{"HPR Output Mixer", "R_DAC Switch", "Right DAC"},
{"HPR Output Mixer", "IN1_R Switch", "IN1_R"},
{"HPR Power", NULL, "HPR Output Mixer"},
{"HPR", NULL, "HPR Power"},
{"LOR Output Mixer", "R_DAC Switch", "Right DAC"},
{"LOR Power", NULL, "LOR Output Mixer"},
{"LOR", NULL, "LOR Power"},
/* Left input */
{"Left Input Mixer", "IN1_L P Switch", "IN1_L"},
{"Left Input Mixer", "IN2_L P Switch", "IN2_L"},
{"Left Input Mixer", "IN3_L P Switch", "IN3_L"},
{"Left ADC", NULL, "Left Input Mixer"},
/* Right Input */
{"Right Input Mixer", "IN1_R P Switch", "IN1_R"},
{"Right Input Mixer", "IN2_R P Switch", "IN2_R"},
{"Right Input Mixer", "IN3_R P Switch", "IN3_R"},
{"Right ADC", NULL, "Right Input Mixer"},
};
static inline int aic32x4_change_page(struct snd_soc_codec *codec,
unsigned int new_page)
{
struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec);
u8 data[2];
int ret;
data[0] = 0x00;
data[1] = new_page & 0xff;
ret = codec->hw_write(codec->control_data, data, 2);
if (ret == 2) {
aic32x4->page_no = new_page;
return 0;
} else {
return ret;
}
}
static int aic32x4_write(struct snd_soc_codec *codec, unsigned int reg,
unsigned int val)
{
struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec);
unsigned int page = reg / 128;
unsigned int fixed_reg = reg % 128;
u8 data[2];
int ret;
/* A write to AIC32X4_PSEL is really a non-explicit page change */
if (reg == AIC32X4_PSEL)
return aic32x4_change_page(codec, val);
if (aic32x4->page_no != page) {
ret = aic32x4_change_page(codec, page);
if (ret != 0)
return ret;
}
data[0] = fixed_reg & 0xff;
data[1] = val & 0xff;
if (codec->hw_write(codec->control_data, data, 2) == 2)
return 0;
else
return -EIO;
}
static unsigned int aic32x4_read(struct snd_soc_codec *codec, unsigned int reg)
{
struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec);
unsigned int page = reg / 128;
unsigned int fixed_reg = reg % 128;
int ret;
if (aic32x4->page_no != page) {
ret = aic32x4_change_page(codec, page);
if (ret != 0)
return ret;
}
return i2c_smbus_read_byte_data(codec->control_data, fixed_reg & 0xff);
}
static inline int aic32x4_get_divs(int mclk, int rate)
{
int i;
for (i = 0; i < ARRAY_SIZE(aic32x4_divs); i++) {
if ((aic32x4_divs[i].rate == rate)
&& (aic32x4_divs[i].mclk == mclk)) {
return i;
}
}
printk(KERN_ERR "aic32x4: master clock and sample rate is not supported\n");
return -EINVAL;
}
static int aic32x4_add_widgets(struct snd_soc_codec *codec)
{
snd_soc_dapm_new_controls(&codec->dapm, aic32x4_dapm_widgets,
ARRAY_SIZE(aic32x4_dapm_widgets));
snd_soc_dapm_add_routes(&codec->dapm, aic32x4_dapm_routes,
ARRAY_SIZE(aic32x4_dapm_routes));
snd_soc_dapm_new_widgets(&codec->dapm);
return 0;
}
static int aic32x4_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
struct snd_soc_codec *codec = codec_dai->codec;
struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec);
switch (freq) {
case AIC32X4_FREQ_12000000:
case AIC32X4_FREQ_24000000:
case AIC32X4_FREQ_25000000:
aic32x4->sysclk = freq;
return 0;
}
printk(KERN_ERR "aic32x4: invalid frequency to set DAI system clock\n");
return -EINVAL;
}
static int aic32x4_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
{
struct snd_soc_codec *codec = codec_dai->codec;
struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec);
u8 iface_reg_1;
u8 iface_reg_2;
u8 iface_reg_3;
iface_reg_1 = snd_soc_read(codec, AIC32X4_IFACE1);
iface_reg_1 = iface_reg_1 & ~(3 << 6 | 3 << 2);
iface_reg_2 = snd_soc_read(codec, AIC32X4_IFACE2);
iface_reg_2 = 0;
iface_reg_3 = snd_soc_read(codec, AIC32X4_IFACE3);
iface_reg_3 = iface_reg_3 & ~(1 << 3);
/* set master/slave audio interface */
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBM_CFM:
aic32x4->master = 1;
iface_reg_1 |= AIC32X4_BCLKMASTER | AIC32X4_WCLKMASTER;
break;
case SND_SOC_DAIFMT_CBS_CFS:
aic32x4->master = 0;
break;
default:
printk(KERN_ERR "aic32x4: invalid DAI master/slave interface\n");
return -EINVAL;
}
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
break;
case SND_SOC_DAIFMT_DSP_A:
iface_reg_1 |= (AIC32X4_DSP_MODE << AIC32X4_PLLJ_SHIFT);
iface_reg_3 |= (1 << 3); /* invert bit clock */
iface_reg_2 = 0x01; /* add offset 1 */
break;
case SND_SOC_DAIFMT_DSP_B:
iface_reg_1 |= (AIC32X4_DSP_MODE << AIC32X4_PLLJ_SHIFT);
iface_reg_3 |= (1 << 3); /* invert bit clock */
break;
case SND_SOC_DAIFMT_RIGHT_J:
iface_reg_1 |=
(AIC32X4_RIGHT_JUSTIFIED_MODE << AIC32X4_PLLJ_SHIFT);
break;
case SND_SOC_DAIFMT_LEFT_J:
iface_reg_1 |=
(AIC32X4_LEFT_JUSTIFIED_MODE << AIC32X4_PLLJ_SHIFT);
break;
default:
printk(KERN_ERR "aic32x4: invalid DAI interface format\n");
return -EINVAL;
}
snd_soc_write(codec, AIC32X4_IFACE1, iface_reg_1);
snd_soc_write(codec, AIC32X4_IFACE2, iface_reg_2);
snd_soc_write(codec, AIC32X4_IFACE3, iface_reg_3);
return 0;
}
static int aic32x4_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec);
u8 data;
int i;
i = aic32x4_get_divs(aic32x4->sysclk, params_rate(params));
if (i < 0) {
printk(KERN_ERR "aic32x4: sampling rate not supported\n");
return i;
}
/* Use PLL as CODEC_CLKIN and DAC_MOD_CLK as BDIV_CLKIN */
snd_soc_write(codec, AIC32X4_CLKMUX, AIC32X4_PLLCLKIN);
snd_soc_write(codec, AIC32X4_IFACE3, AIC32X4_DACMOD2BCLK);
/* We will fix R value to 1 and will make P & J=K.D as varialble */
data = snd_soc_read(codec, AIC32X4_PLLPR);
data &= ~(7 << 4);
snd_soc_write(codec, AIC32X4_PLLPR,
(data | (aic32x4_divs[i].p_val << 4) | 0x01));
snd_soc_write(codec, AIC32X4_PLLJ, aic32x4_divs[i].pll_j);
snd_soc_write(codec, AIC32X4_PLLDMSB, (aic32x4_divs[i].pll_d >> 8));
snd_soc_write(codec, AIC32X4_PLLDLSB,
(aic32x4_divs[i].pll_d & 0xff));
/* NDAC divider value */
data = snd_soc_read(codec, AIC32X4_NDAC);
data &= ~(0x7f);
snd_soc_write(codec, AIC32X4_NDAC, data | aic32x4_divs[i].ndac);
/* MDAC divider value */
data = snd_soc_read(codec, AIC32X4_MDAC);
data &= ~(0x7f);
snd_soc_write(codec, AIC32X4_MDAC, data | aic32x4_divs[i].mdac);
/* DOSR MSB & LSB values */
snd_soc_write(codec, AIC32X4_DOSRMSB, aic32x4_divs[i].dosr >> 8);
snd_soc_write(codec, AIC32X4_DOSRLSB,
(aic32x4_divs[i].dosr & 0xff));
/* NADC divider value */
data = snd_soc_read(codec, AIC32X4_NADC);
data &= ~(0x7f);
snd_soc_write(codec, AIC32X4_NADC, data | aic32x4_divs[i].nadc);
/* MADC divider value */
data = snd_soc_read(codec, AIC32X4_MADC);
data &= ~(0x7f);
snd_soc_write(codec, AIC32X4_MADC, data | aic32x4_divs[i].madc);
/* AOSR value */
snd_soc_write(codec, AIC32X4_AOSR, aic32x4_divs[i].aosr);
/* BCLK N divider */
data = snd_soc_read(codec, AIC32X4_BCLKN);
data &= ~(0x7f);
snd_soc_write(codec, AIC32X4_BCLKN, data | aic32x4_divs[i].blck_N);
data = snd_soc_read(codec, AIC32X4_IFACE1);
data = data & ~(3 << 4);
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE:
break;
case SNDRV_PCM_FORMAT_S20_3LE:
data |= (AIC32X4_WORD_LEN_20BITS << AIC32X4_DOSRMSB_SHIFT);
break;
case SNDRV_PCM_FORMAT_S24_LE:
data |= (AIC32X4_WORD_LEN_24BITS << AIC32X4_DOSRMSB_SHIFT);
break;
case SNDRV_PCM_FORMAT_S32_LE:
data |= (AIC32X4_WORD_LEN_32BITS << AIC32X4_DOSRMSB_SHIFT);
break;
}
snd_soc_write(codec, AIC32X4_IFACE1, data);
return 0;
}
static int aic32x4_mute(struct snd_soc_dai *dai, int mute)
{
struct snd_soc_codec *codec = dai->codec;
u8 dac_reg;
dac_reg = snd_soc_read(codec, AIC32X4_DACMUTE) & ~AIC32X4_MUTEON;
if (mute)
snd_soc_write(codec, AIC32X4_DACMUTE, dac_reg | AIC32X4_MUTEON);
else
snd_soc_write(codec, AIC32X4_DACMUTE, dac_reg);
return 0;
}
static int aic32x4_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec);
u8 value;
switch (level) {
case SND_SOC_BIAS_ON:
if (aic32x4->master) {
/* Switch on PLL */
value = snd_soc_read(codec, AIC32X4_PLLPR);
snd_soc_write(codec, AIC32X4_PLLPR,
(value | AIC32X4_PLLEN));
/* Switch on NDAC Divider */
value = snd_soc_read(codec, AIC32X4_NDAC);
snd_soc_write(codec, AIC32X4_NDAC,
value | AIC32X4_NDACEN);
/* Switch on MDAC Divider */
value = snd_soc_read(codec, AIC32X4_MDAC);
snd_soc_write(codec, AIC32X4_MDAC,
value | AIC32X4_MDACEN);
/* Switch on NADC Divider */
value = snd_soc_read(codec, AIC32X4_NADC);
snd_soc_write(codec, AIC32X4_NADC,
value | AIC32X4_MDACEN);
/* Switch on MADC Divider */
value = snd_soc_read(codec, AIC32X4_MADC);
snd_soc_write(codec, AIC32X4_MADC,
value | AIC32X4_MDACEN);
/* Switch on BCLK_N Divider */
value = snd_soc_read(codec, AIC32X4_BCLKN);
snd_soc_write(codec, AIC32X4_BCLKN,
value | AIC32X4_BCLKEN);
}
break;
case SND_SOC_BIAS_PREPARE:
break;
case SND_SOC_BIAS_STANDBY:
if (aic32x4->master) {
/* Switch off PLL */
value = snd_soc_read(codec, AIC32X4_PLLPR);
snd_soc_write(codec, AIC32X4_PLLPR,
(value & ~AIC32X4_PLLEN));
/* Switch off NDAC Divider */
value = snd_soc_read(codec, AIC32X4_NDAC);
snd_soc_write(codec, AIC32X4_NDAC,
value & ~AIC32X4_NDACEN);
/* Switch off MDAC Divider */
value = snd_soc_read(codec, AIC32X4_MDAC);
snd_soc_write(codec, AIC32X4_MDAC,
value & ~AIC32X4_MDACEN);
/* Switch off NADC Divider */
value = snd_soc_read(codec, AIC32X4_NADC);
snd_soc_write(codec, AIC32X4_NADC,
value & ~AIC32X4_NDACEN);
/* Switch off MADC Divider */
value = snd_soc_read(codec, AIC32X4_MADC);
snd_soc_write(codec, AIC32X4_MADC,
value & ~AIC32X4_MDACEN);
value = snd_soc_read(codec, AIC32X4_BCLKN);
/* Switch off BCLK_N Divider */
snd_soc_write(codec, AIC32X4_BCLKN,
value & ~AIC32X4_BCLKEN);
}
break;
case SND_SOC_BIAS_OFF:
break;
}
codec->dapm.bias_level = level;
return 0;
}
#define AIC32X4_RATES SNDRV_PCM_RATE_8000_48000
#define AIC32X4_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE \
| SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE)
static struct snd_soc_dai_ops aic32x4_ops = {
.hw_params = aic32x4_hw_params,
.digital_mute = aic32x4_mute,
.set_fmt = aic32x4_set_dai_fmt,
.set_sysclk = aic32x4_set_dai_sysclk,
};
static struct snd_soc_dai_driver aic32x4_dai = {
.name = "tlv320aic32x4-hifi",
.playback = {
.stream_name = "Playback",
.channels_min = 1,
.channels_max = 2,
.rates = AIC32X4_RATES,
.formats = AIC32X4_FORMATS,},
.capture = {
.stream_name = "Capture",
.channels_min = 1,
.channels_max = 2,
.rates = AIC32X4_RATES,
.formats = AIC32X4_FORMATS,},
.ops = &aic32x4_ops,
.symmetric_rates = 1,
};
static int aic32x4_suspend(struct snd_soc_codec *codec, pm_message_t state)
{
aic32x4_set_bias_level(codec, SND_SOC_BIAS_OFF);
return 0;
}
static int aic32x4_resume(struct snd_soc_codec *codec)
{
aic32x4_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
return 0;
}
static int aic32x4_probe(struct snd_soc_codec *codec)
{
struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec);
u32 tmp_reg;
codec->hw_write = (hw_write_t) i2c_master_send;
codec->control_data = aic32x4->control_data;
snd_soc_write(codec, AIC32X4_RESET, 0x01);
/* Power platform configuration */
if (aic32x4->power_cfg & AIC32X4_PWR_MICBIAS_2075_LDOIN) {
snd_soc_write(codec, AIC32X4_MICBIAS, AIC32X4_MICBIAS_LDOIN |
AIC32X4_MICBIAS_2075V);
}
if (aic32x4->power_cfg & AIC32X4_PWR_AVDD_DVDD_WEAK_DISABLE) {
snd_soc_write(codec, AIC32X4_PWRCFG, AIC32X4_AVDDWEAKDISABLE);
}
if (aic32x4->power_cfg & AIC32X4_PWR_AIC32X4_LDO_ENABLE) {
snd_soc_write(codec, AIC32X4_LDOCTL, AIC32X4_LDOCTLEN);
}
tmp_reg = snd_soc_read(codec, AIC32X4_CMMODE);
if (aic32x4->power_cfg & AIC32X4_PWR_CMMODE_LDOIN_RANGE_18_36) {
tmp_reg |= AIC32X4_LDOIN_18_36;
}
if (aic32x4->power_cfg & AIC32X4_PWR_CMMODE_HP_LDOIN_POWERED) {
tmp_reg |= AIC32X4_LDOIN2HP;
}
snd_soc_write(codec, AIC32X4_CMMODE, tmp_reg);
/* Do DACs need to be swapped? */
if (aic32x4->swapdacs) {
snd_soc_write(codec, AIC32X4_DACSETUP, AIC32X4_LDAC2RCHN | AIC32X4_RDAC2LCHN);
} else {
snd_soc_write(codec, AIC32X4_DACSETUP, AIC32X4_LDAC2LCHN | AIC32X4_RDAC2RCHN);
}
/* Mic PGA routing */
if (aic32x4->micpga_routing | AIC32X4_MICPGA_ROUTE_LMIC_IN2R_10K) {
snd_soc_write(codec, AIC32X4_LMICPGANIN, AIC32X4_LMICPGANIN_IN2R_10K);
}
if (aic32x4->micpga_routing | AIC32X4_MICPGA_ROUTE_RMIC_IN1L_10K) {
snd_soc_write(codec, AIC32X4_RMICPGANIN, AIC32X4_RMICPGANIN_IN1L_10K);
}
aic32x4_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
snd_soc_add_controls(codec, aic32x4_snd_controls,
ARRAY_SIZE(aic32x4_snd_controls));
aic32x4_add_widgets(codec);
return 0;
}
static int aic32x4_remove(struct snd_soc_codec *codec)
{
aic32x4_set_bias_level(codec, SND_SOC_BIAS_OFF);
return 0;
}
static struct snd_soc_codec_driver soc_codec_dev_aic32x4 = {
.read = aic32x4_read,
.write = aic32x4_write,
.probe = aic32x4_probe,
.remove = aic32x4_remove,
.suspend = aic32x4_suspend,
.resume = aic32x4_resume,
.set_bias_level = aic32x4_set_bias_level,
};
static __devinit int aic32x4_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct aic32x4_pdata *pdata = i2c->dev.platform_data;
struct aic32x4_priv *aic32x4;
int ret;
aic32x4 = kzalloc(sizeof(struct aic32x4_priv), GFP_KERNEL);
if (aic32x4 == NULL)
return -ENOMEM;
aic32x4->control_data = i2c;
i2c_set_clientdata(i2c, aic32x4);
if (pdata) {
aic32x4->power_cfg = pdata->power_cfg;
aic32x4->swapdacs = pdata->swapdacs;
aic32x4->micpga_routing = pdata->micpga_routing;
} else {
aic32x4->power_cfg = 0;
aic32x4->swapdacs = false;
aic32x4->micpga_routing = 0;
}
ret = snd_soc_register_codec(&i2c->dev,
&soc_codec_dev_aic32x4, &aic32x4_dai, 1);
if (ret < 0)
kfree(aic32x4);
return ret;
}
static __devexit int aic32x4_i2c_remove(struct i2c_client *client)
{
snd_soc_unregister_codec(&client->dev);
kfree(i2c_get_clientdata(client));
return 0;
}
static const struct i2c_device_id aic32x4_i2c_id[] = {
{ "tlv320aic32x4", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, aic32x4_i2c_id);
static struct i2c_driver aic32x4_i2c_driver = {
.driver = {
.name = "tlv320aic32x4",
.owner = THIS_MODULE,
},
.probe = aic32x4_i2c_probe,
.remove = __devexit_p(aic32x4_i2c_remove),
.id_table = aic32x4_i2c_id,
};
static int __init aic32x4_modinit(void)
{
int ret = 0;
ret = i2c_add_driver(&aic32x4_i2c_driver);
if (ret != 0) {
printk(KERN_ERR "Failed to register aic32x4 I2C driver: %d\n",
ret);
}
return ret;
}
module_init(aic32x4_modinit);
static void __exit aic32x4_exit(void)
{
i2c_del_driver(&aic32x4_i2c_driver);
}
module_exit(aic32x4_exit);
MODULE_DESCRIPTION("ASoC tlv320aic32x4 codec driver");
MODULE_AUTHOR("Javier Martin <javier.martin@vista-silicon.com>");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,143 @@
/*
* tlv320aic32x4.h
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef _TLV320AIC32X4_H
#define _TLV320AIC32X4_H
/* tlv320aic32x4 register space (in decimal to match datasheet) */
#define AIC32X4_PAGE1 128
#define AIC32X4_PSEL 0
#define AIC32X4_RESET 1
#define AIC32X4_CLKMUX 4
#define AIC32X4_PLLPR 5
#define AIC32X4_PLLJ 6
#define AIC32X4_PLLDMSB 7
#define AIC32X4_PLLDLSB 8
#define AIC32X4_NDAC 11
#define AIC32X4_MDAC 12
#define AIC32X4_DOSRMSB 13
#define AIC32X4_DOSRLSB 14
#define AIC32X4_NADC 18
#define AIC32X4_MADC 19
#define AIC32X4_AOSR 20
#define AIC32X4_CLKMUX2 25
#define AIC32X4_CLKOUTM 26
#define AIC32X4_IFACE1 27
#define AIC32X4_IFACE2 28
#define AIC32X4_IFACE3 29
#define AIC32X4_BCLKN 30
#define AIC32X4_IFACE4 31
#define AIC32X4_IFACE5 32
#define AIC32X4_IFACE6 33
#define AIC32X4_DOUTCTL 53
#define AIC32X4_DINCTL 54
#define AIC32X4_DACSPB 60
#define AIC32X4_ADCSPB 61
#define AIC32X4_DACSETUP 63
#define AIC32X4_DACMUTE 64
#define AIC32X4_LDACVOL 65
#define AIC32X4_RDACVOL 66
#define AIC32X4_ADCSETUP 81
#define AIC32X4_ADCFGA 82
#define AIC32X4_LADCVOL 83
#define AIC32X4_RADCVOL 84
#define AIC32X4_LAGC1 86
#define AIC32X4_LAGC2 87
#define AIC32X4_LAGC3 88
#define AIC32X4_LAGC4 89
#define AIC32X4_LAGC5 90
#define AIC32X4_LAGC6 91
#define AIC32X4_LAGC7 92
#define AIC32X4_RAGC1 94
#define AIC32X4_RAGC2 95
#define AIC32X4_RAGC3 96
#define AIC32X4_RAGC4 97
#define AIC32X4_RAGC5 98
#define AIC32X4_RAGC6 99
#define AIC32X4_RAGC7 100
#define AIC32X4_PWRCFG (AIC32X4_PAGE1 + 1)
#define AIC32X4_LDOCTL (AIC32X4_PAGE1 + 2)
#define AIC32X4_OUTPWRCTL (AIC32X4_PAGE1 + 9)
#define AIC32X4_CMMODE (AIC32X4_PAGE1 + 10)
#define AIC32X4_HPLROUTE (AIC32X4_PAGE1 + 12)
#define AIC32X4_HPRROUTE (AIC32X4_PAGE1 + 13)
#define AIC32X4_LOLROUTE (AIC32X4_PAGE1 + 14)
#define AIC32X4_LORROUTE (AIC32X4_PAGE1 + 15)
#define AIC32X4_HPLGAIN (AIC32X4_PAGE1 + 16)
#define AIC32X4_HPRGAIN (AIC32X4_PAGE1 + 17)
#define AIC32X4_LOLGAIN (AIC32X4_PAGE1 + 18)
#define AIC32X4_LORGAIN (AIC32X4_PAGE1 + 19)
#define AIC32X4_HEADSTART (AIC32X4_PAGE1 + 20)
#define AIC32X4_MICBIAS (AIC32X4_PAGE1 + 51)
#define AIC32X4_LMICPGAPIN (AIC32X4_PAGE1 + 52)
#define AIC32X4_LMICPGANIN (AIC32X4_PAGE1 + 54)
#define AIC32X4_RMICPGAPIN (AIC32X4_PAGE1 + 55)
#define AIC32X4_RMICPGANIN (AIC32X4_PAGE1 + 57)
#define AIC32X4_FLOATINGINPUT (AIC32X4_PAGE1 + 58)
#define AIC32X4_LMICPGAVOL (AIC32X4_PAGE1 + 59)
#define AIC32X4_RMICPGAVOL (AIC32X4_PAGE1 + 60)
#define AIC32X4_FREQ_12000000 12000000
#define AIC32X4_FREQ_24000000 24000000
#define AIC32X4_FREQ_25000000 25000000
#define AIC32X4_WORD_LEN_16BITS 0x00
#define AIC32X4_WORD_LEN_20BITS 0x01
#define AIC32X4_WORD_LEN_24BITS 0x02
#define AIC32X4_WORD_LEN_32BITS 0x03
#define AIC32X4_I2S_MODE 0x00
#define AIC32X4_DSP_MODE 0x01
#define AIC32X4_RIGHT_JUSTIFIED_MODE 0x02
#define AIC32X4_LEFT_JUSTIFIED_MODE 0x03
#define AIC32X4_AVDDWEAKDISABLE 0x08
#define AIC32X4_LDOCTLEN 0x01
#define AIC32X4_LDOIN_18_36 0x01
#define AIC32X4_LDOIN2HP 0x02
#define AIC32X4_DACSPBLOCK_MASK 0x1f
#define AIC32X4_ADCSPBLOCK_MASK 0x1f
#define AIC32X4_PLLJ_SHIFT 6
#define AIC32X4_DOSRMSB_SHIFT 4
#define AIC32X4_PLLCLKIN 0x03
#define AIC32X4_MICBIAS_LDOIN 0x08
#define AIC32X4_MICBIAS_2075V 0x60
#define AIC32X4_LMICPGANIN_IN2R_10K 0x10
#define AIC32X4_RMICPGANIN_IN1L_10K 0x10
#define AIC32X4_LMICPGAVOL_NOGAIN 0x80
#define AIC32X4_RMICPGAVOL_NOGAIN 0x80
#define AIC32X4_BCLKMASTER 0x08
#define AIC32X4_WCLKMASTER 0x04
#define AIC32X4_PLLEN (0x01 << 7)
#define AIC32X4_NDACEN (0x01 << 7)
#define AIC32X4_MDACEN (0x01 << 7)
#define AIC32X4_NADCEN (0x01 << 7)
#define AIC32X4_MADCEN (0x01 << 7)
#define AIC32X4_BCLKEN (0x01 << 7)
#define AIC32X4_DACEN (0x03 << 6)
#define AIC32X4_RDAC2LCHN (0x02 << 2)
#define AIC32X4_LDAC2RCHN (0x02 << 4)
#define AIC32X4_LDAC2LCHN (0x01 << 4)
#define AIC32X4_RDAC2RCHN (0x01 << 2)
#define AIC32X4_SSTEP2WCLK 0x01
#define AIC32X4_MUTEON 0x0C
#define AIC32X4_DACMOD2BCLK 0x01
#endif /* _TLV320AIC32X4_H */

View file

@ -1615,6 +1615,7 @@ static const struct i2c_device_id tlv320dac33_i2c_id[] = {
},
{ },
};
MODULE_DEVICE_TABLE(i2c, tlv320dac33_i2c_id);
static struct i2c_driver tlv320dac33_i2c_driver = {
.driver = {

View file

@ -724,8 +724,8 @@ static int twl6040_power_mode_event(struct snd_soc_dapm_widget *w,
return 0;
}
void twl6040_hs_jack_report(struct snd_soc_codec *codec,
struct snd_soc_jack *jack, int report)
static void twl6040_hs_jack_report(struct snd_soc_codec *codec,
struct snd_soc_jack *jack, int report)
{
struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
int status;

View file

@ -836,24 +836,25 @@ static void wm2000_i2c_shutdown(struct i2c_client *i2c)
}
#ifdef CONFIG_PM
static int wm2000_i2c_suspend(struct i2c_client *i2c, pm_message_t mesg)
static int wm2000_i2c_suspend(struct device *dev)
{
struct i2c_client *i2c = to_i2c_client(dev);
struct wm2000_priv *wm2000 = dev_get_drvdata(&i2c->dev);
return wm2000_anc_transition(wm2000, ANC_OFF);
}
static int wm2000_i2c_resume(struct i2c_client *i2c)
static int wm2000_i2c_resume(struct device *dev)
{
struct i2c_client *i2c = to_i2c_client(dev);
struct wm2000_priv *wm2000 = dev_get_drvdata(&i2c->dev);
return wm2000_anc_set_mode(wm2000);
}
#else
#define wm2000_i2c_suspend NULL
#define wm2000_i2c_resume NULL
#endif
static SIMPLE_DEV_PM_OPS(wm2000_pm, wm2000_i2c_suspend, wm2000_i2c_resume);
static const struct i2c_device_id wm2000_i2c_id[] = {
{ "wm2000", 0 },
{ }
@ -864,11 +865,10 @@ static struct i2c_driver wm2000_i2c_driver = {
.driver = {
.name = "wm2000",
.owner = THIS_MODULE,
.pm = &wm2000_pm,
},
.probe = wm2000_i2c_probe,
.remove = __devexit_p(wm2000_i2c_remove),
.suspend = wm2000_i2c_suspend,
.resume = wm2000_i2c_resume,
.shutdown = wm2000_i2c_shutdown,
.id_table = wm2000_i2c_id,
};

View file

@ -58,7 +58,7 @@ static const u16 wm8523_reg[WM8523_REGISTER_COUNT] = {
0x0000, /* R8 - ZERO_DETECT */
};
static int wm8523_volatile_register(unsigned int reg)
static int wm8523_volatile_register(struct snd_soc_codec *codec, unsigned int reg)
{
switch (reg) {
case WM8523_DEVICE_ID:
@ -414,7 +414,6 @@ static int wm8523_resume(struct snd_soc_codec *codec)
static int wm8523_probe(struct snd_soc_codec *codec)
{
struct wm8523_priv *wm8523 = snd_soc_codec_get_drvdata(codec);
u16 *reg_cache = codec->reg_cache;
int ret, i;
codec->hw_write = (hw_write_t)i2c_master_send;
@ -471,8 +470,9 @@ static int wm8523_probe(struct snd_soc_codec *codec)
}
/* Change some default settings - latch VU and enable ZC */
reg_cache[WM8523_DAC_GAINR] |= WM8523_DACR_VU;
reg_cache[WM8523_DAC_CTRL3] |= WM8523_ZC;
snd_soc_update_bits(codec, WM8523_DAC_GAINR,
WM8523_DACR_VU, WM8523_DACR_VU);
snd_soc_update_bits(codec, WM8523_DAC_CTRL3, WM8523_ZC, WM8523_ZC);
wm8523_set_bias_level(codec, SND_SOC_BIAS_STANDBY);

View file

@ -421,7 +421,6 @@ static int wm8741_resume(struct snd_soc_codec *codec)
static int wm8741_probe(struct snd_soc_codec *codec)
{
struct wm8741_priv *wm8741 = snd_soc_codec_get_drvdata(codec);
u16 *reg_cache = codec->reg_cache;
int ret = 0;
ret = snd_soc_codec_set_cache_io(codec, 7, 9, wm8741->control_type);
@ -437,10 +436,14 @@ static int wm8741_probe(struct snd_soc_codec *codec)
}
/* Change some default settings - latch VU */
reg_cache[WM8741_DACLLSB_ATTENUATION] |= WM8741_UPDATELL;
reg_cache[WM8741_DACLMSB_ATTENUATION] |= WM8741_UPDATELM;
reg_cache[WM8741_DACRLSB_ATTENUATION] |= WM8741_UPDATERL;
reg_cache[WM8741_DACRLSB_ATTENUATION] |= WM8741_UPDATERM;
snd_soc_update_bits(codec, WM8741_DACLLSB_ATTENUATION,
WM8741_UPDATELL, WM8741_UPDATELL);
snd_soc_update_bits(codec, WM8741_DACLMSB_ATTENUATION,
WM8741_UPDATELM, WM8741_UPDATELM);
snd_soc_update_bits(codec, WM8741_DACRLSB_ATTENUATION,
WM8741_UPDATERL, WM8741_UPDATERL);
snd_soc_update_bits(codec, WM8741_DACRLSB_ATTENUATION,
WM8741_UPDATERM, WM8741_UPDATERM);
snd_soc_add_controls(codec, wm8741_snd_controls,
ARRAY_SIZE(wm8741_snd_controls));

View file

@ -55,8 +55,10 @@ static int caps_charge = 2000;
module_param(caps_charge, int, 0);
MODULE_PARM_DESC(caps_charge, "WM8753 cap charge time (msecs)");
static void wm8753_set_dai_mode(struct snd_soc_codec *codec,
struct snd_soc_dai *dai, unsigned int hifi);
static int wm8753_hifi_write_dai_fmt(struct snd_soc_codec *codec,
unsigned int fmt);
static int wm8753_voice_write_dai_fmt(struct snd_soc_codec *codec,
unsigned int fmt);
/*
* wm8753 register cache
@ -87,6 +89,10 @@ struct wm8753_priv {
enum snd_soc_control_type control_type;
unsigned int sysclk;
unsigned int pcmclk;
unsigned int voice_fmt;
unsigned int hifi_fmt;
int dai_func;
};
@ -170,9 +176,9 @@ static int wm8753_get_dai(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
int mode = snd_soc_read(codec, WM8753_IOCTL);
struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec);
ucontrol->value.integer.value[0] = (mode & 0xc) >> 2;
ucontrol->value.integer.value[0] = wm8753->dai_func;
return 0;
}
@ -180,16 +186,26 @@ static int wm8753_set_dai(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
int mode = snd_soc_read(codec, WM8753_IOCTL);
struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec);
u16 ioctl;
if (((mode & 0xc) >> 2) == ucontrol->value.integer.value[0])
return 0;
if (codec->active)
return -EBUSY;
mode &= 0xfff3;
mode |= (ucontrol->value.integer.value[0] << 2);
ioctl = snd_soc_read(codec, WM8753_IOCTL);
wm8753->dai_func = ucontrol->value.integer.value[0];
if (((ioctl >> 2) & 0x3) == wm8753->dai_func)
return 1;
ioctl = (ioctl & 0x1f3) | (wm8753->dai_func << 2);
snd_soc_write(codec, WM8753_IOCTL, ioctl);
wm8753_hifi_write_dai_fmt(codec, wm8753->hifi_fmt);
wm8753_voice_write_dai_fmt(codec, wm8753->voice_fmt);
wm8753->dai_func = ucontrol->value.integer.value[0];
return 1;
}
@ -828,10 +844,9 @@ static int wm8753_set_dai_sysclk(struct snd_soc_dai *codec_dai,
/*
* Set's ADC and Voice DAC format.
*/
static int wm8753_vdac_adc_set_dai_fmt(struct snd_soc_dai *codec_dai,
static int wm8753_vdac_adc_set_dai_fmt(struct snd_soc_codec *codec,
unsigned int fmt)
{
struct snd_soc_codec *codec = codec_dai->codec;
u16 voice = snd_soc_read(codec, WM8753_PCM) & 0x01ec;
/* interface format */
@ -858,13 +873,6 @@ static int wm8753_vdac_adc_set_dai_fmt(struct snd_soc_dai *codec_dai,
return 0;
}
static int wm8753_pcm_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
wm8753_set_dai_mode(dai->codec, dai, 0);
return 0;
}
/*
* Set PCM DAI bit size and sample rate.
*/
@ -905,10 +913,9 @@ static int wm8753_pcm_hw_params(struct snd_pcm_substream *substream,
/*
* Set's PCM dai fmt and BCLK.
*/
static int wm8753_pcm_set_dai_fmt(struct snd_soc_dai *codec_dai,
static int wm8753_pcm_set_dai_fmt(struct snd_soc_codec *codec,
unsigned int fmt)
{
struct snd_soc_codec *codec = codec_dai->codec;
u16 voice, ioctl;
voice = snd_soc_read(codec, WM8753_PCM) & 0x011f;
@ -999,10 +1006,9 @@ static int wm8753_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
/*
* Set's HiFi DAC format.
*/
static int wm8753_hdac_set_dai_fmt(struct snd_soc_dai *codec_dai,
static int wm8753_hdac_set_dai_fmt(struct snd_soc_codec *codec,
unsigned int fmt)
{
struct snd_soc_codec *codec = codec_dai->codec;
u16 hifi = snd_soc_read(codec, WM8753_HIFI) & 0x01e0;
/* interface format */
@ -1032,10 +1038,9 @@ static int wm8753_hdac_set_dai_fmt(struct snd_soc_dai *codec_dai,
/*
* Set's I2S DAI format.
*/
static int wm8753_i2s_set_dai_fmt(struct snd_soc_dai *codec_dai,
static int wm8753_i2s_set_dai_fmt(struct snd_soc_codec *codec,
unsigned int fmt)
{
struct snd_soc_codec *codec = codec_dai->codec;
u16 ioctl, hifi;
hifi = snd_soc_read(codec, WM8753_HIFI) & 0x011f;
@ -1098,13 +1103,6 @@ static int wm8753_i2s_set_dai_fmt(struct snd_soc_dai *codec_dai,
return 0;
}
static int wm8753_i2s_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
wm8753_set_dai_mode(dai->codec, dai, 1);
return 0;
}
/*
* Set PCM DAI bit size and sample rate.
*/
@ -1147,61 +1145,117 @@ static int wm8753_i2s_hw_params(struct snd_pcm_substream *substream,
return 0;
}
static int wm8753_mode1v_set_dai_fmt(struct snd_soc_dai *codec_dai,
static int wm8753_mode1v_set_dai_fmt(struct snd_soc_codec *codec,
unsigned int fmt)
{
struct snd_soc_codec *codec = codec_dai->codec;
u16 clock;
/* set clk source as pcmclk */
clock = snd_soc_read(codec, WM8753_CLOCK) & 0xfffb;
snd_soc_write(codec, WM8753_CLOCK, clock);
if (wm8753_vdac_adc_set_dai_fmt(codec_dai, fmt) < 0)
return -EINVAL;
return wm8753_pcm_set_dai_fmt(codec_dai, fmt);
return wm8753_vdac_adc_set_dai_fmt(codec, fmt);
}
static int wm8753_mode1h_set_dai_fmt(struct snd_soc_dai *codec_dai,
static int wm8753_mode1h_set_dai_fmt(struct snd_soc_codec *codec,
unsigned int fmt)
{
if (wm8753_hdac_set_dai_fmt(codec_dai, fmt) < 0)
return -EINVAL;
return wm8753_i2s_set_dai_fmt(codec_dai, fmt);
return wm8753_hdac_set_dai_fmt(codec, fmt);
}
static int wm8753_mode2_set_dai_fmt(struct snd_soc_dai *codec_dai,
static int wm8753_mode2_set_dai_fmt(struct snd_soc_codec *codec,
unsigned int fmt)
{
struct snd_soc_codec *codec = codec_dai->codec;
u16 clock;
/* set clk source as pcmclk */
clock = snd_soc_read(codec, WM8753_CLOCK) & 0xfffb;
snd_soc_write(codec, WM8753_CLOCK, clock);
if (wm8753_vdac_adc_set_dai_fmt(codec_dai, fmt) < 0)
return -EINVAL;
return wm8753_i2s_set_dai_fmt(codec_dai, fmt);
return wm8753_vdac_adc_set_dai_fmt(codec, fmt);
}
static int wm8753_mode3_4_set_dai_fmt(struct snd_soc_dai *codec_dai,
static int wm8753_mode3_4_set_dai_fmt(struct snd_soc_codec *codec,
unsigned int fmt)
{
struct snd_soc_codec *codec = codec_dai->codec;
u16 clock;
/* set clk source as mclk */
clock = snd_soc_read(codec, WM8753_CLOCK) & 0xfffb;
snd_soc_write(codec, WM8753_CLOCK, clock | 0x4);
if (wm8753_hdac_set_dai_fmt(codec_dai, fmt) < 0)
if (wm8753_hdac_set_dai_fmt(codec, fmt) < 0)
return -EINVAL;
if (wm8753_vdac_adc_set_dai_fmt(codec_dai, fmt) < 0)
return -EINVAL;
return wm8753_i2s_set_dai_fmt(codec_dai, fmt);
return wm8753_vdac_adc_set_dai_fmt(codec, fmt);
}
static int wm8753_hifi_write_dai_fmt(struct snd_soc_codec *codec,
unsigned int fmt)
{
struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec);
int ret = 0;
switch (wm8753->dai_func) {
case 0:
ret = wm8753_mode1h_set_dai_fmt(codec, fmt);
break;
case 1:
ret = wm8753_mode2_set_dai_fmt(codec, fmt);
break;
case 2:
case 3:
ret = wm8753_mode3_4_set_dai_fmt(codec, fmt);
break;
default:
break;
}
if (ret)
return ret;
return wm8753_i2s_set_dai_fmt(codec, fmt);
}
static int wm8753_hifi_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
struct snd_soc_codec *codec = codec_dai->codec;
struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec);
wm8753->hifi_fmt = fmt;
return wm8753_hifi_write_dai_fmt(codec, fmt);
};
static int wm8753_voice_write_dai_fmt(struct snd_soc_codec *codec,
unsigned int fmt)
{
struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec);
int ret = 0;
if (wm8753->dai_func != 0)
return 0;
ret = wm8753_mode1v_set_dai_fmt(codec, fmt);
if (ret)
return ret;
ret = wm8753_pcm_set_dai_fmt(codec, fmt);
if (ret)
return ret;
return 0;
};
static int wm8753_voice_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
struct snd_soc_codec *codec = codec_dai->codec;
struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec);
wm8753->voice_fmt = fmt;
return wm8753_voice_write_dai_fmt(codec, fmt);
};
static int wm8753_mute(struct snd_soc_dai *dai, int mute)
{
struct snd_soc_codec *codec = dai->codec;
@ -1268,57 +1322,25 @@ static int wm8753_set_bias_level(struct snd_soc_codec *codec,
* 3. Voice disabled - HIFI over HIFI
* 4. Voice disabled - HIFI over HIFI, uses voice DAI LRC for capture
*/
static struct snd_soc_dai_ops wm8753_dai_ops_hifi_mode1 = {
.startup = wm8753_i2s_startup,
static struct snd_soc_dai_ops wm8753_dai_ops_hifi_mode = {
.hw_params = wm8753_i2s_hw_params,
.digital_mute = wm8753_mute,
.set_fmt = wm8753_mode1h_set_dai_fmt,
.set_fmt = wm8753_hifi_set_dai_fmt,
.set_clkdiv = wm8753_set_dai_clkdiv,
.set_pll = wm8753_set_dai_pll,
.set_sysclk = wm8753_set_dai_sysclk,
};
static struct snd_soc_dai_ops wm8753_dai_ops_voice_mode1 = {
.startup = wm8753_pcm_startup,
static struct snd_soc_dai_ops wm8753_dai_ops_voice_mode = {
.hw_params = wm8753_pcm_hw_params,
.digital_mute = wm8753_mute,
.set_fmt = wm8753_mode1v_set_dai_fmt,
.set_fmt = wm8753_voice_set_dai_fmt,
.set_clkdiv = wm8753_set_dai_clkdiv,
.set_pll = wm8753_set_dai_pll,
.set_sysclk = wm8753_set_dai_sysclk,
};
static struct snd_soc_dai_ops wm8753_dai_ops_voice_mode2 = {
.startup = wm8753_pcm_startup,
.hw_params = wm8753_pcm_hw_params,
.digital_mute = wm8753_mute,
.set_fmt = wm8753_mode2_set_dai_fmt,
.set_clkdiv = wm8753_set_dai_clkdiv,
.set_pll = wm8753_set_dai_pll,
.set_sysclk = wm8753_set_dai_sysclk,
};
static struct snd_soc_dai_ops wm8753_dai_ops_hifi_mode3 = {
.startup = wm8753_i2s_startup,
.hw_params = wm8753_i2s_hw_params,
.digital_mute = wm8753_mute,
.set_fmt = wm8753_mode3_4_set_dai_fmt,
.set_clkdiv = wm8753_set_dai_clkdiv,
.set_pll = wm8753_set_dai_pll,
.set_sysclk = wm8753_set_dai_sysclk,
};
static struct snd_soc_dai_ops wm8753_dai_ops_hifi_mode4 = {
.startup = wm8753_i2s_startup,
.hw_params = wm8753_i2s_hw_params,
.digital_mute = wm8753_mute,
.set_fmt = wm8753_mode3_4_set_dai_fmt,
.set_clkdiv = wm8753_set_dai_clkdiv,
.set_pll = wm8753_set_dai_pll,
.set_sysclk = wm8753_set_dai_sysclk,
};
static struct snd_soc_dai_driver wm8753_all_dai[] = {
static struct snd_soc_dai_driver wm8753_dai[] = {
/* DAI HiFi mode 1 */
{ .name = "wm8753-hifi",
.playback = {
@ -1326,14 +1348,16 @@ static struct snd_soc_dai_driver wm8753_all_dai[] = {
.channels_min = 1,
.channels_max = 2,
.rates = WM8753_RATES,
.formats = WM8753_FORMATS},
.formats = WM8753_FORMATS
},
.capture = { /* dummy for fast DAI switching */
.stream_name = "Capture",
.channels_min = 1,
.channels_max = 2,
.rates = WM8753_RATES,
.formats = WM8753_FORMATS},
.ops = &wm8753_dai_ops_hifi_mode1,
.formats = WM8753_FORMATS
},
.ops = &wm8753_dai_ops_hifi_mode,
},
/* DAI Voice mode 1 */
{ .name = "wm8753-voice",
@ -1342,97 +1366,19 @@ static struct snd_soc_dai_driver wm8753_all_dai[] = {
.channels_min = 1,
.channels_max = 1,
.rates = WM8753_RATES,
.formats = WM8753_FORMATS,},
.formats = WM8753_FORMATS,
},
.capture = {
.stream_name = "Capture",
.channels_min = 1,
.channels_max = 2,
.rates = WM8753_RATES,
.formats = WM8753_FORMATS,},
.ops = &wm8753_dai_ops_voice_mode1,
},
/* DAI HiFi mode 2 - dummy */
{ .name = "wm8753-hifi",
},
/* DAI Voice mode 2 */
{ .name = "wm8753-voice",
.playback = {
.stream_name = "Voice Playback",
.channels_min = 1,
.channels_max = 1,
.rates = WM8753_RATES,
.formats = WM8753_FORMATS,},
.capture = {
.stream_name = "Capture",
.channels_min = 1,
.channels_max = 2,
.rates = WM8753_RATES,
.formats = WM8753_FORMATS,},
.ops = &wm8753_dai_ops_voice_mode2,
},
/* DAI HiFi mode 3 */
{ .name = "wm8753-hifi",
.playback = {
.stream_name = "HiFi Playback",
.channels_min = 1,
.channels_max = 2,
.rates = WM8753_RATES,
.formats = WM8753_FORMATS,},
.capture = {
.stream_name = "Capture",
.channels_min = 1,
.channels_max = 2,
.rates = WM8753_RATES,
.formats = WM8753_FORMATS,},
.ops = &wm8753_dai_ops_hifi_mode3,
},
/* DAI Voice mode 3 - dummy */
{ .name = "wm8753-voice",
},
/* DAI HiFi mode 4 */
{ .name = "wm8753-hifi",
.playback = {
.stream_name = "HiFi Playback",
.channels_min = 1,
.channels_max = 2,
.rates = WM8753_RATES,
.formats = WM8753_FORMATS,},
.capture = {
.stream_name = "Capture",
.channels_min = 1,
.channels_max = 2,
.rates = WM8753_RATES,
.formats = WM8753_FORMATS,},
.ops = &wm8753_dai_ops_hifi_mode4,
},
/* DAI Voice mode 4 - dummy */
{ .name = "wm8753-voice",
.formats = WM8753_FORMATS,
},
.ops = &wm8753_dai_ops_voice_mode,
},
};
static struct snd_soc_dai_driver wm8753_dai[] = {
{
.name = "wm8753-aif0",
},
{
.name = "wm8753-aif1",
},
};
static void wm8753_set_dai_mode(struct snd_soc_codec *codec,
struct snd_soc_dai *dai, unsigned int hifi)
{
struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec);
if (wm8753->dai_func < 4) {
if (hifi)
dai->driver = &wm8753_all_dai[wm8753->dai_func << 1];
else
dai->driver = &wm8753_all_dai[(wm8753->dai_func << 1) + 1];
}
snd_soc_write(codec, WM8753_IOCTL, wm8753->dai_func);
}
static void wm8753_work(struct work_struct *work)
{
struct snd_soc_dapm_context *dapm =

View file

@ -175,7 +175,7 @@ static int txsrc_put(struct snd_kcontrol *kcontrol,
return 0;
}
static int wm8804_volatile(unsigned int reg)
static int wm8804_volatile(struct snd_soc_codec *codec, unsigned int reg)
{
switch (reg) {
case WM8804_RST_DEVID1:

View file

@ -180,7 +180,7 @@ static const u16 wm8900_reg_defaults[WM8900_MAXREG] = {
/* Remaining registers all zero */
};
static int wm8900_volatile_register(unsigned int reg)
static int wm8900_volatile_register(struct snd_soc_codec *codec, unsigned int reg)
{
switch (reg) {
case WM8900_REG_ID:

View file

@ -2,6 +2,7 @@
* wm8903.c -- WM8903 ALSA SoC Audio driver
*
* Copyright 2008 Wolfson Microelectronics
* Copyright 2011 NVIDIA, Inc.
*
* Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
*
@ -19,6 +20,7 @@
#include <linux/init.h>
#include <linux/completion.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/pm.h>
#include <linux/i2c.h>
#include <linux/platform_device.h>
@ -213,6 +215,7 @@ static u16 wm8903_reg_defaults[] = {
};
struct wm8903_priv {
struct snd_soc_codec *codec;
int sysclk;
int irq;
@ -220,25 +223,36 @@ struct wm8903_priv {
int fs;
int deemph;
int dcs_pending;
int dcs_cache[4];
/* Reference count */
int class_w_users;
struct completion wseq;
struct snd_soc_jack *mic_jack;
int mic_det;
int mic_short;
int mic_last_report;
int mic_delay;
#ifdef CONFIG_GPIOLIB
struct gpio_chip gpio_chip;
#endif
};
static int wm8903_volatile_register(unsigned int reg)
static int wm8903_volatile_register(struct snd_soc_codec *codec, unsigned int reg)
{
switch (reg) {
case WM8903_SW_RESET_AND_ID:
case WM8903_REVISION_NUMBER:
case WM8903_INTERRUPT_STATUS_1:
case WM8903_WRITE_SEQUENCER_4:
case WM8903_POWER_MANAGEMENT_3:
case WM8903_POWER_MANAGEMENT_2:
case WM8903_DC_SERVO_READBACK_1:
case WM8903_DC_SERVO_READBACK_2:
case WM8903_DC_SERVO_READBACK_3:
case WM8903_DC_SERVO_READBACK_4:
return 1;
default:
@ -246,50 +260,6 @@ static int wm8903_volatile_register(unsigned int reg)
}
}
static int wm8903_run_sequence(struct snd_soc_codec *codec, unsigned int start)
{
u16 reg[5];
struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec);
BUG_ON(start > 48);
/* Enable the sequencer if it's not already on */
reg[0] = snd_soc_read(codec, WM8903_WRITE_SEQUENCER_0);
snd_soc_write(codec, WM8903_WRITE_SEQUENCER_0,
reg[0] | WM8903_WSEQ_ENA);
dev_dbg(codec->dev, "Starting sequence at %d\n", start);
snd_soc_write(codec, WM8903_WRITE_SEQUENCER_3,
start | WM8903_WSEQ_START);
/* Wait for it to complete. If we have the interrupt wired up then
* that will break us out of the poll early.
*/
do {
wait_for_completion_timeout(&wm8903->wseq,
msecs_to_jiffies(10));
reg[4] = snd_soc_read(codec, WM8903_WRITE_SEQUENCER_4);
} while (reg[4] & WM8903_WSEQ_BUSY);
dev_dbg(codec->dev, "Sequence complete\n");
/* Disable the sequencer again if we enabled it */
snd_soc_write(codec, WM8903_WRITE_SEQUENCER_0, reg[0]);
return 0;
}
static void wm8903_sync_reg_cache(struct snd_soc_codec *codec, u16 *cache)
{
int i;
/* There really ought to be something better we can do here :/ */
for (i = 0; i < ARRAY_SIZE(wm8903_reg_defaults); i++)
cache[i] = codec->hw_read(codec, i);
}
static void wm8903_reset(struct snd_soc_codec *codec)
{
snd_soc_write(codec, WM8903_SW_RESET_AND_ID, 0);
@ -297,11 +267,6 @@ static void wm8903_reset(struct snd_soc_codec *codec)
sizeof(wm8903_reg_defaults));
}
#define WM8903_OUTPUT_SHORT 0x8
#define WM8903_OUTPUT_OUT 0x4
#define WM8903_OUTPUT_INT 0x2
#define WM8903_OUTPUT_IN 0x1
static int wm8903_cp_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
@ -311,99 +276,103 @@ static int wm8903_cp_event(struct snd_soc_dapm_widget *w,
return 0;
}
/*
* Event for headphone and line out amplifier power changes. Special
* power up/down sequences are required in order to maximise pop/click
* performance.
*/
static int wm8903_output_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
static int wm8903_dcs_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = w->codec;
u16 val;
u16 reg;
u16 dcs_reg;
u16 dcs_bit;
int shift;
struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec);
switch (w->reg) {
case WM8903_POWER_MANAGEMENT_2:
reg = WM8903_ANALOGUE_HP_0;
dcs_bit = 0 + w->shift;
switch (event) {
case SND_SOC_DAPM_POST_PMU:
wm8903->dcs_pending |= 1 << w->shift;
break;
case WM8903_POWER_MANAGEMENT_3:
reg = WM8903_ANALOGUE_LINEOUT_0;
dcs_bit = 2 + w->shift;
case SND_SOC_DAPM_PRE_PMD:
snd_soc_update_bits(codec, WM8903_DC_SERVO_0,
1 << w->shift, 0);
break;
default:
BUG();
return -EINVAL; /* Spurious warning from some compilers */
}
switch (w->shift) {
case 0:
shift = 0;
break;
case 1:
shift = 4;
break;
default:
BUG();
return -EINVAL; /* Spurious warning from some compilers */
}
if (event & SND_SOC_DAPM_PRE_PMU) {
val = snd_soc_read(codec, reg);
/* Short the output */
val &= ~(WM8903_OUTPUT_SHORT << shift);
snd_soc_write(codec, reg, val);
}
if (event & SND_SOC_DAPM_POST_PMU) {
val = snd_soc_read(codec, reg);
val |= (WM8903_OUTPUT_IN << shift);
snd_soc_write(codec, reg, val);
val |= (WM8903_OUTPUT_INT << shift);
snd_soc_write(codec, reg, val);
/* Turn on the output ENA_OUTP */
val |= (WM8903_OUTPUT_OUT << shift);
snd_soc_write(codec, reg, val);
/* Enable the DC servo */
dcs_reg = snd_soc_read(codec, WM8903_DC_SERVO_0);
dcs_reg |= dcs_bit;
snd_soc_write(codec, WM8903_DC_SERVO_0, dcs_reg);
/* Remove the short */
val |= (WM8903_OUTPUT_SHORT << shift);
snd_soc_write(codec, reg, val);
}
if (event & SND_SOC_DAPM_PRE_PMD) {
val = snd_soc_read(codec, reg);
/* Short the output */
val &= ~(WM8903_OUTPUT_SHORT << shift);
snd_soc_write(codec, reg, val);
/* Disable the DC servo */
dcs_reg = snd_soc_read(codec, WM8903_DC_SERVO_0);
dcs_reg &= ~dcs_bit;
snd_soc_write(codec, WM8903_DC_SERVO_0, dcs_reg);
/* Then disable the intermediate and output stages */
val &= ~((WM8903_OUTPUT_OUT | WM8903_OUTPUT_INT |
WM8903_OUTPUT_IN) << shift);
snd_soc_write(codec, reg, val);
}
return 0;
}
#define WM8903_DCS_MODE_WRITE_STOP 0
#define WM8903_DCS_MODE_START_STOP 2
static void wm8903_seq_notifier(struct snd_soc_dapm_context *dapm,
enum snd_soc_dapm_type event, int subseq)
{
struct snd_soc_codec *codec = container_of(dapm,
struct snd_soc_codec, dapm);
struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec);
int dcs_mode = WM8903_DCS_MODE_WRITE_STOP;
int i, val;
/* Complete any pending DC servo starts */
if (wm8903->dcs_pending) {
dev_dbg(codec->dev, "Starting DC servo for %x\n",
wm8903->dcs_pending);
/* If we've no cached values then we need to do startup */
for (i = 0; i < ARRAY_SIZE(wm8903->dcs_cache); i++) {
if (!(wm8903->dcs_pending & (1 << i)))
continue;
if (wm8903->dcs_cache[i]) {
dev_dbg(codec->dev,
"Restore DC servo %d value %x\n",
3 - i, wm8903->dcs_cache[i]);
snd_soc_write(codec, WM8903_DC_SERVO_4 + i,
wm8903->dcs_cache[i] & 0xff);
} else {
dev_dbg(codec->dev,
"Calibrate DC servo %d\n", 3 - i);
dcs_mode = WM8903_DCS_MODE_START_STOP;
}
}
/* Don't trust the cache for analogue */
if (wm8903->class_w_users)
dcs_mode = WM8903_DCS_MODE_START_STOP;
snd_soc_update_bits(codec, WM8903_DC_SERVO_2,
WM8903_DCS_MODE_MASK, dcs_mode);
snd_soc_update_bits(codec, WM8903_DC_SERVO_0,
WM8903_DCS_ENA_MASK, wm8903->dcs_pending);
switch (dcs_mode) {
case WM8903_DCS_MODE_WRITE_STOP:
break;
case WM8903_DCS_MODE_START_STOP:
msleep(270);
/* Cache the measured offsets for digital */
if (wm8903->class_w_users)
break;
for (i = 0; i < ARRAY_SIZE(wm8903->dcs_cache); i++) {
if (!(wm8903->dcs_pending & (1 << i)))
continue;
val = snd_soc_read(codec,
WM8903_DC_SERVO_READBACK_1 + i);
dev_dbg(codec->dev, "DC servo %d: %x\n",
3 - i, val);
wm8903->dcs_cache[i] = val;
}
break;
default:
pr_warn("DCS mode %d delay not set\n", dcs_mode);
break;
}
wm8903->dcs_pending = 0;
}
}
/*
* When used with DAC outputs only the WM8903 charge pump supports
* operation in class W mode, providing very low power consumption
@ -667,6 +636,22 @@ static const struct soc_enum lsidetone_enum =
static const struct soc_enum rsidetone_enum =
SOC_ENUM_SINGLE(WM8903_DAC_DIGITAL_0, 0, 3, sidetone_text);
static const char *aif_text[] = {
"Left", "Right"
};
static const struct soc_enum lcapture_enum =
SOC_ENUM_SINGLE(WM8903_AUDIO_INTERFACE_0, 7, 2, aif_text);
static const struct soc_enum rcapture_enum =
SOC_ENUM_SINGLE(WM8903_AUDIO_INTERFACE_0, 6, 2, aif_text);
static const struct soc_enum lplay_enum =
SOC_ENUM_SINGLE(WM8903_AUDIO_INTERFACE_0, 5, 2, aif_text);
static const struct soc_enum rplay_enum =
SOC_ENUM_SINGLE(WM8903_AUDIO_INTERFACE_0, 4, 2, aif_text);
static const struct snd_kcontrol_new wm8903_snd_controls[] = {
/* Input PGAs - No TLV since the scale depends on PGA mode */
@ -784,6 +769,18 @@ static const struct snd_kcontrol_new lsidetone_mux =
static const struct snd_kcontrol_new rsidetone_mux =
SOC_DAPM_ENUM("DACR Sidetone Mux", rsidetone_enum);
static const struct snd_kcontrol_new lcapture_mux =
SOC_DAPM_ENUM("Left Capture Mux", lcapture_enum);
static const struct snd_kcontrol_new rcapture_mux =
SOC_DAPM_ENUM("Right Capture Mux", rcapture_enum);
static const struct snd_kcontrol_new lplay_mux =
SOC_DAPM_ENUM("Left Playback Mux", lplay_enum);
static const struct snd_kcontrol_new rplay_mux =
SOC_DAPM_ENUM("Right Playback Mux", rplay_enum);
static const struct snd_kcontrol_new left_output_mixer[] = {
SOC_DAPM_SINGLE("DACL Switch", WM8903_ANALOGUE_LEFT_MIX_0, 3, 1, 0),
SOC_DAPM_SINGLE("DACR Switch", WM8903_ANALOGUE_LEFT_MIX_0, 2, 1, 0),
@ -847,14 +844,26 @@ SND_SOC_DAPM_MUX("Right Input Mode Mux", SND_SOC_NOPM, 0, 0, &rinput_mode_mux),
SND_SOC_DAPM_PGA("Left Input PGA", WM8903_POWER_MANAGEMENT_0, 1, 0, NULL, 0),
SND_SOC_DAPM_PGA("Right Input PGA", WM8903_POWER_MANAGEMENT_0, 0, 0, NULL, 0),
SND_SOC_DAPM_ADC("ADCL", "Left HiFi Capture", WM8903_POWER_MANAGEMENT_6, 1, 0),
SND_SOC_DAPM_ADC("ADCR", "Right HiFi Capture", WM8903_POWER_MANAGEMENT_6, 0, 0),
SND_SOC_DAPM_ADC("ADCL", NULL, WM8903_POWER_MANAGEMENT_6, 1, 0),
SND_SOC_DAPM_ADC("ADCR", NULL, WM8903_POWER_MANAGEMENT_6, 0, 0),
SND_SOC_DAPM_MUX("Left Capture Mux", SND_SOC_NOPM, 0, 0, &lcapture_mux),
SND_SOC_DAPM_MUX("Right Capture Mux", SND_SOC_NOPM, 0, 0, &rcapture_mux),
SND_SOC_DAPM_AIF_OUT("AIFTXL", "Left HiFi Capture", 0, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_AIF_OUT("AIFTXR", "Right HiFi Capture", 0, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_MUX("DACL Sidetone", SND_SOC_NOPM, 0, 0, &lsidetone_mux),
SND_SOC_DAPM_MUX("DACR Sidetone", SND_SOC_NOPM, 0, 0, &rsidetone_mux),
SND_SOC_DAPM_DAC("DACL", "Left Playback", WM8903_POWER_MANAGEMENT_6, 3, 0),
SND_SOC_DAPM_DAC("DACR", "Right Playback", WM8903_POWER_MANAGEMENT_6, 2, 0),
SND_SOC_DAPM_AIF_IN("AIFRXL", "Left Playback", 0, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_AIF_IN("AIFRXR", "Right Playback", 0, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_MUX("Left Playback Mux", SND_SOC_NOPM, 0, 0, &lplay_mux),
SND_SOC_DAPM_MUX("Right Playback Mux", SND_SOC_NOPM, 0, 0, &rplay_mux),
SND_SOC_DAPM_DAC("DACL", NULL, WM8903_POWER_MANAGEMENT_6, 3, 0),
SND_SOC_DAPM_DAC("DACR", NULL, WM8903_POWER_MANAGEMENT_6, 2, 0),
SND_SOC_DAPM_MIXER("Left Output Mixer", WM8903_POWER_MANAGEMENT_1, 1, 0,
left_output_mixer, ARRAY_SIZE(left_output_mixer)),
@ -866,23 +875,45 @@ SND_SOC_DAPM_MIXER("Left Speaker Mixer", WM8903_POWER_MANAGEMENT_4, 1, 0,
SND_SOC_DAPM_MIXER("Right Speaker Mixer", WM8903_POWER_MANAGEMENT_4, 0, 0,
right_speaker_mixer, ARRAY_SIZE(right_speaker_mixer)),
SND_SOC_DAPM_PGA_E("Left Headphone Output PGA", WM8903_POWER_MANAGEMENT_2,
1, 0, NULL, 0, wm8903_output_event,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
SND_SOC_DAPM_PRE_PMD),
SND_SOC_DAPM_PGA_E("Right Headphone Output PGA", WM8903_POWER_MANAGEMENT_2,
0, 0, NULL, 0, wm8903_output_event,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
SND_SOC_DAPM_PRE_PMD),
SND_SOC_DAPM_PGA_S("Left Headphone Output PGA", 0, WM8903_ANALOGUE_HP_0,
4, 0, NULL, 0),
SND_SOC_DAPM_PGA_S("Right Headphone Output PGA", 0, WM8903_ANALOGUE_HP_0,
0, 0, NULL, 0),
SND_SOC_DAPM_PGA_E("Left Line Output PGA", WM8903_POWER_MANAGEMENT_3, 1, 0,
NULL, 0, wm8903_output_event,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
SND_SOC_DAPM_PRE_PMD),
SND_SOC_DAPM_PGA_E("Right Line Output PGA", WM8903_POWER_MANAGEMENT_3, 0, 0,
NULL, 0, wm8903_output_event,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
SND_SOC_DAPM_PRE_PMD),
SND_SOC_DAPM_PGA_S("Left Line Output PGA", 0, WM8903_ANALOGUE_LINEOUT_0, 4, 0,
NULL, 0),
SND_SOC_DAPM_PGA_S("Right Line Output PGA", 0, WM8903_ANALOGUE_LINEOUT_0, 0, 0,
NULL, 0),
SND_SOC_DAPM_PGA_S("HPL_RMV_SHORT", 4, WM8903_ANALOGUE_HP_0, 7, 0, NULL, 0),
SND_SOC_DAPM_PGA_S("HPL_ENA_OUTP", 3, WM8903_ANALOGUE_HP_0, 6, 0, NULL, 0),
SND_SOC_DAPM_PGA_S("HPL_ENA_DLY", 1, WM8903_ANALOGUE_HP_0, 5, 0, NULL, 0),
SND_SOC_DAPM_PGA_S("HPR_RMV_SHORT", 4, WM8903_ANALOGUE_HP_0, 3, 0, NULL, 0),
SND_SOC_DAPM_PGA_S("HPR_ENA_OUTP", 3, WM8903_ANALOGUE_HP_0, 2, 0, NULL, 0),
SND_SOC_DAPM_PGA_S("HPR_ENA_DLY", 1, WM8903_ANALOGUE_HP_0, 1, 0, NULL, 0),
SND_SOC_DAPM_PGA_S("LINEOUTL_RMV_SHORT", 4, WM8903_ANALOGUE_LINEOUT_0, 7, 0,
NULL, 0),
SND_SOC_DAPM_PGA_S("LINEOUTL_ENA_OUTP", 3, WM8903_ANALOGUE_LINEOUT_0, 6, 0,
NULL, 0),
SND_SOC_DAPM_PGA_S("LINEOUTL_ENA_DLY", 1, WM8903_ANALOGUE_LINEOUT_0, 5, 0,
NULL, 0),
SND_SOC_DAPM_PGA_S("LINEOUTR_RMV_SHORT", 4, WM8903_ANALOGUE_LINEOUT_0, 3, 0,
NULL, 0),
SND_SOC_DAPM_PGA_S("LINEOUTR_ENA_OUTP", 3, WM8903_ANALOGUE_LINEOUT_0, 2, 0,
NULL, 0),
SND_SOC_DAPM_PGA_S("LINEOUTR_ENA_DLY", 1, WM8903_ANALOGUE_LINEOUT_0, 1, 0,
NULL, 0),
SND_SOC_DAPM_SUPPLY("DCS Master", WM8903_DC_SERVO_0, 4, 0, NULL, 0),
SND_SOC_DAPM_PGA_S("HPL_DCS", 3, SND_SOC_NOPM, 3, 0, wm8903_dcs_event,
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
SND_SOC_DAPM_PGA_S("HPR_DCS", 3, SND_SOC_NOPM, 2, 0, wm8903_dcs_event,
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
SND_SOC_DAPM_PGA_S("LINEOUTL_DCS", 3, SND_SOC_NOPM, 1, 0, wm8903_dcs_event,
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
SND_SOC_DAPM_PGA_S("LINEOUTR_DCS", 3, SND_SOC_NOPM, 0, 0, wm8903_dcs_event,
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
SND_SOC_DAPM_PGA("Left Speaker PGA", WM8903_POWER_MANAGEMENT_5, 1, 0,
NULL, 0),
@ -892,10 +923,18 @@ SND_SOC_DAPM_PGA("Right Speaker PGA", WM8903_POWER_MANAGEMENT_5, 0, 0,
SND_SOC_DAPM_SUPPLY("Charge Pump", WM8903_CHARGE_PUMP_0, 0, 0,
wm8903_cp_event, SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_SUPPLY("CLK_DSP", WM8903_CLOCK_RATES_2, 1, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("CLK_SYS", WM8903_CLOCK_RATES_2, 2, 0, NULL, 0),
};
static const struct snd_soc_dapm_route intercon[] = {
{ "CLK_DSP", NULL, "CLK_SYS" },
{ "Mic Bias", NULL, "CLK_SYS" },
{ "HPL_DCS", NULL, "CLK_SYS" },
{ "HPR_DCS", NULL, "CLK_SYS" },
{ "LINEOUTL_DCS", NULL, "CLK_SYS" },
{ "LINEOUTR_DCS", NULL, "CLK_SYS" },
{ "Left Input Mux", "IN1L", "IN1L" },
{ "Left Input Mux", "IN2L", "IN2L" },
{ "Left Input Mux", "IN3L", "IN3L" },
@ -936,18 +975,36 @@ static const struct snd_soc_dapm_route intercon[] = {
{ "Left Input PGA", NULL, "Left Input Mode Mux" },
{ "Right Input PGA", NULL, "Right Input Mode Mux" },
{ "Left Capture Mux", "Left", "ADCL" },
{ "Left Capture Mux", "Right", "ADCR" },
{ "Right Capture Mux", "Left", "ADCL" },
{ "Right Capture Mux", "Right", "ADCR" },
{ "AIFTXL", NULL, "Left Capture Mux" },
{ "AIFTXR", NULL, "Right Capture Mux" },
{ "ADCL", NULL, "Left Input PGA" },
{ "ADCL", NULL, "CLK_DSP" },
{ "ADCR", NULL, "Right Input PGA" },
{ "ADCR", NULL, "CLK_DSP" },
{ "Left Playback Mux", "Left", "AIFRXL" },
{ "Left Playback Mux", "Right", "AIFRXR" },
{ "Right Playback Mux", "Left", "AIFRXL" },
{ "Right Playback Mux", "Right", "AIFRXR" },
{ "DACL Sidetone", "Left", "ADCL" },
{ "DACL Sidetone", "Right", "ADCR" },
{ "DACR Sidetone", "Left", "ADCL" },
{ "DACR Sidetone", "Right", "ADCR" },
{ "DACL", NULL, "Left Playback Mux" },
{ "DACL", NULL, "DACL Sidetone" },
{ "DACL", NULL, "CLK_DSP" },
{ "DACR", NULL, "Right Playback Mux" },
{ "DACR", NULL, "DACR Sidetone" },
{ "DACR", NULL, "CLK_DSP" },
@ -980,11 +1037,35 @@ static const struct snd_soc_dapm_route intercon[] = {
{ "Left Speaker PGA", NULL, "Left Speaker Mixer" },
{ "Right Speaker PGA", NULL, "Right Speaker Mixer" },
{ "HPOUTL", NULL, "Left Headphone Output PGA" },
{ "HPOUTR", NULL, "Right Headphone Output PGA" },
{ "HPL_ENA_DLY", NULL, "Left Headphone Output PGA" },
{ "HPR_ENA_DLY", NULL, "Right Headphone Output PGA" },
{ "LINEOUTL_ENA_DLY", NULL, "Left Line Output PGA" },
{ "LINEOUTR_ENA_DLY", NULL, "Right Line Output PGA" },
{ "LINEOUTL", NULL, "Left Line Output PGA" },
{ "LINEOUTR", NULL, "Right Line Output PGA" },
{ "HPL_DCS", NULL, "DCS Master" },
{ "HPR_DCS", NULL, "DCS Master" },
{ "LINEOUTL_DCS", NULL, "DCS Master" },
{ "LINEOUTR_DCS", NULL, "DCS Master" },
{ "HPL_DCS", NULL, "HPL_ENA_DLY" },
{ "HPR_DCS", NULL, "HPR_ENA_DLY" },
{ "LINEOUTL_DCS", NULL, "LINEOUTL_ENA_DLY" },
{ "LINEOUTR_DCS", NULL, "LINEOUTR_ENA_DLY" },
{ "HPL_ENA_OUTP", NULL, "HPL_DCS" },
{ "HPR_ENA_OUTP", NULL, "HPR_DCS" },
{ "LINEOUTL_ENA_OUTP", NULL, "LINEOUTL_DCS" },
{ "LINEOUTR_ENA_OUTP", NULL, "LINEOUTR_DCS" },
{ "HPL_RMV_SHORT", NULL, "HPL_ENA_OUTP" },
{ "HPR_RMV_SHORT", NULL, "HPR_ENA_OUTP" },
{ "LINEOUTL_RMV_SHORT", NULL, "LINEOUTL_ENA_OUTP" },
{ "LINEOUTR_RMV_SHORT", NULL, "LINEOUTR_ENA_OUTP" },
{ "HPOUTL", NULL, "HPL_RMV_SHORT" },
{ "HPOUTR", NULL, "HPR_RMV_SHORT" },
{ "LINEOUTL", NULL, "LINEOUTL_RMV_SHORT" },
{ "LINEOUTR", NULL, "LINEOUTR_RMV_SHORT" },
{ "LOP", NULL, "Left Speaker PGA" },
{ "LON", NULL, "Left Speaker PGA" },
@ -1012,29 +1093,71 @@ static int wm8903_add_widgets(struct snd_soc_codec *codec)
static int wm8903_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
u16 reg;
switch (level) {
case SND_SOC_BIAS_ON:
break;
case SND_SOC_BIAS_PREPARE:
reg = snd_soc_read(codec, WM8903_VMID_CONTROL_0);
reg &= ~(WM8903_VMID_RES_MASK);
reg |= WM8903_VMID_RES_50K;
snd_soc_write(codec, WM8903_VMID_CONTROL_0, reg);
snd_soc_update_bits(codec, WM8903_VMID_CONTROL_0,
WM8903_VMID_RES_MASK,
WM8903_VMID_RES_50K);
break;
case SND_SOC_BIAS_STANDBY:
if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
snd_soc_write(codec, WM8903_CLOCK_RATES_2,
WM8903_CLK_SYS_ENA);
snd_soc_update_bits(codec, WM8903_BIAS_CONTROL_0,
WM8903_POBCTRL | WM8903_ISEL_MASK |
WM8903_STARTUP_BIAS_ENA |
WM8903_BIAS_ENA,
WM8903_POBCTRL |
(2 << WM8903_ISEL_SHIFT) |
WM8903_STARTUP_BIAS_ENA);
/* Change DC servo dither level in startup sequence */
snd_soc_write(codec, WM8903_WRITE_SEQUENCER_0, 0x11);
snd_soc_write(codec, WM8903_WRITE_SEQUENCER_1, 0x1257);
snd_soc_write(codec, WM8903_WRITE_SEQUENCER_2, 0x2);
snd_soc_update_bits(codec,
WM8903_ANALOGUE_SPK_OUTPUT_CONTROL_0,
WM8903_SPK_DISCHARGE,
WM8903_SPK_DISCHARGE);
wm8903_run_sequence(codec, 0);
wm8903_sync_reg_cache(codec, codec->reg_cache);
msleep(33);
snd_soc_update_bits(codec, WM8903_POWER_MANAGEMENT_5,
WM8903_SPKL_ENA | WM8903_SPKR_ENA,
WM8903_SPKL_ENA | WM8903_SPKR_ENA);
snd_soc_update_bits(codec,
WM8903_ANALOGUE_SPK_OUTPUT_CONTROL_0,
WM8903_SPK_DISCHARGE, 0);
snd_soc_update_bits(codec, WM8903_VMID_CONTROL_0,
WM8903_VMID_TIE_ENA |
WM8903_BUFIO_ENA |
WM8903_VMID_IO_ENA |
WM8903_VMID_SOFT_MASK |
WM8903_VMID_RES_MASK |
WM8903_VMID_BUF_ENA,
WM8903_VMID_TIE_ENA |
WM8903_BUFIO_ENA |
WM8903_VMID_IO_ENA |
(2 << WM8903_VMID_SOFT_SHIFT) |
WM8903_VMID_RES_250K |
WM8903_VMID_BUF_ENA);
msleep(129);
snd_soc_update_bits(codec, WM8903_POWER_MANAGEMENT_5,
WM8903_SPKL_ENA | WM8903_SPKR_ENA,
0);
snd_soc_update_bits(codec, WM8903_VMID_CONTROL_0,
WM8903_VMID_SOFT_MASK, 0);
snd_soc_update_bits(codec, WM8903_VMID_CONTROL_0,
WM8903_VMID_RES_MASK,
WM8903_VMID_RES_50K);
snd_soc_update_bits(codec, WM8903_BIAS_CONTROL_0,
WM8903_BIAS_ENA | WM8903_POBCTRL,
WM8903_BIAS_ENA);
/* By default no bypass paths are enabled so
* enable Class W support.
@ -1047,17 +1170,32 @@ static int wm8903_set_bias_level(struct snd_soc_codec *codec,
WM8903_CP_DYN_V);
}
reg = snd_soc_read(codec, WM8903_VMID_CONTROL_0);
reg &= ~(WM8903_VMID_RES_MASK);
reg |= WM8903_VMID_RES_250K;
snd_soc_write(codec, WM8903_VMID_CONTROL_0, reg);
snd_soc_update_bits(codec, WM8903_VMID_CONTROL_0,
WM8903_VMID_RES_MASK,
WM8903_VMID_RES_250K);
break;
case SND_SOC_BIAS_OFF:
wm8903_run_sequence(codec, 32);
reg = snd_soc_read(codec, WM8903_CLOCK_RATES_2);
reg &= ~WM8903_CLK_SYS_ENA;
snd_soc_write(codec, WM8903_CLOCK_RATES_2, reg);
snd_soc_update_bits(codec, WM8903_BIAS_CONTROL_0,
WM8903_BIAS_ENA, 0);
snd_soc_update_bits(codec, WM8903_VMID_CONTROL_0,
WM8903_VMID_SOFT_MASK,
2 << WM8903_VMID_SOFT_SHIFT);
snd_soc_update_bits(codec, WM8903_VMID_CONTROL_0,
WM8903_VMID_BUF_ENA, 0);
msleep(290);
snd_soc_update_bits(codec, WM8903_VMID_CONTROL_0,
WM8903_VMID_TIE_ENA | WM8903_BUFIO_ENA |
WM8903_VMID_IO_ENA | WM8903_VMID_RES_MASK |
WM8903_VMID_SOFT_MASK |
WM8903_VMID_BUF_ENA, 0);
snd_soc_update_bits(codec, WM8903_BIAS_CONTROL_0,
WM8903_STARTUP_BIAS_ENA, 0);
break;
}
@ -1510,8 +1648,7 @@ static irqreturn_t wm8903_irq(int irq, void *data)
int_val = snd_soc_read(codec, WM8903_INTERRUPT_STATUS_1) & mask;
if (int_val & WM8903_WSEQ_BUSY_EINT) {
dev_dbg(codec->dev, "Write sequencer done\n");
complete(&wm8903->wseq);
dev_warn(codec->dev, "Write sequencer done\n");
}
/*
@ -1635,6 +1772,120 @@ static int wm8903_resume(struct snd_soc_codec *codec)
return 0;
}
#ifdef CONFIG_GPIOLIB
static inline struct wm8903_priv *gpio_to_wm8903(struct gpio_chip *chip)
{
return container_of(chip, struct wm8903_priv, gpio_chip);
}
static int wm8903_gpio_request(struct gpio_chip *chip, unsigned offset)
{
if (offset >= WM8903_NUM_GPIO)
return -EINVAL;
return 0;
}
static int wm8903_gpio_direction_in(struct gpio_chip *chip, unsigned offset)
{
struct wm8903_priv *wm8903 = gpio_to_wm8903(chip);
struct snd_soc_codec *codec = wm8903->codec;
unsigned int mask, val;
mask = WM8903_GP1_FN_MASK | WM8903_GP1_DIR_MASK;
val = (WM8903_GPn_FN_GPIO_INPUT << WM8903_GP1_FN_SHIFT) |
WM8903_GP1_DIR;
return snd_soc_update_bits(codec, WM8903_GPIO_CONTROL_1 + offset,
mask, val);
}
static int wm8903_gpio_get(struct gpio_chip *chip, unsigned offset)
{
struct wm8903_priv *wm8903 = gpio_to_wm8903(chip);
struct snd_soc_codec *codec = wm8903->codec;
int reg;
reg = snd_soc_read(codec, WM8903_GPIO_CONTROL_1 + offset);
return (reg & WM8903_GP1_LVL_MASK) >> WM8903_GP1_LVL_SHIFT;
}
static int wm8903_gpio_direction_out(struct gpio_chip *chip,
unsigned offset, int value)
{
struct wm8903_priv *wm8903 = gpio_to_wm8903(chip);
struct snd_soc_codec *codec = wm8903->codec;
unsigned int mask, val;
mask = WM8903_GP1_FN_MASK | WM8903_GP1_DIR_MASK | WM8903_GP1_LVL_MASK;
val = (WM8903_GPn_FN_GPIO_OUTPUT << WM8903_GP1_FN_SHIFT) |
(value << WM8903_GP2_LVL_SHIFT);
return snd_soc_update_bits(codec, WM8903_GPIO_CONTROL_1 + offset,
mask, val);
}
static void wm8903_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
{
struct wm8903_priv *wm8903 = gpio_to_wm8903(chip);
struct snd_soc_codec *codec = wm8903->codec;
snd_soc_update_bits(codec, WM8903_GPIO_CONTROL_1 + offset,
WM8903_GP1_LVL_MASK,
!!value << WM8903_GP1_LVL_SHIFT);
}
static struct gpio_chip wm8903_template_chip = {
.label = "wm8903",
.owner = THIS_MODULE,
.request = wm8903_gpio_request,
.direction_input = wm8903_gpio_direction_in,
.get = wm8903_gpio_get,
.direction_output = wm8903_gpio_direction_out,
.set = wm8903_gpio_set,
.can_sleep = 1,
};
static void wm8903_init_gpio(struct snd_soc_codec *codec)
{
struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec);
struct wm8903_platform_data *pdata = dev_get_platdata(codec->dev);
int ret;
wm8903->gpio_chip = wm8903_template_chip;
wm8903->gpio_chip.ngpio = WM8903_NUM_GPIO;
wm8903->gpio_chip.dev = codec->dev;
if (pdata && pdata->gpio_base)
wm8903->gpio_chip.base = pdata->gpio_base;
else
wm8903->gpio_chip.base = -1;
ret = gpiochip_add(&wm8903->gpio_chip);
if (ret != 0)
dev_err(codec->dev, "Failed to add GPIOs: %d\n", ret);
}
static void wm8903_free_gpio(struct snd_soc_codec *codec)
{
struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec);
int ret;
ret = gpiochip_remove(&wm8903->gpio_chip);
if (ret != 0)
dev_err(codec->dev, "Failed to remove GPIOs: %d\n", ret);
}
#else
static void wm8903_init_gpio(struct snd_soc_codec *codec)
{
}
static void wm8903_free_gpio(struct snd_soc_codec *codec)
{
}
#endif
static int wm8903_probe(struct snd_soc_codec *codec)
{
struct wm8903_platform_data *pdata = dev_get_platdata(codec->dev);
@ -1643,7 +1894,7 @@ static int wm8903_probe(struct snd_soc_codec *codec)
int trigger, irq_pol;
u16 val;
init_completion(&wm8903->wseq);
wm8903->codec = codec;
ret = snd_soc_codec_set_cache_io(codec, 8, 16, SND_SOC_I2C);
if (ret != 0) {
@ -1659,19 +1910,33 @@ static int wm8903_probe(struct snd_soc_codec *codec)
}
val = snd_soc_read(codec, WM8903_REVISION_NUMBER);
dev_info(codec->dev, "WM8903 revision %d\n",
val & WM8903_CHIP_REV_MASK);
dev_info(codec->dev, "WM8903 revision %c\n",
(val & WM8903_CHIP_REV_MASK) + 'A');
wm8903_reset(codec);
/* Set up GPIOs and microphone detection */
if (pdata) {
bool mic_gpio = false;
for (i = 0; i < ARRAY_SIZE(pdata->gpio_cfg); i++) {
if (!pdata->gpio_cfg[i])
if (pdata->gpio_cfg[i] == WM8903_GPIO_NO_CONFIG)
continue;
snd_soc_write(codec, WM8903_GPIO_CONTROL_1 + i,
pdata->gpio_cfg[i] & 0xffff);
val = (pdata->gpio_cfg[i] & WM8903_GP1_FN_MASK)
>> WM8903_GP1_FN_SHIFT;
switch (val) {
case WM8903_GPn_FN_MICBIAS_CURRENT_DETECT:
case WM8903_GPn_FN_MICBIAS_SHORT_DETECT:
mic_gpio = true;
break;
default:
break;
}
}
snd_soc_write(codec, WM8903_MIC_BIAS_CONTROL_0,
@ -1682,6 +1947,14 @@ static int wm8903_probe(struct snd_soc_codec *codec)
snd_soc_update_bits(codec, WM8903_WRITE_SEQUENCER_0,
WM8903_WSEQ_ENA, WM8903_WSEQ_ENA);
/* If microphone detection is enabled by pdata but
* detected via IRQ then interrupts can be lost before
* the machine driver has set up microphone detection
* IRQs as the IRQs are clear on read. The detection
* will be enabled when the machine driver configures.
*/
WARN_ON(!mic_gpio && (pdata->micdet_cfg & WM8903_MICDET_ENA));
wm8903->mic_delay = pdata->micdet_delay;
}
@ -1741,20 +2014,23 @@ static int wm8903_probe(struct snd_soc_codec *codec)
snd_soc_write(codec, WM8903_ANALOGUE_OUT3_RIGHT, val);
/* Enable DAC soft mute by default */
val = snd_soc_read(codec, WM8903_DAC_DIGITAL_1);
val |= WM8903_DAC_MUTEMODE;
snd_soc_write(codec, WM8903_DAC_DIGITAL_1, val);
snd_soc_update_bits(codec, WM8903_DAC_DIGITAL_1,
WM8903_DAC_MUTEMODE | WM8903_DAC_MUTE,
WM8903_DAC_MUTEMODE | WM8903_DAC_MUTE);
snd_soc_add_controls(codec, wm8903_snd_controls,
ARRAY_SIZE(wm8903_snd_controls));
wm8903_add_widgets(codec);
wm8903_init_gpio(codec);
return ret;
}
/* power down chip */
static int wm8903_remove(struct snd_soc_codec *codec)
{
wm8903_free_gpio(codec);
wm8903_set_bias_level(codec, SND_SOC_BIAS_OFF);
return 0;
}
@ -1769,6 +2045,7 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8903 = {
.reg_word_size = sizeof(u16),
.reg_cache_default = wm8903_reg_defaults,
.volatile_register = wm8903_volatile_register,
.seq_notifier = wm8903_seq_notifier,
};
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
@ -1807,7 +2084,7 @@ MODULE_DEVICE_TABLE(i2c, wm8903_i2c_id);
static struct i2c_driver wm8903_i2c_driver = {
.driver = {
.name = "wm8903-codec",
.name = "wm8903",
.owner = THIS_MODULE,
},
.probe = wm8903_i2c_probe,

View file

@ -75,6 +75,14 @@ extern int wm8903_mic_detect(struct snd_soc_codec *codec,
#define WM8903_ANALOGUE_SPK_OUTPUT_CONTROL_0 0x41
#define WM8903_DC_SERVO_0 0x43
#define WM8903_DC_SERVO_2 0x45
#define WM8903_DC_SERVO_4 0x47
#define WM8903_DC_SERVO_5 0x48
#define WM8903_DC_SERVO_6 0x49
#define WM8903_DC_SERVO_7 0x4A
#define WM8903_DC_SERVO_READBACK_1 0x51
#define WM8903_DC_SERVO_READBACK_2 0x52
#define WM8903_DC_SERVO_READBACK_3 0x53
#define WM8903_DC_SERVO_READBACK_4 0x54
#define WM8903_ANALOGUE_HP_0 0x5A
#define WM8903_ANALOGUE_LINEOUT_0 0x5E
#define WM8903_CHARGE_PUMP_0 0x62

View file

@ -596,7 +596,7 @@ static struct {
{ 0x003F, 0x003F, 0 }, /* R248 - FLL NCO Test 1 */
};
static int wm8904_volatile_register(unsigned int reg)
static int wm8904_volatile_register(struct snd_soc_codec *codec, unsigned int reg)
{
return wm8904_access[reg].vol;
}
@ -2436,19 +2436,28 @@ static int wm8904_probe(struct snd_soc_codec *codec)
}
/* Change some default settings - latch VU and enable ZC */
reg_cache[WM8904_ADC_DIGITAL_VOLUME_LEFT] |= WM8904_ADC_VU;
reg_cache[WM8904_ADC_DIGITAL_VOLUME_RIGHT] |= WM8904_ADC_VU;
reg_cache[WM8904_DAC_DIGITAL_VOLUME_LEFT] |= WM8904_DAC_VU;
reg_cache[WM8904_DAC_DIGITAL_VOLUME_RIGHT] |= WM8904_DAC_VU;
reg_cache[WM8904_ANALOGUE_OUT1_LEFT] |= WM8904_HPOUT_VU |
WM8904_HPOUTLZC;
reg_cache[WM8904_ANALOGUE_OUT1_RIGHT] |= WM8904_HPOUT_VU |
WM8904_HPOUTRZC;
reg_cache[WM8904_ANALOGUE_OUT2_LEFT] |= WM8904_LINEOUT_VU |
WM8904_LINEOUTLZC;
reg_cache[WM8904_ANALOGUE_OUT2_RIGHT] |= WM8904_LINEOUT_VU |
WM8904_LINEOUTRZC;
reg_cache[WM8904_CLOCK_RATES_0] &= ~WM8904_SR_MODE;
snd_soc_update_bits(codec, WM8904_ADC_DIGITAL_VOLUME_LEFT,
WM8904_ADC_VU, WM8904_ADC_VU);
snd_soc_update_bits(codec, WM8904_ADC_DIGITAL_VOLUME_RIGHT,
WM8904_ADC_VU, WM8904_ADC_VU);
snd_soc_update_bits(codec, WM8904_DAC_DIGITAL_VOLUME_LEFT,
WM8904_DAC_VU, WM8904_DAC_VU);
snd_soc_update_bits(codec, WM8904_DAC_DIGITAL_VOLUME_RIGHT,
WM8904_DAC_VU, WM8904_DAC_VU);
snd_soc_update_bits(codec, WM8904_ANALOGUE_OUT1_LEFT,
WM8904_HPOUT_VU | WM8904_HPOUTLZC,
WM8904_HPOUT_VU | WM8904_HPOUTLZC);
snd_soc_update_bits(codec, WM8904_ANALOGUE_OUT1_RIGHT,
WM8904_HPOUT_VU | WM8904_HPOUTRZC,
WM8904_HPOUT_VU | WM8904_HPOUTRZC);
snd_soc_update_bits(codec, WM8904_ANALOGUE_OUT2_LEFT,
WM8904_LINEOUT_VU | WM8904_LINEOUTLZC,
WM8904_LINEOUT_VU | WM8904_LINEOUTLZC);
snd_soc_update_bits(codec, WM8904_ANALOGUE_OUT2_RIGHT,
WM8904_LINEOUT_VU | WM8904_LINEOUTRZC,
WM8904_LINEOUT_VU | WM8904_LINEOUTRZC);
snd_soc_update_bits(codec, WM8904_CLOCK_RATES_0,
WM8904_SR_MODE, 0);
/* Apply configuration from the platform data. */
if (wm8904->pdata) {
@ -2469,10 +2478,12 @@ static int wm8904_probe(struct snd_soc_codec *codec)
/* Set Class W by default - this will be managed by the Class
* G widget at runtime where bypass paths are available.
*/
reg_cache[WM8904_CLASS_W_0] |= WM8904_CP_DYN_PWR;
snd_soc_update_bits(codec, WM8904_CLASS_W_0,
WM8904_CP_DYN_PWR, WM8904_CP_DYN_PWR);
/* Use normal bias source */
reg_cache[WM8904_BIAS_CONTROL_0] &= ~WM8904_POBCTRL;
snd_soc_update_bits(codec, WM8904_BIAS_CONTROL_0,
WM8904_POBCTRL, 0);
wm8904_set_bias_level(codec, SND_SOC_BIAS_STANDBY);

View file

@ -934,16 +934,27 @@ static int wm8955_probe(struct snd_soc_codec *codec)
}
/* Change some default settings - latch VU and enable ZC */
reg_cache[WM8955_LEFT_DAC_VOLUME] |= WM8955_LDVU;
reg_cache[WM8955_RIGHT_DAC_VOLUME] |= WM8955_RDVU;
reg_cache[WM8955_LOUT1_VOLUME] |= WM8955_LO1VU | WM8955_LO1ZC;
reg_cache[WM8955_ROUT1_VOLUME] |= WM8955_RO1VU | WM8955_RO1ZC;
reg_cache[WM8955_LOUT2_VOLUME] |= WM8955_LO2VU | WM8955_LO2ZC;
reg_cache[WM8955_ROUT2_VOLUME] |= WM8955_RO2VU | WM8955_RO2ZC;
reg_cache[WM8955_MONOOUT_VOLUME] |= WM8955_MOZC;
snd_soc_update_bits(codec, WM8955_LEFT_DAC_VOLUME,
WM8955_LDVU, WM8955_LDVU);
snd_soc_update_bits(codec, WM8955_RIGHT_DAC_VOLUME,
WM8955_RDVU, WM8955_RDVU);
snd_soc_update_bits(codec, WM8955_LOUT1_VOLUME,
WM8955_LO1VU | WM8955_LO1ZC,
WM8955_LO1VU | WM8955_LO1ZC);
snd_soc_update_bits(codec, WM8955_ROUT1_VOLUME,
WM8955_RO1VU | WM8955_RO1ZC,
WM8955_RO1VU | WM8955_RO1ZC);
snd_soc_update_bits(codec, WM8955_LOUT2_VOLUME,
WM8955_LO2VU | WM8955_LO2ZC,
WM8955_LO2VU | WM8955_LO2ZC);
snd_soc_update_bits(codec, WM8955_ROUT2_VOLUME,
WM8955_RO2VU | WM8955_RO2ZC,
WM8955_RO2VU | WM8955_RO2ZC);
snd_soc_update_bits(codec, WM8955_MONOOUT_VOLUME,
WM8955_MOZC, WM8955_MOZC);
/* Also enable adaptive bass boost by default */
reg_cache[WM8955_BASS_CONTROL] |= WM8955_BB;
snd_soc_update_bits(codec, WM8955_BASS_CONTROL, WM8955_BB, WM8955_BB);
/* Set platform data values */
if (pdata) {

View file

@ -291,7 +291,7 @@ struct wm8961_priv {
int sysclk;
};
static int wm8961_volatile_register(unsigned int reg)
static int wm8961_volatile_register(struct snd_soc_codec *codec, unsigned int reg)
{
switch (reg) {
case WM8961_SOFTWARE_RESET:

View file

@ -1938,7 +1938,7 @@ static const struct wm8962_reg_access {
[21139] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R21139 - VSS_XTS32_0 */
};
static int wm8962_volatile_register(unsigned int reg)
static int wm8962_volatile_register(struct snd_soc_codec *codec, unsigned int reg)
{
if (wm8962_reg_access[reg].vol)
return 1;
@ -1946,7 +1946,7 @@ static int wm8962_volatile_register(unsigned int reg)
return 0;
}
static int wm8962_readable_register(unsigned int reg)
static int wm8962_readable_register(struct snd_soc_codec *codec, unsigned int reg)
{
if (wm8962_reg_access[reg].read)
return 1;
@ -3635,7 +3635,7 @@ static void wm8962_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
struct snd_soc_codec *codec = wm8962->codec;
snd_soc_update_bits(codec, WM8962_GPIO_BASE + offset,
WM8962_GP2_LVL, value << WM8962_GP2_LVL_SHIFT);
WM8962_GP2_LVL, !!value << WM8962_GP2_LVL_SHIFT);
}
static int wm8962_gpio_direction_out(struct gpio_chip *chip,
@ -3822,16 +3822,26 @@ static int wm8962_probe(struct snd_soc_codec *codec)
}
/* Latch volume update bits */
reg_cache[WM8962_LEFT_INPUT_VOLUME] |= WM8962_IN_VU;
reg_cache[WM8962_RIGHT_INPUT_VOLUME] |= WM8962_IN_VU;
reg_cache[WM8962_LEFT_ADC_VOLUME] |= WM8962_ADC_VU;
reg_cache[WM8962_RIGHT_ADC_VOLUME] |= WM8962_ADC_VU;
reg_cache[WM8962_LEFT_DAC_VOLUME] |= WM8962_DAC_VU;
reg_cache[WM8962_RIGHT_DAC_VOLUME] |= WM8962_DAC_VU;
reg_cache[WM8962_SPKOUTL_VOLUME] |= WM8962_SPKOUT_VU;
reg_cache[WM8962_SPKOUTR_VOLUME] |= WM8962_SPKOUT_VU;
reg_cache[WM8962_HPOUTL_VOLUME] |= WM8962_HPOUT_VU;
reg_cache[WM8962_HPOUTR_VOLUME] |= WM8962_HPOUT_VU;
snd_soc_update_bits(codec, WM8962_LEFT_INPUT_VOLUME,
WM8962_IN_VU, WM8962_IN_VU);
snd_soc_update_bits(codec, WM8962_RIGHT_INPUT_VOLUME,
WM8962_IN_VU, WM8962_IN_VU);
snd_soc_update_bits(codec, WM8962_LEFT_ADC_VOLUME,
WM8962_ADC_VU, WM8962_ADC_VU);
snd_soc_update_bits(codec, WM8962_RIGHT_ADC_VOLUME,
WM8962_ADC_VU, WM8962_ADC_VU);
snd_soc_update_bits(codec, WM8962_LEFT_DAC_VOLUME,
WM8962_DAC_VU, WM8962_DAC_VU);
snd_soc_update_bits(codec, WM8962_RIGHT_DAC_VOLUME,
WM8962_DAC_VU, WM8962_DAC_VU);
snd_soc_update_bits(codec, WM8962_SPKOUTL_VOLUME,
WM8962_SPKOUT_VU, WM8962_SPKOUT_VU);
snd_soc_update_bits(codec, WM8962_SPKOUTR_VOLUME,
WM8962_SPKOUT_VU, WM8962_SPKOUT_VU);
snd_soc_update_bits(codec, WM8962_HPOUTL_VOLUME,
WM8962_HPOUT_VU, WM8962_HPOUT_VU);
snd_soc_update_bits(codec, WM8962_HPOUTR_VOLUME,
WM8962_HPOUT_VU, WM8962_HPOUT_VU);
wm8962_add_widgets(codec);

View file

@ -93,6 +93,7 @@ static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
static const DECLARE_TLV_DB_SCALE(inpga_tlv, -1200, 75, 0);
static const DECLARE_TLV_DB_SCALE(spk_tlv, -5700, 100, 0);
static const DECLARE_TLV_DB_SCALE(boost_tlv, -1500, 300, 1);
static const DECLARE_TLV_DB_SCALE(limiter_tlv, 0, 100, 0);
static const struct snd_kcontrol_new wm8978_snd_controls[] = {
@ -144,19 +145,19 @@ static const struct snd_kcontrol_new wm8978_snd_controls[] = {
SOC_SINGLE("DAC Playback Limiter Threshold",
WM8978_DAC_LIMITER_2, 4, 7, 0),
SOC_SINGLE("DAC Playback Limiter Boost",
WM8978_DAC_LIMITER_2, 0, 15, 0),
SOC_SINGLE_TLV("DAC Playback Limiter Volume",
WM8978_DAC_LIMITER_2, 0, 12, 0, limiter_tlv),
SOC_ENUM("ALC Enable Switch", alc1),
SOC_SINGLE("ALC Capture Min Gain", WM8978_ALC_CONTROL_1, 0, 7, 0),
SOC_SINGLE("ALC Capture Max Gain", WM8978_ALC_CONTROL_1, 3, 7, 0),
SOC_SINGLE("ALC Capture Hold", WM8978_ALC_CONTROL_2, 4, 7, 0),
SOC_SINGLE("ALC Capture Hold", WM8978_ALC_CONTROL_2, 4, 10, 0),
SOC_SINGLE("ALC Capture Target", WM8978_ALC_CONTROL_2, 0, 15, 0),
SOC_ENUM("ALC Capture Mode", alc3),
SOC_SINGLE("ALC Capture Decay", WM8978_ALC_CONTROL_3, 4, 15, 0),
SOC_SINGLE("ALC Capture Attack", WM8978_ALC_CONTROL_3, 0, 15, 0),
SOC_SINGLE("ALC Capture Decay", WM8978_ALC_CONTROL_3, 4, 10, 0),
SOC_SINGLE("ALC Capture Attack", WM8978_ALC_CONTROL_3, 0, 10, 0),
SOC_SINGLE("ALC Capture Noise Gate Switch", WM8978_NOISE_GATE, 3, 1, 0),
SOC_SINGLE("ALC Capture Noise Gate Threshold",
@ -211,8 +212,10 @@ static const struct snd_kcontrol_new wm8978_snd_controls[] = {
WM8978_LOUT2_SPK_CONTROL, WM8978_ROUT2_SPK_CONTROL, 6, 1, 1),
/* DAC / ADC oversampling */
SOC_SINGLE("DAC 128x Oversampling Switch", WM8978_DAC_CONTROL, 8, 1, 0),
SOC_SINGLE("ADC 128x Oversampling Switch", WM8978_ADC_CONTROL, 8, 1, 0),
SOC_SINGLE("DAC 128x Oversampling Switch", WM8978_DAC_CONTROL,
5, 1, 0),
SOC_SINGLE("ADC 128x Oversampling Switch", WM8978_ADC_CONTROL,
5, 1, 0),
};
/* Mixer #1: Output (OUT1, OUT2) Mixer: mix AUX, Input mixer output and DAC */
@ -965,7 +968,7 @@ static int wm8978_probe(struct snd_soc_codec *codec)
* written.
*/
for (i = 0; i < ARRAY_SIZE(update_reg); i++)
((u16 *)codec->reg_cache)[update_reg[i]] |= 0x100;
snd_soc_update_bits(codec, update_reg[i], 0x100, 0x100);
/* Reset the codec */
ret = snd_soc_write(codec, WM8978_RESET, 0);

1427
sound/soc/codecs/wm8991.c Normal file

File diff suppressed because it is too large Load diff

833
sound/soc/codecs/wm8991.h Normal file
View file

@ -0,0 +1,833 @@
/*
* wm8991.h -- audio driver for WM8991
*
* Copyright 2007 Wolfson Microelectronics PLC.
* Author: Graeme Gregory
* graeme.gregory@wolfsonmicro.com or linux@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.
*/
#ifndef _WM8991_H
#define _WM8991_H
/*
* Register values.
*/
#define WM8991_RESET 0x00
#define WM8991_POWER_MANAGEMENT_1 0x01
#define WM8991_POWER_MANAGEMENT_2 0x02
#define WM8991_POWER_MANAGEMENT_3 0x03
#define WM8991_AUDIO_INTERFACE_1 0x04
#define WM8991_AUDIO_INTERFACE_2 0x05
#define WM8991_CLOCKING_1 0x06
#define WM8991_CLOCKING_2 0x07
#define WM8991_AUDIO_INTERFACE_3 0x08
#define WM8991_AUDIO_INTERFACE_4 0x09
#define WM8991_DAC_CTRL 0x0A
#define WM8991_LEFT_DAC_DIGITAL_VOLUME 0x0B
#define WM8991_RIGHT_DAC_DIGITAL_VOLUME 0x0C
#define WM8991_DIGITAL_SIDE_TONE 0x0D
#define WM8991_ADC_CTRL 0x0E
#define WM8991_LEFT_ADC_DIGITAL_VOLUME 0x0F
#define WM8991_RIGHT_ADC_DIGITAL_VOLUME 0x10
#define WM8991_GPIO_CTRL_1 0x12
#define WM8991_GPIO1_GPIO2 0x13
#define WM8991_GPIO3_GPIO4 0x14
#define WM8991_GPIO5_GPIO6 0x15
#define WM8991_GPIOCTRL_2 0x16
#define WM8991_GPIO_POL 0x17
#define WM8991_LEFT_LINE_INPUT_1_2_VOLUME 0x18
#define WM8991_LEFT_LINE_INPUT_3_4_VOLUME 0x19
#define WM8991_RIGHT_LINE_INPUT_1_2_VOLUME 0x1A
#define WM8991_RIGHT_LINE_INPUT_3_4_VOLUME 0x1B
#define WM8991_LEFT_OUTPUT_VOLUME 0x1C
#define WM8991_RIGHT_OUTPUT_VOLUME 0x1D
#define WM8991_LINE_OUTPUTS_VOLUME 0x1E
#define WM8991_OUT3_4_VOLUME 0x1F
#define WM8991_LEFT_OPGA_VOLUME 0x20
#define WM8991_RIGHT_OPGA_VOLUME 0x21
#define WM8991_SPEAKER_VOLUME 0x22
#define WM8991_CLASSD1 0x23
#define WM8991_CLASSD3 0x25
#define WM8991_INPUT_MIXER1 0x27
#define WM8991_INPUT_MIXER2 0x28
#define WM8991_INPUT_MIXER3 0x29
#define WM8991_INPUT_MIXER4 0x2A
#define WM8991_INPUT_MIXER5 0x2B
#define WM8991_INPUT_MIXER6 0x2C
#define WM8991_OUTPUT_MIXER1 0x2D
#define WM8991_OUTPUT_MIXER2 0x2E
#define WM8991_OUTPUT_MIXER3 0x2F
#define WM8991_OUTPUT_MIXER4 0x30
#define WM8991_OUTPUT_MIXER5 0x31
#define WM8991_OUTPUT_MIXER6 0x32
#define WM8991_OUT3_4_MIXER 0x33
#define WM8991_LINE_MIXER1 0x34
#define WM8991_LINE_MIXER2 0x35
#define WM8991_SPEAKER_MIXER 0x36
#define WM8991_ADDITIONAL_CONTROL 0x37
#define WM8991_ANTIPOP1 0x38
#define WM8991_ANTIPOP2 0x39
#define WM8991_MICBIAS 0x3A
#define WM8991_PLL1 0x3C
#define WM8991_PLL2 0x3D
#define WM8991_PLL3 0x3E
#define WM8991_INTDRIVBITS 0x3F
#define WM8991_REGISTER_COUNT 60
#define WM8991_MAX_REGISTER 0x3F
/*
* Field Definitions.
*/
/*
* R0 (0x00) - Reset
*/
#define WM8991_SW_RESET_CHIP_ID_MASK 0xFFFF /* SW_RESET_CHIP_ID - [15:0] */
/*
* R1 (0x01) - Power Management (1)
*/
#define WM8991_SPK_ENA 0x1000 /* SPK_ENA */
#define WM8991_SPK_ENA_BIT 12
#define WM8991_OUT3_ENA 0x0800 /* OUT3_ENA */
#define WM8991_OUT3_ENA_BIT 11
#define WM8991_OUT4_ENA 0x0400 /* OUT4_ENA */
#define WM8991_OUT4_ENA_BIT 10
#define WM8991_LOUT_ENA 0x0200 /* LOUT_ENA */
#define WM8991_LOUT_ENA_BIT 9
#define WM8991_ROUT_ENA 0x0100 /* ROUT_ENA */
#define WM8991_ROUT_ENA_BIT 8
#define WM8991_MICBIAS_ENA 0x0010 /* MICBIAS_ENA */
#define WM8991_MICBIAS_ENA_BIT 4
#define WM8991_VMID_MODE_MASK 0x0006 /* VMID_MODE - [2:1] */
#define WM8991_VREF_ENA 0x0001 /* VREF_ENA */
#define WM8991_VREF_ENA_BIT 0
/*
* R2 (0x02) - Power Management (2)
*/
#define WM8991_PLL_ENA 0x8000 /* PLL_ENA */
#define WM8991_PLL_ENA_BIT 15
#define WM8991_TSHUT_ENA 0x4000 /* TSHUT_ENA */
#define WM8991_TSHUT_ENA_BIT 14
#define WM8991_TSHUT_OPDIS 0x2000 /* TSHUT_OPDIS */
#define WM8991_TSHUT_OPDIS_BIT 13
#define WM8991_OPCLK_ENA 0x0800 /* OPCLK_ENA */
#define WM8991_OPCLK_ENA_BIT 11
#define WM8991_AINL_ENA 0x0200 /* AINL_ENA */
#define WM8991_AINL_ENA_BIT 9
#define WM8991_AINR_ENA 0x0100 /* AINR_ENA */
#define WM8991_AINR_ENA_BIT 8
#define WM8991_LIN34_ENA 0x0080 /* LIN34_ENA */
#define WM8991_LIN34_ENA_BIT 7
#define WM8991_LIN12_ENA 0x0040 /* LIN12_ENA */
#define WM8991_LIN12_ENA_BIT 6
#define WM8991_RIN34_ENA 0x0020 /* RIN34_ENA */
#define WM8991_RIN34_ENA_BIT 5
#define WM8991_RIN12_ENA 0x0010 /* RIN12_ENA */
#define WM8991_RIN12_ENA_BIT 4
#define WM8991_ADCL_ENA 0x0002 /* ADCL_ENA */
#define WM8991_ADCL_ENA_BIT 1
#define WM8991_ADCR_ENA 0x0001 /* ADCR_ENA */
#define WM8991_ADCR_ENA_BIT 0
/*
* R3 (0x03) - Power Management (3)
*/
#define WM8991_LON_ENA 0x2000 /* LON_ENA */
#define WM8991_LON_ENA_BIT 13
#define WM8991_LOP_ENA 0x1000 /* LOP_ENA */
#define WM8991_LOP_ENA_BIT 12
#define WM8991_RON_ENA 0x0800 /* RON_ENA */
#define WM8991_RON_ENA_BIT 11
#define WM8991_ROP_ENA 0x0400 /* ROP_ENA */
#define WM8991_ROP_ENA_BIT 10
#define WM8991_LOPGA_ENA 0x0080 /* LOPGA_ENA */
#define WM8991_LOPGA_ENA_BIT 7
#define WM8991_ROPGA_ENA 0x0040 /* ROPGA_ENA */
#define WM8991_ROPGA_ENA_BIT 6
#define WM8991_LOMIX_ENA 0x0020 /* LOMIX_ENA */
#define WM8991_LOMIX_ENA_BIT 5
#define WM8991_ROMIX_ENA 0x0010 /* ROMIX_ENA */
#define WM8991_ROMIX_ENA_BIT 4
#define WM8991_DACL_ENA 0x0002 /* DACL_ENA */
#define WM8991_DACL_ENA_BIT 1
#define WM8991_DACR_ENA 0x0001 /* DACR_ENA */
#define WM8991_DACR_ENA_BIT 0
/*
* R4 (0x04) - Audio Interface (1)
*/
#define WM8991_AIFADCL_SRC 0x8000 /* AIFADCL_SRC */
#define WM8991_AIFADCR_SRC 0x4000 /* AIFADCR_SRC */
#define WM8991_AIFADC_TDM 0x2000 /* AIFADC_TDM */
#define WM8991_AIFADC_TDM_CHAN 0x1000 /* AIFADC_TDM_CHAN */
#define WM8991_AIF_BCLK_INV 0x0100 /* AIF_BCLK_INV */
#define WM8991_AIF_LRCLK_INV 0x0080 /* AIF_LRCLK_INV */
#define WM8991_AIF_WL_MASK 0x0060 /* AIF_WL - [6:5] */
#define WM8991_AIF_WL_16BITS (0 << 5)
#define WM8991_AIF_WL_20BITS (1 << 5)
#define WM8991_AIF_WL_24BITS (2 << 5)
#define WM8991_AIF_WL_32BITS (3 << 5)
#define WM8991_AIF_FMT_MASK 0x0018 /* AIF_FMT - [4:3] */
#define WM8991_AIF_TMF_RIGHTJ (0 << 3)
#define WM8991_AIF_TMF_LEFTJ (1 << 3)
#define WM8991_AIF_TMF_I2S (2 << 3)
#define WM8991_AIF_TMF_DSP (3 << 3)
/*
* R5 (0x05) - Audio Interface (2)
*/
#define WM8991_DACL_SRC 0x8000 /* DACL_SRC */
#define WM8991_DACR_SRC 0x4000 /* DACR_SRC */
#define WM8991_AIFDAC_TDM 0x2000 /* AIFDAC_TDM */
#define WM8991_AIFDAC_TDM_CHAN 0x1000 /* AIFDAC_TDM_CHAN */
#define WM8991_DAC_BOOST_MASK 0x0C00 /* DAC_BOOST - [11:10] */
#define WM8991_DAC_COMP 0x0010 /* DAC_COMP */
#define WM8991_DAC_COMPMODE 0x0008 /* DAC_COMPMODE */
#define WM8991_ADC_COMP 0x0004 /* ADC_COMP */
#define WM8991_ADC_COMPMODE 0x0002 /* ADC_COMPMODE */
#define WM8991_LOOPBACK 0x0001 /* LOOPBACK */
/*
* R6 (0x06) - Clocking (1)
*/
#define WM8991_TOCLK_RATE 0x8000 /* TOCLK_RATE */
#define WM8991_TOCLK_ENA 0x4000 /* TOCLK_ENA */
#define WM8991_OPCLKDIV_MASK 0x1E00 /* OPCLKDIV - [12:9] */
#define WM8991_DCLKDIV_MASK 0x01C0 /* DCLKDIV - [8:6] */
#define WM8991_BCLK_DIV_MASK 0x001E /* BCLK_DIV - [4:1] */
#define WM8991_BCLK_DIV_1 (0x0 << 1)
#define WM8991_BCLK_DIV_1_5 (0x1 << 1)
#define WM8991_BCLK_DIV_2 (0x2 << 1)
#define WM8991_BCLK_DIV_3 (0x3 << 1)
#define WM8991_BCLK_DIV_4 (0x4 << 1)
#define WM8991_BCLK_DIV_5_5 (0x5 << 1)
#define WM8991_BCLK_DIV_6 (0x6 << 1)
#define WM8991_BCLK_DIV_8 (0x7 << 1)
#define WM8991_BCLK_DIV_11 (0x8 << 1)
#define WM8991_BCLK_DIV_12 (0x9 << 1)
#define WM8991_BCLK_DIV_16 (0xA << 1)
#define WM8991_BCLK_DIV_22 (0xB << 1)
#define WM8991_BCLK_DIV_24 (0xC << 1)
#define WM8991_BCLK_DIV_32 (0xD << 1)
#define WM8991_BCLK_DIV_44 (0xE << 1)
#define WM8991_BCLK_DIV_48 (0xF << 1)
/*
* R7 (0x07) - Clocking (2)
*/
#define WM8991_MCLK_SRC 0x8000 /* MCLK_SRC */
#define WM8991_SYSCLK_SRC 0x4000 /* SYSCLK_SRC */
#define WM8991_CLK_FORCE 0x2000 /* CLK_FORCE */
#define WM8991_MCLK_DIV_MASK 0x1800 /* MCLK_DIV - [12:11] */
#define WM8991_MCLK_DIV_1 (0 << 11)
#define WM8991_MCLK_DIV_2 ( 2 << 11)
#define WM8991_MCLK_INV 0x0400 /* MCLK_INV */
#define WM8991_ADC_CLKDIV_MASK 0x00E0 /* ADC_CLKDIV - [7:5] */
#define WM8991_ADC_CLKDIV_1 (0 << 5)
#define WM8991_ADC_CLKDIV_1_5 (1 << 5)
#define WM8991_ADC_CLKDIV_2 (2 << 5)
#define WM8991_ADC_CLKDIV_3 (3 << 5)
#define WM8991_ADC_CLKDIV_4 (4 << 5)
#define WM8991_ADC_CLKDIV_5_5 (5 << 5)
#define WM8991_ADC_CLKDIV_6 (6 << 5)
#define WM8991_DAC_CLKDIV_MASK 0x001C /* DAC_CLKDIV - [4:2] */
#define WM8991_DAC_CLKDIV_1 (0 << 2)
#define WM8991_DAC_CLKDIV_1_5 (1 << 2)
#define WM8991_DAC_CLKDIV_2 (2 << 2)
#define WM8991_DAC_CLKDIV_3 (3 << 2)
#define WM8991_DAC_CLKDIV_4 (4 << 2)
#define WM8991_DAC_CLKDIV_5_5 (5 << 2)
#define WM8991_DAC_CLKDIV_6 (6 << 2)
/*
* R8 (0x08) - Audio Interface (3)
*/
#define WM8991_AIF_MSTR1 0x8000 /* AIF_MSTR1 */
#define WM8991_AIF_MSTR2 0x4000 /* AIF_MSTR2 */
#define WM8991_AIF_SEL 0x2000 /* AIF_SEL */
#define WM8991_ADCLRC_DIR 0x0800 /* ADCLRC_DIR */
#define WM8991_ADCLRC_RATE_MASK 0x07FF /* ADCLRC_RATE - [10:0] */
/*
* R9 (0x09) - Audio Interface (4)
*/
#define WM8991_ALRCGPIO1 0x8000 /* ALRCGPIO1 */
#define WM8991_ALRCBGPIO6 0x4000 /* ALRCBGPIO6 */
#define WM8991_AIF_TRIS 0x2000 /* AIF_TRIS */
#define WM8991_DACLRC_DIR 0x0800 /* DACLRC_DIR */
#define WM8991_DACLRC_RATE_MASK 0x07FF /* DACLRC_RATE - [10:0] */
/*
* R10 (0x0A) - DAC CTRL
*/
#define WM8991_AIF_LRCLKRATE 0x0400 /* AIF_LRCLKRATE */
#define WM8991_DAC_MONO 0x0200 /* DAC_MONO */
#define WM8991_DAC_SB_FILT 0x0100 /* DAC_SB_FILT */
#define WM8991_DAC_MUTERATE 0x0080 /* DAC_MUTERATE */
#define WM8991_DAC_MUTEMODE 0x0040 /* DAC_MUTEMODE */
#define WM8991_DEEMP_MASK 0x0030 /* DEEMP - [5:4] */
#define WM8991_DAC_MUTE 0x0004 /* DAC_MUTE */
#define WM8991_DACL_DATINV 0x0002 /* DACL_DATINV */
#define WM8991_DACR_DATINV 0x0001 /* DACR_DATINV */
/*
* R11 (0x0B) - Left DAC Digital Volume
*/
#define WM8991_DAC_VU 0x0100 /* DAC_VU */
#define WM8991_DACL_VOL_MASK 0x00FF /* DACL_VOL - [7:0] */
#define WM8991_DACL_VOL_SHIFT 0
/*
* R12 (0x0C) - Right DAC Digital Volume
*/
#define WM8991_DAC_VU 0x0100 /* DAC_VU */
#define WM8991_DACR_VOL_MASK 0x00FF /* DACR_VOL - [7:0] */
#define WM8991_DACR_VOL_SHIFT 0
/*
* R13 (0x0D) - Digital Side Tone
*/
#define WM8991_ADCL_DAC_SVOL_MASK 0x0F /* ADCL_DAC_SVOL - [12:9] */
#define WM8991_ADCL_DAC_SVOL_SHIFT 9
#define WM8991_ADCR_DAC_SVOL_MASK 0x0F /* ADCR_DAC_SVOL - [8:5] */
#define WM8991_ADCR_DAC_SVOL_SHIFT 5
#define WM8991_ADC_TO_DACL_MASK 0x03 /* ADC_TO_DACL - [3:2] */
#define WM8991_ADC_TO_DACL_SHIFT 2
#define WM8991_ADC_TO_DACR_MASK 0x03 /* ADC_TO_DACR - [1:0] */
#define WM8991_ADC_TO_DACR_SHIFT 0
/*
* R14 (0x0E) - ADC CTRL
*/
#define WM8991_ADC_HPF_ENA 0x0100 /* ADC_HPF_ENA */
#define WM8991_ADC_HPF_ENA_BIT 8
#define WM8991_ADC_HPF_CUT_MASK 0x03 /* ADC_HPF_CUT - [6:5] */
#define WM8991_ADC_HPF_CUT_SHIFT 5
#define WM8991_ADCL_DATINV 0x0002 /* ADCL_DATINV */
#define WM8991_ADCL_DATINV_BIT 1
#define WM8991_ADCR_DATINV 0x0001 /* ADCR_DATINV */
#define WM8991_ADCR_DATINV_BIT 0
/*
* R15 (0x0F) - Left ADC Digital Volume
*/
#define WM8991_ADC_VU 0x0100 /* ADC_VU */
#define WM8991_ADCL_VOL_MASK 0x00FF /* ADCL_VOL - [7:0] */
#define WM8991_ADCL_VOL_SHIFT 0
/*
* R16 (0x10) - Right ADC Digital Volume
*/
#define WM8991_ADC_VU 0x0100 /* ADC_VU */
#define WM8991_ADCR_VOL_MASK 0x00FF /* ADCR_VOL - [7:0] */
#define WM8991_ADCR_VOL_SHIFT 0
/*
* R18 (0x12) - GPIO CTRL 1
*/
#define WM8991_IRQ 0x1000 /* IRQ */
#define WM8991_TEMPOK 0x0800 /* TEMPOK */
#define WM8991_MICSHRT 0x0400 /* MICSHRT */
#define WM8991_MICDET 0x0200 /* MICDET */
#define WM8991_PLL_LCK 0x0100 /* PLL_LCK */
#define WM8991_GPI8_STATUS 0x0080 /* GPI8_STATUS */
#define WM8991_GPI7_STATUS 0x0040 /* GPI7_STATUS */
#define WM8991_GPIO6_STATUS 0x0020 /* GPIO6_STATUS */
#define WM8991_GPIO5_STATUS 0x0010 /* GPIO5_STATUS */
#define WM8991_GPIO4_STATUS 0x0008 /* GPIO4_STATUS */
#define WM8991_GPIO3_STATUS 0x0004 /* GPIO3_STATUS */
#define WM8991_GPIO2_STATUS 0x0002 /* GPIO2_STATUS */
#define WM8991_GPIO1_STATUS 0x0001 /* GPIO1_STATUS */
/*
* R19 (0x13) - GPIO1 & GPIO2
*/
#define WM8991_GPIO2_DEB_ENA 0x8000 /* GPIO2_DEB_ENA */
#define WM8991_GPIO2_IRQ_ENA 0x4000 /* GPIO2_IRQ_ENA */
#define WM8991_GPIO2_PU 0x2000 /* GPIO2_PU */
#define WM8991_GPIO2_PD 0x1000 /* GPIO2_PD */
#define WM8991_GPIO2_SEL_MASK 0x0F00 /* GPIO2_SEL - [11:8] */
#define WM8991_GPIO1_DEB_ENA 0x0080 /* GPIO1_DEB_ENA */
#define WM8991_GPIO1_IRQ_ENA 0x0040 /* GPIO1_IRQ_ENA */
#define WM8991_GPIO1_PU 0x0020 /* GPIO1_PU */
#define WM8991_GPIO1_PD 0x0010 /* GPIO1_PD */
#define WM8991_GPIO1_SEL_MASK 0x000F /* GPIO1_SEL - [3:0] */
/*
* R20 (0x14) - GPIO3 & GPIO4
*/
#define WM8991_GPIO4_DEB_ENA 0x8000 /* GPIO4_DEB_ENA */
#define WM8991_GPIO4_IRQ_ENA 0x4000 /* GPIO4_IRQ_ENA */
#define WM8991_GPIO4_PU 0x2000 /* GPIO4_PU */
#define WM8991_GPIO4_PD 0x1000 /* GPIO4_PD */
#define WM8991_GPIO4_SEL_MASK 0x0F00 /* GPIO4_SEL - [11:8] */
#define WM8991_GPIO3_DEB_ENA 0x0080 /* GPIO3_DEB_ENA */
#define WM8991_GPIO3_IRQ_ENA 0x0040 /* GPIO3_IRQ_ENA */
#define WM8991_GPIO3_PU 0x0020 /* GPIO3_PU */
#define WM8991_GPIO3_PD 0x0010 /* GPIO3_PD */
#define WM8991_GPIO3_SEL_MASK 0x000F /* GPIO3_SEL - [3:0] */
/*
* R21 (0x15) - GPIO5 & GPIO6
*/
#define WM8991_GPIO6_DEB_ENA 0x8000 /* GPIO6_DEB_ENA */
#define WM8991_GPIO6_IRQ_ENA 0x4000 /* GPIO6_IRQ_ENA */
#define WM8991_GPIO6_PU 0x2000 /* GPIO6_PU */
#define WM8991_GPIO6_PD 0x1000 /* GPIO6_PD */
#define WM8991_GPIO6_SEL_MASK 0x0F00 /* GPIO6_SEL - [11:8] */
#define WM8991_GPIO5_DEB_ENA 0x0080 /* GPIO5_DEB_ENA */
#define WM8991_GPIO5_IRQ_ENA 0x0040 /* GPIO5_IRQ_ENA */
#define WM8991_GPIO5_PU 0x0020 /* GPIO5_PU */
#define WM8991_GPIO5_PD 0x0010 /* GPIO5_PD */
#define WM8991_GPIO5_SEL_MASK 0x000F /* GPIO5_SEL - [3:0] */
/*
* R22 (0x16) - GPIOCTRL 2
*/
#define WM8991_RD_3W_ENA 0x8000 /* RD_3W_ENA */
#define WM8991_MODE_3W4W 0x4000 /* MODE_3W4W */
#define WM8991_TEMPOK_IRQ_ENA 0x0800 /* TEMPOK_IRQ_ENA */
#define WM8991_MICSHRT_IRQ_ENA 0x0400 /* MICSHRT_IRQ_ENA */
#define WM8991_MICDET_IRQ_ENA 0x0200 /* MICDET_IRQ_ENA */
#define WM8991_PLL_LCK_IRQ_ENA 0x0100 /* PLL_LCK_IRQ_ENA */
#define WM8991_GPI8_DEB_ENA 0x0080 /* GPI8_DEB_ENA */
#define WM8991_GPI8_IRQ_ENA 0x0040 /* GPI8_IRQ_ENA */
#define WM8991_GPI8_ENA 0x0010 /* GPI8_ENA */
#define WM8991_GPI7_DEB_ENA 0x0008 /* GPI7_DEB_ENA */
#define WM8991_GPI7_IRQ_ENA 0x0004 /* GPI7_IRQ_ENA */
#define WM8991_GPI7_ENA 0x0001 /* GPI7_ENA */
/*
* R23 (0x17) - GPIO_POL
*/
#define WM8991_IRQ_INV 0x1000 /* IRQ_INV */
#define WM8991_TEMPOK_POL 0x0800 /* TEMPOK_POL */
#define WM8991_MICSHRT_POL 0x0400 /* MICSHRT_POL */
#define WM8991_MICDET_POL 0x0200 /* MICDET_POL */
#define WM8991_PLL_LCK_POL 0x0100 /* PLL_LCK_POL */
#define WM8991_GPI8_POL 0x0080 /* GPI8_POL */
#define WM8991_GPI7_POL 0x0040 /* GPI7_POL */
#define WM8991_GPIO6_POL 0x0020 /* GPIO6_POL */
#define WM8991_GPIO5_POL 0x0010 /* GPIO5_POL */
#define WM8991_GPIO4_POL 0x0008 /* GPIO4_POL */
#define WM8991_GPIO3_POL 0x0004 /* GPIO3_POL */
#define WM8991_GPIO2_POL 0x0002 /* GPIO2_POL */
#define WM8991_GPIO1_POL 0x0001 /* GPIO1_POL */
/*
* R24 (0x18) - Left Line Input 1&2 Volume
*/
#define WM8991_IPVU 0x0100 /* IPVU */
#define WM8991_LI12MUTE 0x0080 /* LI12MUTE */
#define WM8991_LI12MUTE_BIT 7
#define WM8991_LI12ZC 0x0040 /* LI12ZC */
#define WM8991_LI12ZC_BIT 6
#define WM8991_LIN12VOL_MASK 0x001F /* LIN12VOL - [4:0] */
#define WM8991_LIN12VOL_SHIFT 0
/*
* R25 (0x19) - Left Line Input 3&4 Volume
*/
#define WM8991_IPVU 0x0100 /* IPVU */
#define WM8991_LI34MUTE 0x0080 /* LI34MUTE */
#define WM8991_LI34MUTE_BIT 7
#define WM8991_LI34ZC 0x0040 /* LI34ZC */
#define WM8991_LI34ZC_BIT 6
#define WM8991_LIN34VOL_MASK 0x001F /* LIN34VOL - [4:0] */
#define WM8991_LIN34VOL_SHIFT 0
/*
* R26 (0x1A) - Right Line Input 1&2 Volume
*/
#define WM8991_IPVU 0x0100 /* IPVU */
#define WM8991_RI12MUTE 0x0080 /* RI12MUTE */
#define WM8991_RI12MUTE_BIT 7
#define WM8991_RI12ZC 0x0040 /* RI12ZC */
#define WM8991_RI12ZC_BIT 6
#define WM8991_RIN12VOL_MASK 0x001F /* RIN12VOL - [4:0] */
#define WM8991_RIN12VOL_SHIFT 0
/*
* R27 (0x1B) - Right Line Input 3&4 Volume
*/
#define WM8991_IPVU 0x0100 /* IPVU */
#define WM8991_RI34MUTE 0x0080 /* RI34MUTE */
#define WM8991_RI34MUTE_BIT 7
#define WM8991_RI34ZC 0x0040 /* RI34ZC */
#define WM8991_RI34ZC_BIT 6
#define WM8991_RIN34VOL_MASK 0x001F /* RIN34VOL - [4:0] */
#define WM8991_RIN34VOL_SHIFT 0
/*
* R28 (0x1C) - Left Output Volume
*/
#define WM8991_OPVU 0x0100 /* OPVU */
#define WM8991_LOZC 0x0080 /* LOZC */
#define WM8991_LOZC_BIT 7
#define WM8991_LOUTVOL_MASK 0x007F /* LOUTVOL - [6:0] */
#define WM8991_LOUTVOL_SHIFT 0
/*
* R29 (0x1D) - Right Output Volume
*/
#define WM8991_OPVU 0x0100 /* OPVU */
#define WM8991_ROZC 0x0080 /* ROZC */
#define WM8991_ROZC_BIT 7
#define WM8991_ROUTVOL_MASK 0x007F /* ROUTVOL - [6:0] */
#define WM8991_ROUTVOL_SHIFT 0
/*
* R30 (0x1E) - Line Outputs Volume
*/
#define WM8991_LONMUTE 0x0040 /* LONMUTE */
#define WM8991_LONMUTE_BIT 6
#define WM8991_LOPMUTE 0x0020 /* LOPMUTE */
#define WM8991_LOPMUTE_BIT 5
#define WM8991_LOATTN 0x0010 /* LOATTN */
#define WM8991_LOATTN_BIT 4
#define WM8991_RONMUTE 0x0004 /* RONMUTE */
#define WM8991_RONMUTE_BIT 2
#define WM8991_ROPMUTE 0x0002 /* ROPMUTE */
#define WM8991_ROPMUTE_BIT 1
#define WM8991_ROATTN 0x0001 /* ROATTN */
#define WM8991_ROATTN_BIT 0
/*
* R31 (0x1F) - Out3/4 Volume
*/
#define WM8991_OUT3MUTE 0x0020 /* OUT3MUTE */
#define WM8991_OUT3MUTE_BIT 5
#define WM8991_OUT3ATTN 0x0010 /* OUT3ATTN */
#define WM8991_OUT3ATTN_BIT 4
#define WM8991_OUT4MUTE 0x0002 /* OUT4MUTE */
#define WM8991_OUT4MUTE_BIT 1
#define WM8991_OUT4ATTN 0x0001 /* OUT4ATTN */
#define WM8991_OUT4ATTN_BIT 0
/*
* R32 (0x20) - Left OPGA Volume
*/
#define WM8991_OPVU 0x0100 /* OPVU */
#define WM8991_LOPGAZC 0x0080 /* LOPGAZC */
#define WM8991_LOPGAZC_BIT 7
#define WM8991_LOPGAVOL_MASK 0x007F /* LOPGAVOL - [6:0] */
#define WM8991_LOPGAVOL_SHIFT 0
/*
* R33 (0x21) - Right OPGA Volume
*/
#define WM8991_OPVU 0x0100 /* OPVU */
#define WM8991_ROPGAZC 0x0080 /* ROPGAZC */
#define WM8991_ROPGAZC_BIT 7
#define WM8991_ROPGAVOL_MASK 0x007F /* ROPGAVOL - [6:0] */
#define WM8991_ROPGAVOL_SHIFT 0
/*
* R34 (0x22) - Speaker Volume
*/
#define WM8991_SPKVOL_MASK 0x0003 /* SPKVOL - [1:0] */
#define WM8991_SPKVOL_SHIFT 0
/*
* R35 (0x23) - ClassD1
*/
#define WM8991_CDMODE 0x0100 /* CDMODE */
#define WM8991_CDMODE_BIT 8
/*
* R37 (0x25) - ClassD3
*/
#define WM8991_DCGAIN_MASK 0x0007 /* DCGAIN - [5:3] */
#define WM8991_DCGAIN_SHIFT 3
#define WM8991_ACGAIN_MASK 0x0007 /* ACGAIN - [2:0] */
#define WM8991_ACGAIN_SHIFT 0
/*
* R39 (0x27) - Input Mixer1
*/
#define WM8991_AINLMODE_MASK 0x000C /* AINLMODE - [3:2] */
#define WM8991_AINLMODE_SHIFT 2
#define WM8991_AINRMODE_MASK 0x0003 /* AINRMODE - [1:0] */
#define WM8991_AINRMODE_SHIFT 0
/*
* R40 (0x28) - Input Mixer2
*/
#define WM8991_LMP4 0x0080 /* LMP4 */
#define WM8991_LMP4_BIT 7 /* LMP4 */
#define WM8991_LMN3 0x0040 /* LMN3 */
#define WM8991_LMN3_BIT 6 /* LMN3 */
#define WM8991_LMP2 0x0020 /* LMP2 */
#define WM8991_LMP2_BIT 5 /* LMP2 */
#define WM8991_LMN1 0x0010 /* LMN1 */
#define WM8991_LMN1_BIT 4 /* LMN1 */
#define WM8991_RMP4 0x0008 /* RMP4 */
#define WM8991_RMP4_BIT 3 /* RMP4 */
#define WM8991_RMN3 0x0004 /* RMN3 */
#define WM8991_RMN3_BIT 2 /* RMN3 */
#define WM8991_RMP2 0x0002 /* RMP2 */
#define WM8991_RMP2_BIT 1 /* RMP2 */
#define WM8991_RMN1 0x0001 /* RMN1 */
#define WM8991_RMN1_BIT 0 /* RMN1 */
/*
* R41 (0x29) - Input Mixer3
*/
#define WM8991_L34MNB 0x0100 /* L34MNB */
#define WM8991_L34MNB_BIT 8
#define WM8991_L34MNBST 0x0080 /* L34MNBST */
#define WM8991_L34MNBST_BIT 7
#define WM8991_L12MNB 0x0020 /* L12MNB */
#define WM8991_L12MNB_BIT 5
#define WM8991_L12MNBST 0x0010 /* L12MNBST */
#define WM8991_L12MNBST_BIT 4
#define WM8991_LDBVOL_MASK 0x0007 /* LDBVOL - [2:0] */
#define WM8991_LDBVOL_SHIFT 0
/*
* R42 (0x2A) - Input Mixer4
*/
#define WM8991_R34MNB 0x0100 /* R34MNB */
#define WM8991_R34MNB_BIT 8
#define WM8991_R34MNBST 0x0080 /* R34MNBST */
#define WM8991_R34MNBST_BIT 7
#define WM8991_R12MNB 0x0020 /* R12MNB */
#define WM8991_R12MNB_BIT 5
#define WM8991_R12MNBST 0x0010 /* R12MNBST */
#define WM8991_R12MNBST_BIT 4
#define WM8991_RDBVOL_MASK 0x0007 /* RDBVOL - [2:0] */
#define WM8991_RDBVOL_SHIFT 0
/*
* R43 (0x2B) - Input Mixer5
*/
#define WM8991_LI2BVOL_MASK 0x07 /* LI2BVOL - [8:6] */
#define WM8991_LI2BVOL_SHIFT 6
#define WM8991_LR4BVOL_MASK 0x07 /* LR4BVOL - [5:3] */
#define WM8991_LR4BVOL_SHIFT 3
#define WM8991_LL4BVOL_MASK 0x07 /* LL4BVOL - [2:0] */
#define WM8991_LL4BVOL_SHIFT 0
/*
* R44 (0x2C) - Input Mixer6
*/
#define WM8991_RI2BVOL_MASK 0x07 /* RI2BVOL - [8:6] */
#define WM8991_RI2BVOL_SHIFT 6
#define WM8991_RL4BVOL_MASK 0x07 /* RL4BVOL - [5:3] */
#define WM8991_RL4BVOL_SHIFT 3
#define WM8991_RR4BVOL_MASK 0x07 /* RR4BVOL - [2:0] */
#define WM8991_RR4BVOL_SHIFT 0
/*
* R45 (0x2D) - Output Mixer1
*/
#define WM8991_LRBLO 0x0080 /* LRBLO */
#define WM8991_LRBLO_BIT 7
#define WM8991_LLBLO 0x0040 /* LLBLO */
#define WM8991_LLBLO_BIT 6
#define WM8991_LRI3LO 0x0020 /* LRI3LO */
#define WM8991_LRI3LO_BIT 5
#define WM8991_LLI3LO 0x0010 /* LLI3LO */
#define WM8991_LLI3LO_BIT 4
#define WM8991_LR12LO 0x0008 /* LR12LO */
#define WM8991_LR12LO_BIT 3
#define WM8991_LL12LO 0x0004 /* LL12LO */
#define WM8991_LL12LO_BIT 2
#define WM8991_LDLO 0x0001 /* LDLO */
#define WM8991_LDLO_BIT 0
/*
* R46 (0x2E) - Output Mixer2
*/
#define WM8991_RLBRO 0x0080 /* RLBRO */
#define WM8991_RLBRO_BIT 7
#define WM8991_RRBRO 0x0040 /* RRBRO */
#define WM8991_RRBRO_BIT 6
#define WM8991_RLI3RO 0x0020 /* RLI3RO */
#define WM8991_RLI3RO_BIT 5
#define WM8991_RRI3RO 0x0010 /* RRI3RO */
#define WM8991_RRI3RO_BIT 4
#define WM8991_RL12RO 0x0008 /* RL12RO */
#define WM8991_RL12RO_BIT 3
#define WM8991_RR12RO 0x0004 /* RR12RO */
#define WM8991_RR12RO_BIT 2
#define WM8991_RDRO 0x0001 /* RDRO */
#define WM8991_RDRO_BIT 0
/*
* R47 (0x2F) - Output Mixer3
*/
#define WM8991_LLI3LOVOL_MASK 0x07 /* LLI3LOVOL - [8:6] */
#define WM8991_LLI3LOVOL_SHIFT 6
#define WM8991_LR12LOVOL_MASK 0x07 /* LR12LOVOL - [5:3] */
#define WM8991_LR12LOVOL_SHIFT 3
#define WM8991_LL12LOVOL_MASK 0x07 /* LL12LOVOL - [2:0] */
#define WM8991_LL12LOVOL_SHIFT 0
/*
* R48 (0x30) - Output Mixer4
*/
#define WM8991_RRI3ROVOL_MASK 0x07 /* RRI3ROVOL - [8:6] */
#define WM8991_RRI3ROVOL_SHIFT 6
#define WM8991_RL12ROVOL_MASK 0x07 /* RL12ROVOL - [5:3] */
#define WM8991_RL12ROVOL_SHIFT 3
#define WM8991_RR12ROVOL_MASK 0x07 /* RR12ROVOL - [2:0] */
#define WM8991_RR12ROVOL_SHIFT 0
/*
* R49 (0x31) - Output Mixer5
*/
#define WM8991_LRI3LOVOL_MASK 0x07 /* LRI3LOVOL - [8:6] */
#define WM8991_LRI3LOVOL_SHIFT 6
#define WM8991_LRBLOVOL_MASK 0x07 /* LRBLOVOL - [5:3] */
#define WM8991_LRBLOVOL_SHIFT 3
#define WM8991_LLBLOVOL_MASK 0x07 /* LLBLOVOL - [2:0] */
#define WM8991_LLBLOVOL_SHIFT 0
/*
* R50 (0x32) - Output Mixer6
*/
#define WM8991_RLI3ROVOL_MASK 0x07 /* RLI3ROVOL - [8:6] */
#define WM8991_RLI3ROVOL_SHIFT 6
#define WM8991_RLBROVOL_MASK 0x07 /* RLBROVOL - [5:3] */
#define WM8991_RLBROVOL_SHIFT 3
#define WM8991_RRBROVOL_MASK 0x07 /* RRBROVOL - [2:0] */
#define WM8991_RRBROVOL_SHIFT 0
/*
* R51 (0x33) - Out3/4 Mixer
*/
#define WM8991_VSEL_MASK 0x0180 /* VSEL - [8:7] */
#define WM8991_LI4O3 0x0020 /* LI4O3 */
#define WM8991_LI4O3_BIT 5
#define WM8991_LPGAO3 0x0010 /* LPGAO3 */
#define WM8991_LPGAO3_BIT 4
#define WM8991_RI4O4 0x0002 /* RI4O4 */
#define WM8991_RI4O4_BIT 1
#define WM8991_RPGAO4 0x0001 /* RPGAO4 */
#define WM8991_RPGAO4_BIT 0
/*
* R52 (0x34) - Line Mixer1
*/
#define WM8991_LLOPGALON 0x0040 /* LLOPGALON */
#define WM8991_LLOPGALON_BIT 6
#define WM8991_LROPGALON 0x0020 /* LROPGALON */
#define WM8991_LROPGALON_BIT 5
#define WM8991_LOPLON 0x0010 /* LOPLON */
#define WM8991_LOPLON_BIT 4
#define WM8991_LR12LOP 0x0004 /* LR12LOP */
#define WM8991_LR12LOP_BIT 2
#define WM8991_LL12LOP 0x0002 /* LL12LOP */
#define WM8991_LL12LOP_BIT 1
#define WM8991_LLOPGALOP 0x0001 /* LLOPGALOP */
#define WM8991_LLOPGALOP_BIT 0
/*
* R53 (0x35) - Line Mixer2
*/
#define WM8991_RROPGARON 0x0040 /* RROPGARON */
#define WM8991_RROPGARON_BIT 6
#define WM8991_RLOPGARON 0x0020 /* RLOPGARON */
#define WM8991_RLOPGARON_BIT 5
#define WM8991_ROPRON 0x0010 /* ROPRON */
#define WM8991_ROPRON_BIT 4
#define WM8991_RL12ROP 0x0004 /* RL12ROP */
#define WM8991_RL12ROP_BIT 2
#define WM8991_RR12ROP 0x0002 /* RR12ROP */
#define WM8991_RR12ROP_BIT 1
#define WM8991_RROPGAROP 0x0001 /* RROPGAROP */
#define WM8991_RROPGAROP_BIT 0
/*
* R54 (0x36) - Speaker Mixer
*/
#define WM8991_LB2SPK 0x0080 /* LB2SPK */
#define WM8991_LB2SPK_BIT 7
#define WM8991_RB2SPK 0x0040 /* RB2SPK */
#define WM8991_RB2SPK_BIT 6
#define WM8991_LI2SPK 0x0020 /* LI2SPK */
#define WM8991_LI2SPK_BIT 5
#define WM8991_RI2SPK 0x0010 /* RI2SPK */
#define WM8991_RI2SPK_BIT 4
#define WM8991_LOPGASPK 0x0008 /* LOPGASPK */
#define WM8991_LOPGASPK_BIT 3
#define WM8991_ROPGASPK 0x0004 /* ROPGASPK */
#define WM8991_ROPGASPK_BIT 2
#define WM8991_LDSPK 0x0002 /* LDSPK */
#define WM8991_LDSPK_BIT 1
#define WM8991_RDSPK 0x0001 /* RDSPK */
#define WM8991_RDSPK_BIT 0
/*
* R55 (0x37) - Additional Control
*/
#define WM8991_VROI 0x0001 /* VROI */
/*
* R56 (0x38) - AntiPOP1
*/
#define WM8991_DIS_LLINE 0x0020 /* DIS_LLINE */
#define WM8991_DIS_RLINE 0x0010 /* DIS_RLINE */
#define WM8991_DIS_OUT3 0x0008 /* DIS_OUT3 */
#define WM8991_DIS_OUT4 0x0004 /* DIS_OUT4 */
#define WM8991_DIS_LOUT 0x0002 /* DIS_LOUT */
#define WM8991_DIS_ROUT 0x0001 /* DIS_ROUT */
/*
* R57 (0x39) - AntiPOP2
*/
#define WM8991_SOFTST 0x0040 /* SOFTST */
#define WM8991_BUFIOEN 0x0008 /* BUFIOEN */
#define WM8991_BUFDCOPEN 0x0004 /* BUFDCOPEN */
#define WM8991_POBCTRL 0x0002 /* POBCTRL */
#define WM8991_VMIDTOG 0x0001 /* VMIDTOG */
/*
* R58 (0x3A) - MICBIAS
*/
#define WM8991_MCDSCTH_MASK 0x00C0 /* MCDSCTH - [7:6] */
#define WM8991_MCDTHR_MASK 0x0038 /* MCDTHR - [5:3] */
#define WM8991_MCD 0x0004 /* MCD */
#define WM8991_MBSEL 0x0001 /* MBSEL */
/*
* R60 (0x3C) - PLL1
*/
#define WM8991_SDM 0x0080 /* SDM */
#define WM8991_PRESCALE 0x0040 /* PRESCALE */
#define WM8991_PLLN_MASK 0x000F /* PLLN - [3:0] */
/*
* R61 (0x3D) - PLL2
*/
#define WM8991_PLLK1_MASK 0x00FF /* PLLK1 - [7:0] */
/*
* R62 (0x3E) - PLL3
*/
#define WM8991_PLLK2_MASK 0x00FF /* PLLK2 - [7:0] */
/*
* R63 (0x3F) - Internal Driver Bits
*/
#define WM8991_INMIXL_PWR_BIT 0
#define WM8991_AINLMUX_PWR_BIT 1
#define WM8991_INMIXR_PWR_BIT 2
#define WM8991_AINRMUX_PWR_BIT 3
#define WM8991_MCLK_DIV 0
#define WM8991_DACCLK_DIV 1
#define WM8991_ADCCLK_DIV 2
#define WM8991_BCLK_DIV 3
#define SOC_WM899X_OUTPGA_SINGLE_R_TLV(xname, reg, shift, max, invert,\
tlv_array) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\
SNDRV_CTL_ELEM_ACCESS_READWRITE,\
.tlv.p = (tlv_array), \
.info = snd_soc_info_volsw, \
.get = snd_soc_get_volsw, .put = wm899x_outpga_put_volsw_vu, \
.private_value = SOC_SINGLE_VALUE(reg, shift, max, invert) }
#endif /* _WM8991_H */

View file

@ -242,7 +242,7 @@ struct wm8993_priv {
int fll_src;
};
static int wm8993_volatile(unsigned int reg)
static int wm8993_volatile(struct snd_soc_codec *codec, unsigned int reg)
{
switch (reg) {
case WM8993_SOFTWARE_RESET:

View file

@ -62,8 +62,8 @@ const struct wm8994_access_mask wm8994_access_masks[WM8994_CACHE_SIZE] = {
{ 0x00FF, 0x00FF }, /* R58 - MICBIAS */
{ 0x000F, 0x000F }, /* R59 - LDO 1 */
{ 0x0007, 0x0007 }, /* R60 - LDO 2 */
{ 0x0000, 0x0000 }, /* R61 */
{ 0x0000, 0x0000 }, /* R62 */
{ 0xFFFF, 0xFFFF }, /* R61 */
{ 0xFFFF, 0xFFFF }, /* R62 */
{ 0x0000, 0x0000 }, /* R63 */
{ 0x0000, 0x0000 }, /* R64 */
{ 0x0000, 0x0000 }, /* R65 */
@ -209,9 +209,9 @@ const struct wm8994_access_mask wm8994_access_masks[WM8994_CACHE_SIZE] = {
{ 0x0000, 0x0000 }, /* R205 */
{ 0x0000, 0x0000 }, /* R206 */
{ 0x0000, 0x0000 }, /* R207 */
{ 0x0000, 0x0000 }, /* R208 */
{ 0x0000, 0x0000 }, /* R209 */
{ 0x0000, 0x0000 }, /* R210 */
{ 0xFFFF, 0xFFFF }, /* R208 */
{ 0xFFFF, 0xFFFF }, /* R209 */
{ 0xFFFF, 0xFFFF }, /* R210 */
{ 0x0000, 0x0000 }, /* R211 */
{ 0x0000, 0x0000 }, /* R212 */
{ 0x0000, 0x0000 }, /* R213 */
@ -1573,7 +1573,7 @@ const struct wm8994_access_mask wm8994_access_masks[WM8994_CACHE_SIZE] = {
{ 0x03C3, 0x03C3 }, /* R1569 - Sidetone */
};
const __devinitdata u16 wm8994_reg_defaults[WM8994_CACHE_SIZE] = {
const u16 wm8994_reg_defaults[WM8994_CACHE_SIZE] = {
0x8994, /* R0 - Software Reset */
0x0000, /* R1 - Power Management (1) */
0x6000, /* R2 - Power Management (2) */

View file

@ -102,8 +102,7 @@ struct wm8994_priv {
wm8958_micdet_cb jack_cb;
void *jack_cb_data;
bool jack_is_mic;
bool jack_is_video;
int micdet_irq;
int revision;
struct wm8994_pdata *pdata;
@ -115,7 +114,7 @@ struct wm8994_priv {
unsigned int aif2clk_disable:1;
};
static int wm8994_readable(unsigned int reg)
static int wm8994_readable(struct snd_soc_codec *codec, unsigned int reg)
{
switch (reg) {
case WM8994_GPIO_1:
@ -142,7 +141,7 @@ static int wm8994_readable(unsigned int reg)
return wm8994_access_masks[reg].readable != 0;
}
static int wm8994_volatile(unsigned int reg)
static int wm8994_volatile(struct snd_soc_codec *codec, unsigned int reg)
{
if (reg >= WM8994_CACHE_SIZE)
return 1;
@ -170,7 +169,7 @@ static int wm8994_write(struct snd_soc_codec *codec, unsigned int reg,
BUG_ON(reg > WM8994_MAX_REGISTER);
if (!wm8994_volatile(reg)) {
if (!wm8994_volatile(codec, reg)) {
ret = snd_soc_cache_write(codec, reg, value);
if (ret != 0)
dev_err(codec->dev, "Cache write to %x failed: %d\n",
@ -188,7 +187,7 @@ static unsigned int wm8994_read(struct snd_soc_codec *codec,
BUG_ON(reg > WM8994_MAX_REGISTER);
if (!wm8994_volatile(reg) && wm8994_readable(reg) &&
if (!wm8994_volatile(codec, reg) && wm8994_readable(codec, reg) &&
reg < codec->driver->reg_cache_size) {
ret = snd_soc_cache_read(codec, reg, &val);
if (ret >= 0)
@ -529,7 +528,7 @@ static int wm8994_get_retune_mobile_enum(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
struct wm8994_priv *wm8994 =snd_soc_codec_get_drvdata(codec);
struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
int block = wm8994_get_retune_mobile_block(kcontrol->id.name);
ucontrol->value.enumerated.item[0] = wm8994->retune_mobile_cfg[block];
@ -1103,6 +1102,13 @@ static int adc_mux_ev(struct snd_soc_dapm_widget *w,
return 0;
}
static int micbias_ev(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
late_enable_ev(w, kcontrol, event);
return 0;
}
static int dac_ev(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
@ -1418,7 +1424,7 @@ SND_SOC_DAPM_DAC_E("DAC1R", NULL, SND_SOC_NOPM, 0, 0,
static const struct snd_soc_dapm_widget wm8994_dac_widgets[] = {
SND_SOC_DAPM_DAC("DAC2L", NULL, WM8994_POWER_MANAGEMENT_5, 3, 0),
SND_SOC_DAPM_DAC("DAC1R", NULL, WM8994_POWER_MANAGEMENT_5, 2, 0),
SND_SOC_DAPM_DAC("DAC2R", NULL, WM8994_POWER_MANAGEMENT_5, 2, 0),
SND_SOC_DAPM_DAC("DAC1L", NULL, WM8994_POWER_MANAGEMENT_5, 1, 0),
SND_SOC_DAPM_DAC("DAC1R", NULL, WM8994_POWER_MANAGEMENT_5, 0, 0),
};
@ -1440,6 +1446,10 @@ SND_SOC_DAPM_INPUT("DMIC1DAT"),
SND_SOC_DAPM_INPUT("DMIC2DAT"),
SND_SOC_DAPM_INPUT("Clock"),
SND_SOC_DAPM_MICBIAS("MICBIAS", WM8994_MICBIAS, 2, 0),
SND_SOC_DAPM_SUPPLY_S("MICBIAS Supply", 1, SND_SOC_NOPM, 0, 0, micbias_ev,
SND_SOC_DAPM_PRE_PMU),
SND_SOC_DAPM_SUPPLY("CLK_SYS", SND_SOC_NOPM, 0, 0, clk_sys_event,
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
@ -1755,6 +1765,8 @@ static const struct snd_soc_dapm_route wm8994_revd_intercon[] = {
{ "AIF2DACDAT", NULL, "AIF1DACDAT" },
{ "AIF1ADCDAT", NULL, "AIF2ADCDAT" },
{ "AIF2ADCDAT", NULL, "AIF1ADCDAT" },
{ "MICBIAS", NULL, "CLK_SYS" },
{ "MICBIAS", NULL, "MICBIAS Supply" },
};
static const struct snd_soc_dapm_route wm8994_intercon[] = {
@ -2883,6 +2895,13 @@ static void wm8994_handle_pdata(struct wm8994_priv *wm8994)
else
snd_soc_add_controls(wm8994->codec, wm8994_eq_controls,
ARRAY_SIZE(wm8994_eq_controls));
for (i = 0; i < ARRAY_SIZE(pdata->micbias); i++) {
if (pdata->micbias[i]) {
snd_soc_write(codec, WM8958_MICBIAS1 + i,
pdata->micbias[i] & 0xffff);
}
}
}
/**
@ -2993,46 +3012,18 @@ static void wm8958_default_micdet(u16 status, void *data)
int report = 0;
/* If nothing present then clear our statuses */
if (!(status & WM8958_MICD_STS)) {
wm8994->jack_is_video = false;
wm8994->jack_is_mic = false;
if (!(status & WM8958_MICD_STS))
goto done;
}
/* Assume anything over 475 ohms is a microphone and remember
* that we've seen one (since buttons override it) */
if (status & 0x600)
wm8994->jack_is_mic = true;
if (wm8994->jack_is_mic)
report |= SND_JACK_MICROPHONE;
/* Video has an impedence of approximately 75 ohms; assume
* this isn't used as a button and remember it since buttons
* override it. */
if (status & 0x40)
wm8994->jack_is_video = true;
if (wm8994->jack_is_video)
report |= SND_JACK_VIDEOOUT;
report = SND_JACK_MICROPHONE;
/* Everything else is buttons; just assign slots */
if (status & 0x4)
if (status & 0x1c0)
report |= SND_JACK_BTN_0;
if (status & 0x8)
report |= SND_JACK_BTN_1;
if (status & 0x10)
report |= SND_JACK_BTN_2;
if (status & 0x20)
report |= SND_JACK_BTN_3;
if (status & 0x80)
report |= SND_JACK_BTN_4;
if (status & 0x100)
report |= SND_JACK_BTN_5;
done:
snd_soc_jack_report(wm8994->micdet[0].jack, report,
SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2 |
SND_JACK_BTN_3 | SND_JACK_BTN_4 | SND_JACK_BTN_5 |
SND_JACK_MICROPHONE | SND_JACK_VIDEOOUT);
SND_JACK_BTN_0 | SND_JACK_MICROPHONE);
}
/**
@ -3131,13 +3122,19 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
wm8994->pdata = dev_get_platdata(codec->dev->parent);
wm8994->codec = codec;
if (wm8994->pdata && wm8994->pdata->micdet_irq)
wm8994->micdet_irq = wm8994->pdata->micdet_irq;
else if (wm8994->pdata && wm8994->pdata->irq_base)
wm8994->micdet_irq = wm8994->pdata->irq_base +
WM8994_IRQ_MIC1_DET;
pm_runtime_enable(codec->dev);
pm_runtime_resume(codec->dev);
/* Read our current status back from the chip - we don't want to
* reset as this may interfere with the GPIO or LDO operation. */
for (i = 0; i < WM8994_CACHE_SIZE; i++) {
if (!wm8994_readable(i) || wm8994_volatile(i))
if (!wm8994_readable(codec, i) || wm8994_volatile(codec, i))
continue;
ret = wm8994_reg_read(codec->control_data, i);
@ -3179,14 +3176,17 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
switch (control->type) {
case WM8994:
ret = wm8994_request_irq(codec->control_data,
WM8994_IRQ_MIC1_DET,
wm8994_mic_irq, "Mic 1 detect",
wm8994);
if (ret != 0)
dev_warn(codec->dev,
"Failed to request Mic1 detect IRQ: %d\n",
ret);
if (wm8994->micdet_irq) {
ret = request_threaded_irq(wm8994->micdet_irq, NULL,
wm8994_mic_irq,
IRQF_TRIGGER_RISING,
"Mic1 detect",
wm8994);
if (ret != 0)
dev_warn(codec->dev,
"Failed to request Mic1 detect IRQ: %d\n",
ret);
}
ret = wm8994_request_irq(codec->control_data,
WM8994_IRQ_MIC1_SHRT,
@ -3217,15 +3217,17 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
break;
case WM8958:
ret = wm8994_request_irq(codec->control_data,
WM8994_IRQ_MIC1_DET,
wm8958_mic_irq, "Mic detect",
wm8994);
if (ret != 0)
dev_warn(codec->dev,
"Failed to request Mic detect IRQ: %d\n",
ret);
break;
if (wm8994->micdet_irq) {
ret = request_threaded_irq(wm8994->micdet_irq, NULL,
wm8958_mic_irq,
IRQF_TRIGGER_RISING,
"Mic detect",
wm8994);
if (ret != 0)
dev_warn(codec->dev,
"Failed to request Mic detect IRQ: %d\n",
ret);
}
}
/* Remember if AIFnLRCLK is configured as a GPIO. This should be
@ -3325,6 +3327,12 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
case WM8958:
snd_soc_add_controls(codec, wm8958_snd_controls,
ARRAY_SIZE(wm8958_snd_controls));
snd_soc_dapm_new_controls(dapm, wm8994_lateclk_widgets,
ARRAY_SIZE(wm8994_lateclk_widgets));
snd_soc_dapm_new_controls(dapm, wm8994_adc_widgets,
ARRAY_SIZE(wm8994_adc_widgets));
snd_soc_dapm_new_controls(dapm, wm8994_dac_widgets,
ARRAY_SIZE(wm8994_dac_widgets));
snd_soc_dapm_new_controls(dapm, wm8958_dapm_widgets,
ARRAY_SIZE(wm8958_dapm_widgets));
break;
@ -3350,6 +3358,8 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
}
break;
case WM8958:
snd_soc_dapm_add_routes(dapm, wm8994_lateclk_intercon,
ARRAY_SIZE(wm8994_lateclk_intercon));
snd_soc_dapm_add_routes(dapm, wm8958_intercon,
ARRAY_SIZE(wm8958_intercon));
break;
@ -3361,7 +3371,8 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC2_SHRT, wm8994);
wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC2_DET, wm8994);
wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC1_SHRT, wm8994);
wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC1_DET, wm8994);
if (wm8994->micdet_irq)
free_irq(wm8994->micdet_irq, wm8994);
err:
kfree(wm8994);
return ret;
@ -3378,8 +3389,8 @@ static int wm8994_codec_remove(struct snd_soc_codec *codec)
switch (control->type) {
case WM8994:
wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC2_SHRT,
wm8994);
if (wm8994->micdet_irq)
free_irq(wm8994->micdet_irq, wm8994);
wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC2_DET,
wm8994);
wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC1_SHRT,
@ -3389,8 +3400,8 @@ static int wm8994_codec_remove(struct snd_soc_codec *codec)
break;
case WM8958:
wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC1_DET,
wm8994);
if (wm8994->micdet_irq)
free_irq(wm8994->micdet_irq, wm8994);
break;
}
kfree(wm8994->retune_mobile_texts);

View file

@ -43,6 +43,6 @@ struct wm8994_access_mask {
};
extern const struct wm8994_access_mask wm8994_access_masks[WM8994_CACHE_SIZE];
extern const __devinitdata u16 wm8994_reg_defaults[WM8994_CACHE_SIZE];
extern const u16 wm8994_reg_defaults[WM8994_CACHE_SIZE];
#endif

View file

@ -19,6 +19,7 @@
#include <linux/pm.h>
#include <linux/i2c.h>
#include <linux/spi/spi.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
@ -30,6 +31,18 @@
#include "wm8995.h"
#define WM8995_NUM_SUPPLIES 8
static const char *wm8995_supply_names[WM8995_NUM_SUPPLIES] = {
"DCVDD",
"DBVDD1",
"DBVDD2",
"DBVDD3",
"AVDD1",
"AVDD2",
"CPVDD",
"MICVDD"
};
static const u16 wm8995_reg_defs[WM8995_MAX_REGISTER + 1] = {
[0] = 0x8995, [5] = 0x0100, [16] = 0x000b, [17] = 0x000b,
[24] = 0x02c0, [25] = 0x02c0, [26] = 0x02c0, [27] = 0x02c0,
@ -126,8 +139,37 @@ struct wm8995_priv {
int mclk[2];
int aifclk[2];
struct fll_config fll[2], fll_suspend[2];
struct regulator_bulk_data supplies[WM8995_NUM_SUPPLIES];
struct notifier_block disable_nb[WM8995_NUM_SUPPLIES];
struct snd_soc_codec *codec;
};
/*
* We can't use the same notifier block for more than one supply and
* there's no way I can see to get from a callback to the caller
* except container_of().
*/
#define WM8995_REGULATOR_EVENT(n) \
static int wm8995_regulator_event_##n(struct notifier_block *nb, \
unsigned long event, void *data) \
{ \
struct wm8995_priv *wm8995 = container_of(nb, struct wm8995_priv, \
disable_nb[n]); \
if (event & REGULATOR_EVENT_DISABLE) { \
wm8995->codec->cache_sync = 1; \
} \
return 0; \
}
WM8995_REGULATOR_EVENT(0)
WM8995_REGULATOR_EVENT(1)
WM8995_REGULATOR_EVENT(2)
WM8995_REGULATOR_EVENT(3)
WM8995_REGULATOR_EVENT(4)
WM8995_REGULATOR_EVENT(5)
WM8995_REGULATOR_EVENT(6)
WM8995_REGULATOR_EVENT(7)
static const DECLARE_TLV_DB_SCALE(digital_tlv, -7200, 75, 1);
static const DECLARE_TLV_DB_SCALE(in1lr_pga_tlv, -1650, 150, 0);
static const DECLARE_TLV_DB_SCALE(in1l_boost_tlv, 0, 600, 0);
@ -909,7 +951,7 @@ static const struct snd_soc_dapm_route wm8995_intercon[] = {
{ "SPK2R", NULL, "SPK2R Driver" }
};
static int wm8995_volatile(unsigned int reg)
static int wm8995_volatile(struct snd_soc_codec *codec, unsigned int reg)
{
/* out of bounds registers are generally considered
* volatile to support register banks that are partially
@ -1483,6 +1525,11 @@ static int wm8995_set_bias_level(struct snd_soc_codec *codec,
break;
case SND_SOC_BIAS_STANDBY:
if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
ret = regulator_bulk_enable(ARRAY_SIZE(wm8995->supplies),
wm8995->supplies);
if (ret)
return ret;
ret = snd_soc_cache_sync(codec);
if (ret) {
dev_err(codec->dev,
@ -1492,12 +1539,13 @@ static int wm8995_set_bias_level(struct snd_soc_codec *codec,
snd_soc_update_bits(codec, WM8995_POWER_MANAGEMENT_1,
WM8995_BG_ENA_MASK, WM8995_BG_ENA);
}
break;
case SND_SOC_BIAS_OFF:
snd_soc_update_bits(codec, WM8995_POWER_MANAGEMENT_1,
WM8995_BG_ENA_MASK, 0);
regulator_bulk_disable(ARRAY_SIZE(wm8995->supplies),
wm8995->supplies);
break;
}
@ -1536,10 +1584,12 @@ static int wm8995_remove(struct snd_soc_codec *codec)
static int wm8995_probe(struct snd_soc_codec *codec)
{
struct wm8995_priv *wm8995;
int i;
int ret;
codec->dapm.idle_bias_off = 1;
wm8995 = snd_soc_codec_get_drvdata(codec);
wm8995->codec = codec;
ret = snd_soc_codec_set_cache_io(codec, 16, 16, wm8995->control_type);
if (ret < 0) {
@ -1547,21 +1597,58 @@ static int wm8995_probe(struct snd_soc_codec *codec)
return ret;
}
for (i = 0; i < ARRAY_SIZE(wm8995->supplies); i++)
wm8995->supplies[i].supply = wm8995_supply_names[i];
ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(wm8995->supplies),
wm8995->supplies);
if (ret) {
dev_err(codec->dev, "Failed to request supplies: %d\n", ret);
return ret;
}
wm8995->disable_nb[0].notifier_call = wm8995_regulator_event_0;
wm8995->disable_nb[1].notifier_call = wm8995_regulator_event_1;
wm8995->disable_nb[2].notifier_call = wm8995_regulator_event_2;
wm8995->disable_nb[3].notifier_call = wm8995_regulator_event_3;
wm8995->disable_nb[4].notifier_call = wm8995_regulator_event_4;
wm8995->disable_nb[5].notifier_call = wm8995_regulator_event_5;
wm8995->disable_nb[6].notifier_call = wm8995_regulator_event_6;
wm8995->disable_nb[7].notifier_call = wm8995_regulator_event_7;
/* This should really be moved into the regulator core */
for (i = 0; i < ARRAY_SIZE(wm8995->supplies); i++) {
ret = regulator_register_notifier(wm8995->supplies[i].consumer,
&wm8995->disable_nb[i]);
if (ret) {
dev_err(codec->dev,
"Failed to register regulator notifier: %d\n",
ret);
}
}
ret = regulator_bulk_enable(ARRAY_SIZE(wm8995->supplies),
wm8995->supplies);
if (ret) {
dev_err(codec->dev, "Failed to enable supplies: %d\n", ret);
goto err_reg_get;
}
ret = snd_soc_read(codec, WM8995_SOFTWARE_RESET);
if (ret < 0) {
dev_err(codec->dev, "Failed to read device ID: %d\n", ret);
return ret;
goto err_reg_enable;
}
if (ret != 0x8995) {
dev_err(codec->dev, "Invalid device ID: %#x\n", ret);
return -EINVAL;
goto err_reg_enable;
}
ret = snd_soc_write(codec, WM8995_SOFTWARE_RESET, 0);
if (ret < 0) {
dev_err(codec->dev, "Failed to issue reset: %d\n", ret);
return ret;
goto err_reg_enable;
}
wm8995_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
@ -1596,6 +1683,12 @@ static int wm8995_probe(struct snd_soc_codec *codec)
ARRAY_SIZE(wm8995_intercon));
return 0;
err_reg_enable:
regulator_bulk_disable(ARRAY_SIZE(wm8995->supplies), wm8995->supplies);
err_reg_get:
regulator_bulk_free(ARRAY_SIZE(wm8995->supplies), wm8995->supplies);
return ret;
}
#define WM8995_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\

View file

@ -167,10 +167,10 @@ struct wm9081_priv {
int fll_fref;
int fll_fout;
int tdm_width;
struct wm9081_retune_mobile_config *retune;
struct wm9081_pdata pdata;
};
static int wm9081_volatile_register(unsigned int reg)
static int wm9081_volatile_register(struct snd_soc_codec *codec, unsigned int reg)
{
switch (reg) {
case WM9081_SOFTWARE_RESET:
@ -389,27 +389,6 @@ SOC_DAPM_SINGLE("IN2 Switch", WM9081_ANALOGUE_MIXER, 2, 1, 0),
SOC_DAPM_SINGLE("Playback Switch", WM9081_ANALOGUE_MIXER, 4, 1, 0),
};
static int speaker_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = w->codec;
unsigned int reg = snd_soc_read(codec, WM9081_POWER_MANAGEMENT);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
reg |= WM9081_SPK_ENA;
break;
case SND_SOC_DAPM_PRE_PMD:
reg &= ~WM9081_SPK_ENA;
break;
}
snd_soc_write(codec, WM9081_POWER_MANAGEMENT, reg);
return 0;
}
struct _fll_div {
u16 fll_fratio;
u16 fll_outdiv;
@ -747,9 +726,8 @@ SND_SOC_DAPM_MIXER_NAMED_CTL("Mixer", SND_SOC_NOPM, 0, 0,
SND_SOC_DAPM_PGA("LINEOUT PGA", WM9081_POWER_MANAGEMENT, 4, 0, NULL, 0),
SND_SOC_DAPM_PGA_E("Speaker PGA", WM9081_POWER_MANAGEMENT, 2, 0, NULL, 0,
speaker_event,
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
SND_SOC_DAPM_PGA("Speaker PGA", WM9081_POWER_MANAGEMENT, 2, 0, NULL, 0),
SND_SOC_DAPM_PGA("Speaker", WM9081_POWER_MANAGEMENT, 1, 0, NULL, 0),
SND_SOC_DAPM_OUTPUT("LINEOUT"),
SND_SOC_DAPM_OUTPUT("SPKN"),
@ -762,7 +740,7 @@ SND_SOC_DAPM_SUPPLY("TOCLK", WM9081_CLOCK_CONTROL_3, 2, 0, NULL, 0),
};
static const struct snd_soc_dapm_route audio_paths[] = {
static const struct snd_soc_dapm_route wm9081_audio_paths[] = {
{ "DAC", NULL, "CLK_SYS" },
{ "DAC", NULL, "CLK_DSP" },
@ -780,8 +758,10 @@ static const struct snd_soc_dapm_route audio_paths[] = {
{ "Speaker PGA", NULL, "TOCLK" },
{ "Speaker PGA", NULL, "CLK_SYS" },
{ "SPKN", NULL, "Speaker PGA" },
{ "SPKP", NULL, "Speaker PGA" },
{ "Speaker", NULL, "Speaker PGA" },
{ "SPKN", NULL, "Speaker" },
{ "SPKP", NULL, "Speaker" },
};
static int wm9081_set_bias_level(struct snd_soc_codec *codec,
@ -1082,21 +1062,22 @@ static int wm9081_hw_params(struct snd_pcm_substream *substream,
aif4 |= wm9081->bclk / wm9081->fs;
/* Apply a ReTune Mobile configuration if it's in use */
if (wm9081->retune) {
struct wm9081_retune_mobile_config *retune = wm9081->retune;
if (wm9081->pdata.num_retune_configs) {
struct wm9081_pdata *pdata = &wm9081->pdata;
struct wm9081_retune_mobile_setting *s;
int eq1;
best = 0;
best_val = abs(retune->configs[0].rate - wm9081->fs);
for (i = 0; i < retune->num_configs; i++) {
cur_val = abs(retune->configs[i].rate - wm9081->fs);
best_val = abs(pdata->retune_configs[0].rate - wm9081->fs);
for (i = 0; i < pdata->num_retune_configs; i++) {
cur_val = abs(pdata->retune_configs[i].rate -
wm9081->fs);
if (cur_val < best_val) {
best_val = cur_val;
best = i;
}
}
s = &retune->configs[best];
s = &pdata->retune_configs[best];
dev_dbg(codec->dev, "ReTune Mobile %s tuned for %dHz\n",
s->name, s->rate);
@ -1139,10 +1120,9 @@ static int wm9081_digital_mute(struct snd_soc_dai *codec_dai, int mute)
return 0;
}
static int wm9081_set_sysclk(struct snd_soc_dai *codec_dai,
static int wm9081_set_sysclk(struct snd_soc_codec *codec,
int clk_id, unsigned int freq, int dir)
{
struct snd_soc_codec *codec = codec_dai->codec;
struct wm9081_priv *wm9081 = snd_soc_codec_get_drvdata(codec);
switch (clk_id) {
@ -1207,7 +1187,6 @@ static int wm9081_set_tdm_slot(struct snd_soc_dai *dai,
static struct snd_soc_dai_ops wm9081_dai_ops = {
.hw_params = wm9081_hw_params,
.set_sysclk = wm9081_set_sysclk,
.set_fmt = wm9081_set_dai_fmt,
.digital_mute = wm9081_digital_mute,
.set_tdm_slot = wm9081_set_tdm_slot,
@ -1231,7 +1210,6 @@ static struct snd_soc_dai_driver wm9081_dai = {
static int wm9081_probe(struct snd_soc_codec *codec)
{
struct wm9081_priv *wm9081 = snd_soc_codec_get_drvdata(codec);
struct snd_soc_dapm_context *dapm = &codec->dapm;
int ret;
u16 reg;
@ -1255,6 +1233,14 @@ static int wm9081_probe(struct snd_soc_codec *codec)
return ret;
}
reg = 0;
if (wm9081->pdata.irq_high)
reg |= WM9081_IRQ_POL;
if (!wm9081->pdata.irq_cmos)
reg |= WM9081_IRQ_OP_CTRL;
snd_soc_update_bits(codec, WM9081_INTERRUPT_CONTROL,
WM9081_IRQ_POL | WM9081_IRQ_OP_CTRL, reg);
wm9081_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
/* Enable zero cross by default */
@ -1266,17 +1252,13 @@ static int wm9081_probe(struct snd_soc_codec *codec)
snd_soc_add_controls(codec, wm9081_snd_controls,
ARRAY_SIZE(wm9081_snd_controls));
if (!wm9081->retune) {
if (!wm9081->pdata.num_retune_configs) {
dev_dbg(codec->dev,
"No ReTune Mobile data, using normal EQ\n");
snd_soc_add_controls(codec, wm9081_eq_controls,
ARRAY_SIZE(wm9081_eq_controls));
}
snd_soc_dapm_new_controls(dapm, wm9081_dapm_widgets,
ARRAY_SIZE(wm9081_dapm_widgets));
snd_soc_dapm_add_routes(dapm, audio_paths, ARRAY_SIZE(audio_paths));
return ret;
}
@ -1320,11 +1302,19 @@ static struct snd_soc_codec_driver soc_codec_dev_wm9081 = {
.remove = wm9081_remove,
.suspend = wm9081_suspend,
.resume = wm9081_resume,
.set_sysclk = wm9081_set_sysclk,
.set_bias_level = wm9081_set_bias_level,
.reg_cache_size = ARRAY_SIZE(wm9081_reg_defaults),
.reg_word_size = sizeof(u16),
.reg_cache_default = wm9081_reg_defaults,
.volatile_register = wm9081_volatile_register,
.dapm_widgets = wm9081_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(wm9081_dapm_widgets),
.dapm_routes = wm9081_audio_paths,
.num_dapm_routes = ARRAY_SIZE(wm9081_audio_paths),
};
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
@ -1343,8 +1333,8 @@ static __devinit int wm9081_i2c_probe(struct i2c_client *i2c,
wm9081->control_data = i2c;
if (dev_get_platdata(&i2c->dev))
memcpy(&wm9081->retune, dev_get_platdata(&i2c->dev),
sizeof(wm9081->retune));
memcpy(&wm9081->pdata, dev_get_platdata(&i2c->dev),
sizeof(wm9081->pdata));
ret = snd_soc_register_codec(&i2c->dev,
&soc_codec_dev_wm9081, &wm9081_dai, 1);
@ -1368,7 +1358,7 @@ MODULE_DEVICE_TABLE(i2c, wm9081_i2c_id);
static struct i2c_driver wm9081_i2c_driver = {
.driver = {
.name = "wm9081-codec",
.name = "wm9081",
.owner = THIS_MODULE,
},
.probe = wm9081_i2c_probe,

View file

@ -144,7 +144,7 @@ struct wm9090_priv {
void *control_data;
};
static int wm9090_volatile(unsigned int reg)
static int wm9090_volatile(struct snd_soc_codec *codec, unsigned int reg)
{
switch (reg) {
case WM9090_SOFTWARE_RESET:
@ -518,7 +518,7 @@ static int wm9090_set_bias_level(struct snd_soc_codec *codec,
for (i = 1; i < codec->driver->reg_cache_size; i++) {
if (reg_cache[i] == wm9090_reg_defaults[i])
continue;
if (wm9090_volatile(i))
if (wm9090_volatile(codec, i))
continue;
ret = snd_soc_write(codec, i, reg_cache[i]);
@ -551,7 +551,6 @@ static int wm9090_set_bias_level(struct snd_soc_codec *codec,
static int wm9090_probe(struct snd_soc_codec *codec)
{
struct wm9090_priv *wm9090 = snd_soc_codec_get_drvdata(codec);
u16 *reg_cache = codec->reg_cache;
int ret;
codec->control_data = wm9090->control_data;
@ -576,22 +575,30 @@ static int wm9090_probe(struct snd_soc_codec *codec)
/* Configure some defaults; they will be written out when we
* bring the bias up.
*/
reg_cache[WM9090_IN1_LINE_INPUT_A_VOLUME] |= WM9090_IN1_VU
| WM9090_IN1A_ZC;
reg_cache[WM9090_IN1_LINE_INPUT_B_VOLUME] |= WM9090_IN1_VU
| WM9090_IN1B_ZC;
reg_cache[WM9090_IN2_LINE_INPUT_A_VOLUME] |= WM9090_IN2_VU
| WM9090_IN2A_ZC;
reg_cache[WM9090_IN2_LINE_INPUT_B_VOLUME] |= WM9090_IN2_VU
| WM9090_IN2B_ZC;
reg_cache[WM9090_SPEAKER_VOLUME_LEFT] |=
WM9090_SPKOUT_VU | WM9090_SPKOUTL_ZC;
reg_cache[WM9090_LEFT_OUTPUT_VOLUME] |=
WM9090_HPOUT1_VU | WM9090_HPOUT1L_ZC;
reg_cache[WM9090_RIGHT_OUTPUT_VOLUME] |=
WM9090_HPOUT1_VU | WM9090_HPOUT1R_ZC;
snd_soc_update_bits(codec, WM9090_IN1_LINE_INPUT_A_VOLUME,
WM9090_IN1_VU | WM9090_IN1A_ZC,
WM9090_IN1_VU | WM9090_IN1A_ZC);
snd_soc_update_bits(codec, WM9090_IN1_LINE_INPUT_B_VOLUME,
WM9090_IN1_VU | WM9090_IN1B_ZC,
WM9090_IN1_VU | WM9090_IN1B_ZC);
snd_soc_update_bits(codec, WM9090_IN2_LINE_INPUT_A_VOLUME,
WM9090_IN2_VU | WM9090_IN2A_ZC,
WM9090_IN2_VU | WM9090_IN2A_ZC);
snd_soc_update_bits(codec, WM9090_IN2_LINE_INPUT_B_VOLUME,
WM9090_IN2_VU | WM9090_IN2B_ZC,
WM9090_IN2_VU | WM9090_IN2B_ZC);
snd_soc_update_bits(codec, WM9090_SPEAKER_VOLUME_LEFT,
WM9090_SPKOUT_VU | WM9090_SPKOUTL_ZC,
WM9090_SPKOUT_VU | WM9090_SPKOUTL_ZC);
snd_soc_update_bits(codec, WM9090_LEFT_OUTPUT_VOLUME,
WM9090_HPOUT1_VU | WM9090_HPOUT1L_ZC,
WM9090_HPOUT1_VU | WM9090_HPOUT1L_ZC);
snd_soc_update_bits(codec, WM9090_RIGHT_OUTPUT_VOLUME,
WM9090_HPOUT1_VU | WM9090_HPOUT1R_ZC,
WM9090_HPOUT1_VU | WM9090_HPOUT1R_ZC);
reg_cache[WM9090_CLOCKING_1] |= WM9090_TOCLK_ENA;
snd_soc_update_bits(codec, WM9090_CLOCKING_1,
WM9090_TOCLK_ENA, WM9090_TOCLK_ENA);
wm9090_set_bias_level(codec, SND_SOC_BIAS_STANDBY);

View file

@ -82,7 +82,8 @@ static void wait_for_dc_servo(struct snd_soc_codec *codec, unsigned int op)
} while (reg & op && count < 400);
if (reg & op)
dev_err(codec->dev, "Timed out waiting for DC Servo\n");
dev_err(codec->dev, "Timed out waiting for DC Servo %x\n",
op);
}
/*

View file

@ -658,7 +658,7 @@ static int davinci_i2s_probe(struct platform_device *pdev)
return -ENODEV;
}
ioarea = request_mem_region(mem->start, (mem->end - mem->start) + 1,
ioarea = request_mem_region(mem->start, resource_size(mem),
pdev->name);
if (!ioarea) {
dev_err(&pdev->dev, "McBSP region already claimed\n");
@ -694,20 +694,25 @@ static int davinci_i2s_probe(struct platform_device *pdev)
}
clk_enable(dev->clk);
dev->base = (void __iomem *)IO_ADDRESS(mem->start);
dev->base = ioremap(mem->start, resource_size(mem));
if (!dev->base) {
dev_err(&pdev->dev, "ioremap failed\n");
ret = -ENOMEM;
goto err_release_clk;
}
dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK].dma_addr =
(dma_addr_t)(io_v2p(dev->base) + DAVINCI_MCBSP_DXR_REG);
(dma_addr_t)(mem->start + DAVINCI_MCBSP_DXR_REG);
dev->dma_params[SNDRV_PCM_STREAM_CAPTURE].dma_addr =
(dma_addr_t)(io_v2p(dev->base) + DAVINCI_MCBSP_DRR_REG);
(dma_addr_t)(mem->start + DAVINCI_MCBSP_DRR_REG);
/* first TX, then RX */
res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
if (!res) {
dev_err(&pdev->dev, "no DMA resource\n");
ret = -ENXIO;
goto err_free_mem;
goto err_iounmap;
}
dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK].channel = res->start;
@ -715,7 +720,7 @@ static int davinci_i2s_probe(struct platform_device *pdev)
if (!res) {
dev_err(&pdev->dev, "no DMA resource\n");
ret = -ENXIO;
goto err_free_mem;
goto err_iounmap;
}
dev->dma_params[SNDRV_PCM_STREAM_CAPTURE].channel = res->start;
dev->dev = &pdev->dev;
@ -724,14 +729,19 @@ static int davinci_i2s_probe(struct platform_device *pdev)
ret = snd_soc_register_dai(&pdev->dev, &davinci_i2s_dai);
if (ret != 0)
goto err_free_mem;
goto err_iounmap;
return 0;
err_iounmap:
iounmap(dev->base);
err_release_clk:
clk_disable(dev->clk);
clk_put(dev->clk);
err_free_mem:
kfree(dev);
err_release_region:
release_mem_region(mem->start, (mem->end - mem->start) + 1);
release_mem_region(mem->start, resource_size(mem));
return ret;
}
@ -747,7 +757,7 @@ static int davinci_i2s_remove(struct platform_device *pdev)
dev->clk = NULL;
kfree(dev);
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
release_mem_region(mem->start, (mem->end - mem->start) + 1);
release_mem_region(mem->start, resource_size(mem));
return 0;
}

View file

@ -868,7 +868,7 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
}
ioarea = request_mem_region(mem->start,
(mem->end - mem->start) + 1, pdev->name);
resource_size(mem), pdev->name);
if (!ioarea) {
dev_err(&pdev->dev, "Audio region already claimed\n");
ret = -EBUSY;
@ -885,7 +885,13 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
clk_enable(dev->clk);
dev->clk_active = 1;
dev->base = (void __iomem *)IO_ADDRESS(mem->start);
dev->base = ioremap(mem->start, resource_size(mem));
if (!dev->base) {
dev_err(&pdev->dev, "ioremap failed\n");
ret = -ENOMEM;
goto err_release_clk;
}
dev->op_mode = pdata->op_mode;
dev->tdm_slots = pdata->tdm_slots;
dev->num_serializer = pdata->num_serializer;
@ -899,14 +905,14 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
dma_data->asp_chan_q = pdata->asp_chan_q;
dma_data->ram_chan_q = pdata->ram_chan_q;
dma_data->dma_addr = (dma_addr_t) (pdata->tx_dma_offset +
io_v2p(dev->base));
mem->start);
/* first TX, then RX */
res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
if (!res) {
dev_err(&pdev->dev, "no DMA resource\n");
ret = -ENODEV;
goto err_release_region;
goto err_iounmap;
}
dma_data->channel = res->start;
@ -915,13 +921,13 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
dma_data->asp_chan_q = pdata->asp_chan_q;
dma_data->ram_chan_q = pdata->ram_chan_q;
dma_data->dma_addr = (dma_addr_t)(pdata->rx_dma_offset +
io_v2p(dev->base));
mem->start);
res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
if (!res) {
dev_err(&pdev->dev, "no DMA resource\n");
ret = -ENODEV;
goto err_release_region;
goto err_iounmap;
}
dma_data->channel = res->start;
@ -929,11 +935,16 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
ret = snd_soc_register_dai(&pdev->dev, &davinci_mcasp_dai[pdata->op_mode]);
if (ret != 0)
goto err_release_region;
goto err_iounmap;
return 0;
err_iounmap:
iounmap(dev->base);
err_release_clk:
clk_disable(dev->clk);
clk_put(dev->clk);
err_release_region:
release_mem_region(mem->start, (mem->end - mem->start) + 1);
release_mem_region(mem->start, resource_size(mem));
err_release_data:
kfree(dev);
@ -951,7 +962,7 @@ static int davinci_mcasp_remove(struct platform_device *pdev)
dev->clk = NULL;
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
release_mem_region(mem->start, (mem->end - mem->start) + 1);
release_mem_region(mem->start, resource_size(mem));
kfree(dev);

View file

@ -30,3 +30,12 @@ config SND_EP93XX_SOC_SIMONE
help
Say Y or M here if you want to add support for AC97 audio on the
Simplemachines Sim.One board.
config SND_EP93XX_SOC_EDB93XX
tristate "SoC Audio support for Cirrus Logic EDB93xx boards"
depends on SND_EP93XX_SOC && (MACH_EDB9301 || MACH_EDB9302 || MACH_EDB9302A || MACH_EDB9307A || MACH_EDB9315A)
select SND_EP93XX_SOC_I2S
select SND_SOC_CS4271
help
Say Y or M here if you want to add support for I2S audio on the
Cirrus Logic EDB93xx boards.

View file

@ -10,6 +10,8 @@ obj-$(CONFIG_SND_EP93XX_SOC_AC97) += snd-soc-ep93xx-ac97.o
# EP93XX Machine Support
snd-soc-snappercl15-objs := snappercl15.o
snd-soc-simone-objs := simone.o
snd-soc-edb93xx-objs := edb93xx.o
obj-$(CONFIG_SND_EP93XX_SOC_SNAPPERCL15) += snd-soc-snappercl15.o
obj-$(CONFIG_SND_EP93XX_SOC_SIMONE) += snd-soc-simone.o
obj-$(CONFIG_SND_EP93XX_SOC_EDB93XX) += snd-soc-edb93xx.o

142
sound/soc/ep93xx/edb93xx.c Normal file
View file

@ -0,0 +1,142 @@
/*
* SoC audio for EDB93xx
*
* Copyright (c) 2010 Alexander Sverdlin <subaparts@yandex.ru>
*
* 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.
*
* This driver support CS4271 codec being master or slave, working
* in control port mode, connected either via SPI or I2C.
* The data format accepted is I2S or left-justified.
* DAPM support not implemented.
*/
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/soc.h>
#include <asm/mach-types.h>
#include <mach/hardware.h>
#include "ep93xx-pcm.h"
#define edb93xx_has_audio() (machine_is_edb9301() || \
machine_is_edb9302() || \
machine_is_edb9302a() || \
machine_is_edb9307a() || \
machine_is_edb9315a())
static int edb93xx_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *codec_dai = rtd->codec_dai;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
int err;
unsigned int mclk_rate;
unsigned int rate = params_rate(params);
/*
* According to CS4271 datasheet we use MCLK/LRCK=256 for
* rates below 50kHz and 128 for higher sample rates
*/
if (rate < 50000)
mclk_rate = rate * 64 * 4;
else
mclk_rate = rate * 64 * 2;
err = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
SND_SOC_DAIFMT_NB_IF |
SND_SOC_DAIFMT_CBS_CFS);
if (err)
return err;
err = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
SND_SOC_DAIFMT_NB_IF |
SND_SOC_DAIFMT_CBS_CFS);
if (err)
return err;
err = snd_soc_dai_set_sysclk(codec_dai, 0, mclk_rate,
SND_SOC_CLOCK_IN);
if (err)
return err;
return snd_soc_dai_set_sysclk(cpu_dai, 0, mclk_rate,
SND_SOC_CLOCK_OUT);
}
static struct snd_soc_ops edb93xx_ops = {
.hw_params = edb93xx_hw_params,
};
static struct snd_soc_dai_link edb93xx_dai = {
.name = "CS4271",
.stream_name = "CS4271 HiFi",
.platform_name = "ep93xx-pcm-audio",
.cpu_dai_name = "ep93xx-i2s",
.codec_name = "spi0.0",
.codec_dai_name = "cs4271-hifi",
.ops = &edb93xx_ops,
};
static struct snd_soc_card snd_soc_edb93xx = {
.name = "EDB93XX",
.dai_link = &edb93xx_dai,
.num_links = 1,
};
static struct platform_device *edb93xx_snd_device;
static int __init edb93xx_init(void)
{
int ret;
if (!edb93xx_has_audio())
return -ENODEV;
ret = ep93xx_i2s_acquire(EP93XX_SYSCON_DEVCFG_I2SONAC97,
EP93XX_SYSCON_I2SCLKDIV_ORIDE |
EP93XX_SYSCON_I2SCLKDIV_SPOL);
if (ret)
return ret;
edb93xx_snd_device = platform_device_alloc("soc-audio", -1);
if (!edb93xx_snd_device) {
ret = -ENOMEM;
goto free_i2s;
}
platform_set_drvdata(edb93xx_snd_device, &snd_soc_edb93xx);
ret = platform_device_add(edb93xx_snd_device);
if (ret)
goto device_put;
return 0;
device_put:
platform_device_put(edb93xx_snd_device);
free_i2s:
ep93xx_i2s_release();
return ret;
}
module_init(edb93xx_init);
static void __exit edb93xx_exit(void)
{
platform_device_unregister(edb93xx_snd_device);
ep93xx_i2s_release();
}
module_exit(edb93xx_exit);
MODULE_AUTHOR("Alexander Sverdlin <subaparts@yandex.ru>");
MODULE_DESCRIPTION("ALSA SoC EDB93xx");
MODULE_LICENSE("GPL");

View file

@ -253,7 +253,6 @@ static int ep93xx_ac97_trigger(struct snd_pcm_substream *substream,
struct ep93xx_ac97_info *info = snd_soc_dai_get_drvdata(dai);
unsigned v = 0;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:

View file

@ -242,7 +242,7 @@ static int ep93xx_i2s_hw_params(struct snd_pcm_substream *substream,
{
struct ep93xx_i2s_info *info = snd_soc_dai_get_drvdata(dai);
unsigned word_len, div, sdiv, lrdiv;
int found = 0, err;
int err;
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE:
@ -275,15 +275,14 @@ static int ep93xx_i2s_hw_params(struct snd_pcm_substream *substream,
* the codec uses.
*/
div = clk_get_rate(info->mclk) / params_rate(params);
for (sdiv = 2; sdiv <= 4; sdiv += 2)
for (lrdiv = 64; lrdiv <= 128; lrdiv <<= 1)
if (sdiv * lrdiv == div) {
found = 1;
goto out;
}
out:
if (!found)
return -EINVAL;
sdiv = 4;
if (div > (256 + 512) / 2) {
lrdiv = 128;
} else {
lrdiv = 64;
if (div < (128 + 256) / 2)
sdiv = 2;
}
err = clk_set_rate(info->sclk, clk_get_rate(info->mclk) / sdiv);
if (err)
@ -314,10 +313,12 @@ static int ep93xx_i2s_suspend(struct snd_soc_dai *dai)
struct ep93xx_i2s_info *info = snd_soc_dai_get_drvdata(dai);
if (!dai->active)
return;
return 0;
ep93xx_i2s_disable(info, SNDRV_PCM_STREAM_PLAYBACK);
ep93xx_i2s_disable(info, SNDRV_PCM_STREAM_CAPTURE);
return 0;
}
static int ep93xx_i2s_resume(struct snd_soc_dai *dai)
@ -325,10 +326,12 @@ static int ep93xx_i2s_resume(struct snd_soc_dai *dai)
struct ep93xx_i2s_info *info = snd_soc_dai_get_drvdata(dai);
if (!dai->active)
return;
return 0;
ep93xx_i2s_enable(info, SNDRV_PCM_STREAM_PLAYBACK);
ep93xx_i2s_enable(info, SNDRV_PCM_STREAM_CAPTURE);
return 0;
}
#else
#define ep93xx_i2s_suspend NULL
@ -352,13 +355,13 @@ static struct snd_soc_dai_driver ep93xx_i2s_dai = {
.playback = {
.channels_min = 2,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_96000,
.rates = SNDRV_PCM_RATE_8000_192000,
.formats = EP93XX_I2S_FORMATS,
},
.capture = {
.channels_min = 2,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_96000,
.rates = SNDRV_PCM_RATE_8000_192000,
.formats = EP93XX_I2S_FORMATS,
},
.ops = &ep93xx_i2s_dai_ops,

View file

@ -35,9 +35,9 @@ static const struct snd_pcm_hardware ep93xx_pcm_hardware = {
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER),
.rates = SNDRV_PCM_RATE_8000_96000,
.rates = SNDRV_PCM_RATE_8000_192000,
.rate_min = SNDRV_PCM_RATE_8000,
.rate_max = SNDRV_PCM_RATE_96000,
.rate_max = SNDRV_PCM_RATE_192000,
.formats = (SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE |

View file

@ -53,9 +53,8 @@ struct mpc8610_hpcd_data {
*
* Here we program the DMACR and PMUXCR registers.
*/
static int mpc8610_hpcd_machine_probe(struct platform_device *sound_device)
static int mpc8610_hpcd_machine_probe(struct snd_soc_card *card)
{
struct snd_soc_card *card = platform_get_drvdata(sound_device);
struct mpc8610_hpcd_data *machine_data =
container_of(card, struct mpc8610_hpcd_data, card);
struct ccsr_guts_86xx __iomem *guts;
@ -138,9 +137,8 @@ static int mpc8610_hpcd_startup(struct snd_pcm_substream *substream)
* This function is called to remove the sound device for one SSI. We
* de-program the DMACR and PMUXCR register.
*/
static int mpc8610_hpcd_machine_remove(struct platform_device *sound_device)
static int mpc8610_hpcd_machine_remove(struct snd_soc_card *card)
{
struct snd_soc_card *card = platform_get_drvdata(sound_device);
struct mpc8610_hpcd_data *machine_data =
container_of(card, struct mpc8610_hpcd_data, card);
struct ccsr_guts_86xx __iomem *guts;

View file

@ -85,9 +85,8 @@ struct machine_data {
*
* Here we program the DMACR and PMUXCR registers.
*/
static int p1022_ds_machine_probe(struct platform_device *sound_device)
static int p1022_ds_machine_probe(struct snd_soc_card *card)
{
struct snd_soc_card *card = platform_get_drvdata(sound_device);
struct machine_data *mdata =
container_of(card, struct machine_data, card);
struct ccsr_guts_85xx __iomem *guts;
@ -160,9 +159,8 @@ static int p1022_ds_startup(struct snd_pcm_substream *substream)
* This function is called to remove the sound device for one SSI. We
* de-program the DMACR and PMUXCR register.
*/
static int p1022_ds_machine_remove(struct platform_device *sound_device)
static int p1022_ds_machine_remove(struct snd_soc_card *card)
{
struct snd_soc_card *card = platform_get_drvdata(sound_device);
struct machine_data *mdata =
container_of(card, struct machine_data, card);
struct ccsr_guts_85xx __iomem *guts;

View file

@ -30,6 +30,16 @@ config SND_MXC_SOC_WM1133_EV1
Enable support for audio on the i.MX31ADS with the WM1133-EV1
PMIC board with WM8835x fitted.
config SND_SOC_MX27VIS_AIC32X4
tristate "SoC audio support for Visstrim M10 boards"
depends on MACH_IMX27_VISSTRIM_M10
select SND_SOC_TVL320AIC32X4
select SND_MXC_SOC_SSI
select SND_MXC_SOC_MX2
help
Say Y if you want to add support for SoC audio on Visstrim SM10
board with TLV320AIC32X4 codec.
config SND_SOC_PHYCORE_AC97
tristate "SoC Audio support for Phytec phyCORE (and phyCARD) boards"
depends on MACH_PCM043 || MACH_PCA100
@ -44,7 +54,8 @@ config SND_SOC_EUKREA_TLV320
tristate "Eukrea TLV320"
depends on MACH_EUKREA_MBIMX27_BASEBOARD \
|| MACH_EUKREA_MBIMXSD25_BASEBOARD \
|| MACH_EUKREA_MBIMXSD35_BASEBOARD
|| MACH_EUKREA_MBIMXSD35_BASEBOARD \
|| MACH_EUKREA_MBIMXSD51_BASEBOARD
select SND_SOC_TLV320AIC23
select SND_MXC_SOC_SSI
select SND_MXC_SOC_FIQ

View file

@ -10,8 +10,10 @@ obj-$(CONFIG_SND_MXC_SOC_MX2) += snd-soc-imx-mx2.o
# i.MX Machine Support
snd-soc-eukrea-tlv320-objs := eukrea-tlv320.o
snd-soc-phycore-ac97-objs := phycore-ac97.o
snd-soc-mx27vis-aic32x4-objs := mx27vis-aic32x4.o
snd-soc-wm1133-ev1-objs := wm1133-ev1.o
obj-$(CONFIG_SND_SOC_EUKREA_TLV320) += snd-soc-eukrea-tlv320.o
obj-$(CONFIG_SND_SOC_PHYCORE_AC97) += snd-soc-phycore-ac97.o
obj-$(CONFIG_SND_SOC_MX27VIS_AIC32X4) += snd-soc-mx27vis-aic32x4.o
obj-$(CONFIG_SND_MXC_SOC_WM1133_EV1) += snd-soc-wm1133-ev1.o

View file

@ -98,7 +98,8 @@ static int __init eukrea_tlv320_init(void)
int ret;
if (!machine_is_eukrea_cpuimx27() && !machine_is_eukrea_cpuimx25sd()
&& !machine_is_eukrea_cpuimx35sd())
&& !machine_is_eukrea_cpuimx35sd()
&& !machine_is_eukrea_cpuimx51sd())
/* return happy. We might run on a totally different machine */
return 0;

View file

@ -108,7 +108,7 @@ static int imx_ssi_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
break;
case SND_SOC_DAIFMT_DSP_B:
/* data on rising edge of bclk, frame high with data */
strcr |= SSI_STCR_TFSL;
strcr |= SSI_STCR_TFSL | SSI_STCR_TXBIT0;
break;
case SND_SOC_DAIFMT_DSP_A:
/* data on rising edge of bclk, frame high 1clk before data */
@ -656,6 +656,9 @@ static int imx_ssi_probe(struct platform_device *pdev)
ssi->dma_params_rx.dma_addr = res->start + SSI_SRX0;
ssi->dma_params_tx.dma_addr = res->start + SSI_STX0;
ssi->dma_params_tx.burstsize = 4;
ssi->dma_params_rx.burstsize = 4;
res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "tx0");
if (res)
ssi->dma_params_tx.dma = res->start;

View file

@ -0,0 +1,137 @@
/*
* mx27vis-aic32x4.c
*
* Copyright 2011 Vista Silicon S.L.
*
* Author: Javier Martin <javier.martin@vista-silicon.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.
*
* 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., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/device.h>
#include <linux/i2c.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <asm/mach-types.h>
#include <mach/audmux.h>
#include "../codecs/tlv320aic32x4.h"
#include "imx-ssi.h"
static int mx27vis_aic32x4_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *codec_dai = rtd->codec_dai;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
int ret;
u32 dai_format;
dai_format = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBM_CFM;
/* set codec DAI configuration */
snd_soc_dai_set_fmt(codec_dai, dai_format);
/* set cpu DAI configuration */
snd_soc_dai_set_fmt(cpu_dai, dai_format);
ret = snd_soc_dai_set_sysclk(codec_dai, 0,
25000000, SND_SOC_CLOCK_OUT);
if (ret) {
pr_err("%s: failed setting codec sysclk\n", __func__);
return ret;
}
ret = snd_soc_dai_set_sysclk(cpu_dai, IMX_SSP_SYS_CLK, 0,
SND_SOC_CLOCK_IN);
if (ret) {
pr_err("can't set CPU system clock IMX_SSP_SYS_CLK\n");
return ret;
}
return 0;
}
static struct snd_soc_ops mx27vis_aic32x4_snd_ops = {
.hw_params = mx27vis_aic32x4_hw_params,
};
static struct snd_soc_dai_link mx27vis_aic32x4_dai = {
.name = "tlv320aic32x4",
.stream_name = "TLV320AIC32X4",
.codec_dai_name = "tlv320aic32x4-hifi",
.platform_name = "imx-pcm-audio.0",
.codec_name = "tlv320aic32x4.0-0018",
.cpu_dai_name = "imx-ssi.0",
.ops = &mx27vis_aic32x4_snd_ops,
};
static struct snd_soc_card mx27vis_aic32x4 = {
.name = "visstrim_m10-audio",
.dai_link = &mx27vis_aic32x4_dai,
.num_links = 1,
};
static struct platform_device *mx27vis_aic32x4_snd_device;
static int __init mx27vis_aic32x4_init(void)
{
int ret;
mx27vis_aic32x4_snd_device = platform_device_alloc("soc-audio", -1);
if (!mx27vis_aic32x4_snd_device)
return -ENOMEM;
platform_set_drvdata(mx27vis_aic32x4_snd_device, &mx27vis_aic32x4);
ret = platform_device_add(mx27vis_aic32x4_snd_device);
if (ret) {
printk(KERN_ERR "ASoC: Platform device allocation failed\n");
platform_device_put(mx27vis_aic32x4_snd_device);
}
/* Connect SSI0 as clock slave to SSI1 external pins */
mxc_audmux_v1_configure_port(MX27_AUDMUX_HPCR1_SSI0,
MXC_AUDMUX_V1_PCR_SYN |
MXC_AUDMUX_V1_PCR_TFSDIR |
MXC_AUDMUX_V1_PCR_TCLKDIR |
MXC_AUDMUX_V1_PCR_TFCSEL(MX27_AUDMUX_PPCR1_SSI_PINS_1) |
MXC_AUDMUX_V1_PCR_RXDSEL(MX27_AUDMUX_PPCR1_SSI_PINS_1)
);
mxc_audmux_v1_configure_port(MX27_AUDMUX_PPCR1_SSI_PINS_1,
MXC_AUDMUX_V1_PCR_SYN |
MXC_AUDMUX_V1_PCR_RXDSEL(MX27_AUDMUX_HPCR1_SSI0)
);
return ret;
}
static void __exit mx27vis_aic32x4_exit(void)
{
platform_device_unregister(mx27vis_aic32x4_snd_device);
}
module_init(mx27vis_aic32x4_init);
module_exit(mx27vis_aic32x4_exit);
MODULE_AUTHOR("Javier Martin <javier.martin@vista-silicon.com>");
MODULE_DESCRIPTION("ALSA SoC AIC32X4 mx27 visstrim");
MODULE_LICENSE("GPL");

14
sound/soc/mid-x86/Kconfig Normal file
View file

@ -0,0 +1,14 @@
config SND_MFLD_MACHINE
tristate "SOC Machine Audio driver for Intel Medfield MID platform"
depends on INTEL_SCU_IPC
depends on SND_INTEL_SST
select SND_SOC_SN95031
select SND_SST_PLATFORM
help
This adds support for ASoC machine driver for Intel(R) MID Medfield platform
used as alsa device in audio substem in Intel(R) MID devices
Say Y if you have such a device
If unsure select "N".
config SND_SST_PLATFORM
tristate

View file

@ -0,0 +1,5 @@
snd-soc-sst-platform-objs := sst_platform.o
snd-soc-mfld-machine-objs := mfld_machine.o
obj-$(CONFIG_SND_SST_PLATFORM) += snd-soc-sst-platform.o
obj-$(CONFIG_SND_MFLD_MACHINE) += snd-soc-mfld-machine.o

View file

@ -0,0 +1,452 @@
/*
* mfld_machine.c - ASoc Machine driver for Intel Medfield MID platform
*
* Copyright (C) 2010 Intel Corp
* Author: Vinod Koul <vinod.koul@intel.com>
* Author: Harsha Priya <priya.harsha@intel.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; version 2 of the License.
*
* 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 pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/init.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/jack.h>
#include "../codecs/sn95031.h"
#define MID_MONO 1
#define MID_STEREO 2
#define MID_MAX_CAP 5
#define MFLD_JACK_INSERT 0x04
enum soc_mic_bias_zones {
MFLD_MV_START = 0,
/* mic bias volutage range for Headphones*/
MFLD_MV_HP = 400,
/* mic bias volutage range for American Headset*/
MFLD_MV_AM_HS = 650,
/* mic bias volutage range for Headset*/
MFLD_MV_HS = 2000,
MFLD_MV_UNDEFINED,
};
static unsigned int hs_switch;
static unsigned int lo_dac;
struct mfld_mc_private {
struct platform_device *socdev;
void __iomem *int_base;
struct snd_soc_codec *codec;
u8 interrupt_status;
};
struct snd_soc_jack mfld_jack;
/*Headset jack detection DAPM pins */
static struct snd_soc_jack_pin mfld_jack_pins[] = {
{
.pin = "Headphones",
.mask = SND_JACK_HEADPHONE,
},
{
.pin = "AMIC1",
.mask = SND_JACK_MICROPHONE,
},
};
/* jack detection voltage zones */
static struct snd_soc_jack_zone mfld_zones[] = {
{MFLD_MV_START, MFLD_MV_AM_HS, SND_JACK_HEADPHONE},
{MFLD_MV_AM_HS, MFLD_MV_HS, SND_JACK_HEADSET},
};
/* sound card controls */
static const char *headset_switch_text[] = {"Earpiece", "Headset"};
static const char *lo_text[] = {"Vibra", "Headset", "IHF", "None"};
static const struct soc_enum headset_enum =
SOC_ENUM_SINGLE_EXT(2, headset_switch_text);
static const struct soc_enum lo_enum =
SOC_ENUM_SINGLE_EXT(4, lo_text);
static int headset_get_switch(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.integer.value[0] = hs_switch;
return 0;
}
static int headset_set_switch(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
if (ucontrol->value.integer.value[0] == hs_switch)
return 0;
if (ucontrol->value.integer.value[0]) {
pr_debug("hs_set HS path\n");
snd_soc_dapm_enable_pin(&codec->dapm, "Headphones");
snd_soc_dapm_disable_pin(&codec->dapm, "EPOUT");
} else {
pr_debug("hs_set EP path\n");
snd_soc_dapm_disable_pin(&codec->dapm, "Headphones");
snd_soc_dapm_enable_pin(&codec->dapm, "EPOUT");
}
snd_soc_dapm_sync(&codec->dapm);
hs_switch = ucontrol->value.integer.value[0];
return 0;
}
static void lo_enable_out_pins(struct snd_soc_codec *codec)
{
snd_soc_dapm_enable_pin(&codec->dapm, "IHFOUTL");
snd_soc_dapm_enable_pin(&codec->dapm, "IHFOUTR");
snd_soc_dapm_enable_pin(&codec->dapm, "LINEOUTL");
snd_soc_dapm_enable_pin(&codec->dapm, "LINEOUTR");
snd_soc_dapm_enable_pin(&codec->dapm, "VIB1OUT");
snd_soc_dapm_enable_pin(&codec->dapm, "VIB2OUT");
if (hs_switch) {
snd_soc_dapm_enable_pin(&codec->dapm, "Headphones");
snd_soc_dapm_disable_pin(&codec->dapm, "EPOUT");
} else {
snd_soc_dapm_disable_pin(&codec->dapm, "Headphones");
snd_soc_dapm_enable_pin(&codec->dapm, "EPOUT");
}
}
static int lo_get_switch(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.integer.value[0] = lo_dac;
return 0;
}
static int lo_set_switch(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
if (ucontrol->value.integer.value[0] == lo_dac)
return 0;
/* we dont want to work with last state of lineout so just enable all
* pins and then disable pins not required
*/
lo_enable_out_pins(codec);
switch (ucontrol->value.integer.value[0]) {
case 0:
pr_debug("set vibra path\n");
snd_soc_dapm_disable_pin(&codec->dapm, "VIB1OUT");
snd_soc_dapm_disable_pin(&codec->dapm, "VIB2OUT");
snd_soc_update_bits(codec, SN95031_LOCTL, 0x66, 0);
break;
case 1:
pr_debug("set hs path\n");
snd_soc_dapm_disable_pin(&codec->dapm, "Headphones");
snd_soc_dapm_disable_pin(&codec->dapm, "EPOUT");
snd_soc_update_bits(codec, SN95031_LOCTL, 0x66, 0x22);
break;
case 2:
pr_debug("set spkr path\n");
snd_soc_dapm_disable_pin(&codec->dapm, "IHFOUTL");
snd_soc_dapm_disable_pin(&codec->dapm, "IHFOUTR");
snd_soc_update_bits(codec, SN95031_LOCTL, 0x66, 0x44);
break;
case 3:
pr_debug("set null path\n");
snd_soc_dapm_disable_pin(&codec->dapm, "LINEOUTL");
snd_soc_dapm_disable_pin(&codec->dapm, "LINEOUTR");
snd_soc_update_bits(codec, SN95031_LOCTL, 0x66, 0x66);
break;
}
snd_soc_dapm_sync(&codec->dapm);
lo_dac = ucontrol->value.integer.value[0];
return 0;
}
static const struct snd_kcontrol_new mfld_snd_controls[] = {
SOC_ENUM_EXT("Playback Switch", headset_enum,
headset_get_switch, headset_set_switch),
SOC_ENUM_EXT("Lineout Mux", lo_enum,
lo_get_switch, lo_set_switch),
};
static const struct snd_soc_dapm_widget mfld_widgets[] = {
SND_SOC_DAPM_HP("Headphones", NULL),
SND_SOC_DAPM_MIC("Mic", NULL),
};
static const struct snd_soc_dapm_route mfld_map[] = {
{"Headphones", NULL, "HPOUTR"},
{"Headphones", NULL, "HPOUTL"},
{"Mic", NULL, "AMIC1"},
};
static void mfld_jack_check(unsigned int intr_status)
{
struct mfld_jack_data jack_data;
jack_data.mfld_jack = &mfld_jack;
jack_data.intr_id = intr_status;
sn95031_jack_detection(&jack_data);
/* TODO: add american headset detection post gpiolib support */
}
static int mfld_init(struct snd_soc_pcm_runtime *runtime)
{
struct snd_soc_codec *codec = runtime->codec;
struct snd_soc_dapm_context *dapm = &codec->dapm;
int ret_val;
/* Add jack sense widgets */
snd_soc_dapm_new_controls(dapm, mfld_widgets, ARRAY_SIZE(mfld_widgets));
/* Set up the map */
snd_soc_dapm_add_routes(dapm, mfld_map, ARRAY_SIZE(mfld_map));
/* always connected */
snd_soc_dapm_enable_pin(dapm, "Headphones");
snd_soc_dapm_enable_pin(dapm, "Mic");
snd_soc_dapm_sync(dapm);
ret_val = snd_soc_add_controls(codec, mfld_snd_controls,
ARRAY_SIZE(mfld_snd_controls));
if (ret_val) {
pr_err("soc_add_controls failed %d", ret_val);
return ret_val;
}
/* default is earpiece pin, userspace sets it explcitly */
snd_soc_dapm_disable_pin(dapm, "Headphones");
/* default is lineout NC, userspace sets it explcitly */
snd_soc_dapm_disable_pin(dapm, "LINEOUTL");
snd_soc_dapm_disable_pin(dapm, "LINEOUTR");
lo_dac = 3;
hs_switch = 0;
/* we dont use linein in this so set to NC */
snd_soc_dapm_disable_pin(dapm, "LINEINL");
snd_soc_dapm_disable_pin(dapm, "LINEINR");
snd_soc_dapm_sync(dapm);
/* Headset and button jack detection */
ret_val = snd_soc_jack_new(codec, "Intel(R) MID Audio Jack",
SND_JACK_HEADSET | SND_JACK_BTN_0 |
SND_JACK_BTN_1, &mfld_jack);
if (ret_val) {
pr_err("jack creation failed\n");
return ret_val;
}
ret_val = snd_soc_jack_add_pins(&mfld_jack,
ARRAY_SIZE(mfld_jack_pins), mfld_jack_pins);
if (ret_val) {
pr_err("adding jack pins failed\n");
return ret_val;
}
ret_val = snd_soc_jack_add_zones(&mfld_jack,
ARRAY_SIZE(mfld_zones), mfld_zones);
if (ret_val) {
pr_err("adding jack zones failed\n");
return ret_val;
}
/* we want to check if anything is inserted at boot,
* so send a fake event to codec and it will read adc
* to find if anything is there or not */
mfld_jack_check(MFLD_JACK_INSERT);
return ret_val;
}
struct snd_soc_dai_link mfld_msic_dailink[] = {
{
.name = "Medfield Headset",
.stream_name = "Headset",
.cpu_dai_name = "Headset-cpu-dai",
.codec_dai_name = "SN95031 Headset",
.codec_name = "sn95031",
.platform_name = "sst-platform",
.init = mfld_init,
},
{
.name = "Medfield Speaker",
.stream_name = "Speaker",
.cpu_dai_name = "Speaker-cpu-dai",
.codec_dai_name = "SN95031 Speaker",
.codec_name = "sn95031",
.platform_name = "sst-platform",
.init = NULL,
},
{
.name = "Medfield Vibra",
.stream_name = "Vibra1",
.cpu_dai_name = "Vibra1-cpu-dai",
.codec_dai_name = "SN95031 Vibra1",
.codec_name = "sn95031",
.platform_name = "sst-platform",
.init = NULL,
},
{
.name = "Medfield Haptics",
.stream_name = "Vibra2",
.cpu_dai_name = "Vibra2-cpu-dai",
.codec_dai_name = "SN95031 Vibra2",
.codec_name = "sn95031",
.platform_name = "sst-platform",
.init = NULL,
},
};
/* SoC card */
static struct snd_soc_card snd_soc_card_mfld = {
.name = "medfield_audio",
.dai_link = mfld_msic_dailink,
.num_links = ARRAY_SIZE(mfld_msic_dailink),
};
static irqreturn_t snd_mfld_jack_intr_handler(int irq, void *dev)
{
struct mfld_mc_private *mc_private = (struct mfld_mc_private *) dev;
memcpy_fromio(&mc_private->interrupt_status,
((void *)(mc_private->int_base)),
sizeof(u8));
return IRQ_WAKE_THREAD;
}
static irqreturn_t snd_mfld_jack_detection(int irq, void *data)
{
struct mfld_mc_private *mc_drv_ctx = (struct mfld_mc_private *) data;
if (mfld_jack.codec == NULL)
return IRQ_HANDLED;
mfld_jack_check(mc_drv_ctx->interrupt_status);
return IRQ_HANDLED;
}
static int __devinit snd_mfld_mc_probe(struct platform_device *pdev)
{
int ret_val = 0, irq;
struct mfld_mc_private *mc_drv_ctx;
struct resource *irq_mem;
pr_debug("snd_mfld_mc_probe called\n");
/* retrive the irq number */
irq = platform_get_irq(pdev, 0);
/* audio interrupt base of SRAM location where
* interrupts are stored by System FW */
mc_drv_ctx = kzalloc(sizeof(*mc_drv_ctx), GFP_ATOMIC);
if (!mc_drv_ctx) {
pr_err("allocation failed\n");
return -ENOMEM;
}
irq_mem = platform_get_resource_byname(
pdev, IORESOURCE_MEM, "IRQ_BASE");
if (!irq_mem) {
pr_err("no mem resource given\n");
ret_val = -ENODEV;
goto unalloc;
}
mc_drv_ctx->int_base = ioremap_nocache(irq_mem->start,
resource_size(irq_mem));
if (!mc_drv_ctx->int_base) {
pr_err("Mapping of cache failed\n");
ret_val = -ENOMEM;
goto unalloc;
}
/* register for interrupt */
ret_val = request_threaded_irq(irq, snd_mfld_jack_intr_handler,
snd_mfld_jack_detection,
IRQF_SHARED, pdev->dev.driver->name, mc_drv_ctx);
if (ret_val) {
pr_err("cannot register IRQ\n");
goto unalloc;
}
/* register the soc card */
snd_soc_card_mfld.dev = &pdev->dev;
ret_val = snd_soc_register_card(&snd_soc_card_mfld);
if (ret_val) {
pr_debug("snd_soc_register_card failed %d\n", ret_val);
goto freeirq;
}
platform_set_drvdata(pdev, mc_drv_ctx);
pr_debug("successfully exited probe\n");
return ret_val;
freeirq:
free_irq(irq, mc_drv_ctx);
unalloc:
kfree(mc_drv_ctx);
return ret_val;
}
static int __devexit snd_mfld_mc_remove(struct platform_device *pdev)
{
struct mfld_mc_private *mc_drv_ctx = platform_get_drvdata(pdev);
pr_debug("snd_mfld_mc_remove called\n");
free_irq(platform_get_irq(pdev, 0), mc_drv_ctx);
snd_soc_unregister_card(&snd_soc_card_mfld);
kfree(mc_drv_ctx);
platform_set_drvdata(pdev, NULL);
return 0;
}
static struct platform_driver snd_mfld_mc_driver = {
.driver = {
.owner = THIS_MODULE,
.name = "msic_audio",
},
.probe = snd_mfld_mc_probe,
.remove = __devexit_p(snd_mfld_mc_remove),
};
static int __init snd_mfld_driver_init(void)
{
pr_debug("snd_mfld_driver_init called\n");
return platform_driver_register(&snd_mfld_mc_driver);
}
module_init(snd_mfld_driver_init);
static void __exit snd_mfld_driver_exit(void)
{
pr_debug("snd_mfld_driver_exit called\n");
platform_driver_unregister(&snd_mfld_mc_driver);
}
module_exit(snd_mfld_driver_exit);
MODULE_DESCRIPTION("ASoC Intel(R) MID Machine driver");
MODULE_AUTHOR("Vinod Koul <vinod.koul@intel.com>");
MODULE_AUTHOR("Harsha Priya <priya.harsha@intel.com>");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:msic-audio");

View file

@ -0,0 +1,474 @@
/*
* sst_platform.c - Intel MID Platform driver
*
* Copyright (C) 2010 Intel Corp
* Author: Vinod Koul <vinod.koul@intel.com>
* Author: Harsha Priya <priya.harsha@intel.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; version 2 of the License.
*
* 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 pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/slab.h>
#include <linux/io.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include "../../../drivers/staging/intel_sst/intel_sst_ioctl.h"
#include "../../../drivers/staging/intel_sst/intel_sst.h"
#include "sst_platform.h"
static struct snd_pcm_hardware sst_platform_pcm_hw = {
.info = (SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_DOUBLE |
SNDRV_PCM_INFO_PAUSE |
SNDRV_PCM_INFO_RESUME |
SNDRV_PCM_INFO_MMAP|
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_SYNC_START),
.formats = (SNDRV_PCM_FMTBIT_S16 | SNDRV_PCM_FMTBIT_U16 |
SNDRV_PCM_FMTBIT_S24 | SNDRV_PCM_FMTBIT_U24 |
SNDRV_PCM_FMTBIT_S32 | SNDRV_PCM_FMTBIT_U32),
.rates = (SNDRV_PCM_RATE_8000|
SNDRV_PCM_RATE_44100 |
SNDRV_PCM_RATE_48000),
.rate_min = SST_MIN_RATE,
.rate_max = SST_MAX_RATE,
.channels_min = SST_MIN_CHANNEL,
.channels_max = SST_MAX_CHANNEL,
.buffer_bytes_max = SST_MAX_BUFFER,
.period_bytes_min = SST_MIN_PERIOD_BYTES,
.period_bytes_max = SST_MAX_PERIOD_BYTES,
.periods_min = SST_MIN_PERIODS,
.periods_max = SST_MAX_PERIODS,
.fifo_size = SST_FIFO_SIZE,
};
/* MFLD - MSIC */
struct snd_soc_dai_driver sst_platform_dai[] = {
{
.name = "Headset-cpu-dai",
.id = 0,
.playback = {
.channels_min = SST_STEREO,
.channels_max = SST_STEREO,
.rates = SNDRV_PCM_RATE_48000,
.formats = SNDRV_PCM_FMTBIT_S24_LE,
},
.capture = {
.channels_min = 1,
.channels_max = 5,
.rates = SNDRV_PCM_RATE_48000,
.formats = SNDRV_PCM_FMTBIT_S24_LE,
},
},
{
.name = "Speaker-cpu-dai",
.id = 1,
.playback = {
.channels_min = SST_MONO,
.channels_max = SST_STEREO,
.rates = SNDRV_PCM_RATE_48000,
.formats = SNDRV_PCM_FMTBIT_S24_LE,
},
},
{
.name = "Vibra1-cpu-dai",
.id = 2,
.playback = {
.channels_min = SST_MONO,
.channels_max = SST_MONO,
.rates = SNDRV_PCM_RATE_48000,
.formats = SNDRV_PCM_FMTBIT_S24_LE,
},
},
{
.name = "Vibra2-cpu-dai",
.id = 3,
.playback = {
.channels_min = SST_MONO,
.channels_max = SST_STEREO,
.rates = SNDRV_PCM_RATE_48000,
.formats = SNDRV_PCM_FMTBIT_S24_LE,
},
},
};
/* helper functions */
static inline void sst_set_stream_status(struct sst_runtime_stream *stream,
int state)
{
spin_lock(&stream->status_lock);
stream->stream_status = state;
spin_unlock(&stream->status_lock);
}
static inline int sst_get_stream_status(struct sst_runtime_stream *stream)
{
int state;
spin_lock(&stream->status_lock);
state = stream->stream_status;
spin_unlock(&stream->status_lock);
return state;
}
static void sst_fill_pcm_params(struct snd_pcm_substream *substream,
struct snd_sst_stream_params *param)
{
param->uc.pcm_params.codec = SST_CODEC_TYPE_PCM;
param->uc.pcm_params.num_chan = (u8) substream->runtime->channels;
param->uc.pcm_params.pcm_wd_sz = substream->runtime->sample_bits;
param->uc.pcm_params.reserved = 0;
param->uc.pcm_params.sfreq = substream->runtime->rate;
param->uc.pcm_params.ring_buffer_size =
snd_pcm_lib_buffer_bytes(substream);
param->uc.pcm_params.period_count = substream->runtime->period_size;
param->uc.pcm_params.ring_buffer_addr =
virt_to_phys(substream->dma_buffer.area);
pr_debug("period_cnt = %d\n", param->uc.pcm_params.period_count);
pr_debug("sfreq= %d, wd_sz = %d\n",
param->uc.pcm_params.sfreq, param->uc.pcm_params.pcm_wd_sz);
}
static int sst_platform_alloc_stream(struct snd_pcm_substream *substream)
{
struct sst_runtime_stream *stream =
substream->runtime->private_data;
struct snd_sst_stream_params param = {{{0,},},};
struct snd_sst_params str_params = {0};
int ret_val;
/* set codec params and inform SST driver the same */
sst_fill_pcm_params(substream, &param);
substream->runtime->dma_area = substream->dma_buffer.area;
str_params.sparams = param;
str_params.codec = param.uc.pcm_params.codec;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
str_params.ops = STREAM_OPS_PLAYBACK;
str_params.device_type = substream->pcm->device + 1;
pr_debug("Playbck stream,Device %d\n",
substream->pcm->device);
} else {
str_params.ops = STREAM_OPS_CAPTURE;
str_params.device_type = SND_SST_DEVICE_CAPTURE;
pr_debug("Capture stream,Device %d\n",
substream->pcm->device);
}
ret_val = stream->sstdrv_ops->pcm_control->open(&str_params);
pr_debug("SST_SND_PLAY/CAPTURE ret_val = %x\n", ret_val);
if (ret_val < 0)
return ret_val;
stream->stream_info.str_id = ret_val;
pr_debug("str id : %d\n", stream->stream_info.str_id);
return ret_val;
}
static void sst_period_elapsed(void *mad_substream)
{
struct snd_pcm_substream *substream = mad_substream;
struct sst_runtime_stream *stream;
int status;
if (!substream || !substream->runtime)
return;
stream = substream->runtime->private_data;
if (!stream)
return;
status = sst_get_stream_status(stream);
if (status != SST_PLATFORM_RUNNING)
return;
snd_pcm_period_elapsed(substream);
}
static int sst_platform_init_stream(struct snd_pcm_substream *substream)
{
struct sst_runtime_stream *stream =
substream->runtime->private_data;
int ret_val;
pr_debug("setting buffer ptr param\n");
sst_set_stream_status(stream, SST_PLATFORM_INIT);
stream->stream_info.period_elapsed = sst_period_elapsed;
stream->stream_info.mad_substream = substream;
stream->stream_info.buffer_ptr = 0;
stream->stream_info.sfreq = substream->runtime->rate;
ret_val = stream->sstdrv_ops->pcm_control->device_control(
SST_SND_STREAM_INIT, &stream->stream_info);
if (ret_val)
pr_err("control_set ret error %d\n", ret_val);
return ret_val;
}
/* end -- helper functions */
static int sst_platform_open(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime;
struct sst_runtime_stream *stream;
int ret_val = 0;
pr_debug("sst_platform_open called\n");
runtime = substream->runtime;
runtime->hw = sst_platform_pcm_hw;
stream = kzalloc(sizeof(*stream), GFP_KERNEL);
if (!stream)
return -ENOMEM;
spin_lock_init(&stream->status_lock);
stream->stream_info.str_id = 0;
sst_set_stream_status(stream, SST_PLATFORM_INIT);
stream->stream_info.mad_substream = substream;
/* allocate memory for SST API set */
stream->sstdrv_ops = kzalloc(sizeof(*stream->sstdrv_ops),
GFP_KERNEL);
if (!stream->sstdrv_ops) {
pr_err("sst: mem allocation for ops fail\n");
kfree(stream);
return -ENOMEM;
}
stream->sstdrv_ops->vendor_id = MSIC_VENDOR_ID;
/* registering with SST driver to get access to SST APIs to use */
ret_val = register_sst_card(stream->sstdrv_ops);
if (ret_val) {
pr_err("sst: sst card registration failed\n");
return ret_val;
}
runtime->private_data = stream;
return snd_pcm_hw_constraint_integer(runtime,
SNDRV_PCM_HW_PARAM_PERIODS);
}
static int sst_platform_close(struct snd_pcm_substream *substream)
{
struct sst_runtime_stream *stream;
int ret_val = 0, str_id;
pr_debug("sst_platform_close called\n");
stream = substream->runtime->private_data;
str_id = stream->stream_info.str_id;
if (str_id)
ret_val = stream->sstdrv_ops->pcm_control->close(str_id);
kfree(stream->sstdrv_ops);
kfree(stream);
return ret_val;
}
static int sst_platform_pcm_prepare(struct snd_pcm_substream *substream)
{
struct sst_runtime_stream *stream;
int ret_val = 0, str_id;
pr_debug("sst_platform_pcm_prepare called\n");
stream = substream->runtime->private_data;
str_id = stream->stream_info.str_id;
if (stream->stream_info.str_id) {
ret_val = stream->sstdrv_ops->pcm_control->device_control(
SST_SND_DROP, &str_id);
return ret_val;
}
ret_val = sst_platform_alloc_stream(substream);
if (ret_val < 0)
return ret_val;
snprintf(substream->pcm->id, sizeof(substream->pcm->id),
"%d", stream->stream_info.str_id);
ret_val = sst_platform_init_stream(substream);
if (ret_val)
return ret_val;
substream->runtime->hw.info = SNDRV_PCM_INFO_BLOCK_TRANSFER;
return ret_val;
}
static int sst_platform_pcm_trigger(struct snd_pcm_substream *substream,
int cmd)
{
int ret_val = 0, str_id;
struct sst_runtime_stream *stream;
int str_cmd, status;
pr_debug("sst_platform_pcm_trigger called\n");
stream = substream->runtime->private_data;
str_id = stream->stream_info.str_id;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
pr_debug("sst: Trigger Start\n");
str_cmd = SST_SND_START;
status = SST_PLATFORM_RUNNING;
stream->stream_info.mad_substream = substream;
break;
case SNDRV_PCM_TRIGGER_STOP:
pr_debug("sst: in stop\n");
str_cmd = SST_SND_DROP;
status = SST_PLATFORM_DROPPED;
break;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
pr_debug("sst: in pause\n");
str_cmd = SST_SND_PAUSE;
status = SST_PLATFORM_PAUSED;
break;
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
pr_debug("sst: in pause release\n");
str_cmd = SST_SND_RESUME;
status = SST_PLATFORM_RUNNING;
break;
default:
return -EINVAL;
}
ret_val = stream->sstdrv_ops->pcm_control->device_control(str_cmd,
&str_id);
if (!ret_val)
sst_set_stream_status(stream, status);
return ret_val;
}
static snd_pcm_uframes_t sst_platform_pcm_pointer
(struct snd_pcm_substream *substream)
{
struct sst_runtime_stream *stream;
int ret_val, status;
struct pcm_stream_info *str_info;
stream = substream->runtime->private_data;
status = sst_get_stream_status(stream);
if (status == SST_PLATFORM_INIT)
return 0;
str_info = &stream->stream_info;
ret_val = stream->sstdrv_ops->pcm_control->device_control(
SST_SND_BUFFER_POINTER, str_info);
if (ret_val) {
pr_err("sst: error code = %d\n", ret_val);
return ret_val;
}
return stream->stream_info.buffer_ptr;
}
static int sst_platform_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
memset(substream->runtime->dma_area, 0, params_buffer_bytes(params));
return 0;
}
static struct snd_pcm_ops sst_platform_ops = {
.open = sst_platform_open,
.close = sst_platform_close,
.ioctl = snd_pcm_lib_ioctl,
.prepare = sst_platform_pcm_prepare,
.trigger = sst_platform_pcm_trigger,
.pointer = sst_platform_pcm_pointer,
.hw_params = sst_platform_pcm_hw_params,
};
static void sst_pcm_free(struct snd_pcm *pcm)
{
pr_debug("sst_pcm_free called\n");
snd_pcm_lib_preallocate_free_for_all(pcm);
}
int sst_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
struct snd_pcm *pcm)
{
int retval = 0;
pr_debug("sst_pcm_new called\n");
if (dai->driver->playback.channels_min ||
dai->driver->capture.channels_min) {
retval = snd_pcm_lib_preallocate_pages_for_all(pcm,
SNDRV_DMA_TYPE_CONTINUOUS,
snd_dma_continuous_data(GFP_KERNEL),
SST_MIN_BUFFER, SST_MAX_BUFFER);
if (retval) {
pr_err("dma buffer allocationf fail\n");
return retval;
}
}
return retval;
}
struct snd_soc_platform_driver sst_soc_platform_drv = {
.ops = &sst_platform_ops,
.pcm_new = sst_pcm_new,
.pcm_free = sst_pcm_free,
};
static int sst_platform_probe(struct platform_device *pdev)
{
int ret;
pr_debug("sst_platform_probe called\n");
ret = snd_soc_register_platform(&pdev->dev, &sst_soc_platform_drv);
if (ret) {
pr_err("registering soc platform failed\n");
return ret;
}
ret = snd_soc_register_dais(&pdev->dev,
sst_platform_dai, ARRAY_SIZE(sst_platform_dai));
if (ret) {
pr_err("registering cpu dais failed\n");
snd_soc_unregister_platform(&pdev->dev);
}
return ret;
}
static int sst_platform_remove(struct platform_device *pdev)
{
snd_soc_unregister_dais(&pdev->dev, ARRAY_SIZE(sst_platform_dai));
snd_soc_unregister_platform(&pdev->dev);
pr_debug("sst_platform_remove sucess\n");
return 0;
}
static struct platform_driver sst_platform_driver = {
.driver = {
.name = "sst-platform",
.owner = THIS_MODULE,
},
.probe = sst_platform_probe,
.remove = sst_platform_remove,
};
static int __init sst_soc_platform_init(void)
{
pr_debug("sst_soc_platform_init called\n");
return platform_driver_register(&sst_platform_driver);
}
module_init(sst_soc_platform_init);
static void __exit sst_soc_platform_exit(void)
{
platform_driver_unregister(&sst_platform_driver);
pr_debug("sst_soc_platform_exit sucess\n");
}
module_exit(sst_soc_platform_exit);
MODULE_DESCRIPTION("ASoC Intel(R) MID Platform driver");
MODULE_AUTHOR("Vinod Koul <vinod.koul@intel.com>");
MODULE_AUTHOR("Harsha Priya <priya.harsha@intel.com>");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:sst-platform");

View file

@ -0,0 +1,63 @@
/*
* sst_platform.h - Intel MID Platform driver header file
*
* Copyright (C) 2010 Intel Corp
* Author: Vinod Koul <vinod.koul@intel.com>
* Author: Harsha Priya <priya.harsha@intel.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; version 2 of the License.
*
* 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 __SST_PLATFORMDRV_H__
#define __SST_PLATFORMDRV_H__
#define SST_MONO 1
#define SST_STEREO 2
#define SST_MAX_CAP 5
#define SST_MIN_RATE 8000
#define SST_MAX_RATE 48000
#define SST_MIN_CHANNEL 1
#define SST_MAX_CHANNEL 5
#define SST_MAX_BUFFER (800*1024)
#define SST_MIN_BUFFER (800*1024)
#define SST_MIN_PERIOD_BYTES 32
#define SST_MAX_PERIOD_BYTES SST_MAX_BUFFER
#define SST_MIN_PERIODS 2
#define SST_MAX_PERIODS (1024*2)
#define SST_FIFO_SIZE 0
#define SST_CARD_NAMES "intel_mid_card"
#define MSIC_VENDOR_ID 3
struct sst_runtime_stream {
int stream_status;
struct pcm_stream_info stream_info;
struct intel_sst_card_ops *sstdrv_ops;
spinlock_t status_lock;
};
enum sst_drv_status {
SST_PLATFORM_INIT = 1,
SST_PLATFORM_STARTED,
SST_PLATFORM_RUNNING,
SST_PLATFORM_PAUSED,
SST_PLATFORM_DROPPED,
};
#endif

View file

@ -24,6 +24,7 @@ config SND_OMAP_SOC_RX51
select OMAP_MCBSP
select SND_OMAP_SOC_MCBSP
select SND_SOC_TLV320AIC3X
select SND_SOC_TPA6130A2
help
Say Y if you want to add support for SoC audio on Nokia RX-51
hardware. This is also known as Nokia N900 product.

View file

@ -31,6 +31,7 @@
#include <sound/pcm.h>
#include <sound/soc.h>
#include <plat/mcbsp.h>
#include "../codecs/tpa6130a2.h"
#include <asm/mach-types.h>
@ -39,6 +40,7 @@
#define RX51_TVOUT_SEL_GPIO 40
#define RX51_JACK_DETECT_GPIO 177
#define RX51_ECI_SW_GPIO 182
/*
* REVISIT: TWL4030 GPIO base in RX-51. Now statically defined to 192. This
* gpio is reserved in arch/arm/mach-omap2/board-rx51-peripherals.c
@ -47,7 +49,9 @@
enum {
RX51_JACK_DISABLED,
RX51_JACK_TVOUT, /* tv-out */
RX51_JACK_TVOUT, /* tv-out with stereo output */
RX51_JACK_HP, /* headphone: stereo output, no mic */
RX51_JACK_HS, /* headset: stereo output with mic */
};
static int rx51_spk_func;
@ -57,6 +61,19 @@ static int rx51_jack_func;
static void rx51_ext_control(struct snd_soc_codec *codec)
{
struct snd_soc_dapm_context *dapm = &codec->dapm;
int hp = 0, hs = 0, tvout = 0;
switch (rx51_jack_func) {
case RX51_JACK_TVOUT:
tvout = 1;
hp = 1;
break;
case RX51_JACK_HS:
hs = 1;
case RX51_JACK_HP:
hp = 1;
break;
}
if (rx51_spk_func)
snd_soc_dapm_enable_pin(dapm, "Ext Spk");
@ -66,9 +83,16 @@ static void rx51_ext_control(struct snd_soc_codec *codec)
snd_soc_dapm_enable_pin(dapm, "DMic");
else
snd_soc_dapm_disable_pin(dapm, "DMic");
if (hp)
snd_soc_dapm_enable_pin(dapm, "Headphone Jack");
else
snd_soc_dapm_disable_pin(dapm, "Headphone Jack");
if (hs)
snd_soc_dapm_enable_pin(dapm, "HS Mic");
else
snd_soc_dapm_disable_pin(dapm, "HS Mic");
gpio_set_value(RX51_TVOUT_SEL_GPIO,
rx51_jack_func == RX51_JACK_TVOUT);
gpio_set_value(RX51_TVOUT_SEL_GPIO, tvout);
snd_soc_dapm_sync(dapm);
}
@ -153,6 +177,19 @@ static int rx51_spk_event(struct snd_soc_dapm_widget *w,
return 0;
}
static int rx51_hp_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *k, int event)
{
struct snd_soc_codec *codec = w->dapm->codec;
if (SND_SOC_DAPM_EVENT_ON(event))
tpa6130a2_stereo_enable(codec, 1);
else
tpa6130a2_stereo_enable(codec, 0);
return 0;
}
static int rx51_get_input(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@ -203,7 +240,7 @@ static struct snd_soc_jack_gpio rx51_av_jack_gpios[] = {
{
.gpio = RX51_JACK_DETECT_GPIO,
.name = "avdet-gpio",
.report = SND_JACK_VIDEOOUT,
.report = SND_JACK_HEADSET,
.invert = 1,
.debounce_time = 200,
},
@ -212,19 +249,38 @@ static struct snd_soc_jack_gpio rx51_av_jack_gpios[] = {
static const struct snd_soc_dapm_widget aic34_dapm_widgets[] = {
SND_SOC_DAPM_SPK("Ext Spk", rx51_spk_event),
SND_SOC_DAPM_MIC("DMic", NULL),
SND_SOC_DAPM_HP("Headphone Jack", rx51_hp_event),
SND_SOC_DAPM_MIC("HS Mic", NULL),
SND_SOC_DAPM_LINE("FM Transmitter", NULL),
};
static const struct snd_soc_dapm_widget aic34_dapm_widgetsb[] = {
SND_SOC_DAPM_SPK("Earphone", NULL),
};
static const struct snd_soc_dapm_route audio_map[] = {
{"Ext Spk", NULL, "HPLOUT"},
{"Ext Spk", NULL, "HPROUT"},
{"Headphone Jack", NULL, "LLOUT"},
{"Headphone Jack", NULL, "RLOUT"},
{"FM Transmitter", NULL, "LLOUT"},
{"FM Transmitter", NULL, "RLOUT"},
{"DMic Rate 64", NULL, "Mic Bias 2V"},
{"Mic Bias 2V", NULL, "DMic"},
};
static const struct snd_soc_dapm_route audio_mapb[] = {
{"b LINE2R", NULL, "MONO_LOUT"},
{"Earphone", NULL, "b HPLOUT"},
{"LINE1L", NULL, "b Mic Bias 2.5V"},
{"b Mic Bias 2.5V", NULL, "HS Mic"}
};
static const char *spk_function[] = {"Off", "On"};
static const char *input_function[] = {"ADC", "Digital Mic"};
static const char *jack_function[] = {"Off", "TV-OUT"};
static const char *jack_function[] = {"Off", "TV-OUT", "Headphone", "Headset"};
static const struct soc_enum rx51_enum[] = {
SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(spk_function), spk_function),
@ -239,6 +295,11 @@ static const struct snd_kcontrol_new aic34_rx51_controls[] = {
rx51_get_input, rx51_set_input),
SOC_ENUM_EXT("Jack Function", rx51_enum[2],
rx51_get_jack, rx51_set_jack),
SOC_DAPM_PIN_SWITCH("FM Transmitter"),
};
static const struct snd_kcontrol_new aic34_rx51_controlsb[] = {
SOC_DAPM_PIN_SWITCH("Earphone"),
};
static int rx51_aic34_init(struct snd_soc_pcm_runtime *rtd)
@ -265,11 +326,21 @@ static int rx51_aic34_init(struct snd_soc_pcm_runtime *rtd)
/* Set up RX-51 specific audio path audio_map */
snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map));
err = tpa6130a2_add_controls(codec);
if (err < 0)
return err;
snd_soc_limit_volume(codec, "TPA6130A2 Headphone Playback Volume", 42);
err = omap_mcbsp_st_add_controls(codec, 1);
if (err < 0)
return err;
snd_soc_dapm_sync(dapm);
/* AV jack detection */
err = snd_soc_jack_new(codec, "AV Jack",
SND_JACK_VIDEOOUT, &rx51_av_jack);
SND_JACK_HEADSET | SND_JACK_VIDEOOUT,
&rx51_av_jack);
if (err)
return err;
err = snd_soc_jack_add_gpios(&rx51_av_jack,
@ -279,6 +350,24 @@ static int rx51_aic34_init(struct snd_soc_pcm_runtime *rtd)
return err;
}
static int rx51_aic34b_init(struct snd_soc_dapm_context *dapm)
{
int err;
err = snd_soc_add_controls(dapm->codec, aic34_rx51_controlsb,
ARRAY_SIZE(aic34_rx51_controlsb));
if (err < 0)
return err;
err = snd_soc_dapm_new_controls(dapm, aic34_dapm_widgetsb,
ARRAY_SIZE(aic34_dapm_widgetsb));
if (err < 0)
return 0;
return snd_soc_dapm_add_routes(dapm, audio_mapb,
ARRAY_SIZE(audio_mapb));
}
/* Digital audio interface glue - connects codec <--> CPU */
static struct snd_soc_dai_link rx51_dai[] = {
{
@ -293,11 +382,30 @@ static struct snd_soc_dai_link rx51_dai[] = {
},
};
struct snd_soc_aux_dev rx51_aux_dev[] = {
{
.name = "TLV320AIC34b",
.codec_name = "tlv320aic3x-codec.2-0019",
.init = rx51_aic34b_init,
},
};
static struct snd_soc_codec_conf rx51_codec_conf[] = {
{
.dev_name = "tlv320aic3x-codec.2-0019",
.name_prefix = "b",
},
};
/* Audio card */
static struct snd_soc_card rx51_sound_card = {
.name = "RX-51",
.dai_link = rx51_dai,
.num_links = ARRAY_SIZE(rx51_dai),
.aux_dev = rx51_aux_dev,
.num_aux_devs = ARRAY_SIZE(rx51_aux_dev),
.codec_conf = rx51_codec_conf,
.num_configs = ARRAY_SIZE(rx51_codec_conf),
};
static struct platform_device *rx51_snd_device;
@ -309,10 +417,14 @@ static int __init rx51_soc_init(void)
if (!machine_is_nokia_rx51())
return -ENODEV;
err = gpio_request(RX51_TVOUT_SEL_GPIO, "tvout_sel");
err = gpio_request_one(RX51_TVOUT_SEL_GPIO,
GPIOF_DIR_OUT | GPIOF_INIT_LOW, "tvout_sel");
if (err)
goto err_gpio_tvout_sel;
gpio_direction_output(RX51_TVOUT_SEL_GPIO, 0);
err = gpio_request_one(RX51_ECI_SW_GPIO,
GPIOF_DIR_OUT | GPIOF_INIT_HIGH, "eci_sw");
if (err)
goto err_gpio_eci_sw;
rx51_snd_device = platform_device_alloc("soc-audio", -1);
if (!rx51_snd_device) {
@ -330,6 +442,8 @@ static int __init rx51_soc_init(void)
err2:
platform_device_put(rx51_snd_device);
err1:
gpio_free(RX51_ECI_SW_GPIO);
err_gpio_eci_sw:
gpio_free(RX51_TVOUT_SEL_GPIO);
err_gpio_tvout_sel:
@ -342,6 +456,7 @@ static void __exit rx51_soc_exit(void)
rx51_av_jack_gpios);
platform_device_unregister(rx51_snd_device);
gpio_free(RX51_ECI_SW_GPIO);
gpio_free(RX51_TVOUT_SEL_GPIO);
}

View file

@ -151,13 +151,13 @@ static struct snd_soc_ops raumfeld_cs4270_ops = {
.hw_params = raumfeld_cs4270_hw_params,
};
static int raumfeld_line_suspend(struct platform_device *pdev, pm_message_t state)
static int raumfeld_line_suspend(struct snd_soc_card *card)
{
raumfeld_enable_audio(false);
return 0;
}
static int raumfeld_line_resume(struct platform_device *pdev)
static int raumfeld_line_resume(struct snd_soc_card *card)
{
raumfeld_enable_audio(true);
return 0;
@ -229,19 +229,19 @@ static struct snd_soc_dai_link raumfeld_dai[] = {
{
.name = "ak4104",
.stream_name = "Playback",
.cpu_dai_name = "pxa-ssp-dai.1",
.codec_dai_name = "ak4104-hifi",
.platform_name = "pxa-pcm-audio",
.cpu_dai_name = "pxa-ssp-dai.1",
.codec_dai_name = "ak4104-hifi",
.platform_name = "pxa-pcm-audio",
.ops = &raumfeld_ak4104_ops,
.codec_name = "ak4104-codec.0",
.codec_name = "ak4104-codec.0",
},
{
.name = "CS4270",
.stream_name = "CS4270",
.cpu_dai_name = "pxa-ssp-dai.0",
.platform_name = "pxa-pcm-audio",
.codec_dai_name = "cs4270-hifi",
.codec_name = "cs4270-codec.0-0048",
.cpu_dai_name = "pxa-ssp-dai.0",
.platform_name = "pxa-pcm-audio",
.codec_dai_name = "cs4270-hifi",
.codec_name = "cs4270-codec.0-0048",
.ops = &raumfeld_cs4270_ops,
},};

View file

@ -237,7 +237,7 @@ static struct snd_soc_dai_link tosa_dai[] = {
},
};
static int tosa_probe(struct platform_device *dev)
static int tosa_probe(struct snd_soc_card *card)
{
int ret;
@ -251,7 +251,7 @@ static int tosa_probe(struct platform_device *dev)
return ret;
}
static int tosa_remove(struct platform_device *dev)
static int tosa_remove(struct snd_soc_card *card)
{
gpio_free(TOSA_GPIO_L_MUTE);
return 0;

View file

@ -95,6 +95,11 @@ static struct snd_soc_jack_pin hs_jack_pins[] = {
.pin = "Headphone Jack",
.mask = SND_JACK_HEADPHONE,
},
{
.pin = "Ext Spk",
.mask = SND_JACK_HEADPHONE,
.invert = 1
},
};
/* Headset jack detection gpios */
@ -147,7 +152,7 @@ static int z2_wm8750_init(struct snd_soc_pcm_runtime *rtd)
snd_soc_dapm_disable_pin(dapm, "LINPUT3");
snd_soc_dapm_disable_pin(dapm, "RINPUT3");
snd_soc_dapm_disable_pin(dapm, "OUT3");
snd_soc_dapm_disable_pin(dapm, "MONO");
snd_soc_dapm_disable_pin(dapm, "MONO1");
/* Add z2 specific widgets */
snd_soc_dapm_new_controls(dapm, wm8750_dapm_widgets,

View file

@ -189,7 +189,7 @@ static struct snd_soc_dai_link zylonite_dai[] = {
},
};
static int zylonite_probe(struct platform_device *pdev)
static int zylonite_probe(struct snd_soc_card *card)
{
int ret;
@ -216,7 +216,7 @@ static int zylonite_probe(struct platform_device *pdev)
return 0;
}
static int zylonite_remove(struct platform_device *pdev)
static int zylonite_remove(struct snd_soc_card *card)
{
if (clk_pout) {
clk_disable(pout);
@ -226,8 +226,7 @@ static int zylonite_remove(struct platform_device *pdev)
return 0;
}
static int zylonite_suspend_post(struct platform_device *pdev,
pm_message_t state)
static int zylonite_suspend_post(struct snd_soc_card *card)
{
if (clk_pout)
clk_disable(pout);
@ -235,7 +234,7 @@ static int zylonite_suspend_post(struct platform_device *pdev,
return 0;
}
static int zylonite_resume_pre(struct platform_device *pdev)
static int zylonite_resume_pre(struct snd_soc_card *card)
{
int ret = 0;

View file

@ -35,23 +35,16 @@ config SND_SAMSUNG_I2S
tristate
config SND_SOC_SAMSUNG_NEO1973_WM8753
tristate "SoC I2S Audio support for NEO1973 - WM8753"
depends on SND_SOC_SAMSUNG && MACH_NEO1973_GTA01
tristate "Audio support for Openmoko Neo1973 Smartphones (GTA01/GTA02)"
depends on SND_SOC_SAMSUNG && (MACH_NEO1973_GTA01 || MACH_NEO1973_GTA02)
select SND_S3C24XX_I2S
select SND_SOC_WM8753
select SND_SOC_LM4857 if MACH_NEO1973_GTA01
select SND_SOC_DFBMCS320
help
Say Y if you want to add support for SoC audio on smdk2440
with the WM8753.
Say Y here to enable audio support for the Openmoko Neo1973
Smartphones.
config SND_SOC_SAMSUNG_NEO1973_GTA02_WM8753
tristate "Audio support for the Openmoko Neo FreeRunner (GTA02)"
depends on SND_SOC_SAMSUNG && MACH_NEO1973_GTA02
select SND_S3C24XX_I2S
select SND_SOC_WM8753
help
This driver provides audio support for the Openmoko Neo FreeRunner
smartphone.
config SND_SOC_SAMSUNG_JIVE_WM8750
tristate "SoC I2S Audio support for Jive"
depends on SND_SOC_SAMSUNG && MACH_JIVE

View file

@ -20,7 +20,6 @@ obj-$(CONFIG_SND_SAMSUNG_I2S) += snd-soc-i2s.o
# S3C24XX Machine Support
snd-soc-jive-wm8750-objs := jive_wm8750.o
snd-soc-neo1973-wm8753-objs := neo1973_wm8753.o
snd-soc-neo1973-gta02-wm8753-objs := neo1973_gta02_wm8753.o
snd-soc-smdk2443-wm9710-objs := smdk2443_wm9710.o
snd-soc-ln2440sbc-alc650-objs := ln2440sbc_alc650.o
snd-soc-s3c24xx-uda134x-objs := s3c24xx_uda134x.o
@ -38,7 +37,6 @@ snd-soc-smdk-spdif-objs := smdk_spdif.o
obj-$(CONFIG_SND_SOC_SAMSUNG_JIVE_WM8750) += snd-soc-jive-wm8750.o
obj-$(CONFIG_SND_SOC_SAMSUNG_NEO1973_WM8753) += snd-soc-neo1973-wm8753.o
obj-$(CONFIG_SND_SOC_SAMSUNG_NEO1973_GTA02_WM8753) += snd-soc-neo1973-gta02-wm8753.o
obj-$(CONFIG_SND_SOC_SAMSUNG_SMDK2443_WM9710) += snd-soc-smdk2443-wm9710.o
obj-$(CONFIG_SND_SOC_SAMSUNG_LN2440SBC_ALC650) += snd-soc-ln2440sbc-alc650.o
obj-$(CONFIG_SND_SOC_SAMSUNG_S3C24XX_UDA134X) += snd-soc-s3c24xx-uda134x.o

View file

@ -12,24 +12,24 @@
* published by the Free Software Foundation.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/io.h>
#include <linux/delay.h>
#include <linux/clk.h>
#include <sound/soc.h>
#include <plat/regs-ac97.h>
#include <mach/dma.h>
#include <plat/regs-ac97.h>
#include <plat/audio.h>
#include "dma.h"
#include "ac97.h"
#define AC_CMD_ADDR(x) (x << 16)
#define AC_CMD_DATA(x) (x & 0xffff)
#define S3C_AC97_DAI_PCM 0
#define S3C_AC97_DAI_MIC 1
struct s3c_ac97_info {
struct clk *ac97_clk;
void __iomem *regs;

View file

@ -1,21 +0,0 @@
/* sound/soc/samsung/ac97.h
*
* ALSA SoC Audio Layer - S3C AC97 Controller driver
* Evolved from s3c2443-ac97.h
*
* Copyright (c) 2010 Samsung Electronics Co. Ltd
* Author: Jaswinder Singh <jassi.brar@samsung.com>
* Credits: Graeme Gregory, Sean Choi
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef __S3C_AC97_H_
#define __S3C_AC97_H_
#define S3C_AC97_DAI_PCM 0
#define S3C_AC97_DAI_MIC 1
#endif /* __S3C_AC97_H_ */

View file

@ -14,17 +14,11 @@
* option) any later version.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/dma-mapping.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/pcm_params.h>
#include <asm/dma.h>
#include <mach/hardware.h>
@ -32,6 +26,9 @@
#include "dma.h"
#define ST_RUNNING (1<<0)
#define ST_OPENED (1<<1)
static const struct snd_pcm_hardware dma_hardware = {
.info = SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
@ -313,7 +310,7 @@ dma_pointer(struct snd_pcm_substream *substream)
/* we seem to be getting the odd error from the pcm library due
* to out-of-bounds pointers. this is maybe due to the dma engine
* not having loaded the new values for the channel before being
* callled... (todo - fix )
* called... (todo - fix )
*/
if (res >= snd_pcm_lib_buffer_bytes(substream)) {

View file

@ -12,9 +12,6 @@
#ifndef _S3C_AUDIO_H
#define _S3C_AUDIO_H
#define ST_RUNNING (1<<0)
#define ST_OPENED (1<<1)
struct s3c_dma_params {
struct s3c2410_dma_client *client; /* stream identifier */
int channel; /* Channel ID */
@ -22,9 +19,4 @@ struct s3c_dma_params {
int dma_size; /* Size of the DMA transfer */
};
#define S3C24XX_DAI_I2S 0
/* platform data */
extern struct snd_ac97_bus_ops s3c24xx_ac97_ops;
#endif

View file

@ -11,21 +11,13 @@
*
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <sound/soc.h>
#include <sound/jack.h>
#include <asm/mach-types.h>
#include <mach/gpio.h>
#include <mach/regs-clock.h>
#include <linux/mfd/wm8994/core.h>
#include <linux/mfd/wm8994/registers.h>
#include "../codecs/wm8994.h"
#include "dma.h"
#include "i2s.h"
#define MACHINE_NAME 0
#define CPU_VOICE_DAI 1

View file

@ -13,25 +13,16 @@
*
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
#include <linux/gpio.h>
#include <sound/soc.h>
#include <sound/uda1380.h>
#include <sound/jack.h>
#include <plat/regs-iis.h>
#include <mach/h1940-latch.h>
#include <asm/mach-types.h>
#include "dma.h"
#include "s3c24xx-i2s.h"
#include "../codecs/uda1380.h"
static unsigned int rates[] = {
11025,

View file

@ -15,9 +15,8 @@
#include <linux/clk.h>
#include <linux/io.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/pcm_params.h>
#include <plat/audio.h>

View file

@ -11,22 +11,11 @@
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/timer.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/soc.h>
#include <asm/mach-types.h>
#include "dma.h"
#include "s3c2412-i2s.h"
#include "../codecs/wm8750.h"
static const struct snd_soc_dapm_route audio_map[] = {

View file

@ -1,32 +0,0 @@
/*
* lm4857.h -- ALSA Soc Audio Layer
*
* Copyright 2007 Wolfson Microelectronics PLC.
* Author: Graeme Gregory
* graeme.gregory@wolfsonmicro.com or linux@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.
*
* Revision history
* 18th Jun 2007 Initial version.
*/
#ifndef LM4857_H_
#define LM4857_H_
/* The register offsets in the cache array */
#define LM4857_MVOL 0
#define LM4857_LVOL 1
#define LM4857_RVOL 2
#define LM4857_CTRL 3
/* the shifts required to set these bits */
#define LM4857_3D 5
#define LM4857_WAKEUP 5
#define LM4857_EPGAIN 4
#endif /*LM4857_H_*/

View file

@ -16,15 +16,8 @@
*
*/
#include <linux/module.h>
#include <linux/device.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/soc.h>
#include "dma.h"
#include "ac97.h"
static struct snd_soc_card ln2440sbc;
static struct snd_soc_dai_link ln2440sbc_dai[] = {

Some files were not shown because too many files have changed in this diff Show more