Merge branch 'topic/asoc' into for-linus
This commit is contained in:
Коммит
27b92d4ff2
|
@ -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[] = {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 @@ fsi_set_rate_end:
|
|||
}
|
||||
|
||||
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,
|
||||
|
|
|
@ -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;
|
||||
};
|
|
@ -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[] = {
|
||||
|
|
|
@ -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[] = {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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 */
|
|
@ -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)
|
||||
|
||||
|
||||
/*
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -465,6 +465,52 @@ error:
|
|||
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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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/
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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");
|
|
@ -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");
|
|
@ -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");
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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");
|
|
@ -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
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -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
|
|
@ -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");
|
|
@ -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
|
|
@ -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");
|
|
@ -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 */
|
|
@ -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 = {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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 =
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -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 */
|
|
@ -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:
|
||||
|
|
|
@ -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) */
|
||||
|
|
|
@ -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 @@ err_irq:
|
|||
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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 |\
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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");
|
|
@ -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:
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 |
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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");
|
|
@ -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
|
|
@ -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
|
|
@ -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");
|
|
@ -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, ¶m);
|
||||
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");
|
|
@ -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
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
},};
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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_ */
|
|
@ -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)) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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[] = {
|
||||
|
|
|
@ -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_*/
|
||||
|
|
@ -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[] = {
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче