Merge branch 'topic/asoc' into for-linus

This commit is contained in:
Takashi Iwai 2011-05-22 10:01:33 +02:00
commit 7ec298dfef
112 changed files with 15254 additions and 2635 deletions

View file

@ -5854,7 +5854,7 @@ F: include/sound/
F: sound/ F: sound/
SOUND - SOC LAYER / DYNAMIC AUDIO POWER MANAGEMENT (ASoC) SOUND - SOC LAYER / DYNAMIC AUDIO POWER MANAGEMENT (ASoC)
M: Liam Girdwood <lrg@slimlogic.co.uk> M: Liam Girdwood <lrg@ti.com>
M: Mark Brown <broonie@opensource.wolfsonmicro.com> M: Mark Brown <broonie@opensource.wolfsonmicro.com>
T: git git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound-2.6.git T: git git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound-2.6.git
L: alsa-devel@alsa-project.org (moderated for non-subscribers) L: alsa-devel@alsa-project.org (moderated for non-subscribers)
@ -6105,7 +6105,7 @@ F: drivers/mmc/host/tifm_sd.c
F: include/linux/tifm.h F: include/linux/tifm.h
TI TWL4030 SERIES SOC CODEC DRIVER TI TWL4030 SERIES SOC CODEC DRIVER
M: Peter Ujfalusi <peter.ujfalusi@nokia.com> M: Peter Ujfalusi <peter.ujfalusi@ti.com>
L: alsa-devel@alsa-project.org (moderated for non-subscribers) L: alsa-devel@alsa-project.org (moderated for non-subscribers)
S: Maintained S: Maintained
F: sound/soc/codecs/twl4030* F: sound/soc/codecs/twl4030*
@ -6749,7 +6749,7 @@ F: drivers/scsi/vmw_pvscsi.c
F: drivers/scsi/vmw_pvscsi.h F: drivers/scsi/vmw_pvscsi.h
VOLTAGE AND CURRENT REGULATOR FRAMEWORK VOLTAGE AND CURRENT REGULATOR FRAMEWORK
M: Liam Girdwood <lrg@slimlogic.co.uk> M: Liam Girdwood <lrg@ti.com>
M: Mark Brown <broonie@opensource.wolfsonmicro.com> M: Mark Brown <broonie@opensource.wolfsonmicro.com>
W: http://opensource.wolfsonmicro.com/node/15 W: http://opensource.wolfsonmicro.com/node/15
W: http://www.slimlogic.co.uk/?p=48 W: http://www.slimlogic.co.uk/?p=48

View file

@ -27,12 +27,14 @@ comment "Tegra board type"
config MACH_HARMONY config MACH_HARMONY
bool "Harmony board" bool "Harmony board"
select MACH_HAS_SND_SOC_TEGRA_WM8903
help help
Support for nVidia Harmony development platform Support for nVidia Harmony development platform
config MACH_KAEN config MACH_KAEN
bool "Kaen board" bool "Kaen board"
select MACH_SEABOARD select MACH_SEABOARD
select MACH_HAS_SND_SOC_TEGRA_WM8903
help help
Support for the Kaen version of Seaboard Support for the Kaen version of Seaboard
@ -43,6 +45,7 @@ config MACH_PAZ00
config MACH_SEABOARD config MACH_SEABOARD
bool "Seaboard board" bool "Seaboard board"
select MACH_HAS_SND_SOC_TEGRA_WM8903
help help
Support for nVidia Seaboard development platform. It will Support for nVidia Seaboard development platform. It will
also be included for some of the derivative boards that also be included for some of the derivative boards that

View file

@ -34,7 +34,7 @@
#include <asm/mach/time.h> #include <asm/mach/time.h>
#include <asm/setup.h> #include <asm/setup.h>
#include <mach/harmony_audio.h> #include <mach/tegra_wm8903_pdata.h>
#include <mach/iomap.h> #include <mach/iomap.h>
#include <mach/irqs.h> #include <mach/irqs.h>
#include <mach/sdhci.h> #include <mach/sdhci.h>
@ -67,15 +67,16 @@ static struct platform_device debug_uart = {
}, },
}; };
static struct harmony_audio_platform_data harmony_audio_pdata = { static struct tegra_wm8903_platform_data harmony_audio_pdata = {
.gpio_spkr_en = TEGRA_GPIO_SPKR_EN, .gpio_spkr_en = TEGRA_GPIO_SPKR_EN,
.gpio_hp_det = TEGRA_GPIO_HP_DET, .gpio_hp_det = TEGRA_GPIO_HP_DET,
.gpio_hp_mute = -1,
.gpio_int_mic_en = TEGRA_GPIO_INT_MIC_EN, .gpio_int_mic_en = TEGRA_GPIO_INT_MIC_EN,
.gpio_ext_mic_en = TEGRA_GPIO_EXT_MIC_EN, .gpio_ext_mic_en = TEGRA_GPIO_EXT_MIC_EN,
}; };
static struct platform_device harmony_audio_device = { static struct platform_device harmony_audio_device = {
.name = "tegra-snd-harmony", .name = "tegra-snd-wm8903",
.id = 0, .id = 0,
.dev = { .dev = {
.platform_data = &harmony_audio_pdata, .platform_data = &harmony_audio_pdata,

View file

@ -1,5 +1,5 @@
/* /*
* arch/arm/mach-tegra/include/mach/harmony_audio.h * arch/arm/mach-tegra/include/mach/tegra_wm8903_pdata.h
* *
* Copyright 2011 NVIDIA, Inc. * Copyright 2011 NVIDIA, Inc.
* *
@ -14,9 +14,10 @@
* *
*/ */
struct harmony_audio_platform_data { struct tegra_wm8903_platform_data {
int gpio_spkr_en; int gpio_spkr_en;
int gpio_hp_det; int gpio_hp_det;
int gpio_hp_mute;
int gpio_int_mic_en; int gpio_int_mic_en;
int gpio_ext_mic_en; int gpio_ext_mic_en;
}; };

View file

@ -508,7 +508,6 @@ int register_sst_card(struct intel_sst_card_ops *card)
sst_drv_ctx->pmic_state = SND_MAD_INIT_DONE; sst_drv_ctx->pmic_state = SND_MAD_INIT_DONE;
sst_drv_ctx->rx_time_slot_status = 0; /*default AMIC*/ sst_drv_ctx->rx_time_slot_status = 0; /*default AMIC*/
card->pcm_control = sst_pmic_ops.pcm_control; card->pcm_control = sst_pmic_ops.pcm_control;
sst_drv_ctx->scard_ops->card_status = SND_CARD_UN_INIT;
return 0; return 0;
} else { } else {
pr_err("strcmp fail %s\n", card->module_name); pr_err("strcmp fail %s\n", card->module_name);

View file

@ -32,6 +32,7 @@
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/firmware.h>
#include <sound/control.h> #include <sound/control.h>
#include <asm/mrst.h> #include <asm/mrst.h>
#include <sound/pcm.h> #include <sound/pcm.h>
@ -40,6 +41,8 @@
#include <sound/initval.h> #include <sound/initval.h>
#include "intel_sst.h" #include "intel_sst.h"
#include "intel_sst_ioctl.h" #include "intel_sst_ioctl.h"
#include "intel_sst_fw_ipc.h"
#include "intel_sst_common.h"
#include "intelmid_snd_control.h" #include "intelmid_snd_control.h"
#include "intelmid.h" #include "intelmid.h"
@ -802,6 +805,7 @@ static int __devinit snd_intelmad_sst_register(
pr_err("sst card registration failed\n"); pr_err("sst card registration failed\n");
return ret_val; return ret_val;
} }
sst_drv_ctx->scard_ops->card_status = SND_CARD_UN_INIT;
sst_card_vendor_id = intelmaddata->sstdrv_ops->vendor_id; sst_card_vendor_id = intelmaddata->sstdrv_ops->vendor_id;
intelmaddata->pmic_status = PMIC_UNINIT; intelmaddata->pmic_status = PMIC_UNINIT;

View file

@ -32,6 +32,10 @@ struct wm8994_ldo_pdata {
#define WM8994_EQ_REGS 20 #define WM8994_EQ_REGS 20
#define WM8958_MBC_CUTOFF_REGS 20 #define WM8958_MBC_CUTOFF_REGS 20
#define WM8958_MBC_COEFF_REGS 48 #define WM8958_MBC_COEFF_REGS 48
#define WM8958_MBC_COMBINED_REGS 56
#define WM8958_VSS_HPF_REGS 2
#define WM8958_VSS_REGS 148
#define WM8958_ENH_EQ_REGS 32
/** /**
* DRC configurations are specified with a label and a set of register * DRC configurations are specified with a label and a set of register
@ -71,6 +75,42 @@ struct wm8958_mbc_cfg {
const char *name; const char *name;
u16 cutoff_regs[WM8958_MBC_CUTOFF_REGS]; u16 cutoff_regs[WM8958_MBC_CUTOFF_REGS];
u16 coeff_regs[WM8958_MBC_COEFF_REGS]; u16 coeff_regs[WM8958_MBC_COEFF_REGS];
/* Coefficient layout when using MBC+VSS firmware */
u16 combined_regs[WM8958_MBC_COMBINED_REGS];
};
/**
* VSS HPF configurations are specified with a label and two values to
* write. Configurations are expected to be generated using the
* multiband compressor configuration panel in WISCE - see
* http://www.wolfsonmicro.com/wisce/
*/
struct wm8958_vss_hpf_cfg {
const char *name;
u16 regs[WM8958_VSS_HPF_REGS];
};
/**
* VSS configurations are specified with a label and array of values
* to write. Configurations are expected to be generated using the
* multiband compressor configuration panel in WISCE - see
* http://www.wolfsonmicro.com/wisce/
*/
struct wm8958_vss_cfg {
const char *name;
u16 regs[WM8958_VSS_REGS];
};
/**
* Enhanced EQ configurations are specified with a label and array of
* values to write. Configurations are expected to be generated using
* the multiband compressor configuration panel in WISCE - see
* http://www.wolfsonmicro.com/wisce/
*/
struct wm8958_enh_eq_cfg {
const char *name;
u16 regs[WM8958_ENH_EQ_REGS];
}; };
struct wm8994_pdata { struct wm8994_pdata {
@ -95,6 +135,15 @@ struct wm8994_pdata {
int num_mbc_cfgs; int num_mbc_cfgs;
struct wm8958_mbc_cfg *mbc_cfgs; struct wm8958_mbc_cfg *mbc_cfgs;
int num_vss_cfgs;
struct wm8958_vss_cfg *vss_cfgs;
int num_vss_hpf_cfgs;
struct wm8958_vss_hpf_cfg *vss_hpf_cfgs;
int num_enh_eq_cfgs;
struct wm8958_enh_eq_cfg *enh_eq_cfgs;
/* LINEOUT can be differential or single ended */ /* LINEOUT can be differential or single ended */
unsigned int lineout1_diff:1; unsigned int lineout1_diff:1;
unsigned int lineout2_diff:1; unsigned int lineout2_diff:1;

26
include/sound/ak4641.h Normal file
View file

@ -0,0 +1,26 @@
/*
* AK4641 ALSA SoC Codec driver
*
* Copyright 2009 Philipp Zabel
*
* 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 __AK4641_H
#define __AK4641_H
/**
* struct ak4641_platform_data - platform specific AK4641 configuration
* @gpio_power: GPIO to control external power to AK4641
* @gpio_npdn: GPIO connected to AK4641 nPDN pin
*
* Both GPIO parameters are optional.
*/
struct ak4641_platform_data {
int gpio_power;
int gpio_npdn;
};
#endif /* __AK4641_H */

54
include/sound/max98095.h Normal file
View file

@ -0,0 +1,54 @@
/*
* Platform data for MAX98095
*
* Copyright 2011 Maxim Integrated Products
*
* 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 __SOUND_MAX98095_PDATA_H__
#define __SOUND_MAX98095_PDATA_H__
/* Equalizer filter response configuration */
struct max98095_eq_cfg {
const char *name;
unsigned int rate;
u16 band1[5];
u16 band2[5];
u16 band3[5];
u16 band4[5];
u16 band5[5];
};
/* Biquad filter response configuration */
struct max98095_biquad_cfg {
const char *name;
unsigned int rate;
u16 band1[5];
u16 band2[5];
};
/* codec platform data */
struct max98095_pdata {
/* Equalizers for DAI1 and DAI2 */
struct max98095_eq_cfg *eq_cfg;
unsigned int eq_cfgcnt;
/* Biquad filter for DAI1 and DAI2 */
struct max98095_biquad_cfg *bq_cfg;
unsigned int bq_cfgcnt;
/* Analog/digital microphone configuration:
* 0 = analog microphone input (normal setting)
* 1 = digital microphone input
*/
unsigned int digmic_left_mode:1;
unsigned int digmic_right_mode:1;
};
#endif

View file

@ -24,7 +24,7 @@
* SoC dynamic audio power management * SoC dynamic audio power management
* *
* We can have up to 4 power domains * We can have up to 4 power domains
* 1. Codec domain - VREF, VMID * 1. Codec domain - VREF, VMID
* Usually controlled at codec probe/remove, although can be set * Usually controlled at codec probe/remove, although can be set
* at stream time if power is not needed for sidetone, etc. * at stream time if power is not needed for sidetone, etc.
* 2. Platform/Machine domain - physically connected inputs and outputs * 2. Platform/Machine domain - physically connected inputs and outputs
@ -39,30 +39,30 @@
/* codec domain */ /* codec domain */
#define SND_SOC_DAPM_VMID(wname) \ #define SND_SOC_DAPM_VMID(wname) \
{ .id = snd_soc_dapm_vmid, .name = wname, .kcontrols = NULL, \ { .id = snd_soc_dapm_vmid, .name = wname, .kcontrol_news = NULL, \
.num_kcontrols = 0} .num_kcontrols = 0}
/* platform domain */ /* platform domain */
#define SND_SOC_DAPM_INPUT(wname) \ #define SND_SOC_DAPM_INPUT(wname) \
{ .id = snd_soc_dapm_input, .name = wname, .kcontrols = NULL, \ { .id = snd_soc_dapm_input, .name = wname, .kcontrol_news = NULL, \
.num_kcontrols = 0, .reg = SND_SOC_NOPM } .num_kcontrols = 0, .reg = SND_SOC_NOPM }
#define SND_SOC_DAPM_OUTPUT(wname) \ #define SND_SOC_DAPM_OUTPUT(wname) \
{ .id = snd_soc_dapm_output, .name = wname, .kcontrols = NULL, \ { .id = snd_soc_dapm_output, .name = wname, .kcontrol_news = NULL, \
.num_kcontrols = 0, .reg = SND_SOC_NOPM } .num_kcontrols = 0, .reg = SND_SOC_NOPM }
#define SND_SOC_DAPM_MIC(wname, wevent) \ #define SND_SOC_DAPM_MIC(wname, wevent) \
{ .id = snd_soc_dapm_mic, .name = wname, .kcontrols = NULL, \ { .id = snd_soc_dapm_mic, .name = wname, .kcontrol_news = NULL, \
.num_kcontrols = 0, .reg = SND_SOC_NOPM, .event = wevent, \ .num_kcontrols = 0, .reg = SND_SOC_NOPM, .event = wevent, \
.event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD} .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD}
#define SND_SOC_DAPM_HP(wname, wevent) \ #define SND_SOC_DAPM_HP(wname, wevent) \
{ .id = snd_soc_dapm_hp, .name = wname, .kcontrols = NULL, \ { .id = snd_soc_dapm_hp, .name = wname, .kcontrol_news = NULL, \
.num_kcontrols = 0, .reg = SND_SOC_NOPM, .event = wevent, \ .num_kcontrols = 0, .reg = SND_SOC_NOPM, .event = wevent, \
.event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD} .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD}
#define SND_SOC_DAPM_SPK(wname, wevent) \ #define SND_SOC_DAPM_SPK(wname, wevent) \
{ .id = snd_soc_dapm_spk, .name = wname, .kcontrols = NULL, \ { .id = snd_soc_dapm_spk, .name = wname, .kcontrol_news = NULL, \
.num_kcontrols = 0, .reg = SND_SOC_NOPM, .event = wevent, \ .num_kcontrols = 0, .reg = SND_SOC_NOPM, .event = wevent, \
.event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD} .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD}
#define SND_SOC_DAPM_LINE(wname, wevent) \ #define SND_SOC_DAPM_LINE(wname, wevent) \
{ .id = snd_soc_dapm_line, .name = wname, .kcontrols = NULL, \ { .id = snd_soc_dapm_line, .name = wname, .kcontrol_news = NULL, \
.num_kcontrols = 0, .reg = SND_SOC_NOPM, .event = wevent, \ .num_kcontrols = 0, .reg = SND_SOC_NOPM, .event = wevent, \
.event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD} .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD}
@ -70,91 +70,91 @@
#define SND_SOC_DAPM_PGA(wname, wreg, wshift, winvert,\ #define SND_SOC_DAPM_PGA(wname, wreg, wshift, winvert,\
wcontrols, wncontrols) \ wcontrols, wncontrols) \
{ .id = snd_soc_dapm_pga, .name = wname, .reg = wreg, .shift = wshift, \ { .id = snd_soc_dapm_pga, .name = wname, .reg = wreg, .shift = wshift, \
.invert = winvert, .kcontrols = wcontrols, .num_kcontrols = wncontrols} .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = wncontrols}
#define SND_SOC_DAPM_OUT_DRV(wname, wreg, wshift, winvert,\ #define SND_SOC_DAPM_OUT_DRV(wname, wreg, wshift, winvert,\
wcontrols, wncontrols) \ wcontrols, wncontrols) \
{ .id = snd_soc_dapm_out_drv, .name = wname, .reg = wreg, .shift = wshift, \ { .id = snd_soc_dapm_out_drv, .name = wname, .reg = wreg, .shift = wshift, \
.invert = winvert, .kcontrols = wcontrols, .num_kcontrols = wncontrols} .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = wncontrols}
#define SND_SOC_DAPM_MIXER(wname, wreg, wshift, winvert, \ #define SND_SOC_DAPM_MIXER(wname, wreg, wshift, winvert, \
wcontrols, wncontrols)\ wcontrols, wncontrols)\
{ .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \ { .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \
.invert = winvert, .kcontrols = wcontrols, .num_kcontrols = wncontrols} .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = wncontrols}
#define SND_SOC_DAPM_MIXER_NAMED_CTL(wname, wreg, wshift, winvert, \ #define SND_SOC_DAPM_MIXER_NAMED_CTL(wname, wreg, wshift, winvert, \
wcontrols, wncontrols)\ wcontrols, wncontrols)\
{ .id = snd_soc_dapm_mixer_named_ctl, .name = wname, .reg = wreg, \ { .id = snd_soc_dapm_mixer_named_ctl, .name = wname, .reg = wreg, \
.shift = wshift, .invert = winvert, .kcontrols = wcontrols, \ .shift = wshift, .invert = winvert, .kcontrol_news = wcontrols, \
.num_kcontrols = wncontrols} .num_kcontrols = wncontrols}
#define SND_SOC_DAPM_MICBIAS(wname, wreg, wshift, winvert) \ #define SND_SOC_DAPM_MICBIAS(wname, wreg, wshift, winvert) \
{ .id = snd_soc_dapm_micbias, .name = wname, .reg = wreg, .shift = wshift, \ { .id = snd_soc_dapm_micbias, .name = wname, .reg = wreg, .shift = wshift, \
.invert = winvert, .kcontrols = NULL, .num_kcontrols = 0} .invert = winvert, .kcontrol_news = NULL, .num_kcontrols = 0}
#define SND_SOC_DAPM_SWITCH(wname, wreg, wshift, winvert, wcontrols) \ #define SND_SOC_DAPM_SWITCH(wname, wreg, wshift, winvert, wcontrols) \
{ .id = snd_soc_dapm_switch, .name = wname, .reg = wreg, .shift = wshift, \ { .id = snd_soc_dapm_switch, .name = wname, .reg = wreg, .shift = wshift, \
.invert = winvert, .kcontrols = wcontrols, .num_kcontrols = 1} .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = 1}
#define SND_SOC_DAPM_MUX(wname, wreg, wshift, winvert, wcontrols) \ #define SND_SOC_DAPM_MUX(wname, wreg, wshift, winvert, wcontrols) \
{ .id = snd_soc_dapm_mux, .name = wname, .reg = wreg, .shift = wshift, \ { .id = snd_soc_dapm_mux, .name = wname, .reg = wreg, .shift = wshift, \
.invert = winvert, .kcontrols = wcontrols, .num_kcontrols = 1} .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = 1}
#define SND_SOC_DAPM_VIRT_MUX(wname, wreg, wshift, winvert, wcontrols) \ #define SND_SOC_DAPM_VIRT_MUX(wname, wreg, wshift, winvert, wcontrols) \
{ .id = snd_soc_dapm_virt_mux, .name = wname, .reg = wreg, .shift = wshift, \ { .id = snd_soc_dapm_virt_mux, .name = wname, .reg = wreg, .shift = wshift, \
.invert = winvert, .kcontrols = wcontrols, .num_kcontrols = 1} .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = 1}
#define SND_SOC_DAPM_VALUE_MUX(wname, wreg, wshift, winvert, wcontrols) \ #define SND_SOC_DAPM_VALUE_MUX(wname, wreg, wshift, winvert, wcontrols) \
{ .id = snd_soc_dapm_value_mux, .name = wname, .reg = wreg, \ { .id = snd_soc_dapm_value_mux, .name = wname, .reg = wreg, \
.shift = wshift, .invert = winvert, .kcontrols = wcontrols, \ .shift = wshift, .invert = winvert, .kcontrol_news = wcontrols, \
.num_kcontrols = 1} .num_kcontrols = 1}
/* Simplified versions of above macros, assuming wncontrols = ARRAY_SIZE(wcontrols) */ /* Simplified versions of above macros, assuming wncontrols = ARRAY_SIZE(wcontrols) */
#define SOC_PGA_ARRAY(wname, wreg, wshift, winvert,\ #define SOC_PGA_ARRAY(wname, wreg, wshift, winvert,\
wcontrols) \ wcontrols) \
{ .id = snd_soc_dapm_pga, .name = wname, .reg = wreg, .shift = wshift, \ { .id = snd_soc_dapm_pga, .name = wname, .reg = wreg, .shift = wshift, \
.invert = winvert, .kcontrols = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols)} .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols)}
#define SOC_MIXER_ARRAY(wname, wreg, wshift, winvert, \ #define SOC_MIXER_ARRAY(wname, wreg, wshift, winvert, \
wcontrols)\ wcontrols)\
{ .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \ { .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \
.invert = winvert, .kcontrols = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols)} .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols)}
#define SOC_MIXER_NAMED_CTL_ARRAY(wname, wreg, wshift, winvert, \ #define SOC_MIXER_NAMED_CTL_ARRAY(wname, wreg, wshift, winvert, \
wcontrols)\ wcontrols)\
{ .id = snd_soc_dapm_mixer_named_ctl, .name = wname, .reg = wreg, \ { .id = snd_soc_dapm_mixer_named_ctl, .name = wname, .reg = wreg, \
.shift = wshift, .invert = winvert, .kcontrols = wcontrols, \ .shift = wshift, .invert = winvert, .kcontrol_news = wcontrols, \
.num_kcontrols = ARRAY_SIZE(wcontrols)} .num_kcontrols = ARRAY_SIZE(wcontrols)}
/* path domain with event - event handler must return 0 for success */ /* path domain with event - event handler must return 0 for success */
#define SND_SOC_DAPM_PGA_E(wname, wreg, wshift, winvert, wcontrols, \ #define SND_SOC_DAPM_PGA_E(wname, wreg, wshift, winvert, wcontrols, \
wncontrols, wevent, wflags) \ wncontrols, wevent, wflags) \
{ .id = snd_soc_dapm_pga, .name = wname, .reg = wreg, .shift = wshift, \ { .id = snd_soc_dapm_pga, .name = wname, .reg = wreg, .shift = wshift, \
.invert = winvert, .kcontrols = wcontrols, .num_kcontrols = wncontrols, \ .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = wncontrols, \
.event = wevent, .event_flags = wflags} .event = wevent, .event_flags = wflags}
#define SND_SOC_DAPM_OUT_DRV_E(wname, wreg, wshift, winvert, wcontrols, \ #define SND_SOC_DAPM_OUT_DRV_E(wname, wreg, wshift, winvert, wcontrols, \
wncontrols, wevent, wflags) \ wncontrols, wevent, wflags) \
{ .id = snd_soc_dapm_out_drv, .name = wname, .reg = wreg, .shift = wshift, \ { .id = snd_soc_dapm_out_drv, .name = wname, .reg = wreg, .shift = wshift, \
.invert = winvert, .kcontrols = wcontrols, .num_kcontrols = wncontrols, \ .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = wncontrols, \
.event = wevent, .event_flags = wflags} .event = wevent, .event_flags = wflags}
#define SND_SOC_DAPM_MIXER_E(wname, wreg, wshift, winvert, wcontrols, \ #define SND_SOC_DAPM_MIXER_E(wname, wreg, wshift, winvert, wcontrols, \
wncontrols, wevent, wflags) \ wncontrols, wevent, wflags) \
{ .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \ { .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \
.invert = winvert, .kcontrols = wcontrols, .num_kcontrols = wncontrols, \ .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = wncontrols, \
.event = wevent, .event_flags = wflags} .event = wevent, .event_flags = wflags}
#define SND_SOC_DAPM_MIXER_NAMED_CTL_E(wname, wreg, wshift, winvert, \ #define SND_SOC_DAPM_MIXER_NAMED_CTL_E(wname, wreg, wshift, winvert, \
wcontrols, wncontrols, wevent, wflags) \ wcontrols, wncontrols, wevent, wflags) \
{ .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \ { .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \
.invert = winvert, .kcontrols = wcontrols, \ .invert = winvert, .kcontrol_news = wcontrols, \
.num_kcontrols = wncontrols, .event = wevent, .event_flags = wflags} .num_kcontrols = wncontrols, .event = wevent, .event_flags = wflags}
#define SND_SOC_DAPM_MICBIAS_E(wname, wreg, wshift, winvert, wevent, wflags) \ #define SND_SOC_DAPM_MICBIAS_E(wname, wreg, wshift, winvert, wevent, wflags) \
{ .id = snd_soc_dapm_micbias, .name = wname, .reg = wreg, .shift = wshift, \ { .id = snd_soc_dapm_micbias, .name = wname, .reg = wreg, .shift = wshift, \
.invert = winvert, .kcontrols = NULL, .num_kcontrols = 0, \ .invert = winvert, .kcontrol_news = NULL, .num_kcontrols = 0, \
.event = wevent, .event_flags = wflags} .event = wevent, .event_flags = wflags}
#define SND_SOC_DAPM_SWITCH_E(wname, wreg, wshift, winvert, wcontrols, \ #define SND_SOC_DAPM_SWITCH_E(wname, wreg, wshift, winvert, wcontrols, \
wevent, wflags) \ wevent, wflags) \
{ .id = snd_soc_dapm_switch, .name = wname, .reg = wreg, .shift = wshift, \ { .id = snd_soc_dapm_switch, .name = wname, .reg = wreg, .shift = wshift, \
.invert = winvert, .kcontrols = wcontrols, .num_kcontrols = 1, \ .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = 1, \
.event = wevent, .event_flags = wflags} .event = wevent, .event_flags = wflags}
#define SND_SOC_DAPM_MUX_E(wname, wreg, wshift, winvert, wcontrols, \ #define SND_SOC_DAPM_MUX_E(wname, wreg, wshift, winvert, wcontrols, \
wevent, wflags) \ wevent, wflags) \
{ .id = snd_soc_dapm_mux, .name = wname, .reg = wreg, .shift = wshift, \ { .id = snd_soc_dapm_mux, .name = wname, .reg = wreg, .shift = wshift, \
.invert = winvert, .kcontrols = wcontrols, .num_kcontrols = 1, \ .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = 1, \
.event = wevent, .event_flags = wflags} .event = wevent, .event_flags = wflags}
#define SND_SOC_DAPM_VIRT_MUX_E(wname, wreg, wshift, winvert, wcontrols, \ #define SND_SOC_DAPM_VIRT_MUX_E(wname, wreg, wshift, winvert, wcontrols, \
wevent, wflags) \ wevent, wflags) \
{ .id = snd_soc_dapm_virt_mux, .name = wname, .reg = wreg, .shift = wshift, \ { .id = snd_soc_dapm_virt_mux, .name = wname, .reg = wreg, .shift = wshift, \
.invert = winvert, .kcontrols = wcontrols, .num_kcontrols = 1, \ .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = 1, \
.event = wevent, .event_flags = wflags} .event = wevent, .event_flags = wflags}
/* additional sequencing control within an event type */ /* additional sequencing control within an event type */
@ -173,26 +173,26 @@
#define SOC_PGA_E_ARRAY(wname, wreg, wshift, winvert, wcontrols, \ #define SOC_PGA_E_ARRAY(wname, wreg, wshift, winvert, wcontrols, \
wevent, wflags) \ wevent, wflags) \
{ .id = snd_soc_dapm_pga, .name = wname, .reg = wreg, .shift = wshift, \ { .id = snd_soc_dapm_pga, .name = wname, .reg = wreg, .shift = wshift, \
.invert = winvert, .kcontrols = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols), \ .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols), \
.event = wevent, .event_flags = wflags} .event = wevent, .event_flags = wflags}
#define SOC_MIXER_E_ARRAY(wname, wreg, wshift, winvert, wcontrols, \ #define SOC_MIXER_E_ARRAY(wname, wreg, wshift, winvert, wcontrols, \
wevent, wflags) \ wevent, wflags) \
{ .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \ { .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \
.invert = winvert, .kcontrols = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols), \ .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols), \
.event = wevent, .event_flags = wflags} .event = wevent, .event_flags = wflags}
#define SOC_MIXER_NAMED_CTL_E_ARRAY(wname, wreg, wshift, winvert, \ #define SOC_MIXER_NAMED_CTL_E_ARRAY(wname, wreg, wshift, winvert, \
wcontrols, wevent, wflags) \ wcontrols, wevent, wflags) \
{ .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \ { .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \
.invert = winvert, .kcontrols = wcontrols, \ .invert = winvert, .kcontrol_news = wcontrols, \
.num_kcontrols = ARRAY_SIZE(wcontrols), .event = wevent, .event_flags = wflags} .num_kcontrols = ARRAY_SIZE(wcontrols), .event = wevent, .event_flags = wflags}
/* events that are pre and post DAPM */ /* events that are pre and post DAPM */
#define SND_SOC_DAPM_PRE(wname, wevent) \ #define SND_SOC_DAPM_PRE(wname, wevent) \
{ .id = snd_soc_dapm_pre, .name = wname, .kcontrols = NULL, \ { .id = snd_soc_dapm_pre, .name = wname, .kcontrol_news = NULL, \
.num_kcontrols = 0, .reg = SND_SOC_NOPM, .event = wevent, \ .num_kcontrols = 0, .reg = SND_SOC_NOPM, .event = wevent, \
.event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD} .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD}
#define SND_SOC_DAPM_POST(wname, wevent) \ #define SND_SOC_DAPM_POST(wname, wevent) \
{ .id = snd_soc_dapm_post, .name = wname, .kcontrols = NULL, \ { .id = snd_soc_dapm_post, .name = wname, .kcontrol_news = NULL, \
.num_kcontrols = 0, .reg = SND_SOC_NOPM, .event = wevent, \ .num_kcontrols = 0, .reg = SND_SOC_NOPM, .event = wevent, \
.event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD} .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD}
@ -232,7 +232,7 @@
/* generic widgets */ /* generic widgets */
#define SND_SOC_DAPM_REG(wid, wname, wreg, wshift, wmask, won_val, woff_val) \ #define SND_SOC_DAPM_REG(wid, wname, wreg, wshift, wmask, won_val, woff_val) \
{ .id = wid, .name = wname, .kcontrols = NULL, .num_kcontrols = 0, \ { .id = wid, .name = wname, .kcontrol_news = NULL, .num_kcontrols = 0, \
.reg = -((wreg) + 1), .shift = wshift, .mask = wmask, \ .reg = -((wreg) + 1), .shift = wshift, .mask = wmask, \
.on_val = won_val, .off_val = woff_val, .event = dapm_reg_event, \ .on_val = won_val, .off_val = woff_val, .event = dapm_reg_event, \
.event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD} .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD}
@ -356,7 +356,8 @@ void snd_soc_dapm_shutdown(struct snd_soc_card *card);
/* dapm sys fs - used by the core */ /* dapm sys fs - used by the core */
int snd_soc_dapm_sys_add(struct device *dev); int snd_soc_dapm_sys_add(struct device *dev);
void snd_soc_dapm_debugfs_init(struct snd_soc_dapm_context *dapm); void snd_soc_dapm_debugfs_init(struct snd_soc_dapm_context *dapm,
struct dentry *parent);
/* dapm audio pin control and status */ /* dapm audio pin control and status */
int snd_soc_dapm_enable_pin(struct snd_soc_dapm_context *dapm, int snd_soc_dapm_enable_pin(struct snd_soc_dapm_context *dapm,
@ -472,7 +473,8 @@ struct snd_soc_dapm_widget {
/* kcontrols that relate to this widget */ /* kcontrols that relate to this widget */
int num_kcontrols; int num_kcontrols;
const struct snd_kcontrol_new *kcontrols; const struct snd_kcontrol_new *kcontrol_news;
struct snd_kcontrol **kcontrols;
/* widget input and outputs */ /* widget input and outputs */
struct list_head sources; struct list_head sources;
@ -516,4 +518,10 @@ struct snd_soc_dapm_context {
#endif #endif
}; };
/* A list of widgets associated with an object, typically a snd_kcontrol */
struct snd_soc_dapm_widget_list {
int num_widgets;
struct snd_soc_dapm_widget *widgets[0];
};
#endif #endif

View file

@ -248,7 +248,7 @@ typedef int (*hw_write_t)(void *,const char* ,int);
extern struct snd_ac97_bus_ops soc_ac97_ops; extern struct snd_ac97_bus_ops soc_ac97_ops;
enum snd_soc_control_type { enum snd_soc_control_type {
SND_SOC_CUSTOM, SND_SOC_CUSTOM = 1,
SND_SOC_I2C, SND_SOC_I2C,
SND_SOC_SPI, SND_SOC_SPI,
}; };
@ -278,6 +278,10 @@ int snd_soc_register_codec(struct device *dev,
void snd_soc_unregister_codec(struct device *dev); void snd_soc_unregister_codec(struct device *dev);
int snd_soc_codec_volatile_register(struct snd_soc_codec *codec, int snd_soc_codec_volatile_register(struct snd_soc_codec *codec,
unsigned int reg); unsigned int reg);
int snd_soc_codec_readable_register(struct snd_soc_codec *codec,
unsigned int reg);
int snd_soc_codec_writable_register(struct snd_soc_codec *codec,
unsigned int reg);
int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec, int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec,
int addr_bits, int data_bits, int addr_bits, int data_bits,
enum snd_soc_control_type control); enum snd_soc_control_type control);
@ -292,6 +296,8 @@ int snd_soc_default_volatile_register(struct snd_soc_codec *codec,
unsigned int reg); unsigned int reg);
int snd_soc_default_readable_register(struct snd_soc_codec *codec, int snd_soc_default_readable_register(struct snd_soc_codec *codec,
unsigned int reg); unsigned int reg);
int snd_soc_default_writable_register(struct snd_soc_codec *codec,
unsigned int reg);
/* Utility functions to get clock rates from various things */ /* Utility functions to get clock rates from various things */
int snd_soc_calc_frame_size(int sample_size, int channels, int tdm_slots); int snd_soc_calc_frame_size(int sample_size, int channels, int tdm_slots);
@ -523,6 +529,7 @@ struct snd_soc_codec {
size_t reg_size; /* reg_cache_size * reg_word_size */ size_t reg_size; /* reg_cache_size * reg_word_size */
int (*volatile_register)(struct snd_soc_codec *, unsigned int); int (*volatile_register)(struct snd_soc_codec *, unsigned int);
int (*readable_register)(struct snd_soc_codec *, unsigned int); int (*readable_register)(struct snd_soc_codec *, unsigned int);
int (*writable_register)(struct snd_soc_codec *, unsigned int);
/* runtime */ /* runtime */
struct snd_ac97 *ac97; /* for ad-hoc ac97 devices */ struct snd_ac97 *ac97; /* for ad-hoc ac97 devices */
@ -539,10 +546,12 @@ struct snd_soc_codec {
/* codec IO */ /* codec IO */
void *control_data; /* codec control (i2c/3wire) data */ void *control_data; /* codec control (i2c/3wire) data */
enum snd_soc_control_type control_type;
hw_write_t hw_write; hw_write_t hw_write;
unsigned int (*hw_read)(struct snd_soc_codec *, unsigned int); unsigned int (*hw_read)(struct snd_soc_codec *, unsigned int);
unsigned int (*read)(struct snd_soc_codec *, unsigned int); unsigned int (*read)(struct snd_soc_codec *, unsigned int);
int (*write)(struct snd_soc_codec *, unsigned int, unsigned int); int (*write)(struct snd_soc_codec *, unsigned int, unsigned int);
int (*bulk_write_raw)(struct snd_soc_codec *, unsigned int, const void *, size_t);
void *reg_cache; void *reg_cache;
const void *reg_def_copy; const void *reg_def_copy;
const struct snd_soc_cache_ops *cache_ops; const struct snd_soc_cache_ops *cache_ops;
@ -568,7 +577,9 @@ struct snd_soc_codec_driver {
pm_message_t state); pm_message_t state);
int (*resume)(struct snd_soc_codec *); int (*resume)(struct snd_soc_codec *);
/* Default DAPM setup, added after probe() is run */ /* Default control and setup, added after probe() is run */
const struct snd_kcontrol_new *controls;
int num_controls;
const struct snd_soc_dapm_widget *dapm_widgets; const struct snd_soc_dapm_widget *dapm_widgets;
int num_dapm_widgets; int num_dapm_widgets;
const struct snd_soc_dapm_route *dapm_routes; const struct snd_soc_dapm_route *dapm_routes;
@ -587,6 +598,7 @@ struct snd_soc_codec_driver {
size_t, unsigned int); size_t, unsigned int);
int (*volatile_register)(struct snd_soc_codec *, unsigned int); int (*volatile_register)(struct snd_soc_codec *, unsigned int);
int (*readable_register)(struct snd_soc_codec *, unsigned int); int (*readable_register)(struct snd_soc_codec *, unsigned int);
int (*writable_register)(struct snd_soc_codec *, unsigned int);
short reg_cache_size; short reg_cache_size;
short reg_cache_step; short reg_cache_step;
short reg_word_size; short reg_word_size;
@ -690,6 +702,8 @@ struct snd_soc_aux_dev {
/* SoC card */ /* SoC card */
struct snd_soc_card { struct snd_soc_card {
const char *name; const char *name;
const char *long_name;
const char *driver_name;
struct device *dev; struct device *dev;
struct snd_card *snd_card; struct snd_card *snd_card;
struct module *owner; struct module *owner;
@ -737,12 +751,15 @@ struct snd_soc_card {
struct snd_soc_pcm_runtime *rtd_aux; struct snd_soc_pcm_runtime *rtd_aux;
int num_aux_rtd; int num_aux_rtd;
const struct snd_kcontrol_new *controls;
int num_controls;
/* /*
* Card-specific routes and widgets. * Card-specific routes and widgets.
*/ */
struct snd_soc_dapm_widget *dapm_widgets; const struct snd_soc_dapm_widget *dapm_widgets;
int num_dapm_widgets; int num_dapm_widgets;
struct snd_soc_dapm_route *dapm_routes; const struct snd_soc_dapm_route *dapm_routes;
int num_dapm_routes; int num_dapm_routes;
struct work_struct deferred_resume_work; struct work_struct deferred_resume_work;
@ -805,7 +822,7 @@ struct soc_enum {
unsigned char shift_r; unsigned char shift_r;
unsigned int max; unsigned int max;
unsigned int mask; unsigned int mask;
const char **texts; const char * const *texts;
const unsigned int *values; const unsigned int *values;
void *dapm; void *dapm;
}; };
@ -814,6 +831,8 @@ struct soc_enum {
unsigned int snd_soc_read(struct snd_soc_codec *codec, unsigned int reg); unsigned int snd_soc_read(struct snd_soc_codec *codec, unsigned int reg);
unsigned int snd_soc_write(struct snd_soc_codec *codec, unsigned int snd_soc_write(struct snd_soc_codec *codec,
unsigned int reg, unsigned int val); unsigned int reg, unsigned int val);
unsigned int snd_soc_bulk_write_raw(struct snd_soc_codec *codec,
unsigned int reg, const void *data, size_t len);
/* device driver data */ /* device driver data */
@ -871,6 +890,9 @@ static inline void snd_soc_initialize_card_lists(struct snd_soc_card *card)
INIT_LIST_HEAD(&card->dapm_list); INIT_LIST_HEAD(&card->dapm_list);
} }
int snd_soc_util_init(void);
void snd_soc_util_exit(void);
#include <sound/soc-dai.h> #include <sound/soc-dai.h>
#ifdef CONFIG_DEBUG_FS #ifdef CONFIG_DEBUG_FS

View file

@ -1,7 +1,7 @@
/* /*
* Platform header for Texas Instruments TLV320DAC33 codec driver * Platform header for Texas Instruments TLV320DAC33 codec driver
* *
* Author: Peter Ujfalusi <peter.ujfalusi@nokia.com> * Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
* *
* Copyright: (C) 2009 Nokia Corporation * Copyright: (C) 2009 Nokia Corporation
* *

View file

@ -3,7 +3,7 @@
* *
* Copyright (C) Nokia Corporation * Copyright (C) Nokia Corporation
* *
* Written by Peter Ujfalusi <peter.ujfalusi@nokia.com> * Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License

55
include/sound/wm8915.h Normal file
View file

@ -0,0 +1,55 @@
/*
* linux/sound/wm8915.h -- Platform data for WM8915
*
* Copyright 2011 Wolfson Microelectronics. PLC.
*
* 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 __LINUX_SND_WM8903_H
#define __LINUX_SND_WM8903_H
enum wm8915_inmode {
WM8915_DIFFERRENTIAL_1 = 0, /* IN1xP - IN1xN */
WM8915_INVERTING = 1, /* IN1xN */
WM8915_NON_INVERTING = 2, /* IN1xP */
WM8915_DIFFERENTIAL_2 = 3, /* IN2xP - IN2xP */
};
/**
* ReTune Mobile configurations are specified with a label, sample
* rate and set of values to write (the enable bits will be ignored).
*
* Configurations are expected to be generated using the ReTune Mobile
* control panel in WISCE - see http://www.wolfsonmicro.com/wisce/
*/
struct wm8915_retune_mobile_config {
const char *name;
int rate;
u16 regs[20];
};
#define WM8915_SET_DEFAULT 0x10000
struct wm8915_pdata {
int irq_flags; /** Set IRQ trigger flags; default active low */
int ldo_ena; /** GPIO for LDO1; -1 for none */
int micdet_def; /** Default MICDET_SRC/HP1FB_SRC/MICD_BIAS */
enum wm8915_inmode inl_mode;
enum wm8915_inmode inr_mode;
u32 spkmute_seq; /** Value for register 0x802 */
int gpio_base;
u32 gpio_default[5];
int num_retune_mobile_cfgs;
struct wm8915_retune_mobile_config *retune_mobile_cfgs;
};
#endif

View file

@ -14,6 +14,28 @@
/* Use to set GPIO default values to zero */ /* Use to set GPIO default values to zero */
#define WM8962_GPIO_SET 0x10000 #define WM8962_GPIO_SET 0x10000
#define WM8962_GPIO_FN_CLKOUT 0
#define WM8962_GPIO_FN_LOGIC 1
#define WM8962_GPIO_FN_SDOUT 2
#define WM8962_GPIO_FN_IRQ 3
#define WM8962_GPIO_FN_THERMAL 4
#define WM8962_GPIO_FN_PLL2_LOCK 6
#define WM8962_GPIO_FN_PLL3_LOCK 7
#define WM8962_GPIO_FN_FLL_LOCK 9
#define WM8962_GPIO_FN_DRC_ACT 10
#define WM8962_GPIO_FN_WSEQ_DONE 11
#define WM8962_GPIO_FN_ALC_NG_ACT 12
#define WM8962_GPIO_FN_ALC_PEAK_LIMIT 13
#define WM8962_GPIO_FN_ALC_SATURATION 14
#define WM8962_GPIO_FN_ALC_LEVEL_THR 15
#define WM8962_GPIO_FN_ALC_LEVEL_LOCK 16
#define WM8962_GPIO_FN_FIFO_ERR 17
#define WM8962_GPIO_FN_OPCLK 18
#define WM8962_GPIO_FN_DMICCLK 19
#define WM8962_GPIO_FN_DMICDAT 20
#define WM8962_GPIO_FN_MICD 21
#define WM8962_GPIO_FN_MICSCD 22
struct wm8962_pdata { struct wm8962_pdata {
int gpio_base; int gpio_base;
u32 gpio_init[WM8962_MAX_GPIO]; u32 gpio_init[WM8962_MAX_GPIO];

View file

@ -184,7 +184,7 @@ static struct snd_soc_dai_link at91sam9g20ek_dai = {
.codec_dai_name = "wm8731-hifi", .codec_dai_name = "wm8731-hifi",
.init = at91sam9g20ek_wm8731_init, .init = at91sam9g20ek_wm8731_init,
.platform_name = "atmel-pcm-audio", .platform_name = "atmel-pcm-audio",
.codec_name = "wm8731-codec.0-001b", .codec_name = "wm8731.0-001b",
.ops = &at91sam9g20ek_ops, .ops = &at91sam9g20ek_ops,
}; };

View file

@ -77,7 +77,7 @@ static struct snd_soc_dai_link db1200_i2s_dai = {
.codec_dai_name = "wm8731-hifi", .codec_dai_name = "wm8731-hifi",
.cpu_dai_name = "au1xpsc_i2s.1", .cpu_dai_name = "au1xpsc_i2s.1",
.platform_name = "au1xpsc-pcm.1", .platform_name = "au1xpsc-pcm.1",
.codec_name = "wm8731-codec.0-001b", .codec_name = "wm8731.0-001b",
.ops = &db1200_i2s_wm8731_ops, .ops = &db1200_i2s_wm8731_ops,
}; };

View file

@ -243,6 +243,9 @@ static snd_pcm_uframes_t bf5xx_pcm_pointer(struct snd_pcm_substream *substream)
static int bf5xx_pcm_open(struct snd_pcm_substream *substream) static int bf5xx_pcm_open(struct snd_pcm_substream *substream)
{ {
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
struct sport_device *sport_handle = snd_soc_dai_get_drvdata(cpu_dai);
struct snd_pcm_runtime *runtime = substream->runtime; struct snd_pcm_runtime *runtime = substream->runtime;
int ret; int ret;
@ -314,6 +317,9 @@ static struct snd_pcm_ops bf5xx_pcm_ac97_ops = {
static int bf5xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) static int bf5xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
{ {
struct snd_soc_pcm_runtime *rtd = pcm->private_data;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
struct sport_device *sport_handle = snd_soc_dai_get_drvdata(cpu_dai);
struct snd_pcm_substream *substream = pcm->streams[stream].substream; struct snd_pcm_substream *substream = pcm->streams[stream].substream;
struct snd_dma_buffer *buf = &substream->dma_buffer; struct snd_dma_buffer *buf = &substream->dma_buffer;
size_t size = bf5xx_pcm_hardware.buffer_bytes_max size_t size = bf5xx_pcm_hardware.buffer_bytes_max
@ -377,6 +383,9 @@ static void bf5xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
struct snd_dma_buffer *buf; struct snd_dma_buffer *buf;
int stream; int stream;
#if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT) #if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
struct snd_soc_pcm_runtime *rtd = pcm->private_data;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
struct sport_device *sport_handle = snd_soc_dai_get_drvdata(cpu_dai);
size_t size = bf5xx_pcm_hardware.buffer_bytes_max * size_t size = bf5xx_pcm_hardware.buffer_bytes_max *
sizeof(struct ac97_frame) / 4; sizeof(struct ac97_frame) / 4;
#endif #endif
@ -405,8 +414,6 @@ static void bf5xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
} }
#endif #endif
} }
if (sport_handle)
sport_done(sport_handle);
} }
static u64 bf5xx_pcm_dmamask = DMA_BIT_MASK(32); static u64 bf5xx_pcm_dmamask = DMA_BIT_MASK(32);
@ -458,7 +465,7 @@ static int __devexit bf5xx_soc_platform_remove(struct platform_device *pdev)
static struct platform_driver bf5xx_pcm_driver = { static struct platform_driver bf5xx_pcm_driver = {
.driver = { .driver = {
.name = "bf5xx-pcm-audio", .name = "bfin-ac97-pcm-audio",
.owner = THIS_MODULE, .owner = THIS_MODULE,
}, },

View file

@ -41,48 +41,7 @@
* anomaly does not affect blackfin sound drivers. * anomaly does not affect blackfin sound drivers.
*/ */
static int *cmd_count; static struct sport_device *ac97_sport_handle;
static int sport_num = CONFIG_SND_BF5XX_SPORT_NUM;
#define SPORT_REQ(x) \
[x] = {P_SPORT##x##_TFS, P_SPORT##x##_DTPRI, P_SPORT##x##_TSCLK, \
P_SPORT##x##_RFS, P_SPORT##x##_DRPRI, P_SPORT##x##_RSCLK, 0}
static u16 sport_req[][7] = {
#ifdef SPORT0_TCR1
SPORT_REQ(0),
#endif
#ifdef SPORT1_TCR1
SPORT_REQ(1),
#endif
#ifdef SPORT2_TCR1
SPORT_REQ(2),
#endif
#ifdef SPORT3_TCR1
SPORT_REQ(3),
#endif
};
#define SPORT_PARAMS(x) \
[x] = { \
.dma_rx_chan = CH_SPORT##x##_RX, \
.dma_tx_chan = CH_SPORT##x##_TX, \
.err_irq = IRQ_SPORT##x##_ERROR, \
.regs = (struct sport_register *)SPORT##x##_TCR1, \
}
static struct sport_param sport_params[4] = {
#ifdef SPORT0_TCR1
SPORT_PARAMS(0),
#endif
#ifdef SPORT1_TCR1
SPORT_PARAMS(1),
#endif
#ifdef SPORT2_TCR1
SPORT_PARAMS(2),
#endif
#ifdef SPORT3_TCR1
SPORT_PARAMS(3),
#endif
};
void bf5xx_pcm_to_ac97(struct ac97_frame *dst, const __u16 *src, void bf5xx_pcm_to_ac97(struct ac97_frame *dst, const __u16 *src,
size_t count, unsigned int chan_mask) size_t count, unsigned int chan_mask)
@ -140,7 +99,8 @@ static unsigned int sport_tx_curr_frag(struct sport_device *sport)
static void enqueue_cmd(struct snd_ac97 *ac97, __u16 addr, __u16 data) static void enqueue_cmd(struct snd_ac97 *ac97, __u16 addr, __u16 data)
{ {
struct sport_device *sport = sport_handle; struct sport_device *sport = ac97_sport_handle;
int *cmd_count = sport->private_data;
int nextfrag = sport_tx_curr_frag(sport); int nextfrag = sport_tx_curr_frag(sport);
struct ac97_frame *nextwrite; struct ac97_frame *nextwrite;
@ -161,6 +121,7 @@ static void enqueue_cmd(struct snd_ac97 *ac97, __u16 addr, __u16 data)
static unsigned short bf5xx_ac97_read(struct snd_ac97 *ac97, static unsigned short bf5xx_ac97_read(struct snd_ac97 *ac97,
unsigned short reg) unsigned short reg)
{ {
struct sport_device *sport_handle = ac97_sport_handle;
struct ac97_frame out_frame[2], in_frame[2]; struct ac97_frame out_frame[2], in_frame[2];
pr_debug("%s enter 0x%x\n", __func__, reg); pr_debug("%s enter 0x%x\n", __func__, reg);
@ -185,6 +146,8 @@ static unsigned short bf5xx_ac97_read(struct snd_ac97 *ac97,
void bf5xx_ac97_write(struct snd_ac97 *ac97, unsigned short reg, void bf5xx_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
unsigned short val) unsigned short val)
{ {
struct sport_device *sport_handle = ac97_sport_handle;
pr_debug("%s enter 0x%x:0x%04x\n", __func__, reg, val); pr_debug("%s enter 0x%x:0x%04x\n", __func__, reg, val);
if (sport_handle->tx_run) { if (sport_handle->tx_run) {
@ -203,28 +166,19 @@ void bf5xx_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
static void bf5xx_ac97_warm_reset(struct snd_ac97 *ac97) static void bf5xx_ac97_warm_reset(struct snd_ac97 *ac97)
{ {
#if defined(CONFIG_BF54x) || defined(CONFIG_BF561) || \ struct sport_device *sport_handle = ac97_sport_handle;
(defined(BF537_FAMILY) && (CONFIG_SND_BF5XX_SPORT_NUM == 1)) u16 gpio = P_IDENT(sport_handle->pin_req[3]);
#define CONCAT(a, b, c) a ## b ## c
#define BFIN_SPORT_RFS(x) CONCAT(P_SPORT, x, _RFS)
u16 per = BFIN_SPORT_RFS(CONFIG_SND_BF5XX_SPORT_NUM);
u16 gpio = P_IDENT(BFIN_SPORT_RFS(CONFIG_SND_BF5XX_SPORT_NUM));
pr_debug("%s enter\n", __func__); pr_debug("%s enter\n", __func__);
peripheral_free(per); peripheral_free_list(sport_handle->pin_req);
gpio_request(gpio, "bf5xx-ac97"); gpio_request(gpio, "bf5xx-ac97");
gpio_direction_output(gpio, 1); gpio_direction_output(gpio, 1);
udelay(2); udelay(2);
gpio_set_value(gpio, 0); gpio_set_value(gpio, 0);
udelay(1); udelay(1);
gpio_free(gpio); gpio_free(gpio);
peripheral_request(per, "soc-audio"); peripheral_request_list(sport_handle->pin_req, "soc-audio");
#else
pr_info("%s: Not implemented\n", __func__);
#endif
} }
static void bf5xx_ac97_cold_reset(struct snd_ac97 *ac97) static void bf5xx_ac97_cold_reset(struct snd_ac97 *ac97)
@ -306,18 +260,32 @@ static int bf5xx_ac97_resume(struct snd_soc_dai *dai)
#define bf5xx_ac97_resume NULL #define bf5xx_ac97_resume NULL
#endif #endif
static int bf5xx_ac97_probe(struct snd_soc_dai *dai) static struct snd_soc_dai_driver bfin_ac97_dai = {
{ .ac97_control = 1,
int ret = 0; .suspend = bf5xx_ac97_suspend,
cmd_count = (int *)get_zeroed_page(GFP_KERNEL); .resume = bf5xx_ac97_resume,
if (cmd_count == NULL) .playback = {
return -ENOMEM; .stream_name = "AC97 Playback",
.channels_min = 2,
#if defined(CONFIG_SND_BF5XX_MULTICHAN_SUPPORT)
.channels_max = 6,
#else
.channels_max = 2,
#endif
.rates = SNDRV_PCM_RATE_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE, },
.capture = {
.stream_name = "AC97 Capture",
.channels_min = 2,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE, },
};
if (peripheral_request_list(sport_req[sport_num], "soc-audio")) { static int __devinit asoc_bfin_ac97_probe(struct platform_device *pdev)
pr_err("Requesting Peripherals failed\n"); {
ret = -EFAULT; struct sport_device *sport_handle;
goto peripheral_err; int ret;
}
#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET #ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
/* Request PB3 as reset pin */ /* Request PB3 as reset pin */
@ -329,12 +297,14 @@ static int bf5xx_ac97_probe(struct snd_soc_dai *dai)
} }
gpio_direction_output(CONFIG_SND_BF5XX_RESET_GPIO_NUM, 1); gpio_direction_output(CONFIG_SND_BF5XX_RESET_GPIO_NUM, 1);
#endif #endif
sport_handle = sport_init(&sport_params[sport_num], 2, \
sizeof(struct ac97_frame), NULL); sport_handle = sport_init(pdev, 2, sizeof(struct ac97_frame),
PAGE_SIZE);
if (!sport_handle) { if (!sport_handle) {
ret = -ENODEV; ret = -ENODEV;
goto sport_err; goto sport_err;
} }
/*SPORT works in TDM mode to simulate AC97 transfers*/ /*SPORT works in TDM mode to simulate AC97 transfers*/
#if defined(CONFIG_SND_BF5XX_MULTICHAN_SUPPORT) #if defined(CONFIG_SND_BF5XX_MULTICHAN_SUPPORT)
ret = sport_set_multichannel(sport_handle, 16, 0x3FF, 1); ret = sport_set_multichannel(sport_handle, 16, 0x3FF, 1);
@ -361,67 +331,37 @@ static int bf5xx_ac97_probe(struct snd_soc_dai *dai)
goto sport_config_err; goto sport_config_err;
} }
ret = snd_soc_register_dai(&pdev->dev, &bfin_ac97_dai);
if (ret) {
pr_err("Failed to register DAI: %d\n", ret);
goto sport_config_err;
}
ac97_sport_handle = sport_handle;
return 0; return 0;
sport_config_err: sport_config_err:
kfree(sport_handle); sport_done(sport_handle);
sport_err: sport_err:
#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET #ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM); gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM);
gpio_err: gpio_err:
#endif #endif
peripheral_free_list(sport_req[sport_num]);
peripheral_err:
free_page((unsigned long)cmd_count);
cmd_count = NULL;
return ret; return ret;
} }
static int bf5xx_ac97_remove(struct snd_soc_dai *dai) static int __devexit asoc_bfin_ac97_remove(struct platform_device *pdev)
{ {
free_page((unsigned long)cmd_count); struct sport_device *sport_handle = platform_get_drvdata(pdev);
cmd_count = NULL;
peripheral_free_list(sport_req[sport_num]); snd_soc_unregister_dai(&pdev->dev);
sport_done(sport_handle);
#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET #ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM); gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM);
#endif #endif
return 0;
}
struct snd_soc_dai_driver bfin_ac97_dai = {
.ac97_control = 1,
.probe = bf5xx_ac97_probe,
.remove = bf5xx_ac97_remove,
.suspend = bf5xx_ac97_suspend,
.resume = bf5xx_ac97_resume,
.playback = {
.stream_name = "AC97 Playback",
.channels_min = 2,
#if defined(CONFIG_SND_BF5XX_MULTICHAN_SUPPORT)
.channels_max = 6,
#else
.channels_max = 2,
#endif
.rates = SNDRV_PCM_RATE_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE, },
.capture = {
.stream_name = "AC97 Capture",
.channels_min = 2,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE, },
};
EXPORT_SYMBOL_GPL(bfin_ac97_dai);
static __devinit int asoc_bfin_ac97_probe(struct platform_device *pdev)
{
return snd_soc_register_dai(&pdev->dev, &bfin_ac97_dai);
}
static int __devexit asoc_bfin_ac97_remove(struct platform_device *pdev)
{
snd_soc_unregister_dai(&pdev->dev);
return 0; return 0;
} }

View file

@ -29,22 +29,12 @@
#include <asm/portmux.h> #include <asm/portmux.h>
#include "../codecs/ad1836.h" #include "../codecs/ad1836.h"
#include "bf5xx-sport.h"
#include "bf5xx-tdm-pcm.h" #include "bf5xx-tdm-pcm.h"
#include "bf5xx-tdm.h" #include "bf5xx-tdm.h"
static struct snd_soc_card bf5xx_ad1836; static struct snd_soc_card bf5xx_ad1836;
static int bf5xx_ad1836_startup(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
snd_soc_dai_set_drvdata(cpu_dai, sport_handle);
return 0;
}
static int bf5xx_ad1836_hw_params(struct snd_pcm_substream *substream, static int bf5xx_ad1836_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params) struct snd_pcm_hw_params *params)
{ {
@ -75,23 +65,33 @@ static int bf5xx_ad1836_hw_params(struct snd_pcm_substream *substream,
} }
static struct snd_soc_ops bf5xx_ad1836_ops = { static struct snd_soc_ops bf5xx_ad1836_ops = {
.startup = bf5xx_ad1836_startup,
.hw_params = bf5xx_ad1836_hw_params, .hw_params = bf5xx_ad1836_hw_params,
}; };
static struct snd_soc_dai_link bf5xx_ad1836_dai = { static struct snd_soc_dai_link bf5xx_ad1836_dai[] = {
.name = "ad1836", {
.stream_name = "AD1836", .name = "ad1836",
.cpu_dai_name = "bf5xx-tdm", .stream_name = "AD1836",
.codec_dai_name = "ad1836-hifi", .cpu_dai_name = "bfin-tdm.0",
.platform_name = "bf5xx-tdm-pcm-audio", .codec_dai_name = "ad1836-hifi",
.codec_name = "ad1836-codec.0", .platform_name = "bfin-tdm-pcm-audio",
.ops = &bf5xx_ad1836_ops, .codec_name = "ad1836.0",
.ops = &bf5xx_ad1836_ops,
},
{
.name = "ad1836",
.stream_name = "AD1836",
.cpu_dai_name = "bfin-tdm.1",
.codec_dai_name = "ad1836-hifi",
.platform_name = "bfin-tdm-pcm-audio",
.codec_name = "ad1836.0",
.ops = &bf5xx_ad1836_ops,
},
}; };
static struct snd_soc_card bf5xx_ad1836 = { static struct snd_soc_card bf5xx_ad1836 = {
.name = "bf5xx_ad1836", .name = "bfin-ad1836",
.dai_link = &bf5xx_ad1836_dai, .dai_link = &bf5xx_ad1836_dai[CONFIG_SND_BF5XX_SPORT_NUM],
.num_links = 1, .num_links = 1,
}; };

View file

@ -38,30 +38,28 @@
#include <asm/portmux.h> #include <asm/portmux.h>
#include "../codecs/ad193x.h" #include "../codecs/ad193x.h"
#include "bf5xx-sport.h"
#include "bf5xx-tdm-pcm.h" #include "bf5xx-tdm-pcm.h"
#include "bf5xx-tdm.h" #include "bf5xx-tdm.h"
static struct snd_soc_card bf5xx_ad193x; static struct snd_soc_card bf5xx_ad193x;
static int bf5xx_ad193x_startup(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
snd_soc_dai_set_drvdata(cpu_dai, sport_handle);
return 0;
}
static int bf5xx_ad193x_hw_params(struct snd_pcm_substream *substream, static int bf5xx_ad193x_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params) struct snd_pcm_hw_params *params)
{ {
struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
struct snd_soc_dai *codec_dai = rtd->codec_dai; struct snd_soc_dai *codec_dai = rtd->codec_dai;
unsigned int clk = 0;
unsigned int channel_map[] = {0, 1, 2, 3, 4, 5, 6, 7}; unsigned int channel_map[] = {0, 1, 2, 3, 4, 5, 6, 7};
int ret = 0; int ret = 0;
switch (params_rate(params)) {
case 48000:
clk = 12288000;
break;
}
/* set cpu DAI configuration */ /* set cpu DAI configuration */
ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_DSP_A | ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_DSP_A |
SND_SOC_DAIFMT_IB_IF | SND_SOC_DAIFMT_CBM_CFM); SND_SOC_DAIFMT_IB_IF | SND_SOC_DAIFMT_CBM_CFM);
@ -74,6 +72,12 @@ static int bf5xx_ad193x_hw_params(struct snd_pcm_substream *substream,
if (ret < 0) if (ret < 0)
return ret; return ret;
/* set the codec system clock for DAC and ADC */
ret = snd_soc_dai_set_sysclk(codec_dai, 0, clk,
SND_SOC_CLOCK_IN);
if (ret < 0)
return ret;
/* set codec DAI slots, 8 channels, all channels are enabled */ /* set codec DAI slots, 8 channels, all channels are enabled */
ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xFF, 0xFF, 8, 32); ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xFF, 0xFF, 8, 32);
if (ret < 0) if (ret < 0)
@ -89,23 +93,33 @@ static int bf5xx_ad193x_hw_params(struct snd_pcm_substream *substream,
} }
static struct snd_soc_ops bf5xx_ad193x_ops = { static struct snd_soc_ops bf5xx_ad193x_ops = {
.startup = bf5xx_ad193x_startup,
.hw_params = bf5xx_ad193x_hw_params, .hw_params = bf5xx_ad193x_hw_params,
}; };
static struct snd_soc_dai_link bf5xx_ad193x_dai = { static struct snd_soc_dai_link bf5xx_ad193x_dai[] = {
.name = "ad193x", {
.stream_name = "AD193X", .name = "ad193x",
.cpu_dai_name = "bf5xx-tdm", .stream_name = "AD193X",
.codec_dai_name ="ad193x-hifi", .cpu_dai_name = "bfin-tdm.0",
.platform_name = "bf5xx-tdm-pcm-audio", .codec_dai_name ="ad193x-hifi",
.codec_name = "ad193x-codec.5", .platform_name = "bfin-tdm-pcm-audio",
.ops = &bf5xx_ad193x_ops, .codec_name = "ad193x.5",
.ops = &bf5xx_ad193x_ops,
},
{
.name = "ad193x",
.stream_name = "AD193X",
.cpu_dai_name = "bfin-tdm.1",
.codec_dai_name ="ad193x-hifi",
.platform_name = "bfin-tdm-pcm-audio",
.codec_name = "ad193x.5",
.ops = &bf5xx_ad193x_ops,
},
}; };
static struct snd_soc_card bf5xx_ad193x = { static struct snd_soc_card bf5xx_ad193x = {
.name = "bf5xx_ad193x", .name = "bfin-ad193x",
.dai_link = &bf5xx_ad193x_dai, .dai_link = &bf5xx_ad193x_dai[CONFIG_SND_BF5XX_SPORT_NUM],
.num_links = 1, .num_links = 1,
}; };

View file

@ -47,39 +47,34 @@
#include <asm/portmux.h> #include <asm/portmux.h>
#include "../codecs/ad1980.h" #include "../codecs/ad1980.h"
#include "bf5xx-sport.h"
#include "bf5xx-ac97-pcm.h" #include "bf5xx-ac97-pcm.h"
#include "bf5xx-ac97.h" #include "bf5xx-ac97.h"
static struct snd_soc_card bf5xx_board; static struct snd_soc_card bf5xx_board;
static int bf5xx_board_startup(struct snd_pcm_substream *substream) static struct snd_soc_dai_link bf5xx_board_dai[] = {
{ {
struct snd_soc_pcm_runtime *rtd = substream->private_data; .name = "AC97",
struct snd_soc_dai *cpu_dai = rtd->cpu_dai; .stream_name = "AC97 HiFi",
.cpu_dai_name = "bfin-ac97.0",
pr_debug("%s enter\n", __func__); .codec_dai_name = "ad1980-hifi",
snd_soc_dai_set_drvdata(cpu_dai, sport_handle); .platform_name = "bfin-ac97-pcm-audio",
return 0; .codec_name = "ad1980",
} },
{
static struct snd_soc_ops bf5xx_board_ops = { .name = "AC97",
.startup = bf5xx_board_startup, .stream_name = "AC97 HiFi",
}; .cpu_dai_name = "bfin-ac97.1",
.codec_dai_name = "ad1980-hifi",
static struct snd_soc_dai_link bf5xx_board_dai = { .platform_name = "bfin-ac97-pcm-audio",
.name = "AC97", .codec_name = "ad1980",
.stream_name = "AC97 HiFi", },
.cpu_dai_name = "bfin-ac97",
.codec_dai_name = "ad1980-hifi",
.platform_name = "bfin-pcm-audio",
.codec_name = "ad1980-codec",
.ops = &bf5xx_board_ops,
}; };
static struct snd_soc_card bf5xx_board = { static struct snd_soc_card bf5xx_board = {
.name = "bf5xx-board", .name = "bfin-ad1980",
.dai_link = &bf5xx_board_dai, .dai_link = &bf5xx_board_dai[CONFIG_SND_BF5XX_SPORT_NUM],
.num_links = 1, .num_links = 1,
}; };

View file

@ -145,16 +145,6 @@ static int bf5xx_probe(struct platform_device *pdev)
return 0; return 0;
} }
static int bf5xx_ad73311_startup(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
pr_debug("%s enter\n", __func__);
snd_soc_dai_set_drvdata(cpu_dai, sport_handle);
return 0;
}
static int bf5xx_ad73311_hw_params(struct snd_pcm_substream *substream, static int bf5xx_ad73311_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params) struct snd_pcm_hw_params *params)
{ {
@ -176,24 +166,34 @@ static int bf5xx_ad73311_hw_params(struct snd_pcm_substream *substream,
static struct snd_soc_ops bf5xx_ad73311_ops = { static struct snd_soc_ops bf5xx_ad73311_ops = {
.startup = bf5xx_ad73311_startup,
.hw_params = bf5xx_ad73311_hw_params, .hw_params = bf5xx_ad73311_hw_params,
}; };
static struct snd_soc_dai_link bf5xx_ad73311_dai = { static struct snd_soc_dai_link bf5xx_ad73311_dai[] = {
.name = "ad73311", {
.stream_name = "AD73311", .name = "ad73311",
.cpu_dai_name = "bf5xx-i2s", .stream_name = "AD73311",
.codec_dai_name = "ad73311-hifi", .cpu_dai_name = "bfin-i2s.0",
.platform_name = "bfin-pcm-audio", .codec_dai_name = "ad73311-hifi",
.codec_name = "ad73311-codec", .platform_name = "bfin-i2s-pcm-audio",
.ops = &bf5xx_ad73311_ops, .codec_name = "ad73311",
.ops = &bf5xx_ad73311_ops,
},
{
.name = "ad73311",
.stream_name = "AD73311",
.cpu_dai_name = "bfin-i2s.1",
.codec_dai_name = "ad73311-hifi",
.platform_name = "bfin-i2s-pcm-audio",
.codec_name = "ad73311",
.ops = &bf5xx_ad73311_ops,
},
}; };
static struct snd_soc_card bf5xx_ad73311 = { static struct snd_soc_card bf5xx_ad73311 = {
.name = "bf5xx_ad73311", .name = "bfin-ad73311",
.probe = bf5xx_probe, .probe = bf5xx_probe,
.dai_link = &bf5xx_ad73311_dai, .dai_link = &bf5xx_ad73311_dai[CONFIG_SND_BF5XX_SPORT_NUM],
.num_links = 1, .num_links = 1,
}; };

View file

@ -148,10 +148,15 @@ static snd_pcm_uframes_t bf5xx_pcm_pointer(struct snd_pcm_substream *substream)
static int bf5xx_pcm_open(struct snd_pcm_substream *substream) static int bf5xx_pcm_open(struct snd_pcm_substream *substream)
{ {
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
struct sport_device *sport_handle = snd_soc_dai_get_drvdata(cpu_dai);
struct snd_pcm_runtime *runtime = substream->runtime; struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_dma_buffer *buf = &substream->dma_buffer;
int ret; int ret;
pr_debug("%s enter\n", __func__); pr_debug("%s enter\n", __func__);
snd_soc_set_runtime_hwparams(substream, &bf5xx_pcm_hardware); snd_soc_set_runtime_hwparams(substream, &bf5xx_pcm_hardware);
ret = snd_pcm_hw_constraint_integer(runtime, \ ret = snd_pcm_hw_constraint_integer(runtime, \
@ -159,9 +164,14 @@ static int bf5xx_pcm_open(struct snd_pcm_substream *substream)
if (ret < 0) if (ret < 0)
goto out; goto out;
if (sport_handle != NULL) if (sport_handle != NULL) {
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
sport_handle->tx_buf = buf->area;
else
sport_handle->rx_buf = buf->area;
runtime->private_data = sport_handle; runtime->private_data = sport_handle;
else { } else {
pr_err("sport_handle is NULL\n"); pr_err("sport_handle is NULL\n");
return -1; return -1;
} }
@ -214,11 +224,6 @@ static int bf5xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
pr_debug("%s, area:%p, size:0x%08lx\n", __func__, pr_debug("%s, area:%p, size:0x%08lx\n", __func__,
buf->area, buf->bytes); buf->area, buf->bytes);
if (stream == SNDRV_PCM_STREAM_PLAYBACK)
sport_handle->tx_buf = buf->area;
else
sport_handle->rx_buf = buf->area;
return 0; return 0;
} }
@ -239,8 +244,6 @@ static void bf5xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
dma_free_coherent(NULL, buf->bytes, buf->area, 0); dma_free_coherent(NULL, buf->bytes, buf->area, 0);
buf->area = NULL; buf->area = NULL;
} }
if (sport_handle)
sport_done(sport_handle);
} }
static u64 bf5xx_pcm_dmamask = DMA_BIT_MASK(32); static u64 bf5xx_pcm_dmamask = DMA_BIT_MASK(32);
@ -292,7 +295,7 @@ static int __devexit bfin_i2s_soc_platform_remove(struct platform_device *pdev)
static struct platform_driver bfin_i2s_pcm_driver = { static struct platform_driver bfin_i2s_pcm_driver = {
.driver = { .driver = {
.name = "bfin-pcm-audio", .name = "bfin-i2s-pcm-audio",
.owner = THIS_MODULE, .owner = THIS_MODULE,
}, },

View file

@ -51,59 +51,24 @@ struct bf5xx_i2s_port {
int configured; int configured;
}; };
static struct bf5xx_i2s_port bf5xx_i2s;
static int sport_num = CONFIG_SND_BF5XX_SPORT_NUM;
static struct sport_param sport_params[2] = {
{
.dma_rx_chan = CH_SPORT0_RX,
.dma_tx_chan = CH_SPORT0_TX,
.err_irq = IRQ_SPORT0_ERROR,
.regs = (struct sport_register *)SPORT0_TCR1,
},
{
.dma_rx_chan = CH_SPORT1_RX,
.dma_tx_chan = CH_SPORT1_TX,
.err_irq = IRQ_SPORT1_ERROR,
.regs = (struct sport_register *)SPORT1_TCR1,
}
};
/*
* Setting the TFS pin selector for SPORT 0 based on whether the selected
* port id F or G. If the port is F then no conflict should exist for the
* TFS. When Port G is selected and EMAC then there is a conflict between
* the PHY interrupt line and TFS. Current settings prevent the conflict
* by ignoring the TFS pin when Port G is selected. This allows both
* codecs and EMAC using Port G concurrently.
*/
#ifdef CONFIG_BF527_SPORT0_PORTG
#define LOCAL_SPORT0_TFS (0)
#else
#define LOCAL_SPORT0_TFS (P_SPORT0_TFS)
#endif
static u16 sport_req[][7] = { {P_SPORT0_DTPRI, P_SPORT0_TSCLK, P_SPORT0_RFS,
P_SPORT0_DRPRI, P_SPORT0_RSCLK, LOCAL_SPORT0_TFS, 0},
{P_SPORT1_DTPRI, P_SPORT1_TSCLK, P_SPORT1_RFS, P_SPORT1_DRPRI,
P_SPORT1_RSCLK, P_SPORT1_TFS, 0} };
static int bf5xx_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai, static int bf5xx_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
unsigned int fmt) unsigned int fmt)
{ {
struct sport_device *sport_handle = snd_soc_dai_get_drvdata(cpu_dai);
struct bf5xx_i2s_port *bf5xx_i2s = sport_handle->private_data;
int ret = 0; int ret = 0;
/* interface format:support I2S,slave mode */ /* interface format:support I2S,slave mode */
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S: case SND_SOC_DAIFMT_I2S:
bf5xx_i2s.tcr1 |= TFSR | TCKFE; bf5xx_i2s->tcr1 |= TFSR | TCKFE;
bf5xx_i2s.rcr1 |= RFSR | RCKFE; bf5xx_i2s->rcr1 |= RFSR | RCKFE;
bf5xx_i2s.tcr2 |= TSFSE; bf5xx_i2s->tcr2 |= TSFSE;
bf5xx_i2s.rcr2 |= RSFSE; bf5xx_i2s->rcr2 |= RSFSE;
break; break;
case SND_SOC_DAIFMT_DSP_A: case SND_SOC_DAIFMT_DSP_A:
bf5xx_i2s.tcr1 |= TFSR; bf5xx_i2s->tcr1 |= TFSR;
bf5xx_i2s.rcr1 |= RFSR; bf5xx_i2s->rcr1 |= RFSR;
break; break;
case SND_SOC_DAIFMT_LEFT_J: case SND_SOC_DAIFMT_LEFT_J:
ret = -EINVAL; ret = -EINVAL;
@ -135,29 +100,35 @@ static int bf5xx_i2s_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai) struct snd_soc_dai *dai)
{ {
struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
struct bf5xx_i2s_port *bf5xx_i2s = sport_handle->private_data;
int ret = 0; int ret = 0;
bf5xx_i2s.tcr2 &= ~0x1f; bf5xx_i2s->tcr2 &= ~0x1f;
bf5xx_i2s.rcr2 &= ~0x1f; bf5xx_i2s->rcr2 &= ~0x1f;
switch (params_format(params)) { switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S8:
bf5xx_i2s->tcr2 |= 7;
bf5xx_i2s->rcr2 |= 7;
sport_handle->wdsize = 1;
case SNDRV_PCM_FORMAT_S16_LE: case SNDRV_PCM_FORMAT_S16_LE:
bf5xx_i2s.tcr2 |= 15; bf5xx_i2s->tcr2 |= 15;
bf5xx_i2s.rcr2 |= 15; bf5xx_i2s->rcr2 |= 15;
sport_handle->wdsize = 2; sport_handle->wdsize = 2;
break; break;
case SNDRV_PCM_FORMAT_S24_LE: case SNDRV_PCM_FORMAT_S24_LE:
bf5xx_i2s.tcr2 |= 23; bf5xx_i2s->tcr2 |= 23;
bf5xx_i2s.rcr2 |= 23; bf5xx_i2s->rcr2 |= 23;
sport_handle->wdsize = 3; sport_handle->wdsize = 3;
break; break;
case SNDRV_PCM_FORMAT_S32_LE: case SNDRV_PCM_FORMAT_S32_LE:
bf5xx_i2s.tcr2 |= 31; bf5xx_i2s->tcr2 |= 31;
bf5xx_i2s.rcr2 |= 31; bf5xx_i2s->rcr2 |= 31;
sport_handle->wdsize = 4; sport_handle->wdsize = 4;
break; break;
} }
if (!bf5xx_i2s.configured) { if (!bf5xx_i2s->configured) {
/* /*
* TX and RX are not independent,they are enabled at the * TX and RX are not independent,they are enabled at the
* same time, even if only one side is running. So, we * same time, even if only one side is running. So, we
@ -166,16 +137,16 @@ static int bf5xx_i2s_hw_params(struct snd_pcm_substream *substream,
* *
* CPU DAI:slave mode. * CPU DAI:slave mode.
*/ */
bf5xx_i2s.configured = 1; bf5xx_i2s->configured = 1;
ret = sport_config_rx(sport_handle, bf5xx_i2s.rcr1, ret = sport_config_rx(sport_handle, bf5xx_i2s->rcr1,
bf5xx_i2s.rcr2, 0, 0); bf5xx_i2s->rcr2, 0, 0);
if (ret) { if (ret) {
pr_err("SPORT is busy!\n"); pr_err("SPORT is busy!\n");
return -EBUSY; return -EBUSY;
} }
ret = sport_config_tx(sport_handle, bf5xx_i2s.tcr1, ret = sport_config_tx(sport_handle, bf5xx_i2s->tcr1,
bf5xx_i2s.tcr2, 0, 0); bf5xx_i2s->tcr2, 0, 0);
if (ret) { if (ret) {
pr_err("SPORT is busy!\n"); pr_err("SPORT is busy!\n");
return -EBUSY; return -EBUSY;
@ -188,41 +159,19 @@ static int bf5xx_i2s_hw_params(struct snd_pcm_substream *substream,
static void bf5xx_i2s_shutdown(struct snd_pcm_substream *substream, static void bf5xx_i2s_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai) struct snd_soc_dai *dai)
{ {
struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
struct bf5xx_i2s_port *bf5xx_i2s = sport_handle->private_data;
pr_debug("%s enter\n", __func__); pr_debug("%s enter\n", __func__);
/* No active stream, SPORT is allowed to be configured again. */ /* No active stream, SPORT is allowed to be configured again. */
if (!dai->active) if (!dai->active)
bf5xx_i2s.configured = 0; bf5xx_i2s->configured = 0;
}
static int bf5xx_i2s_probe(struct snd_soc_dai *dai)
{
pr_debug("%s enter\n", __func__);
if (peripheral_request_list(&sport_req[sport_num][0], "soc-audio")) {
pr_err("Requesting Peripherals failed\n");
return -EFAULT;
}
/* request DMA for SPORT */
sport_handle = sport_init(&sport_params[sport_num], 4, \
2 * sizeof(u32), NULL);
if (!sport_handle) {
peripheral_free_list(&sport_req[sport_num][0]);
return -ENODEV;
}
return 0;
}
static int bf5xx_i2s_remove(struct snd_soc_dai *dai)
{
pr_debug("%s enter\n", __func__);
peripheral_free_list(&sport_req[sport_num][0]);
return 0;
} }
#ifdef CONFIG_PM #ifdef CONFIG_PM
static int bf5xx_i2s_suspend(struct snd_soc_dai *dai) static int bf5xx_i2s_suspend(struct snd_soc_dai *dai)
{ {
struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
pr_debug("%s : sport %d\n", __func__, dai->id); pr_debug("%s : sport %d\n", __func__, dai->id);
@ -235,19 +184,21 @@ static int bf5xx_i2s_suspend(struct snd_soc_dai *dai)
static int bf5xx_i2s_resume(struct snd_soc_dai *dai) static int bf5xx_i2s_resume(struct snd_soc_dai *dai)
{ {
struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
struct bf5xx_i2s_port *bf5xx_i2s = sport_handle->private_data;
int ret; int ret;
pr_debug("%s : sport %d\n", __func__, dai->id); pr_debug("%s : sport %d\n", __func__, dai->id);
ret = sport_config_rx(sport_handle, bf5xx_i2s.rcr1, ret = sport_config_rx(sport_handle, bf5xx_i2s->rcr1,
bf5xx_i2s.rcr2, 0, 0); bf5xx_i2s->rcr2, 0, 0);
if (ret) { if (ret) {
pr_err("SPORT is busy!\n"); pr_err("SPORT is busy!\n");
return -EBUSY; return -EBUSY;
} }
ret = sport_config_tx(sport_handle, bf5xx_i2s.tcr1, ret = sport_config_tx(sport_handle, bf5xx_i2s->tcr1,
bf5xx_i2s.tcr2, 0, 0); bf5xx_i2s->tcr2, 0, 0);
if (ret) { if (ret) {
pr_err("SPORT is busy!\n"); pr_err("SPORT is busy!\n");
return -EBUSY; return -EBUSY;
@ -266,8 +217,11 @@ static int bf5xx_i2s_resume(struct snd_soc_dai *dai)
SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | \ SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | \
SNDRV_PCM_RATE_96000) SNDRV_PCM_RATE_96000)
#define BF5XX_I2S_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |\ #define BF5XX_I2S_FORMATS \
SNDRV_PCM_FMTBIT_S32_LE) (SNDRV_PCM_FMTBIT_S8 | \
SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S24_LE | \
SNDRV_PCM_FMTBIT_S32_LE)
static struct snd_soc_dai_ops bf5xx_i2s_dai_ops = { static struct snd_soc_dai_ops bf5xx_i2s_dai_ops = {
.shutdown = bf5xx_i2s_shutdown, .shutdown = bf5xx_i2s_shutdown,
@ -276,8 +230,6 @@ static struct snd_soc_dai_ops bf5xx_i2s_dai_ops = {
}; };
static struct snd_soc_dai_driver bf5xx_i2s_dai = { static struct snd_soc_dai_driver bf5xx_i2s_dai = {
.probe = bf5xx_i2s_probe,
.remove = bf5xx_i2s_remove,
.suspend = bf5xx_i2s_suspend, .suspend = bf5xx_i2s_suspend,
.resume = bf5xx_i2s_resume, .resume = bf5xx_i2s_resume,
.playback = { .playback = {
@ -293,23 +245,45 @@ static struct snd_soc_dai_driver bf5xx_i2s_dai = {
.ops = &bf5xx_i2s_dai_ops, .ops = &bf5xx_i2s_dai_ops,
}; };
static int bfin_i2s_drv_probe(struct platform_device *pdev) static int __devinit bf5xx_i2s_probe(struct platform_device *pdev)
{ {
return snd_soc_register_dai(&pdev->dev, &bf5xx_i2s_dai); struct sport_device *sport_handle;
int ret;
/* configure SPORT for I2S */
sport_handle = sport_init(pdev, 4, 2 * sizeof(u32),
sizeof(struct bf5xx_i2s_port));
if (!sport_handle)
return -ENODEV;
/* register with the ASoC layers */
ret = snd_soc_register_dai(&pdev->dev, &bf5xx_i2s_dai);
if (ret) {
pr_err("Failed to register DAI: %d\n", ret);
sport_done(sport_handle);
return ret;
}
return 0;
} }
static int __devexit bfin_i2s_drv_remove(struct platform_device *pdev) static int __devexit bf5xx_i2s_remove(struct platform_device *pdev)
{ {
struct sport_device *sport_handle = platform_get_drvdata(pdev);
pr_debug("%s enter\n", __func__);
snd_soc_unregister_dai(&pdev->dev); snd_soc_unregister_dai(&pdev->dev);
sport_done(sport_handle);
return 0; return 0;
} }
static struct platform_driver bfin_i2s_driver = { static struct platform_driver bfin_i2s_driver = {
.probe = bfin_i2s_drv_probe, .probe = bf5xx_i2s_probe,
.remove = __devexit_p(bfin_i2s_drv_remove), .remove = __devexit_p(bf5xx_i2s_remove),
.driver = { .driver = {
.name = "bf5xx-i2s", .name = "bfin-i2s",
.owner = THIS_MODULE, .owner = THIS_MODULE,
}, },
}; };

View file

@ -42,8 +42,6 @@
/* delay between frame sync pulse and first data bit in multichannel mode */ /* delay between frame sync pulse and first data bit in multichannel mode */
#define FRAME_DELAY (1<<12) #define FRAME_DELAY (1<<12)
struct sport_device *sport_handle;
EXPORT_SYMBOL(sport_handle);
/* note: multichannel is in units of 8 channels, /* note: multichannel is in units of 8 channels,
* tdm_count is # channels NOT / 8 ! */ * tdm_count is # channels NOT / 8 ! */
int sport_set_multichannel(struct sport_device *sport, int sport_set_multichannel(struct sport_device *sport,
@ -798,86 +796,164 @@ int sport_set_err_callback(struct sport_device *sport,
} }
EXPORT_SYMBOL(sport_set_err_callback); EXPORT_SYMBOL(sport_set_err_callback);
struct sport_device *sport_init(struct sport_param *param, unsigned wdsize, static int sport_config_pdev(struct platform_device *pdev, struct sport_param *param)
unsigned dummy_count, void *private_data)
{ {
int ret; /* Extract settings from platform data */
struct device *dev = &pdev->dev;
struct bfin_snd_platform_data *pdata = dev->platform_data;
struct resource *res;
param->num = pdev->id;
if (!pdata) {
dev_err(dev, "no platform_data\n");
return -ENODEV;
}
param->pin_req = pdata->pin_req;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(dev, "no MEM resource\n");
return -ENODEV;
}
param->regs = (struct sport_register *)res->start;
/* first RX, then TX */
res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
if (!res) {
dev_err(dev, "no rx DMA resource\n");
return -ENODEV;
}
param->dma_rx_chan = res->start;
res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
if (!res) {
dev_err(dev, "no tx DMA resource\n");
return -ENODEV;
}
param->dma_tx_chan = res->start;
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (!res) {
dev_err(dev, "no irq resource\n");
return -ENODEV;
}
param->err_irq = res->start;
return 0;
}
struct sport_device *sport_init(struct platform_device *pdev,
unsigned int wdsize, unsigned int dummy_count, size_t priv_size)
{
struct device *dev = &pdev->dev;
struct sport_param param;
struct sport_device *sport; struct sport_device *sport;
pr_debug("%s enter\n", __func__); int ret;
BUG_ON(param == NULL);
BUG_ON(wdsize == 0 || dummy_count == 0); dev_dbg(dev, "%s enter\n", __func__);
sport = kmalloc(sizeof(struct sport_device), GFP_KERNEL);
if (!sport) { param.wdsize = wdsize;
pr_err("Failed to allocate for sport device\n"); param.dummy_count = dummy_count;
BUG_ON(param.wdsize == 0 || param.dummy_count == 0);
ret = sport_config_pdev(pdev, &param);
if (ret)
return NULL;
if (peripheral_request_list(param.pin_req, "soc-audio")) {
dev_err(dev, "requesting Peripherals failed\n");
return NULL; return NULL;
} }
memset(sport, 0, sizeof(struct sport_device)); sport = kzalloc(sizeof(*sport), GFP_KERNEL);
sport->dma_rx_chan = param->dma_rx_chan; if (!sport) {
sport->dma_tx_chan = param->dma_tx_chan; dev_err(dev, "failed to allocate for sport device\n");
sport->err_irq = param->err_irq; goto __init_err0;
sport->regs = param->regs; }
sport->private_data = private_data;
sport->num = param.num;
sport->dma_rx_chan = param.dma_rx_chan;
sport->dma_tx_chan = param.dma_tx_chan;
sport->err_irq = param.err_irq;
sport->regs = param.regs;
sport->pin_req = param.pin_req;
if (request_dma(sport->dma_rx_chan, "SPORT RX Data") == -EBUSY) { if (request_dma(sport->dma_rx_chan, "SPORT RX Data") == -EBUSY) {
pr_err("Failed to request RX dma %d\n", \ dev_err(dev, "failed to request RX dma %d\n", sport->dma_rx_chan);
sport->dma_rx_chan);
goto __init_err1; goto __init_err1;
} }
if (set_dma_callback(sport->dma_rx_chan, rx_handler, sport) != 0) { if (set_dma_callback(sport->dma_rx_chan, rx_handler, sport) != 0) {
pr_err("Failed to request RX irq %d\n", \ dev_err(dev, "failed to request RX irq %d\n", sport->dma_rx_chan);
sport->dma_rx_chan);
goto __init_err2; goto __init_err2;
} }
if (request_dma(sport->dma_tx_chan, "SPORT TX Data") == -EBUSY) { if (request_dma(sport->dma_tx_chan, "SPORT TX Data") == -EBUSY) {
pr_err("Failed to request TX dma %d\n", \ dev_err(dev, "failed to request TX dma %d\n", sport->dma_tx_chan);
sport->dma_tx_chan);
goto __init_err2; goto __init_err2;
} }
if (set_dma_callback(sport->dma_tx_chan, tx_handler, sport) != 0) { if (set_dma_callback(sport->dma_tx_chan, tx_handler, sport) != 0) {
pr_err("Failed to request TX irq %d\n", \ dev_err(dev, "failed to request TX irq %d\n", sport->dma_tx_chan);
sport->dma_tx_chan);
goto __init_err3; goto __init_err3;
} }
if (request_irq(sport->err_irq, err_handler, IRQF_SHARED, "SPORT err", if (request_irq(sport->err_irq, err_handler, IRQF_SHARED, "SPORT err",
sport) < 0) { sport) < 0) {
pr_err("Failed to request err irq:%d\n", \ dev_err(dev, "failed to request err irq %d\n", sport->err_irq);
sport->err_irq);
goto __init_err3; goto __init_err3;
} }
pr_err("dma rx:%d tx:%d, err irq:%d, regs:%p\n", dev_info(dev, "dma rx:%d tx:%d, err irq:%d, regs:%p\n",
sport->dma_rx_chan, sport->dma_tx_chan, sport->dma_rx_chan, sport->dma_tx_chan,
sport->err_irq, sport->regs); sport->err_irq, sport->regs);
sport->wdsize = wdsize; sport->wdsize = param.wdsize;
sport->dummy_count = dummy_count; sport->dummy_count = param.dummy_count;
sport->private_data = kzalloc(priv_size, GFP_KERNEL);
if (!sport->private_data) {
dev_err(dev, "could not alloc priv data %zu bytes\n", priv_size);
goto __init_err4;
}
if (L1_DATA_A_LENGTH) if (L1_DATA_A_LENGTH)
sport->dummy_buf = l1_data_sram_zalloc(dummy_count * 2); sport->dummy_buf = l1_data_sram_zalloc(param.dummy_count * 2);
else else
sport->dummy_buf = kzalloc(dummy_count * 2, GFP_KERNEL); sport->dummy_buf = kzalloc(param.dummy_count * 2, GFP_KERNEL);
if (sport->dummy_buf == NULL) { if (sport->dummy_buf == NULL) {
pr_err("Failed to allocate dummy buffer\n"); dev_err(dev, "failed to allocate dummy buffer\n");
goto __error; goto __error1;
} }
ret = sport_config_rx_dummy(sport); ret = sport_config_rx_dummy(sport);
if (ret) { if (ret) {
pr_err("Failed to config rx dummy ring\n"); dev_err(dev, "failed to config rx dummy ring\n");
goto __error; goto __error2;
} }
ret = sport_config_tx_dummy(sport); ret = sport_config_tx_dummy(sport);
if (ret) { if (ret) {
pr_err("Failed to config tx dummy ring\n"); dev_err(dev, "failed to config tx dummy ring\n");
goto __error; goto __error3;
} }
platform_set_drvdata(pdev, sport);
return sport; return sport;
__error: __error3:
if (L1_DATA_A_LENGTH)
l1_data_sram_free(sport->dummy_rx_desc);
else
dma_free_coherent(NULL, 2*sizeof(struct dmasg),
sport->dummy_rx_desc, 0);
__error2:
if (L1_DATA_A_LENGTH)
l1_data_sram_free(sport->dummy_buf);
else
kfree(sport->dummy_buf);
__error1:
kfree(sport->private_data);
__init_err4:
free_irq(sport->err_irq, sport); free_irq(sport->err_irq, sport);
__init_err3: __init_err3:
free_dma(sport->dma_tx_chan); free_dma(sport->dma_tx_chan);
@ -885,6 +961,8 @@ __init_err2:
free_dma(sport->dma_rx_chan); free_dma(sport->dma_rx_chan);
__init_err1: __init_err1:
kfree(sport); kfree(sport);
__init_err0:
peripheral_free_list(param.pin_req);
return NULL; return NULL;
} }
EXPORT_SYMBOL(sport_init); EXPORT_SYMBOL(sport_init);
@ -917,8 +995,9 @@ void sport_done(struct sport_device *sport)
free_dma(sport->dma_tx_chan); free_dma(sport->dma_tx_chan);
free_irq(sport->err_irq, sport); free_irq(sport->err_irq, sport);
kfree(sport->private_data);
peripheral_free_list(sport->pin_req);
kfree(sport); kfree(sport);
sport = NULL;
} }
EXPORT_SYMBOL(sport_done); EXPORT_SYMBOL(sport_done);

View file

@ -1,5 +1,5 @@
/* /*
* File: bf5xx_ac97_sport.h * File: bf5xx_sport.h
* Based on: * Based on:
* Author: Roy Huang <roy.huang@analog.com> * Author: Roy Huang <roy.huang@analog.com>
* *
@ -33,15 +33,18 @@
#include <linux/types.h> #include <linux/types.h>
#include <linux/wait.h> #include <linux/wait.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include <linux/platform_device.h>
#include <asm/dma.h> #include <asm/dma.h>
#include <asm/bfin_sport.h> #include <asm/bfin_sport.h>
#define DESC_ELEMENT_COUNT 9 #define DESC_ELEMENT_COUNT 9
struct sport_device { struct sport_device {
int num;
int dma_rx_chan; int dma_rx_chan;
int dma_tx_chan; int dma_tx_chan;
int err_irq; int err_irq;
const unsigned short *pin_req;
struct sport_register *regs; struct sport_register *regs;
unsigned char *rx_buf; unsigned char *rx_buf;
@ -103,17 +106,20 @@ struct sport_device {
void *private_data; void *private_data;
}; };
extern struct sport_device *sport_handle;
struct sport_param { struct sport_param {
int num;
int dma_rx_chan; int dma_rx_chan;
int dma_tx_chan; int dma_tx_chan;
int err_irq; int err_irq;
const unsigned short *pin_req;
struct sport_register *regs; struct sport_register *regs;
unsigned int wdsize;
unsigned int dummy_count;
void *private_data;
}; };
struct sport_device *sport_init(struct sport_param *param, unsigned wdsize, struct sport_device *sport_init(struct platform_device *pdev,
unsigned dummy_count, void *private_data); unsigned int wdsize, unsigned int dummy_count, size_t priv_size);
void sport_done(struct sport_device *sport); void sport_done(struct sport_device *sport);

View file

@ -44,16 +44,6 @@
static struct snd_soc_card bf5xx_ssm2602; static struct snd_soc_card bf5xx_ssm2602;
static int bf5xx_ssm2602_startup(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
pr_debug("%s enter\n", __func__);
snd_soc_dai_set_drvdata(cpu_dai, sport_handle);
return 0;
}
static int bf5xx_ssm2602_hw_params(struct snd_pcm_substream *substream, static int bf5xx_ssm2602_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params) struct snd_pcm_hw_params *params)
{ {
@ -109,23 +99,33 @@ static int bf5xx_ssm2602_hw_params(struct snd_pcm_substream *substream,
} }
static struct snd_soc_ops bf5xx_ssm2602_ops = { static struct snd_soc_ops bf5xx_ssm2602_ops = {
.startup = bf5xx_ssm2602_startup,
.hw_params = bf5xx_ssm2602_hw_params, .hw_params = bf5xx_ssm2602_hw_params,
}; };
static struct snd_soc_dai_link bf5xx_ssm2602_dai = { static struct snd_soc_dai_link bf5xx_ssm2602_dai[] = {
.name = "ssm2602", {
.stream_name = "SSM2602", .name = "ssm2602",
.cpu_dai_name = "bf5xx-i2s", .stream_name = "SSM2602",
.codec_dai_name = "ssm2602-hifi", .cpu_dai_name = "bfin-i2s.0",
.platform_name = "bf5xx-pcm-audio", .codec_dai_name = "ssm2602-hifi",
.codec_name = "ssm2602-codec.0-001b", .platform_name = "bfin-i2s-pcm-audio",
.ops = &bf5xx_ssm2602_ops, .codec_name = "ssm2602.0-001b",
.ops = &bf5xx_ssm2602_ops,
},
{
.name = "ssm2602",
.stream_name = "SSM2602",
.cpu_dai_name = "bfin-i2s.1",
.codec_dai_name = "ssm2602-hifi",
.platform_name = "bfin-i2s-pcm-audio",
.codec_name = "ssm2602.0-001b",
.ops = &bf5xx_ssm2602_ops,
},
}; };
static struct snd_soc_card bf5xx_ssm2602 = { static struct snd_soc_card bf5xx_ssm2602 = {
.name = "bf5xx_ssm2602", .name = "bfin-ssm2602",
.dai_link = &bf5xx_ssm2602_dai, .dai_link = &bf5xx_ssm2602_dai[CONFIG_SND_BF5XX_SPORT_NUM],
.num_links = 1, .num_links = 1,
}; };

View file

@ -154,7 +154,12 @@ static snd_pcm_uframes_t bf5xx_pcm_pointer(struct snd_pcm_substream *substream)
static int bf5xx_pcm_open(struct snd_pcm_substream *substream) static int bf5xx_pcm_open(struct snd_pcm_substream *substream)
{ {
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
struct sport_device *sport_handle = snd_soc_dai_get_drvdata(cpu_dai);
struct snd_pcm_runtime *runtime = substream->runtime; struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_dma_buffer *buf = &substream->dma_buffer;
int ret = 0; int ret = 0;
snd_soc_set_runtime_hwparams(substream, &bf5xx_pcm_hardware); snd_soc_set_runtime_hwparams(substream, &bf5xx_pcm_hardware);
@ -164,9 +169,14 @@ static int bf5xx_pcm_open(struct snd_pcm_substream *substream)
if (ret < 0) if (ret < 0)
goto out; goto out;
if (sport_handle != NULL) if (sport_handle != NULL) {
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
sport_handle->tx_buf = buf->area;
else
sport_handle->rx_buf = buf->area;
runtime->private_data = sport_handle; runtime->private_data = sport_handle;
else { } else {
pr_err("sport_handle is NULL\n"); pr_err("sport_handle is NULL\n");
ret = -ENODEV; ret = -ENODEV;
} }
@ -249,11 +259,6 @@ static int bf5xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
} }
buf->bytes = size; buf->bytes = size;
if (stream == SNDRV_PCM_STREAM_PLAYBACK)
sport_handle->tx_buf = buf->area;
else
sport_handle->rx_buf = buf->area;
return 0; return 0;
} }
@ -274,8 +279,6 @@ static void bf5xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
dma_free_coherent(NULL, buf->bytes, buf->area, 0); dma_free_coherent(NULL, buf->bytes, buf->area, 0);
buf->area = NULL; buf->area = NULL;
} }
if (sport_handle)
sport_done(sport_handle);
} }
static u64 bf5xx_pcm_dmamask = DMA_BIT_MASK(32); static u64 bf5xx_pcm_dmamask = DMA_BIT_MASK(32);
@ -326,7 +329,7 @@ static int __devexit bf5xx_soc_platform_remove(struct platform_device *pdev)
static struct platform_driver bfin_tdm_driver = { static struct platform_driver bfin_tdm_driver = {
.driver = { .driver = {
.name = "bf5xx-tdm-pcm-audio", .name = "bfin-tdm-pcm-audio",
.owner = THIS_MODULE, .owner = THIS_MODULE,
}, },

View file

@ -46,43 +46,6 @@
#include "bf5xx-sport.h" #include "bf5xx-sport.h"
#include "bf5xx-tdm.h" #include "bf5xx-tdm.h"
static struct bf5xx_tdm_port bf5xx_tdm;
static int sport_num = CONFIG_SND_BF5XX_SPORT_NUM;
static struct sport_param sport_params[2] = {
{
.dma_rx_chan = CH_SPORT0_RX,
.dma_tx_chan = CH_SPORT0_TX,
.err_irq = IRQ_SPORT0_ERROR,
.regs = (struct sport_register *)SPORT0_TCR1,
},
{
.dma_rx_chan = CH_SPORT1_RX,
.dma_tx_chan = CH_SPORT1_TX,
.err_irq = IRQ_SPORT1_ERROR,
.regs = (struct sport_register *)SPORT1_TCR1,
}
};
/*
* Setting the TFS pin selector for SPORT 0 based on whether the selected
* port id F or G. If the port is F then no conflict should exist for the
* TFS. When Port G is selected and EMAC then there is a conflict between
* the PHY interrupt line and TFS. Current settings prevent the conflict
* by ignoring the TFS pin when Port G is selected. This allows both
* codecs and EMAC using Port G concurrently.
*/
#ifdef CONFIG_BF527_SPORT0_PORTG
#define LOCAL_SPORT0_TFS (0)
#else
#define LOCAL_SPORT0_TFS (P_SPORT0_TFS)
#endif
static u16 sport_req[][7] = { {P_SPORT0_DTPRI, P_SPORT0_TSCLK, P_SPORT0_RFS,
P_SPORT0_DRPRI, P_SPORT0_RSCLK, LOCAL_SPORT0_TFS, 0},
{P_SPORT1_DTPRI, P_SPORT1_TSCLK, P_SPORT1_RFS, P_SPORT1_DRPRI,
P_SPORT1_RSCLK, P_SPORT1_TFS, 0} };
static int bf5xx_tdm_set_dai_fmt(struct snd_soc_dai *cpu_dai, static int bf5xx_tdm_set_dai_fmt(struct snd_soc_dai *cpu_dai,
unsigned int fmt) unsigned int fmt)
{ {
@ -119,14 +82,16 @@ static int bf5xx_tdm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai) struct snd_soc_dai *dai)
{ {
struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
struct bf5xx_tdm_port *bf5xx_tdm = sport_handle->private_data;
int ret = 0; int ret = 0;
bf5xx_tdm.tcr2 &= ~0x1f; bf5xx_tdm->tcr2 &= ~0x1f;
bf5xx_tdm.rcr2 &= ~0x1f; bf5xx_tdm->rcr2 &= ~0x1f;
switch (params_format(params)) { switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S32_LE: case SNDRV_PCM_FORMAT_S32_LE:
bf5xx_tdm.tcr2 |= 31; bf5xx_tdm->tcr2 |= 31;
bf5xx_tdm.rcr2 |= 31; bf5xx_tdm->rcr2 |= 31;
sport_handle->wdsize = 4; sport_handle->wdsize = 4;
break; break;
/* at present, we only support 32bit transfer */ /* at present, we only support 32bit transfer */
@ -136,7 +101,7 @@ static int bf5xx_tdm_hw_params(struct snd_pcm_substream *substream,
break; break;
} }
if (!bf5xx_tdm.configured) { if (!bf5xx_tdm->configured) {
/* /*
* TX and RX are not independent,they are enabled at the * TX and RX are not independent,they are enabled at the
* same time, even if only one side is running. So, we * same time, even if only one side is running. So, we
@ -145,21 +110,21 @@ static int bf5xx_tdm_hw_params(struct snd_pcm_substream *substream,
* *
* CPU DAI:slave mode. * CPU DAI:slave mode.
*/ */
ret = sport_config_rx(sport_handle, bf5xx_tdm.rcr1, ret = sport_config_rx(sport_handle, bf5xx_tdm->rcr1,
bf5xx_tdm.rcr2, 0, 0); bf5xx_tdm->rcr2, 0, 0);
if (ret) { if (ret) {
pr_err("SPORT is busy!\n"); pr_err("SPORT is busy!\n");
return -EBUSY; return -EBUSY;
} }
ret = sport_config_tx(sport_handle, bf5xx_tdm.tcr1, ret = sport_config_tx(sport_handle, bf5xx_tdm->tcr1,
bf5xx_tdm.tcr2, 0, 0); bf5xx_tdm->tcr2, 0, 0);
if (ret) { if (ret) {
pr_err("SPORT is busy!\n"); pr_err("SPORT is busy!\n");
return -EBUSY; return -EBUSY;
} }
bf5xx_tdm.configured = 1; bf5xx_tdm->configured = 1;
} }
return 0; return 0;
@ -168,15 +133,20 @@ static int bf5xx_tdm_hw_params(struct snd_pcm_substream *substream,
static void bf5xx_tdm_shutdown(struct snd_pcm_substream *substream, static void bf5xx_tdm_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai) struct snd_soc_dai *dai)
{ {
struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
struct bf5xx_tdm_port *bf5xx_tdm = sport_handle->private_data;
/* No active stream, SPORT is allowed to be configured again. */ /* No active stream, SPORT is allowed to be configured again. */
if (!dai->active) if (!dai->active)
bf5xx_tdm.configured = 0; bf5xx_tdm->configured = 0;
} }
static int bf5xx_tdm_set_channel_map(struct snd_soc_dai *dai, static int bf5xx_tdm_set_channel_map(struct snd_soc_dai *dai,
unsigned int tx_num, unsigned int *tx_slot, unsigned int tx_num, unsigned int *tx_slot,
unsigned int rx_num, unsigned int *rx_slot) unsigned int rx_num, unsigned int *rx_slot)
{ {
struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
struct bf5xx_tdm_port *bf5xx_tdm = sport_handle->private_data;
int i; int i;
unsigned int slot; unsigned int slot;
unsigned int tx_mapped = 0, rx_mapped = 0; unsigned int tx_mapped = 0, rx_mapped = 0;
@ -189,7 +159,7 @@ static int bf5xx_tdm_set_channel_map(struct snd_soc_dai *dai,
slot = tx_slot[i]; slot = tx_slot[i];
if ((slot < BFIN_TDM_DAI_MAX_SLOTS) && if ((slot < BFIN_TDM_DAI_MAX_SLOTS) &&
(!(tx_mapped & (1 << slot)))) { (!(tx_mapped & (1 << slot)))) {
bf5xx_tdm.tx_map[i] = slot; bf5xx_tdm->tx_map[i] = slot;
tx_mapped |= 1 << slot; tx_mapped |= 1 << slot;
} else } else
return -EINVAL; return -EINVAL;
@ -198,7 +168,7 @@ static int bf5xx_tdm_set_channel_map(struct snd_soc_dai *dai,
slot = rx_slot[i]; slot = rx_slot[i];
if ((slot < BFIN_TDM_DAI_MAX_SLOTS) && if ((slot < BFIN_TDM_DAI_MAX_SLOTS) &&
(!(rx_mapped & (1 << slot)))) { (!(rx_mapped & (1 << slot)))) {
bf5xx_tdm.rx_map[i] = slot; bf5xx_tdm->rx_map[i] = slot;
rx_mapped |= 1 << slot; rx_mapped |= 1 << slot;
} else } else
return -EINVAL; return -EINVAL;
@ -212,12 +182,14 @@ static int bf5xx_tdm_suspend(struct snd_soc_dai *dai)
{ {
struct sport_device *sport = snd_soc_dai_get_drvdata(dai); struct sport_device *sport = snd_soc_dai_get_drvdata(dai);
if (!dai->active)
return 0;
if (dai->capture_active)
sport_rx_stop(sport);
if (dai->playback_active) if (dai->playback_active)
sport_tx_stop(sport); sport_tx_stop(sport);
if (dai->capture_active)
sport_rx_stop(sport);
/* isolate sync/clock pins from codec while sports resume */
peripheral_free_list(sport->pin_req);
return 0; return 0;
} }
@ -226,9 +198,6 @@ static int bf5xx_tdm_resume(struct snd_soc_dai *dai)
int ret; int ret;
struct sport_device *sport = snd_soc_dai_get_drvdata(dai); struct sport_device *sport = snd_soc_dai_get_drvdata(dai);
if (!dai->active)
return 0;
ret = sport_set_multichannel(sport, 8, 0xFF, 1); ret = sport_set_multichannel(sport, 8, 0xFF, 1);
if (ret) { if (ret) {
pr_err("SPORT is busy!\n"); pr_err("SPORT is busy!\n");
@ -247,6 +216,8 @@ static int bf5xx_tdm_resume(struct snd_soc_dai *dai)
ret = -EBUSY; ret = -EBUSY;
} }
peripheral_request_list(sport->pin_req, "soc-audio");
return 0; return 0;
} }
@ -280,20 +251,14 @@ static struct snd_soc_dai_driver bf5xx_tdm_dai = {
static int __devinit bfin_tdm_probe(struct platform_device *pdev) static int __devinit bfin_tdm_probe(struct platform_device *pdev)
{ {
int ret = 0; struct sport_device *sport_handle;
int ret;
if (peripheral_request_list(&sport_req[sport_num][0], "soc-audio")) { /* configure SPORT for TDM */
pr_err("Requesting Peripherals failed\n"); sport_handle = sport_init(pdev, 4, 8 * sizeof(u32),
return -EFAULT; sizeof(struct bf5xx_tdm_port));
} if (!sport_handle)
/* request DMA for SPORT */
sport_handle = sport_init(&sport_params[sport_num], 4, \
8 * sizeof(u32), NULL);
if (!sport_handle) {
peripheral_free_list(&sport_req[sport_num][0]);
return -ENODEV; return -ENODEV;
}
/* SPORT works in TDM mode */ /* SPORT works in TDM mode */
ret = sport_set_multichannel(sport_handle, 8, 0xFF, 1); ret = sport_set_multichannel(sport_handle, 8, 0xFF, 1);
@ -323,18 +288,19 @@ static int __devinit bfin_tdm_probe(struct platform_device *pdev)
goto sport_config_err; goto sport_config_err;
} }
sport_handle->private_data = &bf5xx_tdm;
return 0; return 0;
sport_config_err: sport_config_err:
peripheral_free_list(&sport_req[sport_num][0]); sport_done(sport_handle);
return ret; return ret;
} }
static int __devexit bfin_tdm_remove(struct platform_device *pdev) static int __devexit bfin_tdm_remove(struct platform_device *pdev)
{ {
peripheral_free_list(&sport_req[sport_num][0]); struct sport_device *sport_handle = platform_get_drvdata(pdev);
snd_soc_unregister_dai(&pdev->dev); snd_soc_unregister_dai(&pdev->dev);
sport_done(sport_handle);
return 0; return 0;
} }

View file

@ -120,7 +120,7 @@
*/ */
#define PM860X_DAPM_OUTPUT(wname, wevent) \ #define PM860X_DAPM_OUTPUT(wname, wevent) \
{ .id = snd_soc_dapm_pga, .name = wname, .reg = SND_SOC_NOPM, \ { .id = snd_soc_dapm_pga, .name = wname, .reg = SND_SOC_NOPM, \
.shift = 0, .invert = 0, .kcontrols = NULL, \ .shift = 0, .invert = 0, .kcontrol_news = NULL, \
.num_kcontrols = 0, .event = wevent, \ .num_kcontrols = 0, .event = wevent, \
.event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD, } .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD, }

View file

@ -16,10 +16,11 @@ config SND_SOC_ALL_CODECS
select SND_SOC_AD1836 if SPI_MASTER select SND_SOC_AD1836 if SPI_MASTER
select SND_SOC_AD193X if SND_SOC_I2C_AND_SPI select SND_SOC_AD193X if SND_SOC_I2C_AND_SPI
select SND_SOC_AD1980 if SND_SOC_AC97_BUS select SND_SOC_AD1980 if SND_SOC_AC97_BUS
select SND_SOC_AD73311
select SND_SOC_ADS117X select SND_SOC_ADS117X
select SND_SOC_AD73311 if I2C
select SND_SOC_AK4104 if SPI_MASTER select SND_SOC_AK4104 if SPI_MASTER
select SND_SOC_AK4535 if I2C select SND_SOC_AK4535 if I2C
select SND_SOC_AK4641 if I2C
select SND_SOC_AK4642 if I2C select SND_SOC_AK4642 if I2C
select SND_SOC_AK4671 if I2C select SND_SOC_AK4671 if I2C
select SND_SOC_ALC5623 if I2C select SND_SOC_ALC5623 if I2C
@ -33,13 +34,14 @@ config SND_SOC_ALL_CODECS
select SND_SOC_JZ4740_CODEC if SOC_JZ4740 select SND_SOC_JZ4740_CODEC if SOC_JZ4740
select SND_SOC_LM4857 if I2C select SND_SOC_LM4857 if I2C
select SND_SOC_MAX98088 if I2C select SND_SOC_MAX98088 if I2C
select SND_SOC_MAX98095 if I2C
select SND_SOC_MAX9850 if I2C select SND_SOC_MAX9850 if I2C
select SND_SOC_MAX9877 if I2C select SND_SOC_MAX9877 if I2C
select SND_SOC_PCM3008 select SND_SOC_PCM3008
select SND_SOC_SGTL5000 if I2C select SND_SOC_SGTL5000 if I2C
select SND_SOC_SN95031 if INTEL_SCU_IPC select SND_SOC_SN95031 if INTEL_SCU_IPC
select SND_SOC_SPDIF select SND_SOC_SPDIF
select SND_SOC_SSM2602 if I2C select SND_SOC_SSM2602 if SND_SOC_I2C_AND_SPI
select SND_SOC_STAC9766 if SND_SOC_AC97_BUS select SND_SOC_STAC9766 if SND_SOC_AC97_BUS
select SND_SOC_TLV320AIC23 if I2C select SND_SOC_TLV320AIC23 if I2C
select SND_SOC_TLV320AIC26 if SPI_MASTER select SND_SOC_TLV320AIC26 if SPI_MASTER
@ -52,6 +54,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_UDA134X select SND_SOC_UDA134X
select SND_SOC_UDA1380 if I2C select SND_SOC_UDA1380 if I2C
select SND_SOC_WL1273 if MFD_WL1273_CORE select SND_SOC_WL1273 if MFD_WL1273_CORE
select SND_SOC_WM1250_EV1 if I2C
select SND_SOC_WM2000 if I2C select SND_SOC_WM2000 if I2C
select SND_SOC_WM8350 if MFD_WM8350 select SND_SOC_WM8350 if MFD_WM8350
select SND_SOC_WM8400 if MFD_WM8400 select SND_SOC_WM8400 if MFD_WM8400
@ -72,6 +75,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_WM8900 if I2C select SND_SOC_WM8900 if I2C
select SND_SOC_WM8903 if I2C select SND_SOC_WM8903 if I2C
select SND_SOC_WM8904 if I2C select SND_SOC_WM8904 if I2C
select SND_SOC_WM8915 if I2C
select SND_SOC_WM8940 if I2C select SND_SOC_WM8940 if I2C
select SND_SOC_WM8955 if I2C select SND_SOC_WM8955 if I2C
select SND_SOC_WM8960 if I2C select SND_SOC_WM8960 if I2C
@ -136,6 +140,9 @@ config SND_SOC_AK4104
config SND_SOC_AK4535 config SND_SOC_AK4535
tristate tristate
config SND_SOC_AK4641
tristate
config SND_SOC_AK4642 config SND_SOC_AK4642
tristate tristate
@ -187,6 +194,9 @@ config SND_SOC_DMIC
config SND_SOC_MAX98088 config SND_SOC_MAX98088
tristate tristate
config SND_SOC_MAX98095
tristate
config SND_SOC_MAX9850 config SND_SOC_MAX9850
tristate tristate
@ -241,6 +251,9 @@ config SND_SOC_UDA1380
config SND_SOC_WL1273 config SND_SOC_WL1273
tristate tristate
config SND_SOC_WM1250_EV1
tristate
config SND_SOC_WM8350 config SND_SOC_WM8350
tristate tristate
@ -298,6 +311,9 @@ config SND_SOC_WM8903
config SND_SOC_WM8904 config SND_SOC_WM8904
tristate tristate
config SND_SOC_WM8915
tristate
config SND_SOC_WM8940 config SND_SOC_WM8940
tristate tristate

View file

@ -7,6 +7,7 @@ snd-soc-ad73311-objs := ad73311.o
snd-soc-ads117x-objs := ads117x.o snd-soc-ads117x-objs := ads117x.o
snd-soc-ak4104-objs := ak4104.o snd-soc-ak4104-objs := ak4104.o
snd-soc-ak4535-objs := ak4535.o snd-soc-ak4535-objs := ak4535.o
snd-soc-ak4641-objs := ak4641.o
snd-soc-ak4642-objs := ak4642.o snd-soc-ak4642-objs := ak4642.o
snd-soc-ak4671-objs := ak4671.o snd-soc-ak4671-objs := ak4671.o
snd-soc-cq93vc-objs := cq93vc.o snd-soc-cq93vc-objs := cq93vc.o
@ -19,6 +20,7 @@ snd-soc-dfbmcs320-objs := dfbmcs320.o
snd-soc-dmic-objs := dmic.o snd-soc-dmic-objs := dmic.o
snd-soc-l3-objs := l3.o snd-soc-l3-objs := l3.o
snd-soc-max98088-objs := max98088.o snd-soc-max98088-objs := max98088.o
snd-soc-max98095-objs := max98095.o
snd-soc-max9850-objs := max9850.o snd-soc-max9850-objs := max9850.o
snd-soc-pcm3008-objs := pcm3008.o snd-soc-pcm3008-objs := pcm3008.o
snd-soc-sgtl5000-objs := sgtl5000.o snd-soc-sgtl5000-objs := sgtl5000.o
@ -37,6 +39,7 @@ snd-soc-twl6040-objs := twl6040.o
snd-soc-uda134x-objs := uda134x.o snd-soc-uda134x-objs := uda134x.o
snd-soc-uda1380-objs := uda1380.o snd-soc-uda1380-objs := uda1380.o
snd-soc-wl1273-objs := wl1273.o snd-soc-wl1273-objs := wl1273.o
snd-soc-wm1250-ev1-objs := wm1250-ev1.o
snd-soc-wm8350-objs := wm8350.o snd-soc-wm8350-objs := wm8350.o
snd-soc-wm8400-objs := wm8400.o snd-soc-wm8400-objs := wm8400.o
snd-soc-wm8510-objs := wm8510.o snd-soc-wm8510-objs := wm8510.o
@ -56,6 +59,7 @@ snd-soc-wm8804-objs := wm8804.o
snd-soc-wm8900-objs := wm8900.o snd-soc-wm8900-objs := wm8900.o
snd-soc-wm8903-objs := wm8903.o snd-soc-wm8903-objs := wm8903.o
snd-soc-wm8904-objs := wm8904.o snd-soc-wm8904-objs := wm8904.o
snd-soc-wm8915-objs := wm8915.o
snd-soc-wm8940-objs := wm8940.o snd-soc-wm8940-objs := wm8940.o
snd-soc-wm8955-objs := wm8955.o snd-soc-wm8955-objs := wm8955.o
snd-soc-wm8960-objs := wm8960.o snd-soc-wm8960-objs := wm8960.o
@ -69,7 +73,7 @@ snd-soc-wm8988-objs := wm8988.o
snd-soc-wm8990-objs := wm8990.o snd-soc-wm8990-objs := wm8990.o
snd-soc-wm8991-objs := wm8991.o snd-soc-wm8991-objs := wm8991.o
snd-soc-wm8993-objs := wm8993.o snd-soc-wm8993-objs := wm8993.o
snd-soc-wm8994-objs := wm8994.o wm8994-tables.o snd-soc-wm8994-objs := wm8994.o wm8994-tables.o wm8958-dsp2.o
snd-soc-wm8995-objs := wm8995.o snd-soc-wm8995-objs := wm8995.o
snd-soc-wm9081-objs := wm9081.o snd-soc-wm9081-objs := wm9081.o
snd-soc-wm9705-objs := wm9705.o snd-soc-wm9705-objs := wm9705.o
@ -94,6 +98,7 @@ obj-$(CONFIG_SND_SOC_AD73311) += snd-soc-ad73311.o
obj-$(CONFIG_SND_SOC_ADS117X) += snd-soc-ads117x.o obj-$(CONFIG_SND_SOC_ADS117X) += snd-soc-ads117x.o
obj-$(CONFIG_SND_SOC_AK4104) += snd-soc-ak4104.o obj-$(CONFIG_SND_SOC_AK4104) += snd-soc-ak4104.o
obj-$(CONFIG_SND_SOC_AK4535) += snd-soc-ak4535.o obj-$(CONFIG_SND_SOC_AK4535) += snd-soc-ak4535.o
obj-$(CONFIG_SND_SOC_AK4641) += snd-soc-ak4641.o
obj-$(CONFIG_SND_SOC_AK4642) += snd-soc-ak4642.o obj-$(CONFIG_SND_SOC_AK4642) += snd-soc-ak4642.o
obj-$(CONFIG_SND_SOC_AK4671) += snd-soc-ak4671.o obj-$(CONFIG_SND_SOC_AK4671) += snd-soc-ak4671.o
obj-$(CONFIG_SND_SOC_ALC5623) += snd-soc-alc5623.o obj-$(CONFIG_SND_SOC_ALC5623) += snd-soc-alc5623.o
@ -108,6 +113,7 @@ obj-$(CONFIG_SND_SOC_DMIC) += snd-soc-dmic.o
obj-$(CONFIG_SND_SOC_L3) += snd-soc-l3.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_JZ4740_CODEC) += snd-soc-jz4740-codec.o
obj-$(CONFIG_SND_SOC_MAX98088) += snd-soc-max98088.o obj-$(CONFIG_SND_SOC_MAX98088) += snd-soc-max98088.o
obj-$(CONFIG_SND_SOC_MAX98095) += snd-soc-max98095.o
obj-$(CONFIG_SND_SOC_MAX9850) += snd-soc-max9850.o obj-$(CONFIG_SND_SOC_MAX9850) += snd-soc-max9850.o
obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o
obj-$(CONFIG_SND_SOC_SGTL5000) += snd-soc-sgtl5000.o obj-$(CONFIG_SND_SOC_SGTL5000) += snd-soc-sgtl5000.o
@ -125,6 +131,7 @@ obj-$(CONFIG_SND_SOC_TWL6040) += snd-soc-twl6040.o
obj-$(CONFIG_SND_SOC_UDA134X) += snd-soc-uda134x.o obj-$(CONFIG_SND_SOC_UDA134X) += snd-soc-uda134x.o
obj-$(CONFIG_SND_SOC_UDA1380) += snd-soc-uda1380.o obj-$(CONFIG_SND_SOC_UDA1380) += snd-soc-uda1380.o
obj-$(CONFIG_SND_SOC_WL1273) += snd-soc-wl1273.o obj-$(CONFIG_SND_SOC_WL1273) += snd-soc-wl1273.o
obj-$(CONFIG_SND_SOC_WM1250_EV1) += snd-soc-wm1250-ev1.o
obj-$(CONFIG_SND_SOC_WM8350) += snd-soc-wm8350.o obj-$(CONFIG_SND_SOC_WM8350) += snd-soc-wm8350.o
obj-$(CONFIG_SND_SOC_WM8400) += snd-soc-wm8400.o obj-$(CONFIG_SND_SOC_WM8400) += snd-soc-wm8400.o
obj-$(CONFIG_SND_SOC_WM8510) += snd-soc-wm8510.o obj-$(CONFIG_SND_SOC_WM8510) += snd-soc-wm8510.o
@ -144,6 +151,7 @@ obj-$(CONFIG_SND_SOC_WM8804) += snd-soc-wm8804.o
obj-$(CONFIG_SND_SOC_WM8900) += snd-soc-wm8900.o obj-$(CONFIG_SND_SOC_WM8900) += snd-soc-wm8900.o
obj-$(CONFIG_SND_SOC_WM8903) += snd-soc-wm8903.o obj-$(CONFIG_SND_SOC_WM8903) += snd-soc-wm8903.o
obj-$(CONFIG_SND_SOC_WM8904) += snd-soc-wm8904.o obj-$(CONFIG_SND_SOC_WM8904) += snd-soc-wm8904.o
obj-$(CONFIG_SND_SOC_WM8915) += snd-soc-wm8915.o
obj-$(CONFIG_SND_SOC_WM8940) += snd-soc-wm8940.o obj-$(CONFIG_SND_SOC_WM8940) += snd-soc-wm8940.o
obj-$(CONFIG_SND_SOC_WM8955) += snd-soc-wm8955.o obj-$(CONFIG_SND_SOC_WM8955) += snd-soc-wm8955.o
obj-$(CONFIG_SND_SOC_WM8960) += snd-soc-wm8960.o obj-$(CONFIG_SND_SOC_WM8960) += snd-soc-wm8960.o

View file

@ -23,8 +23,7 @@
/* codec private data */ /* codec private data */
struct ad193x_priv { struct ad193x_priv {
enum snd_soc_control_type bus_type; enum snd_soc_control_type control_type;
void *control_data;
int sysclk; int sysclk;
}; };
@ -354,14 +353,12 @@ static int ad193x_probe(struct snd_soc_codec *codec)
struct snd_soc_dapm_context *dapm = &codec->dapm; struct snd_soc_dapm_context *dapm = &codec->dapm;
int ret; int ret;
codec->control_data = ad193x->control_data; if (ad193x->control_type == SND_SOC_I2C)
if (ad193x->bus_type == SND_SOC_I2C) ret = snd_soc_codec_set_cache_io(codec, 8, 8, ad193x->control_type);
ret = snd_soc_codec_set_cache_io(codec, 8, 8, ad193x->bus_type);
else else
ret = snd_soc_codec_set_cache_io(codec, 16, 8, ad193x->bus_type); ret = snd_soc_codec_set_cache_io(codec, 16, 8, ad193x->control_type);
if (ret < 0) { if (ret < 0) {
dev_err(codec->dev, "failed to set cache I/O: %d\n", dev_err(codec->dev, "failed to set cache I/O: %d\n", ret);
ret);
return ret; return ret;
} }
@ -408,8 +405,7 @@ static int __devinit ad193x_spi_probe(struct spi_device *spi)
return -ENOMEM; return -ENOMEM;
spi_set_drvdata(spi, ad193x); spi_set_drvdata(spi, ad193x);
ad193x->control_data = spi; ad193x->control_type = SND_SOC_SPI;
ad193x->bus_type = SND_SOC_SPI;
ret = snd_soc_register_codec(&spi->dev, ret = snd_soc_register_codec(&spi->dev,
&soc_codec_dev_ad193x, &ad193x_dai, 1); &soc_codec_dev_ad193x, &ad193x_dai, 1);
@ -427,7 +423,7 @@ static int __devexit ad193x_spi_remove(struct spi_device *spi)
static struct spi_driver ad193x_spi_driver = { static struct spi_driver ad193x_spi_driver = {
.driver = { .driver = {
.name = "ad193x-codec", .name = "ad193x",
.owner = THIS_MODULE, .owner = THIS_MODULE,
}, },
.probe = ad193x_spi_probe, .probe = ad193x_spi_probe,
@ -454,8 +450,7 @@ static int __devinit ad193x_i2c_probe(struct i2c_client *client,
return -ENOMEM; return -ENOMEM;
i2c_set_clientdata(client, ad193x); i2c_set_clientdata(client, ad193x);
ad193x->control_data = client; ad193x->control_type = SND_SOC_I2C;
ad193x->bus_type = SND_SOC_I2C;
ret = snd_soc_register_codec(&client->dev, ret = snd_soc_register_codec(&client->dev,
&soc_codec_dev_ad193x, &ad193x_dai, 1); &soc_codec_dev_ad193x, &ad193x_dai, 1);
@ -473,7 +468,7 @@ static int __devexit ad193x_i2c_remove(struct i2c_client *client)
static struct i2c_driver ad193x_i2c_driver = { static struct i2c_driver ad193x_i2c_driver = {
.driver = { .driver = {
.name = "ad193x-codec", .name = "ad193x",
}, },
.probe = ad193x_i2c_probe, .probe = ad193x_i2c_probe,
.remove = __devexit_p(ad193x_i2c_remove), .remove = __devexit_p(ad193x_i2c_remove),

View file

@ -266,7 +266,7 @@ static int __devexit ad1980_remove(struct platform_device *pdev)
static struct platform_driver ad1980_codec_driver = { static struct platform_driver ad1980_codec_driver = {
.driver = { .driver = {
.name = "ad1980-codec", .name = "ad1980",
.owner = THIS_MODULE, .owner = THIS_MODULE,
}, },

View file

@ -55,7 +55,7 @@ static int __devexit ad73311_remove(struct platform_device *pdev)
static struct platform_driver ad73311_codec_driver = { static struct platform_driver ad73311_codec_driver = {
.driver = { .driver = {
.name = "ad73311-codec", .name = "ad73311",
.owner = THIS_MODULE, .owner = THIS_MODULE,
}, },

View file

@ -230,7 +230,7 @@ static const struct snd_soc_dapm_widget ak4535_dapm_widgets[] = {
SND_SOC_DAPM_INPUT("AIN"), SND_SOC_DAPM_INPUT("AIN"),
}; };
static const struct snd_soc_dapm_route audio_map[] = { static const struct snd_soc_dapm_route ak4535_audio_map[] = {
/*stereo mixer */ /*stereo mixer */
{"Stereo Mixer", "Playback Switch", "DAC"}, {"Stereo Mixer", "Playback Switch", "DAC"},
{"Stereo Mixer", "Mic Sidetone Switch", "Mic"}, {"Stereo Mixer", "Mic Sidetone Switch", "Mic"},
@ -287,17 +287,6 @@ static const struct snd_soc_dapm_route audio_map[] = {
{"Input Mixer", "Aux Capture Switch", "Aux In"}, {"Input Mixer", "Aux Capture Switch", "Aux In"},
}; };
static int ak4535_add_widgets(struct snd_soc_codec *codec)
{
struct snd_soc_dapm_context *dapm = &codec->dapm;
snd_soc_dapm_new_controls(dapm, ak4535_dapm_widgets,
ARRAY_SIZE(ak4535_dapm_widgets));
snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map));
return 0;
}
static int ak4535_set_dai_sysclk(struct snd_soc_dai *codec_dai, static int ak4535_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir) int clk_id, unsigned int freq, int dir)
{ {
@ -457,8 +446,6 @@ static int ak4535_probe(struct snd_soc_codec *codec)
snd_soc_add_controls(codec, ak4535_snd_controls, snd_soc_add_controls(codec, ak4535_snd_controls,
ARRAY_SIZE(ak4535_snd_controls)); ARRAY_SIZE(ak4535_snd_controls));
ak4535_add_widgets(codec);
return 0; return 0;
} }
@ -480,6 +467,10 @@ static struct snd_soc_codec_driver soc_codec_dev_ak4535 = {
.reg_cache_size = ARRAY_SIZE(ak4535_reg), .reg_cache_size = ARRAY_SIZE(ak4535_reg),
.reg_word_size = sizeof(u8), .reg_word_size = sizeof(u8),
.reg_cache_default = ak4535_reg, .reg_cache_default = ak4535_reg,
.dapm_widgets = ak4535_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(ak4535_dapm_widgets),
.dapm_routes = ak4535_audio_map,
.num_dapm_routes = ARRAY_SIZE(ak4535_audio_map),
}; };
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)

664
sound/soc/codecs/ak4641.c Normal file
View file

@ -0,0 +1,664 @@
/*
* ak4641.c -- AK4641 ALSA Soc Audio driver
*
* Copyright (C) 2008 Harald Welte <laforge@gnufiish.org>
* Copyright (C) 2011 Dmitry Artamonow <mad_soft@inbox.ru>
*
* Based on ak4535.c by Richard Purdie
*
* 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.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/pm.h>
#include <linux/i2c.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/initval.h>
#include <sound/tlv.h>
#include <sound/ak4641.h>
#include "ak4641.h"
/* codec private data */
struct ak4641_priv {
struct snd_soc_codec *codec;
unsigned int sysclk;
int deemph;
int playback_fs;
};
/*
* ak4641 register cache
*/
static const u8 ak4641_reg[AK4641_CACHEREGNUM] = {
0x00, 0x80, 0x00, 0x80,
0x02, 0x00, 0x11, 0x05,
0x00, 0x00, 0x36, 0x10,
0x00, 0x00, 0x57, 0x00,
0x88, 0x88, 0x08, 0x08
};
static const int deemph_settings[] = {44100, 0, 48000, 32000};
static int ak4641_set_deemph(struct snd_soc_codec *codec)
{
struct ak4641_priv *ak4641 = snd_soc_codec_get_drvdata(codec);
int i, best = 0;
for (i = 0 ; i < ARRAY_SIZE(deemph_settings); i++) {
/* if deemphasis is on, select the nearest available rate */
if (ak4641->deemph && deemph_settings[i] != 0 &&
abs(deemph_settings[i] - ak4641->playback_fs) <
abs(deemph_settings[best] - ak4641->playback_fs))
best = i;
if (!ak4641->deemph && deemph_settings[i] == 0)
best = i;
}
dev_dbg(codec->dev, "Set deemphasis %d\n", best);
return snd_soc_update_bits(codec, AK4641_DAC, 0x3, best);
}
static int ak4641_put_deemph(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
struct ak4641_priv *ak4641 = snd_soc_codec_get_drvdata(codec);
int deemph = ucontrol->value.enumerated.item[0];
if (deemph > 1)
return -EINVAL;
ak4641->deemph = deemph;
return ak4641_set_deemph(codec);
}
static int ak4641_get_deemph(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
struct ak4641_priv *ak4641 = snd_soc_codec_get_drvdata(codec);
ucontrol->value.enumerated.item[0] = ak4641->deemph;
return 0;
};
static const char *ak4641_mono_out[] = {"(L + R)/2", "Hi-Z"};
static const char *ak4641_hp_out[] = {"Stereo", "Mono"};
static const char *ak4641_mic_select[] = {"Internal", "External"};
static const char *ak4641_mic_or_dac[] = {"Microphone", "Voice DAC"};
static const DECLARE_TLV_DB_SCALE(mono_gain_tlv, -1700, 2300, 0);
static const DECLARE_TLV_DB_SCALE(mic_boost_tlv, 0, 2000, 0);
static const DECLARE_TLV_DB_SCALE(eq_tlv, -1050, 150, 0);
static const DECLARE_TLV_DB_SCALE(master_tlv, -12750, 50, 0);
static const DECLARE_TLV_DB_SCALE(mic_stereo_sidetone_tlv, -2700, 300, 0);
static const DECLARE_TLV_DB_SCALE(mic_mono_sidetone_tlv, -400, 400, 0);
static const DECLARE_TLV_DB_SCALE(capture_tlv, -800, 50, 0);
static const DECLARE_TLV_DB_SCALE(alc_tlv, -800, 50, 0);
static const DECLARE_TLV_DB_SCALE(aux_in_tlv, -2100, 300, 0);
static const struct soc_enum ak4641_mono_out_enum =
SOC_ENUM_SINGLE(AK4641_SIG1, 6, 2, ak4641_mono_out);
static const struct soc_enum ak4641_hp_out_enum =
SOC_ENUM_SINGLE(AK4641_MODE2, 2, 2, ak4641_hp_out);
static const struct soc_enum ak4641_mic_select_enum =
SOC_ENUM_SINGLE(AK4641_MIC, 1, 2, ak4641_mic_select);
static const struct soc_enum ak4641_mic_or_dac_enum =
SOC_ENUM_SINGLE(AK4641_BTIF, 4, 2, ak4641_mic_or_dac);
static const struct snd_kcontrol_new ak4641_snd_controls[] = {
SOC_ENUM("Mono 1 Output", ak4641_mono_out_enum),
SOC_SINGLE_TLV("Mono 1 Gain Volume", AK4641_SIG1, 7, 1, 1,
mono_gain_tlv),
SOC_ENUM("Headphone Output", ak4641_hp_out_enum),
SOC_SINGLE_BOOL_EXT("Playback Deemphasis Switch", 0,
ak4641_get_deemph, ak4641_put_deemph),
SOC_SINGLE_TLV("Mic Boost Volume", AK4641_MIC, 0, 1, 0, mic_boost_tlv),
SOC_SINGLE("ALC Operation Time", AK4641_TIMER, 0, 3, 0),
SOC_SINGLE("ALC Recovery Time", AK4641_TIMER, 2, 3, 0),
SOC_SINGLE("ALC ZC Time", AK4641_TIMER, 4, 3, 0),
SOC_SINGLE("ALC 1 Switch", AK4641_ALC1, 5, 1, 0),
SOC_SINGLE_TLV("ALC Volume", AK4641_ALC2, 0, 71, 0, alc_tlv),
SOC_SINGLE("Left Out Enable Switch", AK4641_SIG2, 1, 1, 0),
SOC_SINGLE("Right Out Enable Switch", AK4641_SIG2, 0, 1, 0),
SOC_SINGLE_TLV("Capture Volume", AK4641_PGA, 0, 71, 0, capture_tlv),
SOC_DOUBLE_R_TLV("Master Playback Volume", AK4641_LATT,
AK4641_RATT, 0, 255, 1, master_tlv),
SOC_SINGLE_TLV("AUX In Volume", AK4641_VOL, 0, 15, 0, aux_in_tlv),
SOC_SINGLE("Equalizer Switch", AK4641_DAC, 2, 1, 0),
SOC_SINGLE_TLV("EQ1 100 Hz Volume", AK4641_EQLO, 0, 15, 1, eq_tlv),
SOC_SINGLE_TLV("EQ2 250 Hz Volume", AK4641_EQLO, 4, 15, 1, eq_tlv),
SOC_SINGLE_TLV("EQ3 1 kHz Volume", AK4641_EQMID, 0, 15, 1, eq_tlv),
SOC_SINGLE_TLV("EQ4 3.5 kHz Volume", AK4641_EQMID, 4, 15, 1, eq_tlv),
SOC_SINGLE_TLV("EQ5 10 kHz Volume", AK4641_EQHI, 0, 15, 1, eq_tlv),
};
/* Mono 1 Mixer */
static const struct snd_kcontrol_new ak4641_mono1_mixer_controls[] = {
SOC_DAPM_SINGLE_TLV("Mic Mono Sidetone Volume", AK4641_VOL, 7, 1, 0,
mic_mono_sidetone_tlv),
SOC_DAPM_SINGLE("Mic Mono Sidetone Switch", AK4641_SIG1, 4, 1, 0),
SOC_DAPM_SINGLE("Mono Playback Switch", AK4641_SIG1, 5, 1, 0),
};
/* Stereo Mixer */
static const struct snd_kcontrol_new ak4641_stereo_mixer_controls[] = {
SOC_DAPM_SINGLE_TLV("Mic Sidetone Volume", AK4641_VOL, 4, 7, 0,
mic_stereo_sidetone_tlv),
SOC_DAPM_SINGLE("Mic Sidetone Switch", AK4641_SIG2, 4, 1, 0),
SOC_DAPM_SINGLE("Playback Switch", AK4641_SIG2, 7, 1, 0),
SOC_DAPM_SINGLE("Aux Bypass Switch", AK4641_SIG2, 5, 1, 0),
};
/* Input Mixer */
static const struct snd_kcontrol_new ak4641_input_mixer_controls[] = {
SOC_DAPM_SINGLE("Mic Capture Switch", AK4641_MIC, 2, 1, 0),
SOC_DAPM_SINGLE("Aux Capture Switch", AK4641_MIC, 5, 1, 0),
};
/* Mic mux */
static const struct snd_kcontrol_new ak4641_mic_mux_control =
SOC_DAPM_ENUM("Mic Select", ak4641_mic_select_enum);
/* Input mux */
static const struct snd_kcontrol_new ak4641_input_mux_control =
SOC_DAPM_ENUM("Input Select", ak4641_mic_or_dac_enum);
/* mono 2 switch */
static const struct snd_kcontrol_new ak4641_mono2_control =
SOC_DAPM_SINGLE("Switch", AK4641_SIG1, 0, 1, 0);
/* ak4641 dapm widgets */
static const struct snd_soc_dapm_widget ak4641_dapm_widgets[] = {
SND_SOC_DAPM_MIXER("Stereo Mixer", SND_SOC_NOPM, 0, 0,
&ak4641_stereo_mixer_controls[0],
ARRAY_SIZE(ak4641_stereo_mixer_controls)),
SND_SOC_DAPM_MIXER("Mono1 Mixer", SND_SOC_NOPM, 0, 0,
&ak4641_mono1_mixer_controls[0],
ARRAY_SIZE(ak4641_mono1_mixer_controls)),
SND_SOC_DAPM_MIXER("Input Mixer", SND_SOC_NOPM, 0, 0,
&ak4641_input_mixer_controls[0],
ARRAY_SIZE(ak4641_input_mixer_controls)),
SND_SOC_DAPM_MUX("Mic Mux", SND_SOC_NOPM, 0, 0,
&ak4641_mic_mux_control),
SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0,
&ak4641_input_mux_control),
SND_SOC_DAPM_SWITCH("Mono 2 Enable", SND_SOC_NOPM, 0, 0,
&ak4641_mono2_control),
SND_SOC_DAPM_OUTPUT("LOUT"),
SND_SOC_DAPM_OUTPUT("ROUT"),
SND_SOC_DAPM_OUTPUT("MOUT1"),
SND_SOC_DAPM_OUTPUT("MOUT2"),
SND_SOC_DAPM_OUTPUT("MICOUT"),
SND_SOC_DAPM_ADC("ADC", "HiFi Capture", AK4641_PM1, 0, 0),
SND_SOC_DAPM_PGA("Mic", AK4641_PM1, 1, 0, NULL, 0),
SND_SOC_DAPM_PGA("AUX In", AK4641_PM1, 2, 0, NULL, 0),
SND_SOC_DAPM_PGA("Mono Out", AK4641_PM1, 3, 0, NULL, 0),
SND_SOC_DAPM_PGA("Line Out", AK4641_PM1, 4, 0, NULL, 0),
SND_SOC_DAPM_DAC("DAC", "HiFi Playback", AK4641_PM2, 0, 0),
SND_SOC_DAPM_PGA("Mono Out 2", AK4641_PM2, 3, 0, NULL, 0),
SND_SOC_DAPM_ADC("Voice ADC", "Voice Capture", AK4641_BTIF, 0, 0),
SND_SOC_DAPM_ADC("Voice DAC", "Voice Playback", AK4641_BTIF, 1, 0),
SND_SOC_DAPM_MICBIAS("Mic Int Bias", AK4641_MIC, 3, 0),
SND_SOC_DAPM_MICBIAS("Mic Ext Bias", AK4641_MIC, 4, 0),
SND_SOC_DAPM_INPUT("MICIN"),
SND_SOC_DAPM_INPUT("MICEXT"),
SND_SOC_DAPM_INPUT("AUX"),
SND_SOC_DAPM_INPUT("AIN"),
};
static const struct snd_soc_dapm_route ak4641_audio_map[] = {
/* Stereo Mixer */
{"Stereo Mixer", "Playback Switch", "DAC"},
{"Stereo Mixer", "Mic Sidetone Switch", "Input Mux"},
{"Stereo Mixer", "Aux Bypass Switch", "AUX In"},
/* Mono 1 Mixer */
{"Mono1 Mixer", "Mic Mono Sidetone Switch", "Input Mux"},
{"Mono1 Mixer", "Mono Playback Switch", "DAC"},
/* Mic */
{"Mic", NULL, "AIN"},
{"Mic Mux", "Internal", "Mic Int Bias"},
{"Mic Mux", "External", "Mic Ext Bias"},
{"Mic Int Bias", NULL, "MICIN"},
{"Mic Ext Bias", NULL, "MICEXT"},
{"MICOUT", NULL, "Mic Mux"},
/* Input Mux */
{"Input Mux", "Microphone", "Mic"},
{"Input Mux", "Voice DAC", "Voice DAC"},
/* Line Out */
{"LOUT", NULL, "Line Out"},
{"ROUT", NULL, "Line Out"},
{"Line Out", NULL, "Stereo Mixer"},
/* Mono 1 Out */
{"MOUT1", NULL, "Mono Out"},
{"Mono Out", NULL, "Mono1 Mixer"},
/* Mono 2 Out */
{"MOUT2", NULL, "Mono 2 Enable"},
{"Mono 2 Enable", "Switch", "Mono Out 2"},
{"Mono Out 2", NULL, "Stereo Mixer"},
{"Voice ADC", NULL, "Mono 2 Enable"},
/* Aux In */
{"AUX In", NULL, "AUX"},
/* ADC */
{"ADC", NULL, "Input Mixer"},
{"Input Mixer", "Mic Capture Switch", "Mic"},
{"Input Mixer", "Aux Capture Switch", "AUX In"},
};
static int ak4641_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 ak4641_priv *ak4641 = snd_soc_codec_get_drvdata(codec);
ak4641->sysclk = freq;
return 0;
}
static int ak4641_i2s_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 ak4641_priv *ak4641 = snd_soc_codec_get_drvdata(codec);
int rate = params_rate(params), fs = 256;
u8 mode2;
if (rate)
fs = ak4641->sysclk / rate;
else
return -EINVAL;
/* set fs */
switch (fs) {
case 1024:
mode2 = (0x2 << 5);
break;
case 512:
mode2 = (0x1 << 5);
break;
case 256:
mode2 = (0x0 << 5);
break;
default:
dev_err(codec->dev, "Error: unsupported fs=%d\n", fs);
return -EINVAL;
}
snd_soc_update_bits(codec, AK4641_MODE2, (0x3 << 5), mode2);
/* Update de-emphasis filter for the new rate */
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
ak4641->playback_fs = rate;
ak4641_set_deemph(codec);
};
return 0;
}
static int ak4641_pcm_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
struct snd_soc_codec *codec = codec_dai->codec;
u8 btif;
/* interface format */
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
btif = (0x3 << 5);
break;
case SND_SOC_DAIFMT_LEFT_J:
btif = (0x2 << 5);
break;
case SND_SOC_DAIFMT_DSP_A: /* MSB after FRM */
btif = (0x0 << 5);
break;
case SND_SOC_DAIFMT_DSP_B: /* MSB during FRM */
btif = (0x1 << 5);
break;
default:
return -EINVAL;
}
return snd_soc_update_bits(codec, AK4641_BTIF, (0x3 << 5), btif);
}
static int ak4641_i2s_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
struct snd_soc_codec *codec = codec_dai->codec;
u8 mode1 = 0;
/* interface format */
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
mode1 = 0x02;
break;
case SND_SOC_DAIFMT_LEFT_J:
mode1 = 0x01;
break;
default:
return -EINVAL;
}
return snd_soc_write(codec, AK4641_MODE1, mode1);
}
static int ak4641_mute(struct snd_soc_dai *dai, int mute)
{
struct snd_soc_codec *codec = dai->codec;
return snd_soc_update_bits(codec, AK4641_DAC, 0x20, mute ? 0x20 : 0);
}
static int ak4641_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
struct ak4641_platform_data *pdata = codec->dev->platform_data;
int ret;
switch (level) {
case SND_SOC_BIAS_ON:
/* unmute */
snd_soc_update_bits(codec, AK4641_DAC, 0x20, 0);
break;
case SND_SOC_BIAS_PREPARE:
/* mute */
snd_soc_update_bits(codec, AK4641_DAC, 0x20, 0x20);
break;
case SND_SOC_BIAS_STANDBY:
if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
if (pdata && gpio_is_valid(pdata->gpio_power))
gpio_set_value(pdata->gpio_power, 1);
mdelay(1);
if (pdata && gpio_is_valid(pdata->gpio_npdn))
gpio_set_value(pdata->gpio_npdn, 1);
mdelay(1);
ret = snd_soc_cache_sync(codec);
if (ret) {
dev_err(codec->dev,
"Failed to sync cache: %d\n", ret);
return ret;
}
}
snd_soc_update_bits(codec, AK4641_PM1, 0x80, 0x80);
snd_soc_update_bits(codec, AK4641_PM2, 0x80, 0);
break;
case SND_SOC_BIAS_OFF:
snd_soc_update_bits(codec, AK4641_PM1, 0x80, 0);
if (pdata && gpio_is_valid(pdata->gpio_npdn))
gpio_set_value(pdata->gpio_npdn, 0);
if (pdata && gpio_is_valid(pdata->gpio_power))
gpio_set_value(pdata->gpio_power, 0);
codec->cache_sync = 1;
break;
}
codec->dapm.bias_level = level;
return 0;
}
#define AK4641_RATES (SNDRV_PCM_RATE_8000_48000)
#define AK4641_RATES_BT (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
SNDRV_PCM_RATE_16000)
#define AK4641_FORMATS (SNDRV_PCM_FMTBIT_S16_LE)
static struct snd_soc_dai_ops ak4641_i2s_dai_ops = {
.hw_params = ak4641_i2s_hw_params,
.set_fmt = ak4641_i2s_set_dai_fmt,
.digital_mute = ak4641_mute,
.set_sysclk = ak4641_set_dai_sysclk,
};
static struct snd_soc_dai_ops ak4641_pcm_dai_ops = {
.hw_params = NULL, /* rates are controlled by BT chip */
.set_fmt = ak4641_pcm_set_dai_fmt,
.digital_mute = ak4641_mute,
.set_sysclk = ak4641_set_dai_sysclk,
};
struct snd_soc_dai_driver ak4641_dai[] = {
{
.name = "ak4641-hifi",
.id = 1,
.playback = {
.stream_name = "HiFi Playback",
.channels_min = 1,
.channels_max = 2,
.rates = AK4641_RATES,
.formats = AK4641_FORMATS,
},
.capture = {
.stream_name = "HiFi Capture",
.channels_min = 1,
.channels_max = 2,
.rates = AK4641_RATES,
.formats = AK4641_FORMATS,
},
.ops = &ak4641_i2s_dai_ops,
.symmetric_rates = 1,
},
{
.name = "ak4641-voice",
.id = 1,
.playback = {
.stream_name = "Voice Playback",
.channels_min = 1,
.channels_max = 1,
.rates = AK4641_RATES_BT,
.formats = AK4641_FORMATS,
},
.capture = {
.stream_name = "Voice Capture",
.channels_min = 1,
.channels_max = 1,
.rates = AK4641_RATES_BT,
.formats = AK4641_FORMATS,
},
.ops = &ak4641_pcm_dai_ops,
.symmetric_rates = 1,
},
};
static int ak4641_suspend(struct snd_soc_codec *codec, pm_message_t state)
{
ak4641_set_bias_level(codec, SND_SOC_BIAS_OFF);
return 0;
}
static int ak4641_resume(struct snd_soc_codec *codec)
{
ak4641_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
return 0;
}
static int ak4641_probe(struct snd_soc_codec *codec)
{
struct ak4641_platform_data *pdata = codec->dev->platform_data;
int ret;
if (pdata) {
if (gpio_is_valid(pdata->gpio_power)) {
ret = gpio_request_one(pdata->gpio_power,
GPIOF_OUT_INIT_LOW, "ak4641 power");
if (ret)
goto err_out;
}
if (gpio_is_valid(pdata->gpio_npdn)) {
ret = gpio_request_one(pdata->gpio_npdn,
GPIOF_OUT_INIT_LOW, "ak4641 npdn");
if (ret)
goto err_gpio;
udelay(1); /* > 150 ns */
gpio_set_value(pdata->gpio_npdn, 1);
}
}
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);
goto err_register;
}
/* power on device */
ak4641_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
return 0;
err_register:
if (pdata) {
if (gpio_is_valid(pdata->gpio_power))
gpio_set_value(pdata->gpio_power, 0);
if (gpio_is_valid(pdata->gpio_npdn))
gpio_free(pdata->gpio_npdn);
}
err_gpio:
if (pdata && gpio_is_valid(pdata->gpio_power))
gpio_free(pdata->gpio_power);
err_out:
return ret;
}
static int ak4641_remove(struct snd_soc_codec *codec)
{
struct ak4641_platform_data *pdata = codec->dev->platform_data;
ak4641_set_bias_level(codec, SND_SOC_BIAS_OFF);
if (pdata) {
if (gpio_is_valid(pdata->gpio_power)) {
gpio_set_value(pdata->gpio_power, 0);
gpio_free(pdata->gpio_power);
}
if (gpio_is_valid(pdata->gpio_npdn))
gpio_free(pdata->gpio_npdn);
}
return 0;
}
static struct snd_soc_codec_driver soc_codec_dev_ak4641 = {
.probe = ak4641_probe,
.remove = ak4641_remove,
.suspend = ak4641_suspend,
.resume = ak4641_resume,
.controls = ak4641_snd_controls,
.num_controls = ARRAY_SIZE(ak4641_snd_controls),
.dapm_widgets = ak4641_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(ak4641_dapm_widgets),
.dapm_routes = ak4641_audio_map,
.num_dapm_routes = ARRAY_SIZE(ak4641_audio_map),
.set_bias_level = ak4641_set_bias_level,
.reg_cache_size = ARRAY_SIZE(ak4641_reg),
.reg_word_size = sizeof(u8),
.reg_cache_default = ak4641_reg,
.reg_cache_step = 1,
};
static int __devinit ak4641_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct ak4641_priv *ak4641;
int ret;
ak4641 = kzalloc(sizeof(struct ak4641_priv), GFP_KERNEL);
if (!ak4641)
return -ENOMEM;
i2c_set_clientdata(i2c, ak4641);
ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_ak4641,
ak4641_dai, ARRAY_SIZE(ak4641_dai));
if (ret < 0)
kfree(ak4641);
return ret;
}
static int __devexit ak4641_i2c_remove(struct i2c_client *i2c)
{
snd_soc_unregister_codec(&i2c->dev);
kfree(i2c_get_clientdata(i2c));
return 0;
}
static const struct i2c_device_id ak4641_i2c_id[] = {
{ "ak4641", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, ak4641_i2c_id);
static struct i2c_driver ak4641_i2c_driver = {
.driver = {
.name = "ak4641",
.owner = THIS_MODULE,
},
.probe = ak4641_i2c_probe,
.remove = __devexit_p(ak4641_i2c_remove),
.id_table = ak4641_i2c_id,
};
static int __init ak4641_modinit(void)
{
int ret;
ret = i2c_add_driver(&ak4641_i2c_driver);
if (ret != 0)
pr_err("Failed to register AK4641 I2C driver: %d\n", ret);
return ret;
}
module_init(ak4641_modinit);
static void __exit ak4641_exit(void)
{
i2c_del_driver(&ak4641_i2c_driver);
}
module_exit(ak4641_exit);
MODULE_DESCRIPTION("SoC AK4641 driver");
MODULE_AUTHOR("Harald Welte <laforge@gnufiish.org>");
MODULE_LICENSE("GPL");

47
sound/soc/codecs/ak4641.h Normal file
View file

@ -0,0 +1,47 @@
/*
* ak4641.h -- AK4641 SoC Audio driver
*
* Copyright 2008 Harald Welte <laforge@gnufiish.org>
*
* Based on ak4535.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 _AK4641_H
#define _AK4641_H
/* AK4641 register space */
#define AK4641_PM1 0x00
#define AK4641_PM2 0x01
#define AK4641_SIG1 0x02
#define AK4641_SIG2 0x03
#define AK4641_MODE1 0x04
#define AK4641_MODE2 0x05
#define AK4641_DAC 0x06
#define AK4641_MIC 0x07
#define AK4641_TIMER 0x08
#define AK4641_ALC1 0x09
#define AK4641_ALC2 0x0a
#define AK4641_PGA 0x0b
#define AK4641_LATT 0x0c
#define AK4641_RATT 0x0d
#define AK4641_VOL 0x0e
#define AK4641_STATUS 0x0f
#define AK4641_EQLO 0x10
#define AK4641_EQMID 0x11
#define AK4641_EQHI 0x12
#define AK4641_BTIF 0x13
#define AK4641_CACHEREGNUM 0x14
#define AK4641_DAI_HIFI 0
#define AK4641_DAI_VOICE 1
#endif

View file

@ -352,7 +352,7 @@ static const struct snd_soc_dapm_widget ak4671_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY("PMPLL", AK4671_PLL_MODE_SELECT1, 0, 0, NULL, 0), SND_SOC_DAPM_SUPPLY("PMPLL", AK4671_PLL_MODE_SELECT1, 0, 0, NULL, 0),
}; };
static const struct snd_soc_dapm_route intercon[] = { static const struct snd_soc_dapm_route ak4671_intercon[] = {
{"DAC Left", "NULL", "PMPLL"}, {"DAC Left", "NULL", "PMPLL"},
{"DAC Right", "NULL", "PMPLL"}, {"DAC Right", "NULL", "PMPLL"},
{"ADC Left", "NULL", "PMPLL"}, {"ADC Left", "NULL", "PMPLL"},
@ -433,17 +433,6 @@ static const struct snd_soc_dapm_route intercon[] = {
{"ROUT3 Mixer", "RINS4", "RIN4 Mixing Circuit"}, {"ROUT3 Mixer", "RINS4", "RIN4 Mixing Circuit"},
}; };
static int ak4671_add_widgets(struct snd_soc_codec *codec)
{
struct snd_soc_dapm_context *dapm = &codec->dapm;
snd_soc_dapm_new_controls(dapm, ak4671_dapm_widgets,
ARRAY_SIZE(ak4671_dapm_widgets));
snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon));
return 0;
}
static int ak4671_hw_params(struct snd_pcm_substream *substream, static int ak4671_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai) struct snd_soc_dai *dai)
@ -650,7 +639,6 @@ static int ak4671_probe(struct snd_soc_codec *codec)
snd_soc_add_controls(codec, ak4671_snd_controls, snd_soc_add_controls(codec, ak4671_snd_controls,
ARRAY_SIZE(ak4671_snd_controls)); ARRAY_SIZE(ak4671_snd_controls));
ak4671_add_widgets(codec);
ak4671_set_bias_level(codec, SND_SOC_BIAS_STANDBY); ak4671_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
@ -670,6 +658,10 @@ static struct snd_soc_codec_driver soc_codec_dev_ak4671 = {
.reg_cache_size = AK4671_CACHEREGNUM, .reg_cache_size = AK4671_CACHEREGNUM,
.reg_word_size = sizeof(u8), .reg_word_size = sizeof(u8),
.reg_cache_default = ak4671_reg, .reg_cache_default = ak4671_reg,
.dapm_widgets = ak4671_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(ak4671_dapm_widgets),
.dapm_routes = ak4671_intercon,
.num_dapm_routes = ARRAY_SIZE(ak4671_intercon),
}; };
static int __devinit ak4671_i2c_probe(struct i2c_client *client, static int __devinit ak4671_i2c_probe(struct i2c_client *client,

View file

@ -86,18 +86,6 @@ static const struct snd_soc_dapm_route cx20442_audio_map[] = {
{"ADC", NULL, "Input Mixer"}, {"ADC", NULL, "Input Mixer"},
}; };
static int cx20442_add_widgets(struct snd_soc_codec *codec)
{
struct snd_soc_dapm_context *dapm = &codec->dapm;
snd_soc_dapm_new_controls(dapm, cx20442_dapm_widgets,
ARRAY_SIZE(cx20442_dapm_widgets));
snd_soc_dapm_add_routes(dapm, cx20442_audio_map,
ARRAY_SIZE(cx20442_audio_map));
return 0;
}
static unsigned int cx20442_read_reg_cache(struct snd_soc_codec *codec, static unsigned int cx20442_read_reg_cache(struct snd_soc_codec *codec,
unsigned int reg) unsigned int reg)
{ {
@ -344,8 +332,6 @@ static int cx20442_codec_probe(struct snd_soc_codec *codec)
return -ENOMEM; return -ENOMEM;
snd_soc_codec_set_drvdata(codec, cx20442); snd_soc_codec_set_drvdata(codec, cx20442);
cx20442_add_widgets(codec);
cx20442->control_data = NULL; cx20442->control_data = NULL;
codec->hw_write = NULL; codec->hw_write = NULL;
codec->card->pop_time = 0; codec->card->pop_time = 0;
@ -377,6 +363,10 @@ static struct snd_soc_codec_driver cx20442_codec_dev = {
.reg_word_size = sizeof(u8), .reg_word_size = sizeof(u8),
.read = cx20442_read_reg_cache, .read = cx20442_read_reg_cache,
.write = cx20442_write, .write = cx20442_write,
.dapm_widgets = cx20442_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(cx20442_dapm_widgets),
.dapm_routes = cx20442_audio_map,
.num_dapm_routes = ARRAY_SIZE(cx20442_audio_map),
}; };
static int cx20442_platform_probe(struct platform_device *pdev) static int cx20442_platform_probe(struct platform_device *pdev)

View file

@ -39,7 +39,31 @@ static struct snd_soc_dai_driver dmic_dai = {
}, },
}; };
static struct snd_soc_codec_driver soc_dmic = {}; static const struct snd_soc_dapm_widget dmic_dapm_widgets[] = {
SND_SOC_DAPM_AIF_OUT("DMIC AIF", "Capture", 0,
SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_INPUT("DMic"),
};
static const struct snd_soc_dapm_route intercon[] = {
{"DMIC AIF", NULL, "DMic"},
};
static int dmic_probe(struct snd_soc_codec *codec)
{
struct snd_soc_dapm_context *dapm = &codec->dapm;
snd_soc_dapm_new_controls(dapm, dmic_dapm_widgets,
ARRAY_SIZE(dmic_dapm_widgets));
snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon));
snd_soc_dapm_new_widgets(dapm);
return 0;
}
static struct snd_soc_codec_driver soc_dmic = {
.probe = dmic_probe,
};
static int __devinit dmic_dev_probe(struct platform_device *pdev) static int __devinit dmic_dev_probe(struct platform_device *pdev)
{ {

View file

@ -294,20 +294,9 @@ static int jz4740_codec_set_bias_level(struct snd_soc_codec *codec,
static int jz4740_codec_dev_probe(struct snd_soc_codec *codec) static int jz4740_codec_dev_probe(struct snd_soc_codec *codec)
{ {
struct snd_soc_dapm_context *dapm = &codec->dapm;
snd_soc_update_bits(codec, JZ4740_REG_CODEC_1, snd_soc_update_bits(codec, JZ4740_REG_CODEC_1,
JZ4740_CODEC_1_SW2_ENABLE, JZ4740_CODEC_1_SW2_ENABLE); JZ4740_CODEC_1_SW2_ENABLE, JZ4740_CODEC_1_SW2_ENABLE);
snd_soc_add_controls(codec, jz4740_codec_controls,
ARRAY_SIZE(jz4740_codec_controls));
snd_soc_dapm_new_controls(dapm, jz4740_codec_dapm_widgets,
ARRAY_SIZE(jz4740_codec_dapm_widgets));
snd_soc_dapm_add_routes(dapm, jz4740_codec_dapm_routes,
ARRAY_SIZE(jz4740_codec_dapm_routes));
jz4740_codec_set_bias_level(codec, SND_SOC_BIAS_STANDBY); jz4740_codec_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
return 0; return 0;
@ -348,6 +337,13 @@ static struct snd_soc_codec_driver soc_codec_dev_jz4740_codec = {
.reg_cache_default = jz4740_codec_regs, .reg_cache_default = jz4740_codec_regs,
.reg_word_size = sizeof(u32), .reg_word_size = sizeof(u32),
.reg_cache_size = 2, .reg_cache_size = 2,
.controls = jz4740_codec_controls,
.num_controls = ARRAY_SIZE(jz4740_codec_controls),
.dapm_widgets = jz4740_codec_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(jz4740_codec_dapm_widgets),
.dapm_routes = jz4740_codec_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(jz4740_codec_dapm_routes),
}; };
static int __devinit jz4740_codec_probe(struct platform_device *pdev) static int __devinit jz4740_codec_probe(struct platform_device *pdev)

View file

@ -656,8 +656,6 @@ static const struct soc_enum max98088_exmode_enum =
ARRAY_SIZE(max98088_exmode_texts), ARRAY_SIZE(max98088_exmode_texts),
max98088_exmode_texts, max98088_exmode_texts,
max98088_exmode_values); max98088_exmode_values);
static const struct snd_kcontrol_new max98088_exmode_controls =
SOC_DAPM_VALUE_ENUM("Route", max98088_exmode_enum);
static const char *max98088_ex_thresh[] = { /* volts PP */ static const char *max98088_ex_thresh[] = { /* volts PP */
"0.6", "1.2", "1.8", "2.4", "3.0", "3.6", "4.2", "4.8"}; "0.6", "1.2", "1.8", "2.4", "3.0", "3.6", "4.2", "4.8"};
@ -783,6 +781,7 @@ static const struct snd_kcontrol_new max98088_snd_controls[] = {
SOC_SINGLE("EQ1 Switch", M98088_REG_49_CFG_LEVEL, 0, 1, 0), SOC_SINGLE("EQ1 Switch", M98088_REG_49_CFG_LEVEL, 0, 1, 0),
SOC_SINGLE("EQ2 Switch", M98088_REG_49_CFG_LEVEL, 1, 1, 0), SOC_SINGLE("EQ2 Switch", M98088_REG_49_CFG_LEVEL, 1, 1, 0),
SOC_ENUM("EX Limiter Mode", max98088_exmode_enum),
SOC_ENUM("EX Limiter Threshold", max98088_ex_thresh_enum), SOC_ENUM("EX Limiter Threshold", max98088_ex_thresh_enum),
SOC_ENUM("DAI1 Filter Mode", max98088_filter_mode_enum), SOC_ENUM("DAI1 Filter Mode", max98088_filter_mode_enum),
@ -808,10 +807,10 @@ static const struct snd_kcontrol_new max98088_snd_controls[] = {
/* Left speaker mixer switch */ /* Left speaker mixer switch */
static const struct snd_kcontrol_new max98088_left_speaker_mixer_controls[] = { static const struct snd_kcontrol_new max98088_left_speaker_mixer_controls[] = {
SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 7, 1, 0), SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 0, 1, 0),
SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 0, 1, 0), SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 7, 1, 0),
SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_2B_MIX_SPK_LEFT, 7, 1, 0), SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_2B_MIX_SPK_LEFT, 0, 1, 0),
SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_2B_MIX_SPK_LEFT, 0, 1, 0), SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_2B_MIX_SPK_LEFT, 7, 1, 0),
SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 5, 1, 0), SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 5, 1, 0),
SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_2B_MIX_SPK_LEFT, 6, 1, 0), SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_2B_MIX_SPK_LEFT, 6, 1, 0),
SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 1, 1, 0), SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 1, 1, 0),
@ -836,10 +835,10 @@ static const struct snd_kcontrol_new max98088_right_speaker_mixer_controls[] = {
/* Left headphone mixer switch */ /* Left headphone mixer switch */
static const struct snd_kcontrol_new max98088_left_hp_mixer_controls[] = { static const struct snd_kcontrol_new max98088_left_hp_mixer_controls[] = {
SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_25_MIX_HP_LEFT, 7, 1, 0), SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_25_MIX_HP_LEFT, 0, 1, 0),
SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_25_MIX_HP_LEFT, 0, 1, 0), SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_25_MIX_HP_LEFT, 7, 1, 0),
SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_25_MIX_HP_LEFT, 7, 1, 0), SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_25_MIX_HP_LEFT, 0, 1, 0),
SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_25_MIX_HP_LEFT, 0, 1, 0), SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_25_MIX_HP_LEFT, 7, 1, 0),
SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_25_MIX_HP_LEFT, 5, 1, 0), SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_25_MIX_HP_LEFT, 5, 1, 0),
SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_25_MIX_HP_LEFT, 6, 1, 0), SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_25_MIX_HP_LEFT, 6, 1, 0),
SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_25_MIX_HP_LEFT, 1, 1, 0), SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_25_MIX_HP_LEFT, 1, 1, 0),
@ -864,10 +863,10 @@ static const struct snd_kcontrol_new max98088_right_hp_mixer_controls[] = {
/* Left earpiece/receiver mixer switch */ /* Left earpiece/receiver mixer switch */
static const struct snd_kcontrol_new max98088_left_rec_mixer_controls[] = { static const struct snd_kcontrol_new max98088_left_rec_mixer_controls[] = {
SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_28_MIX_REC_LEFT, 7, 1, 0), SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_28_MIX_REC_LEFT, 0, 1, 0),
SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_28_MIX_REC_LEFT, 0, 1, 0), SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_28_MIX_REC_LEFT, 7, 1, 0),
SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_28_MIX_REC_LEFT, 7, 1, 0), SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_28_MIX_REC_LEFT, 0, 1, 0),
SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_28_MIX_REC_LEFT, 0, 1, 0), SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_28_MIX_REC_LEFT, 7, 1, 0),
SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_28_MIX_REC_LEFT, 5, 1, 0), SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_28_MIX_REC_LEFT, 5, 1, 0),
SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_28_MIX_REC_LEFT, 6, 1, 0), SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_28_MIX_REC_LEFT, 6, 1, 0),
SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_28_MIX_REC_LEFT, 1, 1, 0), SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_28_MIX_REC_LEFT, 1, 1, 0),
@ -1094,9 +1093,6 @@ static const struct snd_soc_dapm_widget max98088_dapm_widgets[] = {
SND_SOC_DAPM_MICBIAS("MICBIAS", M98088_REG_4C_PWR_EN_IN, 3, 0), SND_SOC_DAPM_MICBIAS("MICBIAS", M98088_REG_4C_PWR_EN_IN, 3, 0),
SND_SOC_DAPM_MUX("EX Limiter Mode", SND_SOC_NOPM, 0, 0,
&max98088_exmode_controls),
SND_SOC_DAPM_OUTPUT("HPL"), SND_SOC_DAPM_OUTPUT("HPL"),
SND_SOC_DAPM_OUTPUT("HPR"), SND_SOC_DAPM_OUTPUT("HPR"),
SND_SOC_DAPM_OUTPUT("SPKL"), SND_SOC_DAPM_OUTPUT("SPKL"),
@ -1112,7 +1108,7 @@ static const struct snd_soc_dapm_widget max98088_dapm_widgets[] = {
SND_SOC_DAPM_INPUT("INB2"), SND_SOC_DAPM_INPUT("INB2"),
}; };
static const struct snd_soc_dapm_route audio_map[] = { static const struct snd_soc_dapm_route max98088_audio_map[] = {
/* Left headphone output mixer */ /* Left headphone output mixer */
{"Left HP Mixer", "Left DAC1 Switch", "DACL1"}, {"Left HP Mixer", "Left DAC1 Switch", "DACL1"},
{"Left HP Mixer", "Left DAC2 Switch", "DACL2"}, {"Left HP Mixer", "Left DAC2 Switch", "DACL2"},
@ -1226,22 +1222,6 @@ static const struct snd_soc_dapm_route audio_map[] = {
{"MIC2 Input", NULL, "MIC2"}, {"MIC2 Input", NULL, "MIC2"},
}; };
static int max98088_add_widgets(struct snd_soc_codec *codec)
{
struct snd_soc_dapm_context *dapm = &codec->dapm;
snd_soc_dapm_new_controls(dapm, max98088_dapm_widgets,
ARRAY_SIZE(max98088_dapm_widgets));
snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map));
snd_soc_add_controls(codec, max98088_snd_controls,
ARRAY_SIZE(max98088_snd_controls));
snd_soc_dapm_new_widgets(dapm);
return 0;
}
/* codec mclk clock divider coefficients */ /* codec mclk clock divider coefficients */
static const struct { static const struct {
u32 rate; u32 rate;
@ -1586,6 +1566,36 @@ static int max98088_dai2_set_fmt(struct snd_soc_dai *codec_dai,
return 0; return 0;
} }
static int max98088_dai1_digital_mute(struct snd_soc_dai *codec_dai, int mute)
{
struct snd_soc_codec *codec = codec_dai->codec;
int reg;
if (mute)
reg = M98088_DAI_MUTE;
else
reg = 0;
snd_soc_update_bits(codec, M98088_REG_2F_LVL_DAI1_PLAY,
M98088_DAI_MUTE_MASK, reg);
return 0;
}
static int max98088_dai2_digital_mute(struct snd_soc_dai *codec_dai, int mute)
{
struct snd_soc_codec *codec = codec_dai->codec;
int reg;
if (mute)
reg = M98088_DAI_MUTE;
else
reg = 0;
snd_soc_update_bits(codec, M98088_REG_31_LVL_DAI2_PLAY,
M98088_DAI_MUTE_MASK, reg);
return 0;
}
static void max98088_sync_cache(struct snd_soc_codec *codec) static void max98088_sync_cache(struct snd_soc_codec *codec)
{ {
u16 *reg_cache = codec->reg_cache; u16 *reg_cache = codec->reg_cache;
@ -1647,12 +1657,14 @@ static struct snd_soc_dai_ops max98088_dai1_ops = {
.set_sysclk = max98088_dai_set_sysclk, .set_sysclk = max98088_dai_set_sysclk,
.set_fmt = max98088_dai1_set_fmt, .set_fmt = max98088_dai1_set_fmt,
.hw_params = max98088_dai1_hw_params, .hw_params = max98088_dai1_hw_params,
.digital_mute = max98088_dai1_digital_mute,
}; };
static struct snd_soc_dai_ops max98088_dai2_ops = { static struct snd_soc_dai_ops max98088_dai2_ops = {
.set_sysclk = max98088_dai_set_sysclk, .set_sysclk = max98088_dai_set_sysclk,
.set_fmt = max98088_dai2_set_fmt, .set_fmt = max98088_dai2_set_fmt,
.hw_params = max98088_dai2_hw_params, .hw_params = max98088_dai2_hw_params,
.digital_mute = max98088_dai2_digital_mute,
}; };
static struct snd_soc_dai_driver max98088_dai[] = { static struct snd_soc_dai_driver max98088_dai[] = {
@ -2010,7 +2022,8 @@ static int max98088_probe(struct snd_soc_codec *codec)
max98088_handle_pdata(codec); max98088_handle_pdata(codec);
max98088_add_widgets(codec); snd_soc_add_controls(codec, max98088_snd_controls,
ARRAY_SIZE(max98088_snd_controls));
err_access: err_access:
return ret; return ret;
@ -2036,6 +2049,10 @@ static struct snd_soc_codec_driver soc_codec_dev_max98088 = {
.reg_word_size = sizeof(u8), .reg_word_size = sizeof(u8),
.reg_cache_default = max98088_reg, .reg_cache_default = max98088_reg,
.volatile_register = max98088_volatile_register, .volatile_register = max98088_volatile_register,
.dapm_widgets = max98088_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(max98088_dapm_widgets),
.dapm_routes = max98088_audio_map,
.num_dapm_routes = ARRAY_SIZE(max98088_audio_map),
}; };
static int max98088_i2c_probe(struct i2c_client *i2c, static int max98088_i2c_probe(struct i2c_client *i2c,

View file

@ -133,6 +133,19 @@
#define M98088_REC_LINEMODE (1<<7) #define M98088_REC_LINEMODE (1<<7)
#define M98088_REC_LINEMODE_MASK (1<<7) #define M98088_REC_LINEMODE_MASK (1<<7)
/* M98088_REG_2D_MIX_SPK_CNTL */
#define M98088_MIX_SPKR_GAIN_MASK (3<<2)
#define M98088_MIX_SPKR_GAIN_SHIFT 2
#define M98088_MIX_SPKL_GAIN_MASK (3<<0)
#define M98088_MIX_SPKL_GAIN_SHIFT 0
/* M98088_REG_2F_LVL_DAI1_PLAY, M98088_REG_31_LVL_DAI2_PLAY */
#define M98088_DAI_MUTE (1<<7)
#define M98088_DAI_MUTE_MASK (1<<7)
#define M98088_DAI_VOICE_GAIN_MASK (3<<4)
#define M98088_DAI_ATTENUATION_MASK (0xF<<0)
#define M98088_DAI_ATTENUATION_SHIFT 0
/* M98088_REG_35_LVL_MIC1, M98088_REG_36_LVL_MIC2 */ /* M98088_REG_35_LVL_MIC1, M98088_REG_36_LVL_MIC2 */
#define M98088_MICPRE_MASK (3<<5) #define M98088_MICPRE_MASK (3<<5)
#define M98088_MICPRE_SHIFT 5 #define M98088_MICPRE_SHIFT 5

2396
sound/soc/codecs/max98095.c Normal file

File diff suppressed because it is too large Load diff

299
sound/soc/codecs/max98095.h Normal file
View file

@ -0,0 +1,299 @@
/*
* max98095.h -- MAX98095 ALSA SoC Audio driver
*
* Copyright 2011 Maxim Integrated Products
*
* 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 _MAX98095_H
#define _MAX98095_H
/*
* MAX98095 Registers Definition
*/
#define M98095_000_HOST_DATA 0x00
#define M98095_001_HOST_INT_STS 0x01
#define M98095_002_HOST_RSP_STS 0x02
#define M98095_003_HOST_CMD_STS 0x03
#define M98095_004_CODEC_STS 0x04
#define M98095_005_DAI1_ALC_STS 0x05
#define M98095_006_DAI2_ALC_STS 0x06
#define M98095_007_JACK_AUTO_STS 0x07
#define M98095_008_JACK_MANUAL_STS 0x08
#define M98095_009_JACK_VBAT_STS 0x09
#define M98095_00A_ACC_ADC_STS 0x0A
#define M98095_00B_MIC_NG_AGC_STS 0x0B
#define M98095_00C_SPK_L_VOLT_STS 0x0C
#define M98095_00D_SPK_R_VOLT_STS 0x0D
#define M98095_00E_TEMP_SENSOR_STS 0x0E
#define M98095_00F_HOST_CFG 0x0F
#define M98095_010_HOST_INT_CFG 0x10
#define M98095_011_HOST_INT_EN 0x11
#define M98095_012_CODEC_INT_EN 0x12
#define M98095_013_JACK_INT_EN 0x13
#define M98095_014_JACK_INT_EN 0x14
#define M98095_015_DEC 0x15
#define M98095_016_RESERVED 0x16
#define M98095_017_RESERVED 0x17
#define M98095_018_KEYCODE3 0x18
#define M98095_019_KEYCODE2 0x19
#define M98095_01A_KEYCODE1 0x1A
#define M98095_01B_KEYCODE0 0x1B
#define M98095_01C_OEMCODE1 0x1C
#define M98095_01D_OEMCODE0 0x1D
#define M98095_01E_XCFG1 0x1E
#define M98095_01F_XCFG2 0x1F
#define M98095_020_XCFG3 0x20
#define M98095_021_XCFG4 0x21
#define M98095_022_XCFG5 0x22
#define M98095_023_XCFG6 0x23
#define M98095_024_XGPIO 0x24
#define M98095_025_XCLKCFG 0x25
#define M98095_026_SYS_CLK 0x26
#define M98095_027_DAI1_CLKMODE 0x27
#define M98095_028_DAI1_CLKCFG_HI 0x28
#define M98095_029_DAI1_CLKCFG_LO 0x29
#define M98095_02A_DAI1_FORMAT 0x2A
#define M98095_02B_DAI1_CLOCK 0x2B
#define M98095_02C_DAI1_IOCFG 0x2C
#define M98095_02D_DAI1_TDM 0x2D
#define M98095_02E_DAI1_FILTERS 0x2E
#define M98095_02F_DAI1_LVL1 0x2F
#define M98095_030_DAI1_LVL2 0x30
#define M98095_031_DAI2_CLKMODE 0x31
#define M98095_032_DAI2_CLKCFG_HI 0x32
#define M98095_033_DAI2_CLKCFG_LO 0x33
#define M98095_034_DAI2_FORMAT 0x34
#define M98095_035_DAI2_CLOCK 0x35
#define M98095_036_DAI2_IOCFG 0x36
#define M98095_037_DAI2_TDM 0x37
#define M98095_038_DAI2_FILTERS 0x38
#define M98095_039_DAI2_LVL1 0x39
#define M98095_03A_DAI2_LVL2 0x3A
#define M98095_03B_DAI3_CLKMODE 0x3B
#define M98095_03C_DAI3_CLKCFG_HI 0x3C
#define M98095_03D_DAI3_CLKCFG_LO 0x3D
#define M98095_03E_DAI3_FORMAT 0x3E
#define M98095_03F_DAI3_CLOCK 0x3F
#define M98095_040_DAI3_IOCFG 0x40
#define M98095_041_DAI3_TDM 0x41
#define M98095_042_DAI3_FILTERS 0x42
#define M98095_043_DAI3_LVL1 0x43
#define M98095_044_DAI3_LVL2 0x44
#define M98095_045_CFG_DSP 0x45
#define M98095_046_DAC_CTRL1 0x46
#define M98095_047_DAC_CTRL2 0x47
#define M98095_048_MIX_DAC_LR 0x48
#define M98095_049_MIX_DAC_M 0x49
#define M98095_04A_MIX_ADC_LEFT 0x4A
#define M98095_04B_MIX_ADC_RIGHT 0x4B
#define M98095_04C_MIX_HP_LEFT 0x4C
#define M98095_04D_MIX_HP_RIGHT 0x4D
#define M98095_04E_CFG_HP 0x4E
#define M98095_04F_MIX_RCV 0x4F
#define M98095_050_MIX_SPK_LEFT 0x50
#define M98095_051_MIX_SPK_RIGHT 0x51
#define M98095_052_MIX_SPK_CFG 0x52
#define M98095_053_MIX_LINEOUT1 0x53
#define M98095_054_MIX_LINEOUT2 0x54
#define M98095_055_MIX_LINEOUT_CFG 0x55
#define M98095_056_LVL_SIDETONE_DAI12 0x56
#define M98095_057_LVL_SIDETONE_DAI3 0x57
#define M98095_058_LVL_DAI1_PLAY 0x58
#define M98095_059_LVL_DAI1_EQ 0x59
#define M98095_05A_LVL_DAI2_PLAY 0x5A
#define M98095_05B_LVL_DAI2_EQ 0x5B
#define M98095_05C_LVL_DAI3_PLAY 0x5C
#define M98095_05D_LVL_ADC_L 0x5D
#define M98095_05E_LVL_ADC_R 0x5E
#define M98095_05F_LVL_MIC1 0x5F
#define M98095_060_LVL_MIC2 0x60
#define M98095_061_LVL_LINEIN 0x61
#define M98095_062_LVL_LINEOUT1 0x62
#define M98095_063_LVL_LINEOUT2 0x63
#define M98095_064_LVL_HP_L 0x64
#define M98095_065_LVL_HP_R 0x65
#define M98095_066_LVL_RCV 0x66
#define M98095_067_LVL_SPK_L 0x67
#define M98095_068_LVL_SPK_R 0x68
#define M98095_069_MICAGC_CFG 0x69
#define M98095_06A_MICAGC_THRESH 0x6A
#define M98095_06B_SPK_NOISEGATE 0x6B
#define M98095_06C_DAI1_ALC1_TIME 0x6C
#define M98095_06D_DAI1_ALC1_COMP 0x6D
#define M98095_06E_DAI1_ALC1_EXPN 0x6E
#define M98095_06F_DAI1_ALC1_GAIN 0x6F
#define M98095_070_DAI1_ALC2_TIME 0x70
#define M98095_071_DAI1_ALC2_COMP 0x71
#define M98095_072_DAI1_ALC2_EXPN 0x72
#define M98095_073_DAI1_ALC2_GAIN 0x73
#define M98095_074_DAI1_ALC3_TIME 0x74
#define M98095_075_DAI1_ALC3_COMP 0x75
#define M98095_076_DAI1_ALC3_EXPN 0x76
#define M98095_077_DAI1_ALC3_GAIN 0x77
#define M98095_078_DAI2_ALC1_TIME 0x78
#define M98095_079_DAI2_ALC1_COMP 0x79
#define M98095_07A_DAI2_ALC1_EXPN 0x7A
#define M98095_07B_DAI2_ALC1_GAIN 0x7B
#define M98095_07C_DAI2_ALC2_TIME 0x7C
#define M98095_07D_DAI2_ALC2_COMP 0x7D
#define M98095_07E_DAI2_ALC2_EXPN 0x7E
#define M98095_07F_DAI2_ALC2_GAIN 0x7F
#define M98095_080_DAI2_ALC3_TIME 0x80
#define M98095_081_DAI2_ALC3_COMP 0x81
#define M98095_082_DAI2_ALC3_EXPN 0x82
#define M98095_083_DAI2_ALC3_GAIN 0x83
#define M98095_084_HP_NOISE_GATE 0x84
#define M98095_085_AUX_ADC 0x85
#define M98095_086_CFG_LINE 0x86
#define M98095_087_CFG_MIC 0x87
#define M98095_088_CFG_LEVEL 0x88
#define M98095_089_JACK_DET_AUTO 0x89
#define M98095_08A_JACK_DET_MANUAL 0x8A
#define M98095_08B_JACK_KEYSCAN_DBC 0x8B
#define M98095_08C_JACK_KEYSCAN_DLY 0x8C
#define M98095_08D_JACK_KEY_THRESH 0x8D
#define M98095_08E_JACK_DC_SLEW 0x8E
#define M98095_08F_JACK_TEST_CFG 0x8F
#define M98095_090_PWR_EN_IN 0x90
#define M98095_091_PWR_EN_OUT 0x91
#define M98095_092_PWR_EN_OUT 0x92
#define M98095_093_BIAS_CTRL 0x93
#define M98095_094_PWR_DAC_21 0x94
#define M98095_095_PWR_DAC_03 0x95
#define M98095_096_PWR_DAC_CK 0x96
#define M98095_097_PWR_SYS 0x97
#define M98095_0FF_REV_ID 0xFF
#define M98095_REG_CNT (0xFF+1)
#define M98095_REG_MAX_CACHED 0X97
/* MAX98095 Registers Bit Fields */
/* M98095_00F_HOST_CFG */
#define M98095_SEG (1<<0)
#define M98095_XTEN (1<<1)
#define M98095_MDLLEN (1<<2)
/* M98095_027_DAI1_CLKMODE, M98095_031_DAI2_CLKMODE, M98095_03B_DAI3_CLKMODE */
#define M98095_CLKMODE_MASK 0xFF
/* M98095_02A_DAI1_FORMAT, M98095_034_DAI2_FORMAT, M98095_03E_DAI3_FORMAT */
#define M98095_DAI_MAS (1<<7)
#define M98095_DAI_WCI (1<<6)
#define M98095_DAI_BCI (1<<5)
#define M98095_DAI_DLY (1<<4)
#define M98095_DAI_TDM (1<<2)
#define M98095_DAI_FSW (1<<1)
#define M98095_DAI_WS (1<<0)
/* M98095_02B_DAI1_CLOCK, M98095_035_DAI2_CLOCK, M98095_03F_DAI3_CLOCK */
#define M98095_DAI_BSEL64 (1<<0)
#define M98095_DAI_DOSR_DIV2 (0<<5)
#define M98095_DAI_DOSR_DIV4 (1<<5)
/* M98095_02C_DAI1_IOCFG, M98095_036_DAI2_IOCFG, M98095_040_DAI3_IOCFG */
#define M98095_S1NORMAL (1<<6)
#define M98095_S2NORMAL (2<<6)
#define M98095_S3NORMAL (3<<6)
#define M98095_SDATA (3<<0)
/* M98095_02E_DAI1_FILTERS, M98095_038_DAI2_FILTERS, M98095_042_DAI3_FILTERS */
#define M98095_DAI_DHF (1<<3)
/* M98095_045_DSP_CFG */
#define M98095_DSPNORMAL (5<<4)
/* M98095_048_MIX_DAC_LR */
#define M98095_DAI1L_TO_DACR (1<<7)
#define M98095_DAI1R_TO_DACR (1<<6)
#define M98095_DAI2M_TO_DACR (1<<5)
#define M98095_DAI1L_TO_DACL (1<<3)
#define M98095_DAI1R_TO_DACL (1<<2)
#define M98095_DAI2M_TO_DACL (1<<1)
#define M98095_DAI3M_TO_DACL (1<<0)
/* M98095_049_MIX_DAC_M */
#define M98095_DAI1L_TO_DACM (1<<3)
#define M98095_DAI1R_TO_DACM (1<<2)
#define M98095_DAI2M_TO_DACM (1<<1)
#define M98095_DAI3M_TO_DACM (1<<0)
/* M98095_04E_MIX_HP_CFG */
#define M98095_HPNORMAL (3<<4)
/* M98095_05F_LVL_MIC1, M98095_060_LVL_MIC2 */
#define M98095_MICPRE_MASK (3<<5)
#define M98095_MICPRE_SHIFT 5
/* M98095_064_LVL_HP_L, M98095_065_LVL_HP_R */
#define M98095_HP_MUTE (1<<7)
/* M98095_066_LVL_RCV */
#define M98095_REC_MUTE (1<<7)
/* M98095_067_LVL_SPK_L, M98095_068_LVL_SPK_R */
#define M98095_SP_MUTE (1<<7)
/* M98095_087_CFG_MIC */
#define M98095_MICSEL_MASK (3<<0)
#define M98095_DIGMIC_L (1<<2)
#define M98095_DIGMIC_R (1<<3)
#define M98095_DIGMIC2L (1<<4)
#define M98095_DIGMIC2R (1<<5)
/* M98095_088_CFG_LEVEL */
#define M98095_VSEN (1<<6)
#define M98095_ZDEN (1<<5)
#define M98095_BQ2EN (1<<3)
#define M98095_BQ1EN (1<<2)
#define M98095_EQ2EN (1<<1)
#define M98095_EQ1EN (1<<0)
/* M98095_090_PWR_EN_IN */
#define M98095_INEN (1<<7)
#define M98095_MB2EN (1<<3)
#define M98095_MB1EN (1<<2)
#define M98095_MBEN (3<<2)
#define M98095_ADREN (1<<1)
#define M98095_ADLEN (1<<0)
/* M98095_091_PWR_EN_OUT */
#define M98095_HPLEN (1<<7)
#define M98095_HPREN (1<<6)
#define M98095_SPLEN (1<<5)
#define M98095_SPREN (1<<4)
#define M98095_RECEN (1<<3)
#define M98095_DALEN (1<<1)
#define M98095_DAREN (1<<0)
/* M98095_092_PWR_EN_OUT */
#define M98095_SPK_FIXEDSPECTRUM (0<<4)
#define M98095_SPK_SPREADSPECTRUM (1<<4)
/* M98095_097_PWR_SYS */
#define M98095_SHDNRUN (1<<7)
#define M98095_PERFMODE (1<<3)
#define M98095_HPPLYBACK (1<<2)
#define M98095_PWRSV8K (1<<1)
#define M98095_PWRSV (1<<0)
#define M98095_COEFS_PER_BAND 5
#define M98095_BYTE1(w) ((w >> 8) & 0xff)
#define M98095_BYTE0(w) (w & 0xff)
/* Equalizer filter coefficients */
#define M98095_110_DAI1_EQ_BASE 0x10
#define M98095_142_DAI2_EQ_BASE 0x42
/* Biquad filter coefficients */
#define M98095_174_DAI1_BQ_BASE 0x74
#define M98095_17E_DAI2_BQ_BASE 0x7E
#endif

View file

@ -827,8 +827,6 @@ EXPORT_SYMBOL_GPL(sn95031_jack_detection);
/* codec registration */ /* codec registration */
static int sn95031_codec_probe(struct snd_soc_codec *codec) static int sn95031_codec_probe(struct snd_soc_codec *codec)
{ {
int ret;
pr_debug("codec_probe called\n"); pr_debug("codec_probe called\n");
codec->dapm.bias_level = SND_SOC_BIAS_OFF; codec->dapm.bias_level = SND_SOC_BIAS_OFF;
@ -879,16 +877,7 @@ static int sn95031_codec_probe(struct snd_soc_codec *codec)
snd_soc_add_controls(codec, sn95031_snd_controls, snd_soc_add_controls(codec, sn95031_snd_controls,
ARRAY_SIZE(sn95031_snd_controls)); ARRAY_SIZE(sn95031_snd_controls));
ret = snd_soc_dapm_new_controls(&codec->dapm, sn95031_dapm_widgets, return 0;
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) static int sn95031_codec_remove(struct snd_soc_codec *codec)
@ -905,6 +894,10 @@ struct snd_soc_codec_driver sn95031_codec = {
.read = sn95031_read, .read = sn95031_read,
.write = sn95031_write, .write = sn95031_write,
.set_bias_level = sn95031_set_vaud_bias, .set_bias_level = sn95031_set_vaud_bias,
.dapm_widgets = sn95031_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(sn95031_dapm_widgets),
.dapm_routes = sn95031_audio_map,
.num_dapm_routes = ARRAY_SIZE(sn95031_audio_map),
}; };
static int __devinit sn95031_device_probe(struct platform_device *pdev) static int __devinit sn95031_device_probe(struct platform_device *pdev)

View file

@ -21,7 +21,7 @@
#include <sound/pcm.h> #include <sound/pcm.h>
#include <sound/initval.h> #include <sound/initval.h>
MODULE_LICENSE("GPL"); #define DRV_NAME "spdif-dit"
#define STUB_RATES SNDRV_PCM_RATE_8000_96000 #define STUB_RATES SNDRV_PCM_RATE_8000_96000
#define STUB_FORMATS SNDRV_PCM_FMTBIT_S16_LE #define STUB_FORMATS SNDRV_PCM_FMTBIT_S16_LE
@ -56,7 +56,7 @@ static struct platform_driver spdif_dit_driver = {
.probe = spdif_dit_probe, .probe = spdif_dit_probe,
.remove = spdif_dit_remove, .remove = spdif_dit_remove,
.driver = { .driver = {
.name = "spdif-dit", .name = DRV_NAME,
.owner = THIS_MODULE, .owner = THIS_MODULE,
}, },
}; };
@ -74,3 +74,7 @@ static void __exit dit_exit(void)
module_init(dit_modinit); module_init(dit_modinit);
module_exit(dit_exit); module_exit(dit_exit);
MODULE_AUTHOR("Steve Chen <schen@mvista.com>");
MODULE_DESCRIPTION("SPDIF dummy codec driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:" DRV_NAME);

View file

@ -32,6 +32,7 @@
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/pm.h> #include <linux/pm.h>
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/spi/spi.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <sound/core.h> #include <sound/core.h>
@ -39,18 +40,25 @@
#include <sound/pcm_params.h> #include <sound/pcm_params.h>
#include <sound/soc.h> #include <sound/soc.h>
#include <sound/initval.h> #include <sound/initval.h>
#include <sound/tlv.h>
#include "ssm2602.h" #include "ssm2602.h"
#define SSM2602_VERSION "0.1" #define SSM2602_VERSION "0.1"
enum ssm2602_type {
SSM2602,
SSM2604,
};
/* codec private data */ /* codec private data */
struct ssm2602_priv { struct ssm2602_priv {
unsigned int sysclk; unsigned int sysclk;
enum snd_soc_control_type control_type; enum snd_soc_control_type control_type;
void *control_data;
struct snd_pcm_substream *master_substream; struct snd_pcm_substream *master_substream;
struct snd_pcm_substream *slave_substream; struct snd_pcm_substream *slave_substream;
enum ssm2602_type type;
}; };
/* /*
@ -60,60 +68,12 @@ struct ssm2602_priv {
* There is no point in caching the reset register * There is no point in caching the reset register
*/ */
static const u16 ssm2602_reg[SSM2602_CACHEREGNUM] = { static const u16 ssm2602_reg[SSM2602_CACHEREGNUM] = {
0x0017, 0x0017, 0x0079, 0x0079, 0x0097, 0x0097, 0x0079, 0x0079,
0x0000, 0x0000, 0x0000, 0x000a, 0x000a, 0x0008, 0x009f, 0x000a,
0x0000, 0x0000 0x0000, 0x0000
}; };
/* #define ssm2602_reset(c) snd_soc_write(c, SSM2602_RESET, 0)
* read ssm2602 register cache
*/
static inline unsigned int ssm2602_read_reg_cache(struct snd_soc_codec *codec,
unsigned int reg)
{
u16 *cache = codec->reg_cache;
if (reg == SSM2602_RESET)
return 0;
if (reg >= SSM2602_CACHEREGNUM)
return -1;
return cache[reg];
}
/*
* write ssm2602 register cache
*/
static inline void ssm2602_write_reg_cache(struct snd_soc_codec *codec,
u16 reg, unsigned int value)
{
u16 *cache = codec->reg_cache;
if (reg >= SSM2602_CACHEREGNUM)
return;
cache[reg] = value;
}
/*
* write to the ssm2602 register space
*/
static int ssm2602_write(struct snd_soc_codec *codec, unsigned int reg,
unsigned int value)
{
u8 data[2];
/* data is
* D15..D9 ssm2602 register offset
* D8...D0 register data
*/
data[0] = (reg << 1) | ((value >> 8) & 0x0001);
data[1] = value & 0x00ff;
ssm2602_write_reg_cache(codec, reg, value);
if (codec->hw_write(codec->control_data, data, 2) == 2)
return 0;
else
return -EIO;
}
#define ssm2602_reset(c) ssm2602_write(c, SSM2602_RESET, 0)
/*Appending several "None"s just for OSS mixer use*/ /*Appending several "None"s just for OSS mixer use*/
static const char *ssm2602_input_select[] = { static const char *ssm2602_input_select[] = {
@ -128,174 +88,187 @@ static const struct soc_enum ssm2602_enum[] = {
SOC_ENUM_SINGLE(SSM2602_APDIGI, 1, 4, ssm2602_deemph), SOC_ENUM_SINGLE(SSM2602_APDIGI, 1, 4, ssm2602_deemph),
}; };
static const struct snd_kcontrol_new ssm2602_snd_controls[] = { static const unsigned int ssm260x_outmix_tlv[] = {
TLV_DB_RANGE_HEAD(2),
0, 47, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 0),
48, 127, TLV_DB_SCALE_ITEM(-7400, 100, 0),
};
SOC_DOUBLE_R("Master Playback Volume", SSM2602_LOUT1V, SSM2602_ROUT1V, static const DECLARE_TLV_DB_SCALE(ssm260x_inpga_tlv, -3450, 150, 0);
0, 127, 0), static const DECLARE_TLV_DB_SCALE(ssm260x_sidetone_tlv, -1500, 300, 0);
SOC_DOUBLE_R("Master Playback ZC Switch", SSM2602_LOUT1V, SSM2602_ROUT1V,
7, 1, 0),
SOC_DOUBLE_R("Capture Volume", SSM2602_LINVOL, SSM2602_RINVOL, 0, 31, 0), static const struct snd_kcontrol_new ssm260x_snd_controls[] = {
SOC_DOUBLE_R_TLV("Capture Volume", SSM2602_LINVOL, SSM2602_RINVOL, 0, 45, 0,
ssm260x_inpga_tlv),
SOC_DOUBLE_R("Capture Switch", SSM2602_LINVOL, SSM2602_RINVOL, 7, 1, 1), SOC_DOUBLE_R("Capture Switch", SSM2602_LINVOL, SSM2602_RINVOL, 7, 1, 1),
SOC_SINGLE("Mic Boost (+20dB)", SSM2602_APANA, 0, 1, 0),
SOC_SINGLE("Mic Boost2 (+20dB)", SSM2602_APANA, 7, 1, 0),
SOC_SINGLE("Mic Switch", SSM2602_APANA, 1, 1, 1),
SOC_SINGLE("Sidetone Playback Volume", SSM2602_APANA, 6, 3, 1),
SOC_SINGLE("ADC High Pass Filter Switch", SSM2602_APDIGI, 0, 1, 1), SOC_SINGLE("ADC High Pass Filter Switch", SSM2602_APDIGI, 0, 1, 1),
SOC_SINGLE("Store DC Offset Switch", SSM2602_APDIGI, 4, 1, 0), SOC_SINGLE("Store DC Offset Switch", SSM2602_APDIGI, 4, 1, 0),
SOC_ENUM("Capture Source", ssm2602_enum[0]),
SOC_ENUM("Playback De-emphasis", ssm2602_enum[1]), SOC_ENUM("Playback De-emphasis", ssm2602_enum[1]),
}; };
static const struct snd_kcontrol_new ssm2602_snd_controls[] = {
SOC_DOUBLE_R_TLV("Master Playback Volume", SSM2602_LOUT1V, SSM2602_ROUT1V,
0, 127, 0, ssm260x_outmix_tlv),
SOC_DOUBLE_R("Master Playback ZC Switch", SSM2602_LOUT1V, SSM2602_ROUT1V,
7, 1, 0),
SOC_SINGLE_TLV("Sidetone Playback Volume", SSM2602_APANA, 6, 3, 1,
ssm260x_sidetone_tlv),
SOC_SINGLE("Mic Boost (+20dB)", SSM2602_APANA, 0, 1, 0),
SOC_SINGLE("Mic Boost2 (+20dB)", SSM2602_APANA, 8, 1, 0),
SOC_SINGLE("Mic Switch", SSM2602_APANA, 1, 1, 1),
};
/* Output Mixer */ /* Output Mixer */
static const struct snd_kcontrol_new ssm2602_output_mixer_controls[] = { static const struct snd_kcontrol_new ssm260x_output_mixer_controls[] = {
SOC_DAPM_SINGLE("Line Bypass Switch", SSM2602_APANA, 3, 1, 0), SOC_DAPM_SINGLE("Line Bypass Switch", SSM2602_APANA, 3, 1, 0),
SOC_DAPM_SINGLE("Mic Sidetone Switch", SSM2602_APANA, 5, 1, 0),
SOC_DAPM_SINGLE("HiFi Playback Switch", SSM2602_APANA, 4, 1, 0), SOC_DAPM_SINGLE("HiFi Playback Switch", SSM2602_APANA, 4, 1, 0),
SOC_DAPM_SINGLE("Mic Sidetone Switch", SSM2602_APANA, 5, 1, 0),
}; };
/* Input mux */ /* Input mux */
static const struct snd_kcontrol_new ssm2602_input_mux_controls = static const struct snd_kcontrol_new ssm2602_input_mux_controls =
SOC_DAPM_ENUM("Input Select", ssm2602_enum[0]); SOC_DAPM_ENUM("Input Select", ssm2602_enum[0]);
static const struct snd_soc_dapm_widget ssm2602_dapm_widgets[] = { static const struct snd_soc_dapm_widget ssm260x_dapm_widgets[] = {
SND_SOC_DAPM_MIXER("Output Mixer", SSM2602_PWR, 4, 1,
&ssm2602_output_mixer_controls[0],
ARRAY_SIZE(ssm2602_output_mixer_controls)),
SND_SOC_DAPM_DAC("DAC", "HiFi Playback", SSM2602_PWR, 3, 1), SND_SOC_DAPM_DAC("DAC", "HiFi Playback", SSM2602_PWR, 3, 1),
SND_SOC_DAPM_OUTPUT("LOUT"),
SND_SOC_DAPM_OUTPUT("LHPOUT"),
SND_SOC_DAPM_OUTPUT("ROUT"),
SND_SOC_DAPM_OUTPUT("RHPOUT"),
SND_SOC_DAPM_ADC("ADC", "HiFi Capture", SSM2602_PWR, 2, 1), SND_SOC_DAPM_ADC("ADC", "HiFi Capture", SSM2602_PWR, 2, 1),
SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0, &ssm2602_input_mux_controls),
SND_SOC_DAPM_PGA("Line Input", SSM2602_PWR, 0, 1, NULL, 0), SND_SOC_DAPM_PGA("Line Input", SSM2602_PWR, 0, 1, NULL, 0),
SND_SOC_DAPM_MICBIAS("Mic Bias", SSM2602_PWR, 1, 1),
SND_SOC_DAPM_INPUT("MICIN"), SND_SOC_DAPM_SUPPLY("Digital Core Power", SSM2602_ACTIVE, 0, 0, NULL, 0),
SND_SOC_DAPM_OUTPUT("LOUT"),
SND_SOC_DAPM_OUTPUT("ROUT"),
SND_SOC_DAPM_INPUT("RLINEIN"), SND_SOC_DAPM_INPUT("RLINEIN"),
SND_SOC_DAPM_INPUT("LLINEIN"), SND_SOC_DAPM_INPUT("LLINEIN"),
}; };
static const struct snd_soc_dapm_route audio_conn[] = { static const struct snd_soc_dapm_widget ssm2602_dapm_widgets[] = {
/* output mixer */ SND_SOC_DAPM_MIXER("Output Mixer", SSM2602_PWR, 4, 1,
ssm260x_output_mixer_controls,
ARRAY_SIZE(ssm260x_output_mixer_controls)),
SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0, &ssm2602_input_mux_controls),
SND_SOC_DAPM_MICBIAS("Mic Bias", SSM2602_PWR, 1, 1),
SND_SOC_DAPM_OUTPUT("LHPOUT"),
SND_SOC_DAPM_OUTPUT("RHPOUT"),
SND_SOC_DAPM_INPUT("MICIN"),
};
static const struct snd_soc_dapm_widget ssm2604_dapm_widgets[] = {
SND_SOC_DAPM_MIXER("Output Mixer", SND_SOC_NOPM, 0, 0,
ssm260x_output_mixer_controls,
ARRAY_SIZE(ssm260x_output_mixer_controls) - 1), /* Last element is the mic */
};
static const struct snd_soc_dapm_route ssm260x_routes[] = {
{"DAC", NULL, "Digital Core Power"},
{"ADC", NULL, "Digital Core Power"},
{"Output Mixer", "Line Bypass Switch", "Line Input"}, {"Output Mixer", "Line Bypass Switch", "Line Input"},
{"Output Mixer", "HiFi Playback Switch", "DAC"}, {"Output Mixer", "HiFi Playback Switch", "DAC"},
{"Output Mixer", "Mic Sidetone Switch", "Mic Bias"},
/* outputs */
{"RHPOUT", NULL, "Output Mixer"},
{"ROUT", NULL, "Output Mixer"}, {"ROUT", NULL, "Output Mixer"},
{"LHPOUT", NULL, "Output Mixer"},
{"LOUT", NULL, "Output Mixer"}, {"LOUT", NULL, "Output Mixer"},
/* input mux */ {"Line Input", NULL, "LLINEIN"},
{"Line Input", NULL, "RLINEIN"},
};
static const struct snd_soc_dapm_route ssm2602_routes[] = {
{"Output Mixer", "Mic Sidetone Switch", "Mic Bias"},
{"RHPOUT", NULL, "Output Mixer"},
{"LHPOUT", NULL, "Output Mixer"},
{"Input Mux", "Line", "Line Input"}, {"Input Mux", "Line", "Line Input"},
{"Input Mux", "Mic", "Mic Bias"}, {"Input Mux", "Mic", "Mic Bias"},
{"ADC", NULL, "Input Mux"}, {"ADC", NULL, "Input Mux"},
/* inputs */
{"Line Input", NULL, "LLINEIN"},
{"Line Input", NULL, "RLINEIN"},
{"Mic Bias", NULL, "MICIN"}, {"Mic Bias", NULL, "MICIN"},
}; };
static int ssm2602_add_widgets(struct snd_soc_codec *codec) static const struct snd_soc_dapm_route ssm2604_routes[] = {
{ {"ADC", NULL, "Line Input"},
struct snd_soc_dapm_context *dapm = &codec->dapm; };
snd_soc_dapm_new_controls(dapm, ssm2602_dapm_widgets, struct ssm2602_coeff {
ARRAY_SIZE(ssm2602_dapm_widgets));
snd_soc_dapm_add_routes(dapm, audio_conn, ARRAY_SIZE(audio_conn));
return 0;
}
struct _coeff_div {
u32 mclk; u32 mclk;
u32 rate; u32 rate;
u16 fs; u8 srate;
u8 sr:4;
u8 bosr:1;
u8 usb:1;
}; };
/* codec mclk clock divider coefficients */ #define SSM2602_COEFF_SRATE(sr, bosr, usb) (((sr) << 2) | ((bosr) << 1) | (usb))
static const struct _coeff_div coeff_div[] = {
/* codec mclk clock coefficients */
static const struct ssm2602_coeff ssm2602_coeff_table[] = {
/* 48k */ /* 48k */
{12288000, 48000, 256, 0x0, 0x0, 0x0}, {12288000, 48000, SSM2602_COEFF_SRATE(0x0, 0x0, 0x0)},
{18432000, 48000, 384, 0x0, 0x1, 0x0}, {18432000, 48000, SSM2602_COEFF_SRATE(0x0, 0x1, 0x0)},
{12000000, 48000, 250, 0x0, 0x0, 0x1}, {12000000, 48000, SSM2602_COEFF_SRATE(0x0, 0x0, 0x1)},
/* 32k */ /* 32k */
{12288000, 32000, 384, 0x6, 0x0, 0x0}, {12288000, 32000, SSM2602_COEFF_SRATE(0x6, 0x0, 0x0)},
{18432000, 32000, 576, 0x6, 0x1, 0x0}, {18432000, 32000, SSM2602_COEFF_SRATE(0x6, 0x1, 0x0)},
{12000000, 32000, 375, 0x6, 0x0, 0x1}, {12000000, 32000, SSM2602_COEFF_SRATE(0x6, 0x0, 0x1)},
/* 8k */ /* 8k */
{12288000, 8000, 1536, 0x3, 0x0, 0x0}, {12288000, 8000, SSM2602_COEFF_SRATE(0x3, 0x0, 0x0)},
{18432000, 8000, 2304, 0x3, 0x1, 0x0}, {18432000, 8000, SSM2602_COEFF_SRATE(0x3, 0x1, 0x0)},
{11289600, 8000, 1408, 0xb, 0x0, 0x0}, {11289600, 8000, SSM2602_COEFF_SRATE(0xb, 0x0, 0x0)},
{16934400, 8000, 2112, 0xb, 0x1, 0x0}, {16934400, 8000, SSM2602_COEFF_SRATE(0xb, 0x1, 0x0)},
{12000000, 8000, 1500, 0x3, 0x0, 0x1}, {12000000, 8000, SSM2602_COEFF_SRATE(0x3, 0x0, 0x1)},
/* 96k */ /* 96k */
{12288000, 96000, 128, 0x7, 0x0, 0x0}, {12288000, 96000, SSM2602_COEFF_SRATE(0x7, 0x0, 0x0)},
{18432000, 96000, 192, 0x7, 0x1, 0x0}, {18432000, 96000, SSM2602_COEFF_SRATE(0x7, 0x1, 0x0)},
{12000000, 96000, 125, 0x7, 0x0, 0x1}, {12000000, 96000, SSM2602_COEFF_SRATE(0x7, 0x0, 0x1)},
/* 44.1k */ /* 44.1k */
{11289600, 44100, 256, 0x8, 0x0, 0x0}, {11289600, 44100, SSM2602_COEFF_SRATE(0x8, 0x0, 0x0)},
{16934400, 44100, 384, 0x8, 0x1, 0x0}, {16934400, 44100, SSM2602_COEFF_SRATE(0x8, 0x1, 0x0)},
{12000000, 44100, 272, 0x8, 0x1, 0x1}, {12000000, 44100, SSM2602_COEFF_SRATE(0x8, 0x1, 0x1)},
/* 88.2k */ /* 88.2k */
{11289600, 88200, 128, 0xf, 0x0, 0x0}, {11289600, 88200, SSM2602_COEFF_SRATE(0xf, 0x0, 0x0)},
{16934400, 88200, 192, 0xf, 0x1, 0x0}, {16934400, 88200, SSM2602_COEFF_SRATE(0xf, 0x1, 0x0)},
{12000000, 88200, 136, 0xf, 0x1, 0x1}, {12000000, 88200, SSM2602_COEFF_SRATE(0xf, 0x1, 0x1)},
}; };
static inline int get_coeff(int mclk, int rate) static inline int ssm2602_get_coeff(int mclk, int rate)
{ {
int i; int i;
for (i = 0; i < ARRAY_SIZE(coeff_div); i++) { for (i = 0; i < ARRAY_SIZE(ssm2602_coeff_table); i++) {
if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk) if (ssm2602_coeff_table[i].rate == rate &&
return i; ssm2602_coeff_table[i].mclk == mclk)
return ssm2602_coeff_table[i].srate;
} }
return i; return -EINVAL;
} }
static int ssm2602_hw_params(struct snd_pcm_substream *substream, static int ssm2602_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai) struct snd_soc_dai *dai)
{ {
u16 srate;
struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_codec *codec = rtd->codec; struct snd_soc_codec *codec = rtd->codec;
struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec); struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
struct i2c_client *i2c = codec->control_data; u16 iface = snd_soc_read(codec, SSM2602_IFACE) & 0xfff3;
u16 iface = ssm2602_read_reg_cache(codec, SSM2602_IFACE) & 0xfff3; int srate = ssm2602_get_coeff(ssm2602->sysclk, params_rate(params));
int i = get_coeff(ssm2602->sysclk, params_rate(params));
if (substream == ssm2602->slave_substream) { if (substream == ssm2602->slave_substream) {
dev_dbg(&i2c->dev, "Ignoring hw_params for slave substream\n"); dev_dbg(codec->dev, "Ignoring hw_params for slave substream\n");
return 0; return 0;
} }
/*no match is found*/ if (srate < 0)
if (i == ARRAY_SIZE(coeff_div)) return srate;
return -EINVAL;
srate = (coeff_div[i].sr << 2) | snd_soc_write(codec, SSM2602_SRATE, srate);
(coeff_div[i].bosr << 1) | coeff_div[i].usb;
ssm2602_write(codec, SSM2602_ACTIVE, 0);
ssm2602_write(codec, SSM2602_SRATE, srate);
/* bit size */ /* bit size */
switch (params_format(params)) { switch (params_format(params)) {
@ -311,8 +284,7 @@ static int ssm2602_hw_params(struct snd_pcm_substream *substream,
iface |= 0x000c; iface |= 0x000c;
break; break;
} }
ssm2602_write(codec, SSM2602_IFACE, iface); snd_soc_write(codec, SSM2602_IFACE, iface);
ssm2602_write(codec, SSM2602_ACTIVE, ACTIVE_ACTIVATE_CODEC);
return 0; return 0;
} }
@ -354,17 +326,6 @@ static int ssm2602_startup(struct snd_pcm_substream *substream,
return 0; return 0;
} }
static int ssm2602_pcm_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_codec *codec = rtd->codec;
/* set active */
ssm2602_write(codec, SSM2602_ACTIVE, ACTIVE_ACTIVATE_CODEC);
return 0;
}
static void ssm2602_shutdown(struct snd_pcm_substream *substream, static void ssm2602_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai) struct snd_soc_dai *dai)
{ {
@ -372,25 +333,22 @@ static void ssm2602_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_codec *codec = rtd->codec; struct snd_soc_codec *codec = rtd->codec;
struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec); struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
/* deactivate */
if (!codec->active)
ssm2602_write(codec, SSM2602_ACTIVE, 0);
if (ssm2602->master_substream == substream) if (ssm2602->master_substream == substream)
ssm2602->master_substream = ssm2602->slave_substream; ssm2602->master_substream = ssm2602->slave_substream;
ssm2602->slave_substream = NULL; ssm2602->slave_substream = NULL;
} }
static int ssm2602_mute(struct snd_soc_dai *dai, int mute) static int ssm2602_mute(struct snd_soc_dai *dai, int mute)
{ {
struct snd_soc_codec *codec = dai->codec; struct snd_soc_codec *codec = dai->codec;
u16 mute_reg = ssm2602_read_reg_cache(codec, SSM2602_APDIGI) & ~APDIGI_ENABLE_DAC_MUTE; u16 mute_reg = snd_soc_read(codec, SSM2602_APDIGI) & ~APDIGI_ENABLE_DAC_MUTE;
if (mute) if (mute)
ssm2602_write(codec, SSM2602_APDIGI, snd_soc_write(codec, SSM2602_APDIGI,
mute_reg | APDIGI_ENABLE_DAC_MUTE); mute_reg | APDIGI_ENABLE_DAC_MUTE);
else else
ssm2602_write(codec, SSM2602_APDIGI, mute_reg); snd_soc_write(codec, SSM2602_APDIGI, mute_reg);
return 0; return 0;
} }
@ -466,30 +424,29 @@ static int ssm2602_set_dai_fmt(struct snd_soc_dai *codec_dai,
} }
/* set iface */ /* set iface */
ssm2602_write(codec, SSM2602_IFACE, iface); snd_soc_write(codec, SSM2602_IFACE, iface);
return 0; return 0;
} }
static int ssm2602_set_bias_level(struct snd_soc_codec *codec, static int ssm2602_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level) enum snd_soc_bias_level level)
{ {
u16 reg = ssm2602_read_reg_cache(codec, SSM2602_PWR) & 0xff7f; u16 reg = snd_soc_read(codec, SSM2602_PWR) & 0xff7f;
switch (level) { switch (level) {
case SND_SOC_BIAS_ON: case SND_SOC_BIAS_ON:
/* vref/mid, osc on, dac unmute */ /* vref/mid, osc on, dac unmute */
ssm2602_write(codec, SSM2602_PWR, reg); snd_soc_write(codec, SSM2602_PWR, reg);
break; break;
case SND_SOC_BIAS_PREPARE: case SND_SOC_BIAS_PREPARE:
break; break;
case SND_SOC_BIAS_STANDBY: case SND_SOC_BIAS_STANDBY:
/* everything off except vref/vmid, */ /* everything off except vref/vmid, */
ssm2602_write(codec, SSM2602_PWR, reg | PWR_CLK_OUT_PDN); snd_soc_write(codec, SSM2602_PWR, reg | PWR_CLK_OUT_PDN);
break; break;
case SND_SOC_BIAS_OFF: case SND_SOC_BIAS_OFF:
/* everything off, dac mute, inactive */ /* everything off, dac mute, inactive */
ssm2602_write(codec, SSM2602_ACTIVE, 0); snd_soc_write(codec, SSM2602_PWR, 0xffff);
ssm2602_write(codec, SSM2602_PWR, 0xffff);
break; break;
} }
@ -506,7 +463,6 @@ static int ssm2602_set_bias_level(struct snd_soc_codec *codec,
static struct snd_soc_dai_ops ssm2602_dai_ops = { static struct snd_soc_dai_ops ssm2602_dai_ops = {
.startup = ssm2602_startup, .startup = ssm2602_startup,
.prepare = ssm2602_pcm_prepare,
.hw_params = ssm2602_hw_params, .hw_params = ssm2602_hw_params,
.shutdown = ssm2602_shutdown, .shutdown = ssm2602_shutdown,
.digital_mute = ssm2602_mute, .digital_mute = ssm2602_mute,
@ -539,50 +495,87 @@ static int ssm2602_suspend(struct snd_soc_codec *codec, pm_message_t state)
static int ssm2602_resume(struct snd_soc_codec *codec) static int ssm2602_resume(struct snd_soc_codec *codec)
{ {
int i; snd_soc_cache_sync(codec);
u8 data[2];
u16 *cache = codec->reg_cache;
/* Sync reg_cache with the hardware */
for (i = 0; i < ARRAY_SIZE(ssm2602_reg); i++) {
data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
data[1] = cache[i] & 0x00ff;
codec->hw_write(codec->control_data, data, 2);
}
ssm2602_set_bias_level(codec, SND_SOC_BIAS_STANDBY); ssm2602_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
return 0; return 0;
} }
static int ssm2602_probe(struct snd_soc_codec *codec) static int ssm2602_probe(struct snd_soc_codec *codec)
{
struct snd_soc_dapm_context *dapm = &codec->dapm;
int ret, reg;
reg = snd_soc_read(codec, SSM2602_LOUT1V);
snd_soc_write(codec, SSM2602_LOUT1V, reg | LOUT1V_LRHP_BOTH);
reg = snd_soc_read(codec, SSM2602_ROUT1V);
snd_soc_write(codec, SSM2602_ROUT1V, reg | ROUT1V_RLHP_BOTH);
ret = snd_soc_add_controls(codec, ssm2602_snd_controls,
ARRAY_SIZE(ssm2602_snd_controls));
if (ret)
return ret;
ret = snd_soc_dapm_new_controls(dapm, ssm2602_dapm_widgets,
ARRAY_SIZE(ssm2602_dapm_widgets));
if (ret)
return ret;
return snd_soc_dapm_add_routes(dapm, ssm2602_routes,
ARRAY_SIZE(ssm2602_routes));
}
static int ssm2604_probe(struct snd_soc_codec *codec)
{
struct snd_soc_dapm_context *dapm = &codec->dapm;
int ret;
ret = snd_soc_dapm_new_controls(dapm, ssm2604_dapm_widgets,
ARRAY_SIZE(ssm2604_dapm_widgets));
if (ret)
return ret;
return snd_soc_dapm_add_routes(dapm, ssm2604_routes,
ARRAY_SIZE(ssm2604_routes));
}
static int ssm260x_probe(struct snd_soc_codec *codec)
{ {
struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec); struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
int ret = 0, reg; int ret, reg;
pr_info("ssm2602 Audio Codec %s", SSM2602_VERSION); pr_info("ssm2602 Audio Codec %s", SSM2602_VERSION);
codec->control_data = ssm2602->control_data; ret = snd_soc_codec_set_cache_io(codec, 7, 9, ssm2602->control_type);
if (ret < 0) {
dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
return ret;
}
ssm2602_reset(codec); ret = ssm2602_reset(codec);
if (ret < 0) {
dev_err(codec->dev, "Failed to issue reset: %d\n", ret);
return ret;
}
/*power on device*/
ssm2602_write(codec, SSM2602_ACTIVE, 0);
/* set the update bits */ /* set the update bits */
reg = ssm2602_read_reg_cache(codec, SSM2602_LINVOL); reg = snd_soc_read(codec, SSM2602_LINVOL);
ssm2602_write(codec, SSM2602_LINVOL, reg | LINVOL_LRIN_BOTH); snd_soc_write(codec, SSM2602_LINVOL, reg | LINVOL_LRIN_BOTH);
reg = ssm2602_read_reg_cache(codec, SSM2602_RINVOL); reg = snd_soc_read(codec, SSM2602_RINVOL);
ssm2602_write(codec, SSM2602_RINVOL, reg | RINVOL_RLIN_BOTH); snd_soc_write(codec, SSM2602_RINVOL, reg | RINVOL_RLIN_BOTH);
reg = ssm2602_read_reg_cache(codec, SSM2602_LOUT1V);
ssm2602_write(codec, SSM2602_LOUT1V, reg | LOUT1V_LRHP_BOTH);
reg = ssm2602_read_reg_cache(codec, SSM2602_ROUT1V);
ssm2602_write(codec, SSM2602_ROUT1V, reg | ROUT1V_RLHP_BOTH);
/*select Line in as default input*/ /*select Line in as default input*/
ssm2602_write(codec, SSM2602_APANA, APANA_SELECT_DAC | snd_soc_write(codec, SSM2602_APANA, APANA_SELECT_DAC |
APANA_ENABLE_MIC_BOOST); APANA_ENABLE_MIC_BOOST);
ssm2602_write(codec, SSM2602_PWR, 0);
snd_soc_add_controls(codec, ssm2602_snd_controls, switch (ssm2602->type) {
ARRAY_SIZE(ssm2602_snd_controls)); case SSM2602:
ssm2602_add_widgets(codec); ret = ssm2602_probe(codec);
break;
case SSM2604:
ret = ssm2604_probe(codec);
break;
}
return ret; return ret;
} }
@ -595,18 +588,61 @@ static int ssm2602_remove(struct snd_soc_codec *codec)
} }
static struct snd_soc_codec_driver soc_codec_dev_ssm2602 = { static struct snd_soc_codec_driver soc_codec_dev_ssm2602 = {
.probe = ssm2602_probe, .probe = ssm260x_probe,
.remove = ssm2602_remove, .remove = ssm2602_remove,
.suspend = ssm2602_suspend, .suspend = ssm2602_suspend,
.resume = ssm2602_resume, .resume = ssm2602_resume,
.read = ssm2602_read_reg_cache,
.write = ssm2602_write,
.set_bias_level = ssm2602_set_bias_level, .set_bias_level = ssm2602_set_bias_level,
.reg_cache_size = sizeof(ssm2602_reg), .reg_cache_size = ARRAY_SIZE(ssm2602_reg),
.reg_word_size = sizeof(u16), .reg_word_size = sizeof(u16),
.reg_cache_default = ssm2602_reg, .reg_cache_default = ssm2602_reg,
.controls = ssm260x_snd_controls,
.num_controls = ARRAY_SIZE(ssm260x_snd_controls),
.dapm_widgets = ssm260x_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(ssm260x_dapm_widgets),
.dapm_routes = ssm260x_routes,
.num_dapm_routes = ARRAY_SIZE(ssm260x_routes),
}; };
#if defined(CONFIG_SPI_MASTER)
static int __devinit ssm2602_spi_probe(struct spi_device *spi)
{
struct ssm2602_priv *ssm2602;
int ret;
ssm2602 = kzalloc(sizeof(struct ssm2602_priv), GFP_KERNEL);
if (ssm2602 == NULL)
return -ENOMEM;
spi_set_drvdata(spi, ssm2602);
ssm2602->control_type = SND_SOC_SPI;
ssm2602->type = SSM2602;
ret = snd_soc_register_codec(&spi->dev,
&soc_codec_dev_ssm2602, &ssm2602_dai, 1);
if (ret < 0)
kfree(ssm2602);
return ret;
}
static int __devexit ssm2602_spi_remove(struct spi_device *spi)
{
snd_soc_unregister_codec(&spi->dev);
kfree(spi_get_drvdata(spi));
return 0;
}
static struct spi_driver ssm2602_spi_driver = {
.driver = {
.name = "ssm2602",
.owner = THIS_MODULE,
},
.probe = ssm2602_spi_probe,
.remove = __devexit_p(ssm2602_spi_remove),
};
#endif
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
/* /*
* ssm2602 2 wire address is determined by GPIO5 * ssm2602 2 wire address is determined by GPIO5
@ -614,7 +650,7 @@ static struct snd_soc_codec_driver soc_codec_dev_ssm2602 = {
* low = 0x1a * low = 0x1a
* high = 0x1b * high = 0x1b
*/ */
static int ssm2602_i2c_probe(struct i2c_client *i2c, static int __devinit ssm2602_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id) const struct i2c_device_id *id)
{ {
struct ssm2602_priv *ssm2602; struct ssm2602_priv *ssm2602;
@ -625,8 +661,8 @@ static int ssm2602_i2c_probe(struct i2c_client *i2c,
return -ENOMEM; return -ENOMEM;
i2c_set_clientdata(i2c, ssm2602); i2c_set_clientdata(i2c, ssm2602);
ssm2602->control_data = i2c;
ssm2602->control_type = SND_SOC_I2C; ssm2602->control_type = SND_SOC_I2C;
ssm2602->type = id->driver_data;
ret = snd_soc_register_codec(&i2c->dev, ret = snd_soc_register_codec(&i2c->dev,
&soc_codec_dev_ssm2602, &ssm2602_dai, 1); &soc_codec_dev_ssm2602, &ssm2602_dai, 1);
@ -635,7 +671,7 @@ static int ssm2602_i2c_probe(struct i2c_client *i2c,
return ret; return ret;
} }
static int ssm2602_i2c_remove(struct i2c_client *client) static int __devexit ssm2602_i2c_remove(struct i2c_client *client)
{ {
snd_soc_unregister_codec(&client->dev); snd_soc_unregister_codec(&client->dev);
kfree(i2c_get_clientdata(client)); kfree(i2c_get_clientdata(client));
@ -643,7 +679,9 @@ static int ssm2602_i2c_remove(struct i2c_client *client)
} }
static const struct i2c_device_id ssm2602_i2c_id[] = { static const struct i2c_device_id ssm2602_i2c_id[] = {
{ "ssm2602", 0 }, { "ssm2602", SSM2602 },
{ "ssm2603", SSM2602 },
{ "ssm2604", SSM2604 },
{ } { }
}; };
MODULE_DEVICE_TABLE(i2c, ssm2602_i2c_id); MODULE_DEVICE_TABLE(i2c, ssm2602_i2c_id);
@ -651,11 +689,11 @@ MODULE_DEVICE_TABLE(i2c, ssm2602_i2c_id);
/* corgi i2c codec control layer */ /* corgi i2c codec control layer */
static struct i2c_driver ssm2602_i2c_driver = { static struct i2c_driver ssm2602_i2c_driver = {
.driver = { .driver = {
.name = "ssm2602-codec", .name = "ssm2602",
.owner = THIS_MODULE, .owner = THIS_MODULE,
}, },
.probe = ssm2602_i2c_probe, .probe = ssm2602_i2c_probe,
.remove = ssm2602_i2c_remove, .remove = __devexit_p(ssm2602_i2c_remove),
.id_table = ssm2602_i2c_id, .id_table = ssm2602_i2c_id,
}; };
#endif #endif
@ -664,25 +702,35 @@ static struct i2c_driver ssm2602_i2c_driver = {
static int __init ssm2602_modinit(void) static int __init ssm2602_modinit(void)
{ {
int ret = 0; int ret = 0;
#if defined(CONFIG_SPI_MASTER)
ret = spi_register_driver(&ssm2602_spi_driver);
if (ret)
return ret;
#endif
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
ret = i2c_add_driver(&ssm2602_i2c_driver); ret = i2c_add_driver(&ssm2602_i2c_driver);
if (ret != 0) { if (ret)
printk(KERN_ERR "Failed to register SSM2602 I2C driver: %d\n", return ret;
ret);
}
#endif #endif
return ret; return ret;
} }
module_init(ssm2602_modinit); module_init(ssm2602_modinit);
static void __exit ssm2602_exit(void) static void __exit ssm2602_exit(void)
{ {
#if defined(CONFIG_SPI_MASTER)
spi_unregister_driver(&ssm2602_spi_driver);
#endif
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
i2c_del_driver(&ssm2602_i2c_driver); i2c_del_driver(&ssm2602_i2c_driver);
#endif #endif
} }
module_exit(ssm2602_exit); module_exit(ssm2602_exit);
MODULE_DESCRIPTION("ASoC ssm2602 driver"); MODULE_DESCRIPTION("ASoC SSM2602/SSM2603/SSM2604 driver");
MODULE_AUTHOR("Cliff Cai"); MODULE_AUTHOR("Cliff Cai");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");

View file

@ -117,11 +117,5 @@
#define SSM2602_CACHEREGNUM 10 #define SSM2602_CACHEREGNUM 10
#define SSM2602_SYSCLK 0 #define SSM2602_SYSCLK 0
#define SSM2602_DAI 0
struct ssm2602_setup_data {
int i2c_bus;
unsigned short i2c_address;
};
#endif #endif

View file

@ -212,7 +212,7 @@ static const struct snd_soc_dapm_widget tlv320aic23_dapm_widgets[] = {
SND_SOC_DAPM_INPUT("MICIN"), SND_SOC_DAPM_INPUT("MICIN"),
}; };
static const struct snd_soc_dapm_route intercon[] = { static const struct snd_soc_dapm_route tlv320aic23_intercon[] = {
/* Output Mixer */ /* Output Mixer */
{"Output Mixer", "Line Bypass Switch", "Line Input"}, {"Output Mixer", "Line Bypass Switch", "Line Input"},
{"Output Mixer", "Playback Switch", "DAC"}, {"Output Mixer", "Playback Switch", "DAC"},
@ -388,18 +388,6 @@ static int set_sample_rate_control(struct snd_soc_codec *codec, int mclk,
return 0; return 0;
} }
static int tlv320aic23_add_widgets(struct snd_soc_codec *codec)
{
struct snd_soc_dapm_context *dapm = &codec->dapm;
snd_soc_dapm_new_controls(dapm, tlv320aic23_dapm_widgets,
ARRAY_SIZE(tlv320aic23_dapm_widgets));
/* set up audio path interconnects */
snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon));
return 0;
}
static int tlv320aic23_hw_params(struct snd_pcm_substream *substream, static int tlv320aic23_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai) struct snd_soc_dai *dai)
@ -676,7 +664,6 @@ static int tlv320aic23_probe(struct snd_soc_codec *codec)
snd_soc_add_controls(codec, tlv320aic23_snd_controls, snd_soc_add_controls(codec, tlv320aic23_snd_controls,
ARRAY_SIZE(tlv320aic23_snd_controls)); ARRAY_SIZE(tlv320aic23_snd_controls));
tlv320aic23_add_widgets(codec);
return 0; return 0;
} }
@ -698,6 +685,10 @@ static struct snd_soc_codec_driver soc_codec_dev_tlv320aic23 = {
.read = tlv320aic23_read_reg_cache, .read = tlv320aic23_read_reg_cache,
.write = tlv320aic23_write, .write = tlv320aic23_write,
.set_bias_level = tlv320aic23_set_bias_level, .set_bias_level = tlv320aic23_set_bias_level,
.dapm_widgets = tlv320aic23_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(tlv320aic23_dapm_widgets),
.dapm_routes = tlv320aic23_intercon,
.num_dapm_routes = ARRAY_SIZE(tlv320aic23_intercon),
}; };
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)

View file

@ -157,7 +157,8 @@ static int aic3x_read(struct snd_soc_codec *codec, unsigned int reg,
static int snd_soc_dapm_put_volsw_aic3x(struct snd_kcontrol *kcontrol, static int snd_soc_dapm_put_volsw_aic3x(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol) struct snd_ctl_elem_value *ucontrol)
{ {
struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol); struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
struct snd_soc_dapm_widget *widget = wlist->widgets[0];
struct soc_mixer_control *mc = struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value; (struct soc_mixer_control *)kcontrol->private_value;
unsigned int reg = mc->reg; unsigned int reg = mc->reg;

View file

@ -1,7 +1,7 @@
/* /*
* ALSA SoC Texas Instruments TLV320DAC33 codec driver * ALSA SoC Texas Instruments TLV320DAC33 codec driver
* *
* Author: Peter Ujfalusi <peter.ujfalusi@nokia.com> * Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
* *
* Copyright: (C) 2009 Nokia Corporation * Copyright: (C) 2009 Nokia Corporation
* *
@ -587,6 +587,9 @@ static const struct snd_soc_dapm_widget dac33_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY("Right DAC Power", SND_SOC_DAPM_SUPPLY("Right DAC Power",
DAC33_RDAC_PWR_CTRL, 2, 0, NULL, 0), DAC33_RDAC_PWR_CTRL, 2, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("Codec Power",
DAC33_PWR_CTRL, 4, 0, NULL, 0),
SND_SOC_DAPM_PRE("Pre Playback", dac33_playback_event), SND_SOC_DAPM_PRE("Pre Playback", dac33_playback_event),
SND_SOC_DAPM_POST("Post Playback", dac33_playback_event), SND_SOC_DAPM_POST("Post Playback", dac33_playback_event),
}; };
@ -619,6 +622,9 @@ static const struct snd_soc_dapm_route audio_map[] = {
/* output */ /* output */
{"LEFT_LO", NULL, "Output Left Amplifier"}, {"LEFT_LO", NULL, "Output Left Amplifier"},
{"RIGHT_LO", NULL, "Output Right Amplifier"}, {"RIGHT_LO", NULL, "Output Right Amplifier"},
{"LEFT_LO", NULL, "Codec Power"},
{"RIGHT_LO", NULL, "Codec Power"},
}; };
static int dac33_add_widgets(struct snd_soc_codec *codec) static int dac33_add_widgets(struct snd_soc_codec *codec)
@ -636,13 +642,10 @@ static int dac33_add_widgets(struct snd_soc_codec *codec)
static int dac33_set_bias_level(struct snd_soc_codec *codec, static int dac33_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level) enum snd_soc_bias_level level)
{ {
struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
int ret; int ret;
switch (level) { switch (level) {
case SND_SOC_BIAS_ON: case SND_SOC_BIAS_ON:
if (!dac33->substream)
dac33_soft_power(codec, 1);
break; break;
case SND_SOC_BIAS_PREPARE: case SND_SOC_BIAS_PREPARE:
break; break;
@ -943,8 +946,8 @@ static int dac33_prepare_chip(struct snd_pcm_substream *substream)
/* Write registers 0x08 and 0x09 (MSB, LSB) */ /* Write registers 0x08 and 0x09 (MSB, LSB) */
dac33_write16(codec, DAC33_INT_OSC_FREQ_RAT_A, oscset); dac33_write16(codec, DAC33_INT_OSC_FREQ_RAT_A, oscset);
/* calib time: 128 is a nice number ;) */ /* OSC calibration time */
dac33_write(codec, DAC33_CALIB_TIME, 128); dac33_write(codec, DAC33_CALIB_TIME, 96);
/* adjustment treshold & step */ /* adjustment treshold & step */
dac33_write(codec, DAC33_INT_OSC_CTRL_B, DAC33_ADJTHRSHLD(2) | dac33_write(codec, DAC33_INT_OSC_CTRL_B, DAC33_ADJTHRSHLD(2) |
@ -1655,5 +1658,5 @@ module_exit(dac33_module_exit);
MODULE_DESCRIPTION("ASoC TLV320DAC33 codec driver"); MODULE_DESCRIPTION("ASoC TLV320DAC33 codec driver");
MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@nokia.com>"); MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");

View file

@ -1,7 +1,7 @@
/* /*
* ALSA SoC Texas Instruments TLV320DAC33 codec driver * ALSA SoC Texas Instruments TLV320DAC33 codec driver
* *
* Author: Peter Ujfalusi <peter.ujfalusi@nokia.com> * Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
* *
* Copyright: (C) 2009 Nokia Corporation * Copyright: (C) 2009 Nokia Corporation
* *

View file

@ -3,7 +3,7 @@
* *
* Copyright (C) Nokia Corporation * Copyright (C) Nokia Corporation
* *
* Author: Peter Ujfalusi <peter.ujfalusi@nokia.com> * Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
@ -495,7 +495,7 @@ static void __exit tpa6130a2_exit(void)
i2c_del_driver(&tpa6130a2_i2c_driver); i2c_del_driver(&tpa6130a2_i2c_driver);
} }
MODULE_AUTHOR("Peter Ujfalusi"); MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>");
MODULE_DESCRIPTION("TPA6130A2 Headphone amplifier driver"); MODULE_DESCRIPTION("TPA6130A2 Headphone amplifier driver");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");

View file

@ -3,7 +3,7 @@
* *
* Copyright (C) Nokia Corporation * Copyright (C) Nokia Corporation
* *
* Author: Peter Ujfalusi <peter.ujfalusi@nokia.com> * Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License

View file

@ -960,9 +960,9 @@ static DECLARE_TLV_DB_SCALE(mic_amp_tlv, -600, 600, 0);
/* /*
* AFMGAIN volume control: * AFMGAIN volume control:
* from 18 to 24 dB in 6 dB steps * from -18 to 24 dB in 6 dB steps
*/ */
static DECLARE_TLV_DB_SCALE(afm_amp_tlv, 1800, 600, 0); static DECLARE_TLV_DB_SCALE(afm_amp_tlv, -1800, 600, 0);
/* /*
* HSGAIN volume control: * HSGAIN volume control:
@ -1049,7 +1049,7 @@ static const struct snd_kcontrol_new twl6040_snd_controls[] = {
/* AFM gains */ /* AFM gains */
SOC_DOUBLE_TLV("Aux FM Volume", SOC_DOUBLE_TLV("Aux FM Volume",
TWL6040_REG_LINEGAIN, 0, 4, 0xF, 0, afm_amp_tlv), TWL6040_REG_LINEGAIN, 0, 3, 7, 0, afm_amp_tlv),
/* Playback gains */ /* Playback gains */
SOC_TWL6040_DOUBLE_TLV("Headset Playback Volume", SOC_TWL6040_DOUBLE_TLV("Headset Playback Volume",

View file

@ -601,9 +601,7 @@ static struct snd_soc_codec_driver soc_codec_dev_uda134x = {
.reg_cache_step = 1, .reg_cache_step = 1,
.read = uda134x_read_reg_cache, .read = uda134x_read_reg_cache,
.write = uda134x_write, .write = uda134x_write,
#ifdef POWER_OFF_ON_STANDBY
.set_bias_level = uda134x_set_bias_level, .set_bias_level = uda134x_set_bias_level,
#endif
}; };
static int __devinit uda134x_codec_probe(struct platform_device *pdev) static int __devinit uda134x_codec_probe(struct platform_device *pdev)

View file

@ -0,0 +1,108 @@
/*
* Driver for the 1250-EV1 audio I/O module
*
* Copyright 2011 Wolfson Microelectronics plc
*
* 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 <sound/soc.h>
#include <sound/soc-dapm.h>
static const struct snd_soc_dapm_widget wm1250_ev1_dapm_widgets[] = {
SND_SOC_DAPM_ADC("ADC", "wm1250-ev1 Capture", SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_DAC("DAC", "wm1250-ev1 Playback", SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_INPUT("WM1250 Input"),
SND_SOC_DAPM_INPUT("WM1250 Output"),
};
static const struct snd_soc_dapm_route wm1250_ev1_dapm_routes[] = {
{ "ADC", NULL, "WM1250 Input" },
{ "WM1250 Output", NULL, "DAC" },
};
static struct snd_soc_dai_driver wm1250_ev1_dai = {
.name = "wm1250-ev1",
.playback = {
.stream_name = "Playback",
.channels_min = 1,
.channels_max = 1,
.rates = SNDRV_PCM_RATE_8000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
},
.capture = {
.stream_name = "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_wm1250_ev1 = {
.dapm_widgets = wm1250_ev1_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(wm1250_ev1_dapm_widgets),
.dapm_routes = wm1250_ev1_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(wm1250_ev1_dapm_routes),
};
static int __devinit wm1250_ev1_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_wm1250_ev1,
&wm1250_ev1_dai, 1);
}
static int __devexit wm1250_ev1_remove(struct i2c_client *i2c)
{
snd_soc_unregister_codec(&i2c->dev);
return 0;
}
static const struct i2c_device_id wm1250_ev1_i2c_id[] = {
{ "wm1250-ev1", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, wm1250_ev1_i2c_id);
static struct i2c_driver wm1250_ev1_i2c_driver = {
.driver = {
.name = "wm1250-ev1",
.owner = THIS_MODULE,
},
.probe = wm1250_ev1_probe,
.remove = __devexit_p(wm1250_ev1_remove),
.id_table = wm1250_ev1_i2c_id,
};
static int __init wm1250_ev1_modinit(void)
{
int ret = 0;
ret = i2c_add_driver(&wm1250_ev1_i2c_driver);
if (ret != 0)
pr_err("Failed to register WM1250-EV1 I2C driver: %d\n", ret);
return ret;
}
module_init(wm1250_ev1_modinit);
static void __exit wm1250_ev1_exit(void)
{
i2c_del_driver(&wm1250_ev1_i2c_driver);
}
module_exit(wm1250_ev1_exit);
MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
MODULE_DESCRIPTION("WM1250-EV1 audio I/O module driver");
MODULE_LICENSE("GPL");

View file

@ -77,7 +77,7 @@ SND_SOC_DAPM_OUTPUT("ROUT"),
SND_SOC_DAPM_OUTPUT("RHPOUT"), SND_SOC_DAPM_OUTPUT("RHPOUT"),
}; };
static const struct snd_soc_dapm_route intercon[] = { static const struct snd_soc_dapm_route wm8711_intercon[] = {
/* output mixer */ /* output mixer */
{"Output Mixer", "Line Bypass Switch", "Line Input"}, {"Output Mixer", "Line Bypass Switch", "Line Input"},
{"Output Mixer", "HiFi Playback Switch", "DAC"}, {"Output Mixer", "HiFi Playback Switch", "DAC"},
@ -89,17 +89,6 @@ static const struct snd_soc_dapm_route intercon[] = {
{"LOUT", NULL, "Output Mixer"}, {"LOUT", NULL, "Output Mixer"},
}; };
static int wm8711_add_widgets(struct snd_soc_codec *codec)
{
struct snd_soc_dapm_context *dapm = &codec->dapm;
snd_soc_dapm_new_controls(dapm, wm8711_dapm_widgets,
ARRAY_SIZE(wm8711_dapm_widgets));
snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon));
return 0;
}
struct _coeff_div { struct _coeff_div {
u32 mclk; u32 mclk;
u32 rate; u32 rate;
@ -398,7 +387,6 @@ static int wm8711_probe(struct snd_soc_codec *codec)
snd_soc_add_controls(codec, wm8711_snd_controls, snd_soc_add_controls(codec, wm8711_snd_controls,
ARRAY_SIZE(wm8711_snd_controls)); ARRAY_SIZE(wm8711_snd_controls));
wm8711_add_widgets(codec);
return ret; return ret;
@ -420,6 +408,10 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8711 = {
.reg_cache_size = ARRAY_SIZE(wm8711_reg), .reg_cache_size = ARRAY_SIZE(wm8711_reg),
.reg_word_size = sizeof(u16), .reg_word_size = sizeof(u16),
.reg_cache_default = wm8711_reg, .reg_cache_default = wm8711_reg,
.dapm_widgets = wm8711_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(wm8711_dapm_widgets),
.dapm_routes = wm8711_intercon,
.num_dapm_routes = ARRAY_SIZE(wm8711_intercon),
}; };
#if defined(CONFIG_SPI_MASTER) #if defined(CONFIG_SPI_MASTER)

View file

@ -65,22 +65,11 @@ SND_SOC_DAPM_OUTPUT("VOUTL"),
SND_SOC_DAPM_OUTPUT("VOUTR"), SND_SOC_DAPM_OUTPUT("VOUTR"),
}; };
static const struct snd_soc_dapm_route intercon[] = { static const struct snd_soc_dapm_route wm8728_intercon[] = {
{"VOUTL", NULL, "DAC"}, {"VOUTL", NULL, "DAC"},
{"VOUTR", NULL, "DAC"}, {"VOUTR", NULL, "DAC"},
}; };
static int wm8728_add_widgets(struct snd_soc_codec *codec)
{
struct snd_soc_dapm_context *dapm = &codec->dapm;
snd_soc_dapm_new_controls(dapm, wm8728_dapm_widgets,
ARRAY_SIZE(wm8728_dapm_widgets));
snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon));
return 0;
}
static int wm8728_mute(struct snd_soc_dai *dai, int mute) static int wm8728_mute(struct snd_soc_dai *dai, int mute)
{ {
struct snd_soc_codec *codec = dai->codec; struct snd_soc_codec *codec = dai->codec;
@ -255,7 +244,6 @@ static int wm8728_probe(struct snd_soc_codec *codec)
snd_soc_add_controls(codec, wm8728_snd_controls, snd_soc_add_controls(codec, wm8728_snd_controls,
ARRAY_SIZE(wm8728_snd_controls)); ARRAY_SIZE(wm8728_snd_controls));
wm8728_add_widgets(codec);
return ret; return ret;
} }
@ -275,6 +263,10 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8728 = {
.reg_cache_size = ARRAY_SIZE(wm8728_reg_defaults), .reg_cache_size = ARRAY_SIZE(wm8728_reg_defaults),
.reg_word_size = sizeof(u16), .reg_word_size = sizeof(u16),
.reg_cache_default = wm8728_reg_defaults, .reg_cache_default = wm8728_reg_defaults,
.dapm_widgets = wm8728_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(wm8728_dapm_widgets),
.dapm_routes = wm8728_intercon,
.num_dapm_routes = ARRAY_SIZE(wm8728_intercon),
}; };
#if defined(CONFIG_SPI_MASTER) #if defined(CONFIG_SPI_MASTER)

View file

@ -201,7 +201,7 @@ static int wm8731_check_osc(struct snd_soc_dapm_widget *source,
return wm8731->sysclk_type == WM8731_SYSCLK_MCLK; return wm8731->sysclk_type == WM8731_SYSCLK_MCLK;
} }
static const struct snd_soc_dapm_route intercon[] = { static const struct snd_soc_dapm_route wm8731_intercon[] = {
{"DAC", NULL, "OSC", wm8731_check_osc}, {"DAC", NULL, "OSC", wm8731_check_osc},
{"ADC", NULL, "OSC", wm8731_check_osc}, {"ADC", NULL, "OSC", wm8731_check_osc},
@ -227,17 +227,6 @@ static const struct snd_soc_dapm_route intercon[] = {
{"Mic Bias", NULL, "MICIN"}, {"Mic Bias", NULL, "MICIN"},
}; };
static int wm8731_add_widgets(struct snd_soc_codec *codec)
{
struct snd_soc_dapm_context *dapm = &codec->dapm;
snd_soc_dapm_new_controls(dapm, wm8731_dapm_widgets,
ARRAY_SIZE(wm8731_dapm_widgets));
snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon));
return 0;
}
struct _coeff_div { struct _coeff_div {
u32 mclk; u32 mclk;
u32 rate; u32 rate;
@ -599,7 +588,6 @@ static int wm8731_probe(struct snd_soc_codec *codec)
snd_soc_add_controls(codec, wm8731_snd_controls, snd_soc_add_controls(codec, wm8731_snd_controls,
ARRAY_SIZE(wm8731_snd_controls)); ARRAY_SIZE(wm8731_snd_controls));
wm8731_add_widgets(codec);
/* Regulators will have been enabled by bias management */ /* Regulators will have been enabled by bias management */
regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies), wm8731->supplies); regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies), wm8731->supplies);
@ -636,6 +624,10 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8731 = {
.reg_cache_size = ARRAY_SIZE(wm8731_reg), .reg_cache_size = ARRAY_SIZE(wm8731_reg),
.reg_word_size = sizeof(u16), .reg_word_size = sizeof(u16),
.reg_cache_default = wm8731_reg, .reg_cache_default = wm8731_reg,
.dapm_widgets = wm8731_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(wm8731_dapm_widgets),
.dapm_routes = wm8731_intercon,
.num_dapm_routes = ARRAY_SIZE(wm8731_intercon),
}; };
#if defined(CONFIG_SPI_MASTER) #if defined(CONFIG_SPI_MASTER)
@ -667,7 +659,7 @@ static int __devexit wm8731_spi_remove(struct spi_device *spi)
static struct spi_driver wm8731_spi_driver = { static struct spi_driver wm8731_spi_driver = {
.driver = { .driver = {
.name = "wm8731-codec", .name = "wm8731",
.owner = THIS_MODULE, .owner = THIS_MODULE,
}, },
.probe = wm8731_spi_probe, .probe = wm8731_spi_probe,
@ -711,7 +703,7 @@ MODULE_DEVICE_TABLE(i2c, wm8731_i2c_id);
static struct i2c_driver wm8731_i2c_driver = { static struct i2c_driver wm8731_i2c_driver = {
.driver = { .driver = {
.name = "wm8731-codec", .name = "wm8731",
.owner = THIS_MODULE, .owner = THIS_MODULE,
}, },
.probe = wm8731_i2c_probe, .probe = wm8731_i2c_probe,

View file

@ -382,7 +382,8 @@ static void wm8903_seq_notifier(struct snd_soc_dapm_context *dapm,
static int wm8903_class_w_put(struct snd_kcontrol *kcontrol, static int wm8903_class_w_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol) struct snd_ctl_elem_value *ucontrol)
{ {
struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol); struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
struct snd_soc_dapm_widget *widget = wlist->widgets[0];
struct snd_soc_codec *codec = widget->codec; struct snd_soc_codec *codec = widget->codec;
struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec); struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec);
u16 reg; u16 reg;
@ -634,6 +635,13 @@ static const struct soc_enum lsidetone_enum =
static const struct soc_enum rsidetone_enum = static const struct soc_enum rsidetone_enum =
SOC_ENUM_SINGLE(WM8903_DAC_DIGITAL_0, 0, 3, sidetone_text); SOC_ENUM_SINGLE(WM8903_DAC_DIGITAL_0, 0, 3, sidetone_text);
static const char *adcinput_text[] = {
"ADC", "DMIC"
};
static const struct soc_enum adcinput_enum =
SOC_ENUM_SINGLE(WM8903_CLOCK_RATE_TEST_4, 9, 2, adcinput_text);
static const char *aif_text[] = { static const char *aif_text[] = {
"Left", "Right" "Left", "Right"
}; };
@ -692,7 +700,7 @@ SOC_ENUM("DRC Smoothing Threshold", drc_smoothing),
SOC_SINGLE_TLV("DRC Startup Volume", WM8903_DRC_0, 6, 18, 0, drc_tlv_startup), SOC_SINGLE_TLV("DRC Startup Volume", WM8903_DRC_0, 6, 18, 0, drc_tlv_startup),
SOC_DOUBLE_R_TLV("Digital Capture Volume", WM8903_ADC_DIGITAL_VOLUME_LEFT, SOC_DOUBLE_R_TLV("Digital Capture Volume", WM8903_ADC_DIGITAL_VOLUME_LEFT,
WM8903_ADC_DIGITAL_VOLUME_RIGHT, 1, 96, 0, digital_tlv), WM8903_ADC_DIGITAL_VOLUME_RIGHT, 1, 120, 0, digital_tlv),
SOC_ENUM("ADC Companding Mode", adc_companding), SOC_ENUM("ADC Companding Mode", adc_companding),
SOC_SINGLE("ADC Companding Switch", WM8903_AUDIO_INTERFACE_0, 3, 1, 0), SOC_SINGLE("ADC Companding Switch", WM8903_AUDIO_INTERFACE_0, 3, 1, 0),
@ -767,6 +775,9 @@ static const struct snd_kcontrol_new lsidetone_mux =
static const struct snd_kcontrol_new rsidetone_mux = static const struct snd_kcontrol_new rsidetone_mux =
SOC_DAPM_ENUM("DACR Sidetone Mux", rsidetone_enum); SOC_DAPM_ENUM("DACR Sidetone Mux", rsidetone_enum);
static const struct snd_kcontrol_new adcinput_mux =
SOC_DAPM_ENUM("ADC Input", adcinput_enum);
static const struct snd_kcontrol_new lcapture_mux = static const struct snd_kcontrol_new lcapture_mux =
SOC_DAPM_ENUM("Left Capture Mux", lcapture_enum); SOC_DAPM_ENUM("Left Capture Mux", lcapture_enum);
@ -817,6 +828,7 @@ SND_SOC_DAPM_INPUT("IN2L"),
SND_SOC_DAPM_INPUT("IN2R"), SND_SOC_DAPM_INPUT("IN2R"),
SND_SOC_DAPM_INPUT("IN3L"), SND_SOC_DAPM_INPUT("IN3L"),
SND_SOC_DAPM_INPUT("IN3R"), SND_SOC_DAPM_INPUT("IN3R"),
SND_SOC_DAPM_INPUT("DMICDAT"),
SND_SOC_DAPM_OUTPUT("HPOUTL"), SND_SOC_DAPM_OUTPUT("HPOUTL"),
SND_SOC_DAPM_OUTPUT("HPOUTR"), SND_SOC_DAPM_OUTPUT("HPOUTR"),
@ -842,6 +854,9 @@ 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("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_PGA("Right Input PGA", WM8903_POWER_MANAGEMENT_0, 0, 0, NULL, 0),
SND_SOC_DAPM_MUX("Left ADC Input", SND_SOC_NOPM, 0, 0, &adcinput_mux),
SND_SOC_DAPM_MUX("Right ADC Input", SND_SOC_NOPM, 0, 0, &adcinput_mux),
SND_SOC_DAPM_ADC("ADCL", NULL, WM8903_POWER_MANAGEMENT_6, 1, 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_ADC("ADCR", NULL, WM8903_POWER_MANAGEMENT_6, 0, 0),
@ -930,7 +945,7 @@ 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), SND_SOC_DAPM_SUPPLY("CLK_SYS", WM8903_CLOCK_RATES_2, 2, 0, NULL, 0),
}; };
static const struct snd_soc_dapm_route intercon[] = { static const struct snd_soc_dapm_route wm8903_intercon[] = {
{ "CLK_DSP", NULL, "CLK_SYS" }, { "CLK_DSP", NULL, "CLK_SYS" },
{ "Mic Bias", NULL, "CLK_SYS" }, { "Mic Bias", NULL, "CLK_SYS" },
@ -979,6 +994,11 @@ static const struct snd_soc_dapm_route intercon[] = {
{ "Left Input PGA", NULL, "Left Input Mode Mux" }, { "Left Input PGA", NULL, "Left Input Mode Mux" },
{ "Right Input PGA", NULL, "Right Input Mode Mux" }, { "Right Input PGA", NULL, "Right Input Mode Mux" },
{ "Left ADC Input", "ADC", "Left Input PGA" },
{ "Left ADC Input", "DMIC", "DMICDAT" },
{ "Right ADC Input", "ADC", "Right Input PGA" },
{ "Right ADC Input", "DMIC", "DMICDAT" },
{ "Left Capture Mux", "Left", "ADCL" }, { "Left Capture Mux", "Left", "ADCL" },
{ "Left Capture Mux", "Right", "ADCR" }, { "Left Capture Mux", "Right", "ADCR" },
@ -988,9 +1008,9 @@ static const struct snd_soc_dapm_route intercon[] = {
{ "AIFTXL", NULL, "Left Capture Mux" }, { "AIFTXL", NULL, "Left Capture Mux" },
{ "AIFTXR", NULL, "Right Capture Mux" }, { "AIFTXR", NULL, "Right Capture Mux" },
{ "ADCL", NULL, "Left Input PGA" }, { "ADCL", NULL, "Left ADC Input" },
{ "ADCL", NULL, "CLK_DSP" }, { "ADCL", NULL, "CLK_DSP" },
{ "ADCR", NULL, "Right Input PGA" }, { "ADCR", NULL, "Right ADC Input" },
{ "ADCR", NULL, "CLK_DSP" }, { "ADCR", NULL, "CLK_DSP" },
{ "Left Playback Mux", "Left", "AIFRXL" }, { "Left Playback Mux", "Left", "AIFRXL" },
@ -1087,17 +1107,6 @@ static const struct snd_soc_dapm_route intercon[] = {
{ "Right Line Output PGA", NULL, "Charge Pump" }, { "Right Line Output PGA", NULL, "Charge Pump" },
}; };
static int wm8903_add_widgets(struct snd_soc_codec *codec)
{
struct snd_soc_dapm_context *dapm = &codec->dapm;
snd_soc_dapm_new_controls(dapm, wm8903_dapm_widgets,
ARRAY_SIZE(wm8903_dapm_widgets));
snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon));
return 0;
}
static int wm8903_set_bias_level(struct snd_soc_codec *codec, static int wm8903_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level) enum snd_soc_bias_level level)
{ {
@ -2028,7 +2037,6 @@ static int wm8903_probe(struct snd_soc_codec *codec)
snd_soc_add_controls(codec, wm8903_snd_controls, snd_soc_add_controls(codec, wm8903_snd_controls,
ARRAY_SIZE(wm8903_snd_controls)); ARRAY_SIZE(wm8903_snd_controls));
wm8903_add_widgets(codec);
wm8903_init_gpio(codec); wm8903_init_gpio(codec);
@ -2054,6 +2062,10 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8903 = {
.reg_cache_default = wm8903_reg_defaults, .reg_cache_default = wm8903_reg_defaults,
.volatile_register = wm8903_volatile_register, .volatile_register = wm8903_volatile_register,
.seq_notifier = wm8903_seq_notifier, .seq_notifier = wm8903_seq_notifier,
.dapm_widgets = wm8903_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(wm8903_dapm_widgets),
.dapm_routes = wm8903_intercon,
.num_dapm_routes = ARRAY_SIZE(wm8903_intercon),
}; };
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)

2931
sound/soc/codecs/wm8915.c Normal file

File diff suppressed because it is too large Load diff

3717
sound/soc/codecs/wm8915.h Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -58,6 +58,7 @@ struct wm8962_priv {
int bclk; /* Desired BCLK */ int bclk; /* Desired BCLK */
int lrclk; int lrclk;
struct completion fll_lock;
int fll_src; int fll_src;
int fll_fref; int fll_fref;
int fll_fout; int fll_fout;
@ -2038,6 +2039,13 @@ static int wm8962_put_spk_sw(struct snd_kcontrol *kcontrol,
return 0; return 0;
} }
static const char *cap_hpf_mode_text[] = {
"Hi-fi", "Application"
};
static const struct soc_enum cap_hpf_mode =
SOC_ENUM_SINGLE(WM8962_ADC_DAC_CONTROL_2, 10, 2, cap_hpf_mode_text);
static const struct snd_kcontrol_new wm8962_snd_controls[] = { static const struct snd_kcontrol_new wm8962_snd_controls[] = {
SOC_DOUBLE("Input Mixer Switch", WM8962_INPUT_MIXER_CONTROL_1, 3, 2, 1, 1), SOC_DOUBLE("Input Mixer Switch", WM8962_INPUT_MIXER_CONTROL_1, 3, 2, 1, 1),
@ -2063,6 +2071,9 @@ SOC_DOUBLE_R("Capture Switch", WM8962_LEFT_INPUT_VOLUME,
WM8962_RIGHT_INPUT_VOLUME, 7, 1, 1), WM8962_RIGHT_INPUT_VOLUME, 7, 1, 1),
SOC_DOUBLE_R("Capture ZC Switch", WM8962_LEFT_INPUT_VOLUME, SOC_DOUBLE_R("Capture ZC Switch", WM8962_LEFT_INPUT_VOLUME,
WM8962_RIGHT_INPUT_VOLUME, 6, 1, 1), WM8962_RIGHT_INPUT_VOLUME, 6, 1, 1),
SOC_SINGLE("Capture HPF Switch", WM8962_ADC_DAC_CONTROL_1, 0, 1, 1),
SOC_ENUM("Capture HPF Mode", cap_hpf_mode),
SOC_SINGLE("Capture HPF Cutoff", WM8962_ADC_DAC_CONTROL_2, 7, 7, 0),
SOC_DOUBLE_R_TLV("Sidetone Volume", WM8962_DAC_DSP_MIXING_1, SOC_DOUBLE_R_TLV("Sidetone Volume", WM8962_DAC_DSP_MIXING_1,
WM8962_DAC_DSP_MIXING_2, 4, 12, 0, st_tlv), WM8962_DAC_DSP_MIXING_2, 4, 12, 0, st_tlv),
@ -2467,6 +2478,7 @@ SND_SOC_DAPM_INPUT("IN3R"),
SND_SOC_DAPM_INPUT("IN4L"), SND_SOC_DAPM_INPUT("IN4L"),
SND_SOC_DAPM_INPUT("IN4R"), SND_SOC_DAPM_INPUT("IN4R"),
SND_SOC_DAPM_INPUT("Beep"), SND_SOC_DAPM_INPUT("Beep"),
SND_SOC_DAPM_INPUT("DMICDAT"),
SND_SOC_DAPM_MICBIAS("MICBIAS", WM8962_PWR_MGMT_1, 1, 0), SND_SOC_DAPM_MICBIAS("MICBIAS", WM8962_PWR_MGMT_1, 1, 0),
@ -2486,6 +2498,8 @@ SND_SOC_DAPM_MIXER("MIXINL", WM8962_PWR_MGMT_1, 5, 0,
SND_SOC_DAPM_MIXER("MIXINR", WM8962_PWR_MGMT_1, 4, 0, SND_SOC_DAPM_MIXER("MIXINR", WM8962_PWR_MGMT_1, 4, 0,
mixinr, ARRAY_SIZE(mixinr)), mixinr, ARRAY_SIZE(mixinr)),
SND_SOC_DAPM_AIF_IN("DMIC", NULL, 0, WM8962_PWR_MGMT_1, 10, 0),
SND_SOC_DAPM_ADC("ADCL", "Capture", WM8962_PWR_MGMT_1, 3, 0), SND_SOC_DAPM_ADC("ADCL", "Capture", WM8962_PWR_MGMT_1, 3, 0),
SND_SOC_DAPM_ADC("ADCR", "Capture", WM8962_PWR_MGMT_1, 2, 0), SND_SOC_DAPM_ADC("ADCR", "Capture", WM8962_PWR_MGMT_1, 2, 0),
@ -2563,13 +2577,17 @@ static const struct snd_soc_dapm_route wm8962_intercon[] = {
{ "MICBIAS", NULL, "SYSCLK" }, { "MICBIAS", NULL, "SYSCLK" },
{ "DMIC", NULL, "DMICDAT" },
{ "ADCL", NULL, "SYSCLK" }, { "ADCL", NULL, "SYSCLK" },
{ "ADCL", NULL, "TOCLK" }, { "ADCL", NULL, "TOCLK" },
{ "ADCL", NULL, "MIXINL" }, { "ADCL", NULL, "MIXINL" },
{ "ADCL", NULL, "DMIC" },
{ "ADCR", NULL, "SYSCLK" }, { "ADCR", NULL, "SYSCLK" },
{ "ADCR", NULL, "TOCLK" }, { "ADCR", NULL, "TOCLK" },
{ "ADCR", NULL, "MIXINR" }, { "ADCR", NULL, "MIXINR" },
{ "ADCR", NULL, "DMIC" },
{ "STL", "Left", "ADCL" }, { "STL", "Left", "ADCL" },
{ "STL", "Right", "ADCR" }, { "STL", "Right", "ADCR" },
@ -2990,7 +3008,6 @@ static int wm8962_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
case WM8962_SYSCLK_FLL: case WM8962_SYSCLK_FLL:
wm8962->sysclk = WM8962_SYSCLK_FLL; wm8962->sysclk = WM8962_SYSCLK_FLL;
src = 1 << WM8962_SYSCLK_SRC_SHIFT; src = 1 << WM8962_SYSCLK_SRC_SHIFT;
WARN_ON(freq != wm8962->fll_fout);
break; break;
default: default:
return -EINVAL; return -EINVAL;
@ -3172,12 +3189,12 @@ static int fll_factors(struct _fll_div *fll_div, unsigned int Fref,
return 0; return 0;
} }
static int wm8962_set_fll(struct snd_soc_dai *dai, int fll_id, int source, static int wm8962_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
unsigned int Fref, unsigned int Fout) unsigned int Fref, unsigned int Fout)
{ {
struct snd_soc_codec *codec = dai->codec;
struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec); struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
struct _fll_div fll_div; struct _fll_div fll_div;
unsigned long timeout;
int ret; int ret;
int fll1 = snd_soc_read(codec, WM8962_FLL_CONTROL_1) & WM8962_FLL_ENA; int fll1 = snd_soc_read(codec, WM8962_FLL_CONTROL_1) & WM8962_FLL_ENA;
@ -3244,6 +3261,11 @@ static int wm8962_set_fll(struct snd_soc_dai *dai, int fll_id, int source,
dev_dbg(codec->dev, "FLL configured for %dHz->%dHz\n", Fref, Fout); dev_dbg(codec->dev, "FLL configured for %dHz->%dHz\n", Fref, Fout);
/* This should be a massive overestimate */
timeout = msecs_to_jiffies(1);
wait_for_completion_timeout(&wm8962->fll_lock, timeout);
wm8962->fll_fref = Fref; wm8962->fll_fref = Fref;
wm8962->fll_fout = Fout; wm8962->fll_fout = Fout;
wm8962->fll_src = source; wm8962->fll_src = source;
@ -3274,7 +3296,6 @@ static struct snd_soc_dai_ops wm8962_dai_ops = {
.hw_params = wm8962_hw_params, .hw_params = wm8962_hw_params,
.set_sysclk = wm8962_set_dai_sysclk, .set_sysclk = wm8962_set_dai_sysclk,
.set_fmt = wm8962_set_dai_fmt, .set_fmt = wm8962_set_dai_fmt,
.set_pll = wm8962_set_fll,
.digital_mute = wm8962_mute, .digital_mute = wm8962_mute,
}; };
@ -3340,6 +3361,11 @@ static irqreturn_t wm8962_irq(int irq, void *data)
active = snd_soc_read(codec, WM8962_INTERRUPT_STATUS_2); active = snd_soc_read(codec, WM8962_INTERRUPT_STATUS_2);
active &= ~mask; active &= ~mask;
if (active & WM8962_FLL_LOCK_EINT) {
dev_dbg(codec->dev, "FLL locked\n");
complete(&wm8962->fll_lock);
}
if (active & WM8962_FIFOS_ERR_EINT) if (active & WM8962_FIFOS_ERR_EINT)
dev_err(codec->dev, "FIFO error\n"); dev_err(codec->dev, "FIFO error\n");
@ -3709,9 +3735,11 @@ static int wm8962_probe(struct snd_soc_codec *codec)
dev); dev);
u16 *reg_cache = codec->reg_cache; u16 *reg_cache = codec->reg_cache;
int i, trigger, irq_pol; int i, trigger, irq_pol;
bool dmicclk, dmicdat;
wm8962->codec = codec; wm8962->codec = codec;
INIT_DELAYED_WORK(&wm8962->mic_work, wm8962_mic_work); INIT_DELAYED_WORK(&wm8962->mic_work, wm8962_mic_work);
init_completion(&wm8962->fll_lock);
codec->cache_sync = 1; codec->cache_sync = 1;
codec->dapm.idle_bias_off = 1; codec->dapm.idle_bias_off = 1;
@ -3845,6 +3873,29 @@ static int wm8962_probe(struct snd_soc_codec *codec)
wm8962_add_widgets(codec); wm8962_add_widgets(codec);
/* Save boards having to disable DMIC when not in use */
dmicclk = false;
dmicdat = false;
for (i = 0; i < WM8962_MAX_GPIO; i++) {
switch (snd_soc_read(codec, WM8962_GPIO_BASE + i)
& WM8962_GP2_FN_MASK) {
case WM8962_GPIO_FN_DMICCLK:
dmicclk = true;
break;
case WM8962_GPIO_FN_DMICDAT:
dmicdat = true;
break;
default:
break;
}
}
if (!dmicclk || !dmicdat) {
dev_dbg(codec->dev, "DMIC not in use, disabling\n");
snd_soc_dapm_nc_pin(&codec->dapm, "DMICDAT");
}
if (dmicclk != dmicdat)
dev_warn(codec->dev, "DMIC GPIOs partially configured\n");
wm8962_init_beep(codec); wm8962_init_beep(codec);
wm8962_init_gpio(codec); wm8962_init_gpio(codec);
@ -3868,9 +3919,10 @@ static int wm8962_probe(struct snd_soc_codec *codec)
i2c->irq, ret); i2c->irq, ret);
/* Non-fatal */ /* Non-fatal */
} else { } else {
/* Enable error reporting IRQs by default */ /* Enable some IRQs by default */
snd_soc_update_bits(codec, snd_soc_update_bits(codec,
WM8962_INTERRUPT_STATUS_2_MASK, WM8962_INTERRUPT_STATUS_2_MASK,
WM8962_FLL_LOCK_EINT |
WM8962_TEMP_SHUT_EINT | WM8962_TEMP_SHUT_EINT |
WM8962_FIFOS_ERR_EINT, 0); WM8962_FIFOS_ERR_EINT, 0);
} }
@ -3918,6 +3970,7 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8962 = {
.reg_cache_default = wm8962_reg, .reg_cache_default = wm8962_reg,
.volatile_register = wm8962_volatile_register, .volatile_register = wm8962_volatile_register,
.readable_register = wm8962_readable_register, .readable_register = wm8962_readable_register,
.set_pll = wm8962_set_fll,
}; };
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)

View file

@ -718,7 +718,8 @@ static int clk_sys_event(struct snd_soc_dapm_widget *w,
static int class_w_put(struct snd_kcontrol *kcontrol, static int class_w_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol) struct snd_ctl_elem_value *ucontrol)
{ {
struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol); struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
struct snd_soc_dapm_widget *widget = wlist->widgets[0];
struct snd_soc_codec *codec = widget->codec; struct snd_soc_codec *codec = widget->codec;
struct wm8993_priv *wm8993 = snd_soc_codec_get_drvdata(codec); struct wm8993_priv *wm8993 = snd_soc_codec_get_drvdata(codec);
int ret; int ret;

View file

@ -38,12 +38,6 @@
#include "wm8994.h" #include "wm8994.h"
#include "wm_hubs.h" #include "wm_hubs.h"
struct fll_config {
int src;
int in;
int out;
};
#define WM8994_NUM_DRC 3 #define WM8994_NUM_DRC 3
#define WM8994_NUM_EQ 3 #define WM8994_NUM_EQ 3
@ -59,63 +53,11 @@ static int wm8994_retune_mobile_base[] = {
WM8994_AIF2_EQ_GAINS_1, WM8994_AIF2_EQ_GAINS_1,
}; };
struct wm8994_micdet {
struct snd_soc_jack *jack;
int det;
int shrt;
};
/* codec private data */
struct wm8994_priv {
struct wm_hubs_data hubs;
enum snd_soc_control_type control_type;
void *control_data;
struct snd_soc_codec *codec;
int sysclk[2];
int sysclk_rate[2];
int mclk[2];
int aifclk[2];
struct fll_config fll[2], fll_suspend[2];
int dac_rates[2];
int lrclk_shared[2];
int mbc_ena[3];
/* Platform dependent DRC configuration */
const char **drc_texts;
int drc_cfg[WM8994_NUM_DRC];
struct soc_enum drc_enum;
/* Platform dependent ReTune mobile configuration */
int num_retune_mobile_texts;
const char **retune_mobile_texts;
int retune_mobile_cfg[WM8994_NUM_EQ];
struct soc_enum retune_mobile_enum;
/* Platform dependent MBC configuration */
int mbc_cfg;
const char **mbc_texts;
struct soc_enum mbc_enum;
struct wm8994_micdet micdet[2];
wm8958_micdet_cb jack_cb;
void *jack_cb_data;
int micdet_irq;
int revision;
struct wm8994_pdata *pdata;
unsigned int aif1clk_enable:1;
unsigned int aif2clk_enable:1;
unsigned int aif1clk_disable:1;
unsigned int aif2clk_disable:1;
};
static int wm8994_readable(struct snd_soc_codec *codec, unsigned int reg) static int wm8994_readable(struct snd_soc_codec *codec, unsigned int reg)
{ {
struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
struct wm8994 *control = wm8994->control_data;
switch (reg) { switch (reg) {
case WM8994_GPIO_1: case WM8994_GPIO_1:
case WM8994_GPIO_2: case WM8994_GPIO_2:
@ -132,6 +74,15 @@ static int wm8994_readable(struct snd_soc_codec *codec, unsigned int reg)
case WM8994_INTERRUPT_STATUS_2: case WM8994_INTERRUPT_STATUS_2:
case WM8994_INTERRUPT_RAW_STATUS_2: case WM8994_INTERRUPT_RAW_STATUS_2:
return 1; return 1;
case WM8958_DSP2_PROGRAM:
case WM8958_DSP2_CONFIG:
case WM8958_DSP2_EXECCONTROL:
if (control->type == WM8958)
return 1;
else
return 0;
default: default:
break; break;
} }
@ -574,215 +525,6 @@ static const struct soc_enum dac_osr =
static const struct soc_enum adc_osr = static const struct soc_enum adc_osr =
SOC_ENUM_SINGLE(WM8994_OVERSAMPLING, 1, 2, osr_text); SOC_ENUM_SINGLE(WM8994_OVERSAMPLING, 1, 2, osr_text);
static void wm8958_mbc_apply(struct snd_soc_codec *codec, int mbc, int start)
{
struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
struct wm8994_pdata *pdata = wm8994->pdata;
int pwr_reg = snd_soc_read(codec, WM8994_POWER_MANAGEMENT_5);
int ena, reg, aif, i;
switch (mbc) {
case 0:
pwr_reg &= (WM8994_AIF1DAC1L_ENA | WM8994_AIF1DAC1R_ENA);
aif = 0;
break;
case 1:
pwr_reg &= (WM8994_AIF1DAC2L_ENA | WM8994_AIF1DAC2R_ENA);
aif = 0;
break;
case 2:
pwr_reg &= (WM8994_AIF2DACL_ENA | WM8994_AIF2DACR_ENA);
aif = 1;
break;
default:
BUG();
return;
}
/* We can only enable the MBC if the AIF is enabled and we
* want it to be enabled. */
ena = pwr_reg && wm8994->mbc_ena[mbc];
reg = snd_soc_read(codec, WM8958_DSP2_PROGRAM);
dev_dbg(codec->dev, "MBC %d startup: %d, power: %x, DSP: %x\n",
mbc, start, pwr_reg, reg);
if (start && ena) {
/* If the DSP is already running then noop */
if (reg & WM8958_DSP2_ENA)
return;
/* Switch the clock over to the appropriate AIF */
snd_soc_update_bits(codec, WM8994_CLOCKING_1,
WM8958_DSP2CLK_SRC | WM8958_DSP2CLK_ENA,
aif << WM8958_DSP2CLK_SRC_SHIFT |
WM8958_DSP2CLK_ENA);
snd_soc_update_bits(codec, WM8958_DSP2_PROGRAM,
WM8958_DSP2_ENA, WM8958_DSP2_ENA);
/* If we've got user supplied MBC settings use them */
if (pdata && pdata->num_mbc_cfgs) {
struct wm8958_mbc_cfg *cfg
= &pdata->mbc_cfgs[wm8994->mbc_cfg];
for (i = 0; i < ARRAY_SIZE(cfg->coeff_regs); i++)
snd_soc_write(codec, i + WM8958_MBC_BAND_1_K_1,
cfg->coeff_regs[i]);
for (i = 0; i < ARRAY_SIZE(cfg->cutoff_regs); i++)
snd_soc_write(codec,
i + WM8958_MBC_BAND_2_LOWER_CUTOFF_C1_1,
cfg->cutoff_regs[i]);
}
/* Run the DSP */
snd_soc_write(codec, WM8958_DSP2_EXECCONTROL,
WM8958_DSP2_RUNR);
/* And we're off! */
snd_soc_update_bits(codec, WM8958_DSP2_CONFIG,
WM8958_MBC_ENA | WM8958_MBC_SEL_MASK,
mbc << WM8958_MBC_SEL_SHIFT |
WM8958_MBC_ENA);
} else {
/* If the DSP is already stopped then noop */
if (!(reg & WM8958_DSP2_ENA))
return;
snd_soc_update_bits(codec, WM8958_DSP2_CONFIG,
WM8958_MBC_ENA, 0);
snd_soc_update_bits(codec, WM8958_DSP2_PROGRAM,
WM8958_DSP2_ENA, 0);
snd_soc_update_bits(codec, WM8994_CLOCKING_1,
WM8958_DSP2CLK_ENA, 0);
}
}
static int wm8958_aif_ev(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = w->codec;
int mbc;
switch (w->shift) {
case 13:
case 12:
mbc = 2;
break;
case 11:
case 10:
mbc = 1;
break;
case 9:
case 8:
mbc = 0;
break;
default:
BUG();
return -EINVAL;
}
switch (event) {
case SND_SOC_DAPM_POST_PMU:
wm8958_mbc_apply(codec, mbc, 1);
break;
case SND_SOC_DAPM_POST_PMD:
wm8958_mbc_apply(codec, mbc, 0);
break;
}
return 0;
}
static int wm8958_put_mbc_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_pdata *pdata = wm8994->pdata;
int value = ucontrol->value.integer.value[0];
int reg;
/* Don't allow on the fly reconfiguration */
reg = snd_soc_read(codec, WM8994_CLOCKING_1);
if (reg < 0 || reg & WM8958_DSP2CLK_ENA)
return -EBUSY;
if (value >= pdata->num_mbc_cfgs)
return -EINVAL;
wm8994->mbc_cfg = value;
return 0;
}
static int wm8958_get_mbc_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);
ucontrol->value.enumerated.item[0] = wm8994->mbc_cfg;
return 0;
}
static int wm8958_mbc_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
uinfo->count = 1;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = 1;
return 0;
}
static int wm8958_mbc_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
int mbc = kcontrol->private_value;
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
ucontrol->value.integer.value[0] = wm8994->mbc_ena[mbc];
return 0;
}
static int wm8958_mbc_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
int mbc = kcontrol->private_value;
int i;
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
if (ucontrol->value.integer.value[0] > 1)
return -EINVAL;
for (i = 0; i < ARRAY_SIZE(wm8994->mbc_ena); i++) {
if (mbc != i && wm8994->mbc_ena[i]) {
dev_dbg(codec->dev, "MBC %d active already\n", mbc);
return -EBUSY;
}
}
wm8994->mbc_ena[mbc] = ucontrol->value.integer.value[0];
wm8958_mbc_apply(codec, mbc, wm8994->mbc_ena[mbc]);
return 0;
}
#define WM8958_MBC_SWITCH(xname, xval) {\
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,\
.info = wm8958_mbc_info, \
.get = wm8958_mbc_get, .put = wm8958_mbc_put, \
.private_value = xval }
static const struct snd_kcontrol_new wm8994_snd_controls[] = { static const struct snd_kcontrol_new wm8994_snd_controls[] = {
SOC_DOUBLE_R_TLV("AIF1ADC1 Volume", WM8994_AIF1_ADC1_LEFT_VOLUME, SOC_DOUBLE_R_TLV("AIF1ADC1 Volume", WM8994_AIF1_ADC1_LEFT_VOLUME,
WM8994_AIF1_ADC1_RIGHT_VOLUME, WM8994_AIF1_ADC1_RIGHT_VOLUME,
@ -924,9 +666,6 @@ SOC_SINGLE_TLV("AIF2 EQ5 Volume", WM8994_AIF2_EQ_GAINS_2, 6, 31, 0,
static const struct snd_kcontrol_new wm8958_snd_controls[] = { static const struct snd_kcontrol_new wm8958_snd_controls[] = {
SOC_SINGLE_TLV("AIF3 Boost Volume", WM8958_AIF3_CONTROL_2, 10, 3, 0, aif_tlv), SOC_SINGLE_TLV("AIF3 Boost Volume", WM8958_AIF3_CONTROL_2, 10, 3, 0, aif_tlv),
WM8958_MBC_SWITCH("AIF1DAC1 MBC Switch", 0),
WM8958_MBC_SWITCH("AIF1DAC2 MBC Switch", 1),
WM8958_MBC_SWITCH("AIF2DAC MBC Switch", 2),
}; };
static int clk_sys_event(struct snd_soc_dapm_widget *w, static int clk_sys_event(struct snd_soc_dapm_widget *w,
@ -1032,6 +771,9 @@ static int late_enable_ev(struct snd_soc_dapm_widget *w,
break; break;
} }
/* We may also have postponed startup of DSP, handle that. */
wm8958_aif_ev(w, kcontrol, event);
return 0; return 0;
} }
@ -1135,7 +877,8 @@ static const char *hp_mux_text[] = {
static int wm8994_put_hp_enum(struct snd_kcontrol *kcontrol, static int wm8994_put_hp_enum(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol) struct snd_ctl_elem_value *ucontrol)
{ {
struct snd_soc_dapm_widget *w = snd_kcontrol_chip(kcontrol); struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
struct snd_soc_dapm_widget *w = wlist->widgets[0];
struct snd_soc_codec *codec = w->codec; struct snd_soc_codec *codec = w->codec;
int ret; int ret;
@ -1262,7 +1005,8 @@ SOC_DAPM_SINGLE("AIF1.1 Switch", WM8994_DAC2_RIGHT_MIXER_ROUTING,
static int wm8994_put_class_w(struct snd_kcontrol *kcontrol, static int wm8994_put_class_w(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol) struct snd_ctl_elem_value *ucontrol)
{ {
struct snd_soc_dapm_widget *w = snd_kcontrol_chip(kcontrol); struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
struct snd_soc_dapm_widget *w = wlist->widgets[0];
struct snd_soc_codec *codec = w->codec; struct snd_soc_codec *codec = w->codec;
int ret; int ret;
@ -2180,6 +1924,8 @@ static int wm8994_set_bias_level(struct snd_soc_codec *codec,
WM8994_VMID_BUF_ENA | WM8994_VMID_BUF_ENA |
WM8994_VMID_RAMP_MASK, 0); WM8994_VMID_RAMP_MASK, 0);
wm8994->cur_fw = NULL;
pm_runtime_put(codec->dev); pm_runtime_put(codec->dev);
} }
break; break;
@ -2672,11 +2418,22 @@ static struct snd_soc_dai_driver wm8994_dai[] = {
static int wm8994_suspend(struct snd_soc_codec *codec, pm_message_t state) static int wm8994_suspend(struct snd_soc_codec *codec, pm_message_t state)
{ {
struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
struct wm8994 *control = codec->control_data;
int i, ret; int i, ret;
switch (control->type) {
case WM8994:
snd_soc_update_bits(codec, WM8994_MICBIAS, WM8994_MICD_ENA, 0);
break;
case WM8958:
snd_soc_update_bits(codec, WM8958_MIC_DETECT_1,
WM8958_MICD_ENA, 0);
break;
}
for (i = 0; i < ARRAY_SIZE(wm8994->fll); i++) { for (i = 0; i < ARRAY_SIZE(wm8994->fll); i++) {
memcpy(&wm8994->fll_suspend[i], &wm8994->fll[i], memcpy(&wm8994->fll_suspend[i], &wm8994->fll[i],
sizeof(struct fll_config)); sizeof(struct wm8994_fll_config));
ret = _wm8994_set_fll(codec, i + 1, 0, 0, 0); ret = _wm8994_set_fll(codec, i + 1, 0, 0, 0);
if (ret < 0) if (ret < 0)
dev_warn(codec->dev, "Failed to stop FLL%d: %d\n", dev_warn(codec->dev, "Failed to stop FLL%d: %d\n",
@ -2691,6 +2448,7 @@ static int wm8994_suspend(struct snd_soc_codec *codec, pm_message_t state)
static int wm8994_resume(struct snd_soc_codec *codec) static int wm8994_resume(struct snd_soc_codec *codec)
{ {
struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
struct wm8994 *control = codec->control_data;
int i, ret; int i, ret;
unsigned int val, mask; unsigned int val, mask;
@ -2729,6 +2487,19 @@ static int wm8994_resume(struct snd_soc_codec *codec)
i + 1, ret); i + 1, ret);
} }
switch (control->type) {
case WM8994:
if (wm8994->micdet[0].jack || wm8994->micdet[1].jack)
snd_soc_update_bits(codec, WM8994_MICBIAS,
WM8994_MICD_ENA, WM8994_MICD_ENA);
break;
case WM8958:
if (wm8994->jack_cb)
snd_soc_update_bits(codec, WM8958_MIC_DETECT_1,
WM8958_MICD_ENA, WM8958_MICD_ENA);
break;
}
return 0; return 0;
} }
#else #else
@ -2862,34 +2633,6 @@ static void wm8994_handle_pdata(struct wm8994_priv *wm8994)
dev_dbg(codec->dev, "%d ReTune Mobile configurations\n", dev_dbg(codec->dev, "%d ReTune Mobile configurations\n",
pdata->num_retune_mobile_cfgs); pdata->num_retune_mobile_cfgs);
if (pdata->num_mbc_cfgs) {
struct snd_kcontrol_new control[] = {
SOC_ENUM_EXT("MBC Mode", wm8994->mbc_enum,
wm8958_get_mbc_enum, wm8958_put_mbc_enum),
};
/* We need an array of texts for the enum API */
wm8994->mbc_texts = kmalloc(sizeof(char *)
* pdata->num_mbc_cfgs, GFP_KERNEL);
if (!wm8994->mbc_texts) {
dev_err(wm8994->codec->dev,
"Failed to allocate %d MBC config texts\n",
pdata->num_mbc_cfgs);
return;
}
for (i = 0; i < pdata->num_mbc_cfgs; i++)
wm8994->mbc_texts[i] = pdata->mbc_cfgs[i].name;
wm8994->mbc_enum.max = pdata->num_mbc_cfgs;
wm8994->mbc_enum.texts = wm8994->mbc_texts;
ret = snd_soc_add_controls(wm8994->codec, control, 1);
if (ret != 0)
dev_err(wm8994->codec->dev,
"Failed to add MBC mode controls: %d\n", ret);
}
if (pdata->num_retune_mobile_cfgs) if (pdata->num_retune_mobile_cfgs)
wm8994_handle_retune_mobile_pdata(wm8994); wm8994_handle_retune_mobile_pdata(wm8994);
else else
@ -3343,14 +3086,23 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
case WM8958: case WM8958:
snd_soc_add_controls(codec, wm8958_snd_controls, snd_soc_add_controls(codec, wm8958_snd_controls,
ARRAY_SIZE(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, snd_soc_dapm_new_controls(dapm, wm8958_dapm_widgets,
ARRAY_SIZE(wm8958_dapm_widgets)); ARRAY_SIZE(wm8958_dapm_widgets));
if (wm8994->revision < 1) {
snd_soc_dapm_new_controls(dapm, wm8994_lateclk_revd_widgets,
ARRAY_SIZE(wm8994_lateclk_revd_widgets));
snd_soc_dapm_new_controls(dapm, wm8994_adc_revd_widgets,
ARRAY_SIZE(wm8994_adc_revd_widgets));
snd_soc_dapm_new_controls(dapm, wm8994_dac_revd_widgets,
ARRAY_SIZE(wm8994_dac_revd_widgets));
} else {
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));
}
break; break;
} }
@ -3374,10 +3126,19 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
} }
break; break;
case WM8958: case WM8958:
snd_soc_dapm_add_routes(dapm, wm8994_lateclk_intercon, if (wm8994->revision < 1) {
ARRAY_SIZE(wm8994_lateclk_intercon)); snd_soc_dapm_add_routes(dapm, wm8994_revd_intercon,
snd_soc_dapm_add_routes(dapm, wm8958_intercon, ARRAY_SIZE(wm8994_revd_intercon));
ARRAY_SIZE(wm8958_intercon)); snd_soc_dapm_add_routes(dapm, wm8994_lateclk_revd_intercon,
ARRAY_SIZE(wm8994_lateclk_revd_intercon));
} else {
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));
}
wm8958_dsp2_init(codec);
break; break;
} }
@ -3420,6 +3181,12 @@ static int wm8994_codec_remove(struct snd_soc_codec *codec)
free_irq(wm8994->micdet_irq, wm8994); free_irq(wm8994->micdet_irq, wm8994);
break; break;
} }
if (wm8994->mbc)
release_firmware(wm8994->mbc);
if (wm8994->mbc_vss)
release_firmware(wm8994->mbc_vss);
if (wm8994->enh_eq)
release_firmware(wm8994->enh_eq);
kfree(wm8994->retune_mobile_texts); kfree(wm8994->retune_mobile_texts);
kfree(wm8994->drc_texts); kfree(wm8994->drc_texts);
kfree(wm8994); kfree(wm8994);

View file

@ -10,6 +10,9 @@
#define _WM8994_H #define _WM8994_H
#include <sound/soc.h> #include <sound/soc.h>
#include <linux/firmware.h>
#include "wm_hubs.h"
/* Sources for AIF1/2 SYSCLK - use with set_dai_sysclk() */ /* Sources for AIF1/2 SYSCLK - use with set_dai_sysclk() */
#define WM8994_SYSCLK_MCLK1 1 #define WM8994_SYSCLK_MCLK1 1
@ -45,4 +48,98 @@ struct wm8994_access_mask {
extern const struct wm8994_access_mask wm8994_access_masks[WM8994_CACHE_SIZE]; extern const struct wm8994_access_mask wm8994_access_masks[WM8994_CACHE_SIZE];
extern const u16 wm8994_reg_defaults[WM8994_CACHE_SIZE]; extern const u16 wm8994_reg_defaults[WM8994_CACHE_SIZE];
int wm8958_aif_ev(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event);
void wm8958_dsp2_init(struct snd_soc_codec *codec);
struct wm8994_micdet {
struct snd_soc_jack *jack;
int det;
int shrt;
};
/* codec private data */
struct wm8994_fll_config {
int src;
int in;
int out;
};
#define WM8994_NUM_DRC 3
#define WM8994_NUM_EQ 3
struct wm8994_priv {
struct wm_hubs_data hubs;
enum snd_soc_control_type control_type;
void *control_data;
struct snd_soc_codec *codec;
int sysclk[2];
int sysclk_rate[2];
int mclk[2];
int aifclk[2];
struct wm8994_fll_config fll[2], fll_suspend[2];
int dac_rates[2];
int lrclk_shared[2];
int mbc_ena[3];
int hpf1_ena[3];
int hpf2_ena[3];
int vss_ena[3];
int enh_eq_ena[3];
/* Platform dependant DRC configuration */
const char **drc_texts;
int drc_cfg[WM8994_NUM_DRC];
struct soc_enum drc_enum;
/* Platform dependant ReTune mobile configuration */
int num_retune_mobile_texts;
const char **retune_mobile_texts;
int retune_mobile_cfg[WM8994_NUM_EQ];
struct soc_enum retune_mobile_enum;
/* Platform dependant MBC configuration */
int mbc_cfg;
const char **mbc_texts;
struct soc_enum mbc_enum;
/* Platform dependant VSS configuration */
int vss_cfg;
const char **vss_texts;
struct soc_enum vss_enum;
/* Platform dependant VSS HPF configuration */
int vss_hpf_cfg;
const char **vss_hpf_texts;
struct soc_enum vss_hpf_enum;
/* Platform dependant enhanced EQ configuration */
int enh_eq_cfg;
const char **enh_eq_texts;
struct soc_enum enh_eq_enum;
struct wm8994_micdet micdet[2];
wm8958_micdet_cb jack_cb;
void *jack_cb_data;
int micdet_irq;
int revision;
struct wm8994_pdata *pdata;
unsigned int aif1clk_enable:1;
unsigned int aif2clk_enable:1;
unsigned int aif1clk_disable:1;
unsigned int aif2clk_disable:1;
int dsp_active;
const struct firmware *cur_fw;
const struct firmware *mbc;
const struct firmware *mbc_vss;
const struct firmware *enh_eq;
};
#endif #endif

View file

@ -305,11 +305,11 @@ static int check_clk_sys(struct snd_soc_dapm_widget *source,
static int wm8995_put_class_w(struct snd_kcontrol *kcontrol, static int wm8995_put_class_w(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol) struct snd_ctl_elem_value *ucontrol)
{ {
struct snd_soc_dapm_widget *w; struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
struct snd_soc_dapm_widget *w = wlist->widgets[0];
struct snd_soc_codec *codec; struct snd_soc_codec *codec;
int ret; int ret;
w = snd_kcontrol_chip(kcontrol);
codec = w->codec; codec = w->codec;
ret = snd_soc_dapm_put_volsw(kcontrol, ucontrol); ret = snd_soc_dapm_put_volsw(kcontrol, ucontrol);
wm8995_update_class_w(codec); wm8995_update_class_w(codec);

View file

@ -142,7 +142,7 @@ static const struct snd_soc_dapm_widget wm9705_dapm_widgets[] = {
* constantly enabled, we use the mutes on those inputs to simulate such * constantly enabled, we use the mutes on those inputs to simulate such
* controls. * controls.
*/ */
static const struct snd_soc_dapm_route audio_map[] = { static const struct snd_soc_dapm_route wm9705_audio_map[] = {
/* HP mixer */ /* HP mixer */
{"HP Mixer", "PCBeep Playback Switch", "PCBEEP PGA"}, {"HP Mixer", "PCBeep Playback Switch", "PCBEEP PGA"},
{"HP Mixer", "CD Playback Switch", "CD PGA"}, {"HP Mixer", "CD Playback Switch", "CD PGA"},
@ -200,17 +200,6 @@ static const struct snd_soc_dapm_route audio_map[] = {
{"Right ADC", NULL, "ADC PGA"}, {"Right ADC", NULL, "ADC PGA"},
}; };
static int wm9705_add_widgets(struct snd_soc_codec *codec)
{
struct snd_soc_dapm_context *dapm = &codec->dapm;
snd_soc_dapm_new_controls(dapm, wm9705_dapm_widgets,
ARRAY_SIZE(wm9705_dapm_widgets));
snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map));
return 0;
}
/* We use a register cache to enhance read performance. */ /* We use a register cache to enhance read performance. */
static unsigned int ac97_read(struct snd_soc_codec *codec, unsigned int reg) static unsigned int ac97_read(struct snd_soc_codec *codec, unsigned int reg)
{ {
@ -364,7 +353,6 @@ static int wm9705_soc_probe(struct snd_soc_codec *codec)
snd_soc_add_controls(codec, wm9705_snd_ac97_controls, snd_soc_add_controls(codec, wm9705_snd_ac97_controls,
ARRAY_SIZE(wm9705_snd_ac97_controls)); ARRAY_SIZE(wm9705_snd_ac97_controls));
wm9705_add_widgets(codec);
return 0; return 0;
@ -390,6 +378,10 @@ static struct snd_soc_codec_driver soc_codec_dev_wm9705 = {
.reg_word_size = sizeof(u16), .reg_word_size = sizeof(u16),
.reg_cache_step = 2, .reg_cache_step = 2,
.reg_cache_default = wm9705_reg, .reg_cache_default = wm9705_reg,
.dapm_widgets = wm9705_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(wm9705_dapm_widgets),
.dapm_routes = wm9705_audio_map,
.num_dapm_routes = ARRAY_SIZE(wm9705_audio_map),
}; };
static __devinit int wm9705_probe(struct platform_device *pdev) static __devinit int wm9705_probe(struct platform_device *pdev)

View file

@ -332,7 +332,7 @@ SND_SOC_DAPM_INPUT("MIC1"),
SND_SOC_DAPM_INPUT("MIC2"), SND_SOC_DAPM_INPUT("MIC2"),
}; };
static const struct snd_soc_dapm_route audio_map[] = { static const struct snd_soc_dapm_route wm9712_audio_map[] = {
/* virtual mixer - mixes left & right channels for spk and mono */ /* virtual mixer - mixes left & right channels for spk and mono */
{"AC97 Mixer", NULL, "Left DAC"}, {"AC97 Mixer", NULL, "Left DAC"},
{"AC97 Mixer", NULL, "Right DAC"}, {"AC97 Mixer", NULL, "Right DAC"},
@ -429,17 +429,6 @@ static const struct snd_soc_dapm_route audio_map[] = {
{"ROUT2", NULL, "Speaker PGA"}, {"ROUT2", NULL, "Speaker PGA"},
}; };
static int wm9712_add_widgets(struct snd_soc_codec *codec)
{
struct snd_soc_dapm_context *dapm = &codec->dapm;
snd_soc_dapm_new_controls(dapm, wm9712_dapm_widgets,
ARRAY_SIZE(wm9712_dapm_widgets));
snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map));
return 0;
}
static unsigned int ac97_read(struct snd_soc_codec *codec, static unsigned int ac97_read(struct snd_soc_codec *codec,
unsigned int reg) unsigned int reg)
{ {
@ -651,7 +640,6 @@ static int wm9712_soc_probe(struct snd_soc_codec *codec)
wm9712_set_bias_level(codec, SND_SOC_BIAS_STANDBY); wm9712_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
snd_soc_add_controls(codec, wm9712_snd_ac97_controls, snd_soc_add_controls(codec, wm9712_snd_ac97_controls,
ARRAY_SIZE(wm9712_snd_ac97_controls)); ARRAY_SIZE(wm9712_snd_ac97_controls));
wm9712_add_widgets(codec);
return 0; return 0;
@ -678,6 +666,10 @@ static struct snd_soc_codec_driver soc_codec_dev_wm9712 = {
.reg_word_size = sizeof(u16), .reg_word_size = sizeof(u16),
.reg_cache_step = 2, .reg_cache_step = 2,
.reg_cache_default = wm9712_reg, .reg_cache_default = wm9712_reg,
.dapm_widgets = wm9712_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(wm9712_dapm_widgets),
.dapm_routes = wm9712_audio_map,
.num_dapm_routes = ARRAY_SIZE(wm9712_audio_map),
}; };
static __devinit int wm9712_probe(struct platform_device *pdev) static __devinit int wm9712_probe(struct platform_device *pdev)

View file

@ -487,7 +487,7 @@ SND_SOC_DAPM_INPUT("MIC2B"),
SND_SOC_DAPM_VMID("VMID"), SND_SOC_DAPM_VMID("VMID"),
}; };
static const struct snd_soc_dapm_route audio_map[] = { static const struct snd_soc_dapm_route wm9713_audio_map[] = {
/* left HP mixer */ /* left HP mixer */
{"Left HP Mixer", "Beep Playback Switch", "PCBEEP"}, {"Left HP Mixer", "Beep Playback Switch", "PCBEEP"},
{"Left HP Mixer", "Voice Playback Switch", "Voice DAC"}, {"Left HP Mixer", "Voice Playback Switch", "Voice DAC"},
@ -644,18 +644,6 @@ static const struct snd_soc_dapm_route audio_map[] = {
{"Capture Mono Mux", "Right", "Right Capture Source"}, {"Capture Mono Mux", "Right", "Right Capture Source"},
}; };
static int wm9713_add_widgets(struct snd_soc_codec *codec)
{
struct snd_soc_dapm_context *dapm = &codec->dapm;
snd_soc_dapm_new_controls(dapm, wm9713_dapm_widgets,
ARRAY_SIZE(wm9713_dapm_widgets));
snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map));
return 0;
}
static unsigned int ac97_read(struct snd_soc_codec *codec, static unsigned int ac97_read(struct snd_soc_codec *codec,
unsigned int reg) unsigned int reg)
{ {
@ -1231,7 +1219,6 @@ static int wm9713_soc_probe(struct snd_soc_codec *codec)
snd_soc_add_controls(codec, wm9713_snd_ac97_controls, snd_soc_add_controls(codec, wm9713_snd_ac97_controls,
ARRAY_SIZE(wm9713_snd_ac97_controls)); ARRAY_SIZE(wm9713_snd_ac97_controls));
wm9713_add_widgets(codec);
return 0; return 0;
@ -1262,6 +1249,10 @@ static struct snd_soc_codec_driver soc_codec_dev_wm9713 = {
.reg_word_size = sizeof(u16), .reg_word_size = sizeof(u16),
.reg_cache_step = 2, .reg_cache_step = 2,
.reg_cache_default = wm9713_reg, .reg_cache_default = wm9713_reg,
.dapm_widgets = wm9713_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(wm9713_dapm_widgets),
.dapm_routes = wm9713_audio_map,
.num_dapm_routes = ARRAY_SIZE(wm9713_audio_map),
}; };
static __devinit int wm9713_probe(struct platform_device *pdev) static __devinit int wm9713_probe(struct platform_device *pdev)

View file

@ -787,17 +787,17 @@ static const struct snd_soc_dapm_route analogue_routes[] = {
static const struct snd_soc_dapm_route lineout1_diff_routes[] = { static const struct snd_soc_dapm_route lineout1_diff_routes[] = {
{ "LINEOUT1 Mixer", "IN1L Switch", "IN1L PGA" }, { "LINEOUT1 Mixer", "IN1L Switch", "IN1L PGA" },
{ "LINEOUT1 Mixer", "IN1R Switch", "IN1R PGA" }, { "LINEOUT1 Mixer", "IN1R Switch", "IN1R PGA" },
{ "LINEOUT1 Mixer", "Output Switch", "Left Output Mixer" }, { "LINEOUT1 Mixer", "Output Switch", "Left Output PGA" },
{ "LINEOUT1N Driver", NULL, "LINEOUT1 Mixer" }, { "LINEOUT1N Driver", NULL, "LINEOUT1 Mixer" },
{ "LINEOUT1P Driver", NULL, "LINEOUT1 Mixer" }, { "LINEOUT1P Driver", NULL, "LINEOUT1 Mixer" },
}; };
static const struct snd_soc_dapm_route lineout1_se_routes[] = { static const struct snd_soc_dapm_route lineout1_se_routes[] = {
{ "LINEOUT1N Mixer", "Left Output Switch", "Left Output Mixer" }, { "LINEOUT1N Mixer", "Left Output Switch", "Left Output PGA" },
{ "LINEOUT1N Mixer", "Right Output Switch", "Left Output Mixer" }, { "LINEOUT1N Mixer", "Right Output Switch", "Right Output PGA" },
{ "LINEOUT1P Mixer", "Left Output Switch", "Left Output Mixer" }, { "LINEOUT1P Mixer", "Left Output Switch", "Left Output PGA" },
{ "LINEOUT1N Driver", NULL, "LINEOUT1N Mixer" }, { "LINEOUT1N Driver", NULL, "LINEOUT1N Mixer" },
{ "LINEOUT1P Driver", NULL, "LINEOUT1P Mixer" }, { "LINEOUT1P Driver", NULL, "LINEOUT1P Mixer" },
@ -806,17 +806,17 @@ static const struct snd_soc_dapm_route lineout1_se_routes[] = {
static const struct snd_soc_dapm_route lineout2_diff_routes[] = { static const struct snd_soc_dapm_route lineout2_diff_routes[] = {
{ "LINEOUT2 Mixer", "IN2L Switch", "IN2L PGA" }, { "LINEOUT2 Mixer", "IN2L Switch", "IN2L PGA" },
{ "LINEOUT2 Mixer", "IN2R Switch", "IN2R PGA" }, { "LINEOUT2 Mixer", "IN2R Switch", "IN2R PGA" },
{ "LINEOUT2 Mixer", "Output Switch", "Right Output Mixer" }, { "LINEOUT2 Mixer", "Output Switch", "Right Output PGA" },
{ "LINEOUT2N Driver", NULL, "LINEOUT2 Mixer" }, { "LINEOUT2N Driver", NULL, "LINEOUT2 Mixer" },
{ "LINEOUT2P Driver", NULL, "LINEOUT2 Mixer" }, { "LINEOUT2P Driver", NULL, "LINEOUT2 Mixer" },
}; };
static const struct snd_soc_dapm_route lineout2_se_routes[] = { static const struct snd_soc_dapm_route lineout2_se_routes[] = {
{ "LINEOUT2N Mixer", "Left Output Switch", "Left Output Mixer" }, { "LINEOUT2N Mixer", "Left Output Switch", "Left Output PGA" },
{ "LINEOUT2N Mixer", "Right Output Switch", "Left Output Mixer" }, { "LINEOUT2N Mixer", "Right Output Switch", "Right Output PGA" },
{ "LINEOUT2P Mixer", "Right Output Switch", "Right Output Mixer" }, { "LINEOUT2P Mixer", "Right Output Switch", "Right Output PGA" },
{ "LINEOUT2N Driver", NULL, "LINEOUT2N Mixer" }, { "LINEOUT2N Driver", NULL, "LINEOUT2N Mixer" },
{ "LINEOUT2P Driver", NULL, "LINEOUT2P Mixer" }, { "LINEOUT2P Driver", NULL, "LINEOUT2P Mixer" },
@ -836,17 +836,21 @@ int wm_hubs_add_analogue_controls(struct snd_soc_codec *codec)
snd_soc_update_bits(codec, WM8993_RIGHT_LINE_INPUT_3_4_VOLUME, snd_soc_update_bits(codec, WM8993_RIGHT_LINE_INPUT_3_4_VOLUME,
WM8993_IN2_VU, WM8993_IN2_VU); WM8993_IN2_VU, WM8993_IN2_VU);
snd_soc_update_bits(codec, WM8993_SPEAKER_VOLUME_LEFT,
WM8993_SPKOUT_VU, WM8993_SPKOUT_VU);
snd_soc_update_bits(codec, WM8993_SPEAKER_VOLUME_RIGHT, snd_soc_update_bits(codec, WM8993_SPEAKER_VOLUME_RIGHT,
WM8993_SPKOUT_VU, WM8993_SPKOUT_VU); WM8993_SPKOUT_VU, WM8993_SPKOUT_VU);
snd_soc_update_bits(codec, WM8993_LEFT_OUTPUT_VOLUME, snd_soc_update_bits(codec, WM8993_LEFT_OUTPUT_VOLUME,
WM8993_HPOUT1L_ZC, WM8993_HPOUT1L_ZC); WM8993_HPOUT1_VU | WM8993_HPOUT1L_ZC,
WM8993_HPOUT1_VU | WM8993_HPOUT1L_ZC);
snd_soc_update_bits(codec, WM8993_RIGHT_OUTPUT_VOLUME, snd_soc_update_bits(codec, WM8993_RIGHT_OUTPUT_VOLUME,
WM8993_HPOUT1_VU | WM8993_HPOUT1R_ZC, WM8993_HPOUT1_VU | WM8993_HPOUT1R_ZC,
WM8993_HPOUT1_VU | WM8993_HPOUT1R_ZC); WM8993_HPOUT1_VU | WM8993_HPOUT1R_ZC);
snd_soc_update_bits(codec, WM8993_LEFT_OPGA_VOLUME, snd_soc_update_bits(codec, WM8993_LEFT_OPGA_VOLUME,
WM8993_MIXOUTL_ZC, WM8993_MIXOUTL_ZC); WM8993_MIXOUTL_ZC | WM8993_MIXOUT_VU,
WM8993_MIXOUTL_ZC | WM8993_MIXOUT_VU);
snd_soc_update_bits(codec, WM8993_RIGHT_OPGA_VOLUME, snd_soc_update_bits(codec, WM8993_RIGHT_OPGA_VOLUME,
WM8993_MIXOUTR_ZC | WM8993_MIXOUT_VU, WM8993_MIXOUTR_ZC | WM8993_MIXOUT_VU,
WM8993_MIXOUTR_ZC | WM8993_MIXOUT_VU); WM8993_MIXOUTR_ZC | WM8993_MIXOUT_VU);

View file

@ -909,6 +909,7 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
dma_data = &dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK]; dma_data = &dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK];
dma_data->asp_chan_q = pdata->asp_chan_q; dma_data->asp_chan_q = pdata->asp_chan_q;
dma_data->ram_chan_q = pdata->ram_chan_q; dma_data->ram_chan_q = pdata->ram_chan_q;
dma_data->sram_size = pdata->sram_size_playback;
dma_data->dma_addr = (dma_addr_t) (pdata->tx_dma_offset + dma_data->dma_addr = (dma_addr_t) (pdata->tx_dma_offset +
mem->start); mem->start);
@ -925,6 +926,7 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
dma_data = &dev->dma_params[SNDRV_PCM_STREAM_CAPTURE]; dma_data = &dev->dma_params[SNDRV_PCM_STREAM_CAPTURE];
dma_data->asp_chan_q = pdata->asp_chan_q; dma_data->asp_chan_q = pdata->asp_chan_q;
dma_data->ram_chan_q = pdata->ram_chan_q; dma_data->ram_chan_q = pdata->ram_chan_q;
dma_data->sram_size = pdata->sram_size_capture;
dma_data->dma_addr = (dma_addr_t)(pdata->rx_dma_offset + dma_data->dma_addr = (dma_addr_t)(pdata->rx_dma_offset +
mem->start); mem->start);

View file

@ -667,12 +667,6 @@ static int imx_ssi_probe(struct platform_device *pdev)
if (res) if (res)
ssi->dma_params_rx.dma = res->start; ssi->dma_params_rx.dma = res->start;
if ((cpu_is_mx27() || cpu_is_mx21()) &&
!(ssi->flags & IMX_SSI_USE_AC97) &&
(ssi->flags & IMX_SSI_DMA)) {
ssi->flags |= IMX_SSI_DMA;
}
platform_set_drvdata(pdev, ssi); platform_set_drvdata(pdev, ssi);
ret = snd_soc_register_dai(&pdev->dev, dai); ret = snd_soc_register_dai(&pdev->dev, dai);

View file

@ -133,7 +133,7 @@ static void jz4740_i2s_shutdown(struct snd_pcm_substream *substream,
struct jz4740_i2s *i2s = snd_soc_dai_get_drvdata(dai); struct jz4740_i2s *i2s = snd_soc_dai_get_drvdata(dai);
uint32_t conf; uint32_t conf;
if (!dai->active) if (dai->active)
return; return;
conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF); conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF);

View file

@ -27,11 +27,7 @@
static int qi_lb60_spk_event(struct snd_soc_dapm_widget *widget, static int qi_lb60_spk_event(struct snd_soc_dapm_widget *widget,
struct snd_kcontrol *ctrl, int event) struct snd_kcontrol *ctrl, int event)
{ {
int on = 0; int on = !SND_SOC_DAPM_EVENT_OFF(event);
if (event & SND_SOC_DAPM_POST_PMU)
on = 1;
else if (event & SND_SOC_DAPM_PRE_PMD)
on = 0;
gpio_set_value(QI_LB60_SND_GPIO, on); gpio_set_value(QI_LB60_SND_GPIO, on);
gpio_set_value(QI_LB60_AMP_GPIO, on); gpio_set_value(QI_LB60_AMP_GPIO, on);
@ -70,12 +66,6 @@ static int qi_lb60_codec_init(struct snd_soc_pcm_runtime *rtd)
return ret; return ret;
} }
snd_soc_dapm_new_controls(dapm, qi_lb60_widgets,
ARRAY_SIZE(qi_lb60_widgets));
snd_soc_dapm_add_routes(dapm, qi_lb60_routes,
ARRAY_SIZE(qi_lb60_routes));
snd_soc_dapm_sync(dapm);
return 0; return 0;
} }
@ -93,10 +83,20 @@ static struct snd_soc_card qi_lb60 = {
.name = "QI LB60", .name = "QI LB60",
.dai_link = &qi_lb60_dai, .dai_link = &qi_lb60_dai,
.num_links = 1, .num_links = 1,
.dapm_widgets = qi_lb60_widgets,
.num_dapm_widgets = ARRAY_SIZE(qi_lb60_widgets),
.dapm_routes = qi_lb60_routes,
.num_dapm_routes = ARRAY_SIZE(qi_lb60_routes),
}; };
static struct platform_device *qi_lb60_snd_device; static struct platform_device *qi_lb60_snd_device;
static const struct gpio qi_lb60_gpios[] = {
{ QI_LB60_SND_GPIO, GPIOF_OUT_INIT_LOW, "SND" },
{ QI_LB60_AMP_GPIO, GPIOF_OUT_INIT_LOW, "AMP" },
};
static int __init qi_lb60_init(void) static int __init qi_lb60_init(void)
{ {
int ret; int ret;
@ -106,23 +106,12 @@ static int __init qi_lb60_init(void)
if (!qi_lb60_snd_device) if (!qi_lb60_snd_device)
return -ENOMEM; return -ENOMEM;
ret = gpio_request(QI_LB60_SND_GPIO, "SND"); ret = gpio_request_array(qi_lb60_gpios, ARRAY_SIZE(qi_lb60_gpios));
if (ret) { if (ret) {
pr_err("qi_lb60 snd: Failed to request SND GPIO(%d): %d\n", pr_err("qi_lb60 snd: Failed to request gpios: %d\n", ret);
QI_LB60_SND_GPIO, ret);
goto err_device_put; goto err_device_put;
} }
ret = gpio_request(QI_LB60_AMP_GPIO, "AMP");
if (ret) {
pr_err("qi_lb60 snd: Failed to request AMP GPIO(%d): %d\n",
QI_LB60_AMP_GPIO, ret);
goto err_gpio_free_snd;
}
gpio_direction_output(QI_LB60_SND_GPIO, 0);
gpio_direction_output(QI_LB60_AMP_GPIO, 0);
platform_set_drvdata(qi_lb60_snd_device, &qi_lb60); platform_set_drvdata(qi_lb60_snd_device, &qi_lb60);
ret = platform_device_add(qi_lb60_snd_device); ret = platform_device_add(qi_lb60_snd_device);
@ -135,10 +124,8 @@ static int __init qi_lb60_init(void)
err_unset_pdata: err_unset_pdata:
platform_set_drvdata(qi_lb60_snd_device, NULL); platform_set_drvdata(qi_lb60_snd_device, NULL);
/*err_gpio_free_amp:*/ /*err_gpio_free_array:*/
gpio_free(QI_LB60_AMP_GPIO); gpio_free_array(qi_lb60_gpios, ARRAY_SIZE(qi_lb60_gpios));
err_gpio_free_snd:
gpio_free(QI_LB60_SND_GPIO);
err_device_put: err_device_put:
platform_device_put(qi_lb60_snd_device); platform_device_put(qi_lb60_snd_device);
@ -148,9 +135,8 @@ module_init(qi_lb60_init);
static void __exit qi_lb60_exit(void) static void __exit qi_lb60_exit(void)
{ {
gpio_free(QI_LB60_AMP_GPIO);
gpio_free(QI_LB60_SND_GPIO);
platform_device_unregister(qi_lb60_snd_device); platform_device_unregister(qi_lb60_snd_device);
gpio_free_array(qi_lb60_gpios, ARRAY_SIZE(qi_lb60_gpios));
} }
module_exit(qi_lb60_exit); module_exit(qi_lb60_exit);

View file

@ -249,10 +249,13 @@ static int sst_platform_open(struct snd_pcm_substream *substream)
return -ENOMEM; return -ENOMEM;
} }
stream->sstdrv_ops->vendor_id = MSIC_VENDOR_ID; stream->sstdrv_ops->vendor_id = MSIC_VENDOR_ID;
stream->sstdrv_ops->module_name = SST_CARD_NAMES;
/* registering with SST driver to get access to SST APIs to use */ /* registering with SST driver to get access to SST APIs to use */
ret_val = register_sst_card(stream->sstdrv_ops); ret_val = register_sst_card(stream->sstdrv_ops);
if (ret_val) { if (ret_val) {
pr_err("sst: sst card registration failed\n"); pr_err("sst: sst card registration failed\n");
kfree(stream->sstdrv_ops);
kfree(stream);
return ret_val; return ret_val;
} }
runtime->private_data = stream; runtime->private_data = stream;
@ -270,6 +273,7 @@ static int sst_platform_close(struct snd_pcm_substream *substream)
str_id = stream->stream_info.str_id; str_id = stream->stream_info.str_id;
if (str_id) if (str_id)
ret_val = stream->sstdrv_ops->pcm_control->close(str_id); ret_val = stream->sstdrv_ops->pcm_control->close(str_id);
unregister_sst_card(stream->sstdrv_ops);
kfree(stream->sstdrv_ops); kfree(stream->sstdrv_ops);
kfree(stream); kfree(stream);
return ret_val; return ret_val;
@ -376,6 +380,11 @@ static int sst_platform_pcm_hw_params(struct snd_pcm_substream *substream,
return 0; return 0;
} }
static int sst_platform_pcm_hw_free(struct snd_pcm_substream *substream)
{
return snd_pcm_lib_free_pages(substream);
}
static struct snd_pcm_ops sst_platform_ops = { static struct snd_pcm_ops sst_platform_ops = {
.open = sst_platform_open, .open = sst_platform_open,
.close = sst_platform_close, .close = sst_platform_close,
@ -384,6 +393,7 @@ static struct snd_pcm_ops sst_platform_ops = {
.trigger = sst_platform_pcm_trigger, .trigger = sst_platform_pcm_trigger,
.pointer = sst_platform_pcm_pointer, .pointer = sst_platform_pcm_pointer,
.hw_params = sst_platform_pcm_hw_params, .hw_params = sst_platform_pcm_hw_params,
.hw_free = sst_platform_pcm_hw_free,
}; };
static void sst_pcm_free(struct snd_pcm *pcm) static void sst_pcm_free(struct snd_pcm *pcm)

View file

@ -4,7 +4,7 @@
* Copyright (C) 2008 Nokia Corporation * Copyright (C) 2008 Nokia Corporation
* *
* Contact: Jarkko Nikula <jhnikula@gmail.com> * Contact: Jarkko Nikula <jhnikula@gmail.com>
* Peter Ujfalusi <peter.ujfalusi@nokia.com> * Peter Ujfalusi <peter.ujfalusi@ti.com>
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
@ -146,7 +146,7 @@ static int omap_mcbsp_dai_startup(struct snd_pcm_substream *substream,
* 2 channels (stereo): size is 128 / 2 = 64 frames (2 * 64 words) * 2 channels (stereo): size is 128 / 2 = 64 frames (2 * 64 words)
* 4 channels: size is 128 / 4 = 32 frames (4 * 32 words) * 4 channels: size is 128 / 4 = 32 frames (4 * 32 words)
*/ */
if (cpu_is_omap343x() || cpu_is_omap44xx()) { if (cpu_is_omap34xx() || cpu_is_omap44xx()) {
/* /*
* Rule for the buffer size. We should not allow * Rule for the buffer size. We should not allow
* smaller buffer than the FIFO size to avoid underruns * smaller buffer than the FIFO size to avoid underruns
@ -258,7 +258,7 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream,
default: default:
return -EINVAL; return -EINVAL;
} }
if (cpu_is_omap343x()) { if (cpu_is_omap34xx()) {
dma_data->set_threshold = omap_mcbsp_set_threshold; dma_data->set_threshold = omap_mcbsp_set_threshold;
/* TODO: Currently, MODE_ELEMENT == MODE_FRAME */ /* TODO: Currently, MODE_ELEMENT == MODE_FRAME */
if (omap_mcbsp_get_dma_op_mode(bus_id) == if (omap_mcbsp_get_dma_op_mode(bus_id) ==

View file

@ -4,7 +4,7 @@
* Copyright (C) 2008 Nokia Corporation * Copyright (C) 2008 Nokia Corporation
* *
* Contact: Jarkko Nikula <jhnikula@gmail.com> * Contact: Jarkko Nikula <jhnikula@gmail.com>
* Peter Ujfalusi <peter.ujfalusi@nokia.com> * Peter Ujfalusi <peter.ujfalusi@ti.com>
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License

View file

@ -4,7 +4,7 @@
* Copyright (C) 2008 Nokia Corporation * Copyright (C) 2008 Nokia Corporation
* *
* Contact: Jarkko Nikula <jhnikula@gmail.com> * Contact: Jarkko Nikula <jhnikula@gmail.com>
* Peter Ujfalusi <peter.ujfalusi@nokia.com> * Peter Ujfalusi <peter.ujfalusi@ti.com>
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
@ -37,7 +37,8 @@ static const struct snd_pcm_hardware omap_pcm_hardware = {
SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_PAUSE |
SNDRV_PCM_INFO_RESUME, SNDRV_PCM_INFO_RESUME |
SNDRV_PCM_INFO_NO_PERIOD_WAKEUP,
.formats = SNDRV_PCM_FMTBIT_S16_LE | .formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S32_LE, SNDRV_PCM_FMTBIT_S32_LE,
.period_bytes_min = 32, .period_bytes_min = 32,
@ -195,7 +196,7 @@ static int omap_pcm_prepare(struct snd_pcm_substream *substream)
if ((cpu_is_omap1510())) if ((cpu_is_omap1510()))
omap_enable_dma_irq(prtd->dma_ch, OMAP_DMA_FRAME_IRQ | omap_enable_dma_irq(prtd->dma_ch, OMAP_DMA_FRAME_IRQ |
OMAP_DMA_LAST_IRQ | OMAP_DMA_BLOCK_IRQ); OMAP_DMA_LAST_IRQ | OMAP_DMA_BLOCK_IRQ);
else else if (!substream->runtime->no_period_wakeup)
omap_enable_dma_irq(prtd->dma_ch, OMAP_DMA_FRAME_IRQ); omap_enable_dma_irq(prtd->dma_ch, OMAP_DMA_FRAME_IRQ);
if (!(cpu_class_is_omap1())) { if (!(cpu_class_is_omap1())) {

View file

@ -4,7 +4,7 @@
* Copyright (C) 2008 Nokia Corporation * Copyright (C) 2008 Nokia Corporation
* *
* Contact: Jarkko Nikula <jhnikula@gmail.com> * Contact: Jarkko Nikula <jhnikula@gmail.com>
* Peter Ujfalusi <peter.ujfalusi@nokia.com> * Peter Ujfalusi <peter.ujfalusi@ti.com>
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License

View file

@ -3,7 +3,7 @@
* *
* Copyright (C) 2008 - 2009 Nokia Corporation * Copyright (C) 2008 - 2009 Nokia Corporation
* *
* Contact: Peter Ujfalusi <peter.ujfalusi@nokia.com> * Contact: Peter Ujfalusi <peter.ujfalusi@ti.com>
* Eduardo Valentin <eduardo.valentin@nokia.com> * Eduardo Valentin <eduardo.valentin@nokia.com>
* Jarkko Nikula <jhnikula@gmail.com> * Jarkko Nikula <jhnikula@gmail.com>
* *

View file

@ -155,6 +155,15 @@ config SND_SOC_RAUMFELD
help help
Say Y if you want to add support for SoC audio on Raumfeld devices Say Y if you want to add support for SoC audio on Raumfeld devices
config SND_PXA2XX_SOC_HX4700
tristate "SoC Audio support for HP iPAQ hx4700"
depends on SND_PXA2XX_SOC && MACH_H4700
select SND_PXA2XX_SOC_I2S
select SND_SOC_AK4641
help
Say Y if you want to add support for SoC audio on the
HP iPAQ hx4700.
config SND_PXA2XX_SOC_MAGICIAN config SND_PXA2XX_SOC_MAGICIAN
tristate "SoC Audio support for HTC Magician" tristate "SoC Audio support for HTC Magician"
depends on SND_PXA2XX_SOC && MACH_MAGICIAN depends on SND_PXA2XX_SOC && MACH_MAGICIAN

View file

@ -22,6 +22,7 @@ snd-soc-palm27x-objs := palm27x.o
snd-soc-saarb-objs := saarb.o snd-soc-saarb-objs := saarb.o
snd-soc-tavorevb3-objs := tavorevb3.o snd-soc-tavorevb3-objs := tavorevb3.o
snd-soc-zylonite-objs := zylonite.o snd-soc-zylonite-objs := zylonite.o
snd-soc-hx4700-objs := hx4700.o
snd-soc-magician-objs := magician.o snd-soc-magician-objs := magician.o
snd-soc-mioa701-objs := mioa701_wm9713.o snd-soc-mioa701-objs := mioa701_wm9713.o
snd-soc-z2-objs := z2.o snd-soc-z2-objs := z2.o
@ -37,6 +38,7 @@ obj-$(CONFIG_SND_PXA2XX_SOC_E800) += snd-soc-e800.o
obj-$(CONFIG_SND_PXA2XX_SOC_SPITZ) += snd-soc-spitz.o obj-$(CONFIG_SND_PXA2XX_SOC_SPITZ) += snd-soc-spitz.o
obj-$(CONFIG_SND_PXA2XX_SOC_EM_X270) += snd-soc-em-x270.o obj-$(CONFIG_SND_PXA2XX_SOC_EM_X270) += snd-soc-em-x270.o
obj-$(CONFIG_SND_PXA2XX_SOC_PALM27X) += snd-soc-palm27x.o obj-$(CONFIG_SND_PXA2XX_SOC_PALM27X) += snd-soc-palm27x.o
obj-$(CONFIG_SND_PXA2XX_SOC_HX4700) += snd-soc-hx4700.o
obj-$(CONFIG_SND_PXA2XX_SOC_MAGICIAN) += snd-soc-magician.o obj-$(CONFIG_SND_PXA2XX_SOC_MAGICIAN) += snd-soc-magician.o
obj-$(CONFIG_SND_PXA2XX_SOC_MIOA701) += snd-soc-mioa701.o obj-$(CONFIG_SND_PXA2XX_SOC_MIOA701) += snd-soc-mioa701.o
obj-$(CONFIG_SND_PXA2XX_SOC_Z2) += snd-soc-z2.o obj-$(CONFIG_SND_PXA2XX_SOC_Z2) += snd-soc-z2.o

View file

@ -310,7 +310,7 @@ static struct snd_soc_dai_link corgi_dai = {
.cpu_dai_name = "pxa2xx-i2s", .cpu_dai_name = "pxa2xx-i2s",
.codec_dai_name = "wm8731-hifi", .codec_dai_name = "wm8731-hifi",
.platform_name = "pxa-pcm-audio", .platform_name = "pxa-pcm-audio",
.codec_name = "wm8731-codec.0-001b", .codec_name = "wm8731.0-001b",
.init = corgi_wm8731_init, .init = corgi_wm8731_init,
.ops = &corgi_ops, .ops = &corgi_ops,
}; };

255
sound/soc/pxa/hx4700.c Normal file
View file

@ -0,0 +1,255 @@
/*
* SoC audio for HP iPAQ hx4700
*
* Copyright (c) 2009 Philipp Zabel
*
* 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/timer.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include <sound/core.h>
#include <sound/jack.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <mach/hx4700.h>
#include <asm/mach-types.h>
#include "pxa2xx-i2s.h"
#include "../codecs/ak4641.h"
static struct snd_soc_jack hs_jack;
/* Headphones jack detection DAPM pin */
static struct snd_soc_jack_pin hs_jack_pin[] = {
{
.pin = "Headphone Jack",
.mask = SND_JACK_HEADPHONE,
},
{
.pin = "Speaker",
/* disable speaker when hp jack is inserted */
.mask = SND_JACK_HEADPHONE,
.invert = 1,
},
};
/* Headphones jack detection GPIO */
static struct snd_soc_jack_gpio hs_jack_gpio = {
.gpio = GPIO75_HX4700_EARPHONE_nDET,
.invert = true,
.name = "hp-gpio",
.report = SND_JACK_HEADPHONE,
.debounce_time = 200,
};
/*
* iPAQ hx4700 uses I2S for capture and playback.
*/
static int hx4700_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 = 0;
/* set codec DAI configuration */
ret = snd_soc_dai_set_fmt(codec_dai,
SND_SOC_DAIFMT_MSB | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBS_CFS);
if (ret < 0)
return ret;
/* set cpu DAI configuration */
ret = snd_soc_dai_set_fmt(cpu_dai,
SND_SOC_DAIFMT_MSB | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBS_CFS);
if (ret < 0)
return ret;
/* set the I2S system clock as output */
ret = snd_soc_dai_set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0,
SND_SOC_CLOCK_OUT);
if (ret < 0)
return ret;
/* inform codec driver about clock freq *
* (PXA I2S always uses divider 256) */
ret = snd_soc_dai_set_sysclk(codec_dai, 0, 256 * params_rate(params),
SND_SOC_CLOCK_IN);
if (ret < 0)
return ret;
return 0;
}
static struct snd_soc_ops hx4700_ops = {
.hw_params = hx4700_hw_params,
};
static int hx4700_spk_power(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *k, int event)
{
gpio_set_value(GPIO107_HX4700_SPK_nSD, !!SND_SOC_DAPM_EVENT_ON(event));
return 0;
}
static int hx4700_hp_power(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *k, int event)
{
gpio_set_value(GPIO92_HX4700_HP_DRIVER, !!SND_SOC_DAPM_EVENT_ON(event));
return 0;
}
/* hx4700 machine dapm widgets */
static const struct snd_soc_dapm_widget hx4700_dapm_widgets[] = {
SND_SOC_DAPM_HP("Headphone Jack", hx4700_hp_power),
SND_SOC_DAPM_SPK("Speaker", hx4700_spk_power),
SND_SOC_DAPM_MIC("Built-in Microphone", NULL),
};
/* hx4700 machine audio_map */
static const struct snd_soc_dapm_route hx4700_audio_map[] = {
/* Headphone connected to LOUT, ROUT */
{"Headphone Jack", NULL, "LOUT"},
{"Headphone Jack", NULL, "ROUT"},
/* Speaker connected to MOUT2 */
{"Speaker", NULL, "MOUT2"},
/* Microphone connected to MICIN */
{"MICIN", NULL, "Built-in Microphone"},
{"AIN", NULL, "MICOUT"},
};
/*
* Logic for a ak4641 as connected on a HP iPAQ hx4700
*/
static int hx4700_ak4641_init(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_codec *codec = rtd->codec;
struct snd_soc_dapm_context *dapm = &codec->dapm;
int err;
/* NC codec pins */
/* FIXME: is anything connected here? */
snd_soc_dapm_nc_pin(dapm, "MOUT1");
snd_soc_dapm_nc_pin(dapm, "MICEXT");
snd_soc_dapm_nc_pin(dapm, "AUX");
/* Jack detection API stuff */
err = snd_soc_jack_new(codec, "Headphone Jack",
SND_JACK_HEADPHONE, &hs_jack);
if (err)
return err;
err = snd_soc_jack_add_pins(&hs_jack, ARRAY_SIZE(hs_jack_pin),
hs_jack_pin);
if (err)
return err;
err = snd_soc_jack_add_gpios(&hs_jack, 1, &hs_jack_gpio);
return err;
}
/* hx4700 digital audio interface glue - connects codec <--> CPU */
static struct snd_soc_dai_link hx4700_dai = {
.name = "ak4641",
.stream_name = "AK4641",
.cpu_dai_name = "pxa2xx-i2s",
.codec_dai_name = "ak4641-hifi",
.platform_name = "pxa-pcm-audio",
.codec_name = "ak4641.0-0012",
.init = hx4700_ak4641_init,
.ops = &hx4700_ops,
};
/* hx4700 audio machine driver */
static struct snd_soc_card snd_soc_card_hx4700 = {
.name = "iPAQ hx4700",
.dai_link = &hx4700_dai,
.num_links = 1,
.dapm_widgets = hx4700_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(hx4700_dapm_widgets),
.dapm_routes = hx4700_audio_map,
.num_dapm_routes = ARRAY_SIZE(hx4700_audio_map),
};
static struct gpio hx4700_audio_gpios[] = {
{ GPIO107_HX4700_SPK_nSD, GPIOF_OUT_INIT_HIGH, "SPK_POWER" },
{ GPIO92_HX4700_HP_DRIVER, GPIOF_OUT_INIT_LOW, "EP_POWER" },
};
static int __devinit hx4700_audio_probe(struct platform_device *pdev)
{
int ret;
if (!machine_is_h4700())
return -ENODEV;
ret = gpio_request_array(hx4700_audio_gpios,
ARRAY_SIZE(hx4700_audio_gpios));
if (ret)
return ret;
snd_soc_card_hx4700.dev = &pdev->dev;
ret = snd_soc_register_card(&snd_soc_card_hx4700);
if (ret)
return ret;
return 0;
}
static int __devexit hx4700_audio_remove(struct platform_device *pdev)
{
snd_soc_jack_free_gpios(&hs_jack, 1, &hs_jack_gpio);
snd_soc_unregister_card(&snd_soc_card_hx4700);
gpio_set_value(GPIO92_HX4700_HP_DRIVER, 0);
gpio_set_value(GPIO107_HX4700_SPK_nSD, 0);
gpio_free_array(hx4700_audio_gpios, ARRAY_SIZE(hx4700_audio_gpios));
return 0;
}
static struct platform_driver hx4700_audio_driver = {
.driver = {
.name = "hx4700-audio",
.owner = THIS_MODULE,
.pm = &snd_soc_pm_ops,
},
.probe = hx4700_audio_probe,
.remove = __devexit_p(hx4700_audio_remove),
};
static int __init hx4700_modinit(void)
{
return platform_driver_register(&hx4700_audio_driver);
}
module_init(hx4700_modinit);
static void __exit hx4700_modexit(void)
{
platform_driver_unregister(&hx4700_audio_driver);
}
module_exit(hx4700_modexit);
MODULE_AUTHOR("Philipp Zabel");
MODULE_DESCRIPTION("ALSA SoC iPAQ hx4700");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:hx4700-audio");

View file

@ -276,7 +276,7 @@ static struct snd_soc_dai_link poodle_dai = {
.cpu_dai_name = "pxa2xx-i2s", .cpu_dai_name = "pxa2xx-i2s",
.codec_dai_name = "wm8731-hifi", .codec_dai_name = "wm8731-hifi",
.platform_name = "pxa-pcm-audio", .platform_name = "pxa-pcm-audio",
.codec_name = "wm8731-codec.0-001b", .codec_name = "wm8731.0-001b",
.init = poodle_wm8731_init, .init = poodle_wm8731_init,
.ops = &poodle_ops, .ops = &poodle_ops,
}; };

View file

@ -42,6 +42,7 @@
static int spitz_jack_func; static int spitz_jack_func;
static int spitz_spk_func; static int spitz_spk_func;
static int spitz_mic_gpio;
static void spitz_ext_control(struct snd_soc_codec *codec) static void spitz_ext_control(struct snd_soc_codec *codec)
{ {
@ -217,14 +218,7 @@ static int spitz_set_spk(struct snd_kcontrol *kcontrol,
static int spitz_mic_bias(struct snd_soc_dapm_widget *w, static int spitz_mic_bias(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *k, int event) struct snd_kcontrol *k, int event)
{ {
if (machine_is_borzoi() || machine_is_spitz()) gpio_set_value_cansleep(spitz_mic_gpio, SND_SOC_DAPM_EVENT_ON(event));
gpio_set_value(SPITZ_GPIO_MIC_BIAS,
SND_SOC_DAPM_EVENT_ON(event));
if (machine_is_akita())
gpio_set_value(AKITA_GPIO_MIC_BIAS,
SND_SOC_DAPM_EVENT_ON(event));
return 0; return 0;
} }
@ -339,22 +333,45 @@ static int __init spitz_init(void)
if (!(machine_is_spitz() || machine_is_borzoi() || machine_is_akita())) if (!(machine_is_spitz() || machine_is_borzoi() || machine_is_akita()))
return -ENODEV; return -ENODEV;
if (machine_is_borzoi() || machine_is_spitz())
spitz_mic_gpio = SPITZ_GPIO_MIC_BIAS;
else
spitz_mic_gpio = AKITA_GPIO_MIC_BIAS;
ret = gpio_request(spitz_mic_gpio, "MIC GPIO");
if (ret)
goto err1;
ret = gpio_direction_output(spitz_mic_gpio, 0);
if (ret)
goto err2;
spitz_snd_device = platform_device_alloc("soc-audio", -1); spitz_snd_device = platform_device_alloc("soc-audio", -1);
if (!spitz_snd_device) if (!spitz_snd_device) {
return -ENOMEM; ret = -ENOMEM;
goto err2;
}
platform_set_drvdata(spitz_snd_device, &snd_soc_spitz); platform_set_drvdata(spitz_snd_device, &snd_soc_spitz);
ret = platform_device_add(spitz_snd_device); ret = platform_device_add(spitz_snd_device);
if (ret) if (ret)
platform_device_put(spitz_snd_device); goto err3;
return 0;
err3:
platform_device_put(spitz_snd_device);
err2:
gpio_free(spitz_mic_gpio);
err1:
return ret; return ret;
} }
static void __exit spitz_exit(void) static void __exit spitz_exit(void)
{ {
platform_device_unregister(spitz_snd_device); platform_device_unregister(spitz_snd_device);
gpio_free(spitz_mic_gpio);
} }
module_init(spitz_init); module_init(spitz_init);

View file

@ -162,3 +162,18 @@ config SND_SOC_SAMSUNG_SMDK_SPDIF
select SND_SAMSUNG_SPDIF select SND_SAMSUNG_SPDIF
help help
Say Y if you want to add support for SoC S/PDIF audio on the SMDK. Say Y if you want to add support for SoC S/PDIF audio on the SMDK.
config SND_SOC_SMDK_WM8580_PCM
tristate "SoC PCM Audio support for WM8580 on SMDK"
depends on SND_SOC_SAMSUNG && (MACH_SMDK6450 || MACH_SMDKV210 || MACH_SMDKC110)
select SND_SOC_WM8580
select SND_SAMSUNG_PCM
help
Say Y if you want to add support for SoC audio on the SMDK.
config SND_SOC_SPEYSIDE
tristate "Audio support for Wolfson Speyside"
depends on SND_SOC_SAMSUNG && MACH_WLF_CRAGG_6410
select SND_SAMSUNG_I2S
select SND_SOC_WM8915
select SND_SOC_WM9081

View file

@ -34,6 +34,8 @@ snd-soc-smdk-wm9713-objs := smdk_wm9713.o
snd-soc-s3c64xx-smartq-wm8987-objs := smartq_wm8987.o snd-soc-s3c64xx-smartq-wm8987-objs := smartq_wm8987.o
snd-soc-goni-wm8994-objs := goni_wm8994.o snd-soc-goni-wm8994-objs := goni_wm8994.o
snd-soc-smdk-spdif-objs := smdk_spdif.o snd-soc-smdk-spdif-objs := smdk_spdif.o
snd-soc-smdk-wm8580pcm-objs := smdk_wm8580pcm.o
snd-soc-speyside-objs := speyside.o
obj-$(CONFIG_SND_SOC_SAMSUNG_JIVE_WM8750) += snd-soc-jive-wm8750.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_WM8753) += snd-soc-neo1973-wm8753.o
@ -51,3 +53,5 @@ obj-$(CONFIG_SND_SOC_SAMSUNG_SMDK_WM9713) += snd-soc-smdk-wm9713.o
obj-$(CONFIG_SND_SOC_SMARTQ) += snd-soc-s3c64xx-smartq-wm8987.o obj-$(CONFIG_SND_SOC_SMARTQ) += snd-soc-s3c64xx-smartq-wm8987.o
obj-$(CONFIG_SND_SOC_SAMSUNG_SMDK_SPDIF) += snd-soc-smdk-spdif.o obj-$(CONFIG_SND_SOC_SAMSUNG_SMDK_SPDIF) += snd-soc-smdk-spdif.o
obj-$(CONFIG_SND_SOC_GONI_AQUILA_WM8994) += snd-soc-goni-wm8994.o obj-$(CONFIG_SND_SOC_GONI_AQUILA_WM8994) += snd-soc-goni-wm8994.o
obj-$(CONFIG_SND_SOC_SMDK_WM8580_PCM) += snd-soc-smdk-wm8580pcm.o
obj-$(CONFIG_SND_SOC_SPEYSIDE) += snd-soc-speyside.o

View file

@ -246,7 +246,6 @@ static struct snd_soc_dai_link goni_dai[] = {
.stream_name = "Voice", .stream_name = "Voice",
.cpu_dai_name = "goni-voice-dai", .cpu_dai_name = "goni-voice-dai",
.codec_dai_name = "wm8994-aif2", .codec_dai_name = "wm8994-aif2",
.platform_name = "samsung-audio",
.codec_name = "wm8994-codec.0-001a", .codec_name = "wm8994-codec.0-001a",
.ops = &goni_voice_ops, .ops = &goni_voice_ops,
}, },

View file

@ -432,7 +432,6 @@ static struct snd_soc_dai_link neo1973_dai[] = {
{ /* Voice via BT */ { /* Voice via BT */
.name = "Bluetooth", .name = "Bluetooth",
.stream_name = "Voice", .stream_name = "Voice",
.platform_name = "samsung-audio",
.cpu_dai_name = "dfbmcs320-pcm", .cpu_dai_name = "dfbmcs320-pcm",
.codec_dai_name = "wm8753-voice", .codec_dai_name = "wm8753-voice",
.codec_name = "wm8753-codec.0-001a", .codec_name = "wm8753-codec.0-001a",

View file

@ -0,0 +1,206 @@
/*
* sound/soc/samsung/smdk_wm8580pcm.c
*
* Copyright (c) 2011 Samsung Electronics Co. Ltd
*
* 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 <sound/soc.h>
#include <sound/pcm_params.h>
#include <sound/pcm.h>
#include <asm/mach-types.h>
#include "../codecs/wm8580.h"
#include "dma.h"
#include "pcm.h"
/*
* Board Settings:
* o '1' means 'ON'
* o '0' means 'OFF'
* o 'X' means 'Don't care'
*
* SMDK6410, SMDK6440, SMDK6450 Base B/D: CFG1-0000, CFG2-1111
* SMDKC110, SMDKV210: CFGB11-100100, CFGB12-0000
*/
#define SMDK_WM8580_EXT_OSC 12000000
#define SMDK_WM8580_EXT_MCLK 4096000
#define SMDK_WM8580_EXT_VOICE 2048000
static unsigned long mclk_freq;
static unsigned long xtal_freq;
/*
* If MCLK clock directly gets from XTAL, we don't have to use PLL
* to make MCLK, but if XTAL clock source connects with other codec
* pin (like XTI), we should have to set codec's PLL to make MCLK.
* Because Samsung SoC does not support pcmcdclk output like I2S.
*/
static int smdk_wm8580_pcm_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 rfs, ret;
switch (params_rate(params)) {
case 8000:
break;
default:
printk(KERN_ERR "%s:%d Sampling Rate %u not supported!\n",
__func__, __LINE__, params_rate(params));
return -EINVAL;
}
rfs = mclk_freq / params_rate(params) / 2;
/* Set the codec DAI configuration */
ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_DSP_B
| SND_SOC_DAIFMT_IB_NF
| SND_SOC_DAIFMT_CBS_CFS);
if (ret < 0)
return ret;
/* Set the cpu DAI configuration */
ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_DSP_B
| SND_SOC_DAIFMT_IB_NF
| SND_SOC_DAIFMT_CBS_CFS);
if (ret < 0)
return ret;
if (mclk_freq == xtal_freq) {
ret = snd_soc_dai_set_sysclk(codec_dai, WM8580_CLKSRC_MCLK,
mclk_freq, SND_SOC_CLOCK_IN);
if (ret < 0)
return ret;
ret = snd_soc_dai_set_clkdiv(codec_dai, WM8580_MCLK,
WM8580_CLKSRC_MCLK);
if (ret < 0)
return ret;
} else {
ret = snd_soc_dai_set_sysclk(codec_dai, WM8580_CLKSRC_PLLA,
mclk_freq, SND_SOC_CLOCK_IN);
if (ret < 0)
return ret;
ret = snd_soc_dai_set_clkdiv(codec_dai, WM8580_MCLK,
WM8580_CLKSRC_PLLA);
if (ret < 0)
return ret;
ret = snd_soc_dai_set_pll(codec_dai, WM8580_PLLA, 0,
xtal_freq, mclk_freq);
if (ret < 0)
return ret;
}
/* Set PCM source clock on CPU */
ret = snd_soc_dai_set_sysclk(cpu_dai, S3C_PCM_CLKSRC_MUX,
mclk_freq, SND_SOC_CLOCK_IN);
if (ret < 0)
return ret;
/* Set SCLK_DIV for making bclk */
ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C_PCM_SCLK_PER_FS, rfs);
if (ret < 0)
return ret;
return 0;
}
static struct snd_soc_ops smdk_wm8580_pcm_ops = {
.hw_params = smdk_wm8580_pcm_hw_params,
};
static struct snd_soc_dai_link smdk_dai[] = {
{
.name = "WM8580 PAIF PCM RX",
.stream_name = "Playback",
.cpu_dai_name = "samsung-pcm.0",
.codec_dai_name = "wm8580-hifi-playback",
.platform_name = "samsung-audio",
.codec_name = "wm8580-codec.0-001b",
.ops = &smdk_wm8580_pcm_ops,
}, {
.name = "WM8580 PAIF PCM TX",
.stream_name = "Capture",
.cpu_dai_name = "samsung-pcm.0",
.codec_dai_name = "wm8580-hifi-capture",
.platform_name = "samsung-audio",
.codec_name = "wm8580-codec.0-001b",
.ops = &smdk_wm8580_pcm_ops,
},
};
static struct snd_soc_card smdk_pcm = {
.name = "SMDK-PCM",
.dai_link = smdk_dai,
.num_links = 2,
};
/*
* After SMDKC110 Base Board's Rev is '0.1', 12MHz External OSC(X1)
* is absent (or not connected), so we connect EXT_VOICE_CLK(OSC4),
* 2.0484Mhz, directly with MCLK both Codec and SoC.
*/
static int __devinit snd_smdk_probe(struct platform_device *pdev)
{
int ret = 0;
xtal_freq = SMDK_WM8580_EXT_OSC;
mclk_freq = SMDK_WM8580_EXT_MCLK;
if (machine_is_smdkc110() || machine_is_smdkv210())
xtal_freq = mclk_freq = SMDK_WM8580_EXT_VOICE;
smdk_pcm.dev = &pdev->dev;
ret = snd_soc_register_card(&smdk_pcm);
if (ret) {
dev_err(&pdev->dev, "snd_soc_register_card failed %d\n", ret);
return ret;
}
return 0;
}
static int __devexit snd_smdk_remove(struct platform_device *pdev)
{
snd_soc_unregister_card(&smdk_pcm);
platform_set_drvdata(pdev, NULL);
return 0;
}
static struct platform_driver snd_smdk_driver = {
.driver = {
.owner = THIS_MODULE,
.name = "samsung-smdk-pcm",
},
.probe = snd_smdk_probe,
.remove = __devexit_p(snd_smdk_remove),
};
static int __init smdk_audio_init(void)
{
return platform_driver_register(&snd_smdk_driver);
}
module_init(smdk_audio_init);
static void __exit smdk_audio_exit(void)
{
platform_driver_unregister(&snd_smdk_driver);
}
module_exit(smdk_audio_exit);
MODULE_AUTHOR("Sangbeom Kim, <sbkim73@samsung.com>");
MODULE_DESCRIPTION("ALSA SoC SMDK WM8580 for PCM");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,332 @@
/*
* Speyside audio support
*
* Copyright 2011 Wolfson Microelectronics
*
* 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 <sound/soc.h>
#include <sound/soc-dapm.h>
#include <sound/jack.h>
#include <linux/gpio.h>
#include "../codecs/wm8915.h"
#include "../codecs/wm9081.h"
#define WM8915_HPSEL_GPIO 214
static int speyside_set_bias_level(struct snd_soc_card *card,
enum snd_soc_bias_level level)
{
struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai;
int ret;
switch (level) {
case SND_SOC_BIAS_STANDBY:
ret = snd_soc_dai_set_sysclk(codec_dai, WM8915_SYSCLK_MCLK1,
32768, SND_SOC_CLOCK_IN);
if (ret < 0)
return ret;
ret = snd_soc_dai_set_pll(codec_dai, WM8915_FLL_MCLK1,
0, 0, 0);
if (ret < 0) {
pr_err("Failed to stop FLL\n");
return ret;
}
default:
break;
}
return 0;
}
static int speyside_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 *cpu_dai = rtd->cpu_dai;
struct snd_soc_dai *codec_dai = rtd->codec_dai;
int ret;
ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S
| SND_SOC_DAIFMT_NB_NF
| SND_SOC_DAIFMT_CBM_CFM);
if (ret < 0)
return ret;
ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S
| SND_SOC_DAIFMT_NB_NF
| SND_SOC_DAIFMT_CBM_CFM);
if (ret < 0)
return ret;
ret = snd_soc_dai_set_pll(codec_dai, 0, WM8915_FLL_MCLK1,
32768, 256 * 48000);
if (ret < 0)
return ret;
ret = snd_soc_dai_set_sysclk(codec_dai, WM8915_SYSCLK_FLL,
256 * 48000, SND_SOC_CLOCK_IN);
if (ret < 0)
return ret;
return 0;
}
static struct snd_soc_ops speyside_ops = {
.hw_params = speyside_hw_params,
};
static struct snd_soc_jack speyside_headset;
/* Headset jack detection DAPM pins */
static struct snd_soc_jack_pin speyside_headset_pins[] = {
{
.pin = "Headset Mic",
.mask = SND_JACK_MICROPHONE,
},
{
.pin = "Headphone",
.mask = SND_JACK_HEADPHONE,
},
};
/* Default the headphone selection to active high */
static int speyside_jack_polarity;
static int speyside_get_micbias(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink)
{
if (speyside_jack_polarity && (strcmp(source->name, "MICB1") == 0))
return 1;
if (!speyside_jack_polarity && (strcmp(source->name, "MICB2") == 0))
return 1;
return 0;
}
static void speyside_set_polarity(struct snd_soc_codec *codec,
int polarity)
{
speyside_jack_polarity = !polarity;
gpio_direction_output(WM8915_HPSEL_GPIO, speyside_jack_polarity);
/* Re-run DAPM to make sure we're using the correct mic bias */
snd_soc_dapm_sync(&codec->dapm);
}
static int speyside_wm8915_init(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_dai *dai = rtd->codec_dai;
struct snd_soc_codec *codec = rtd->codec;
int ret;
ret = snd_soc_dai_set_sysclk(dai, WM8915_SYSCLK_MCLK1, 32768, 0);
if (ret < 0)
return ret;
ret = gpio_request(WM8915_HPSEL_GPIO, "HP_SEL");
if (ret != 0)
pr_err("Failed to request HP_SEL GPIO: %d\n", ret);
gpio_direction_output(WM8915_HPSEL_GPIO, speyside_jack_polarity);
ret = snd_soc_jack_new(codec, "Headset",
SND_JACK_HEADSET | SND_JACK_BTN_0,
&speyside_headset);
if (ret)
return ret;
ret = snd_soc_jack_add_pins(&speyside_headset,
ARRAY_SIZE(speyside_headset_pins),
speyside_headset_pins);
if (ret)
return ret;
wm8915_detect(codec, &speyside_headset, speyside_set_polarity);
return 0;
}
static int speyside_late_probe(struct snd_soc_card *card)
{
snd_soc_dapm_ignore_suspend(&card->dapm, "Headphone");
snd_soc_dapm_ignore_suspend(&card->dapm, "Headset Mic");
snd_soc_dapm_ignore_suspend(&card->dapm, "Main AMIC");
snd_soc_dapm_ignore_suspend(&card->dapm, "Main DMIC");
snd_soc_dapm_ignore_suspend(&card->dapm, "Speaker");
snd_soc_dapm_ignore_suspend(&card->dapm, "WM1250 Output");
snd_soc_dapm_ignore_suspend(&card->dapm, "WM1250 Input");
return 0;
}
static struct snd_soc_dai_link speyside_dai[] = {
{
.name = "CPU",
.stream_name = "CPU",
.cpu_dai_name = "samsung-i2s.0",
.codec_dai_name = "wm8915-aif1",
.platform_name = "samsung-audio",
.codec_name = "wm8915.1-001a",
.init = speyside_wm8915_init,
.ops = &speyside_ops,
},
{
.name = "Baseband",
.stream_name = "Baseband",
.cpu_dai_name = "wm8915-aif2",
.codec_dai_name = "wm1250-ev1",
.codec_name = "wm1250-ev1.1-0027",
.ops = &speyside_ops,
.ignore_suspend = 1,
},
};
static int speyside_wm9081_init(struct snd_soc_dapm_context *dapm)
{
snd_soc_dapm_nc_pin(dapm, "LINEOUT");
/* At any time the WM9081 is active it will have this clock */
return snd_soc_codec_set_sysclk(dapm->codec, WM9081_SYSCLK_MCLK,
48000 * 256, 0);
}
static struct snd_soc_aux_dev speyside_aux_dev[] = {
{
.name = "wm9081",
.codec_name = "wm9081.1-006c",
.init = speyside_wm9081_init,
},
};
static struct snd_soc_codec_conf speyside_codec_conf[] = {
{
.dev_name = "wm9081.1-006c",
.name_prefix = "Sub",
},
};
static const struct snd_kcontrol_new controls[] = {
SOC_DAPM_PIN_SWITCH("Main Speaker"),
SOC_DAPM_PIN_SWITCH("Main DMIC"),
SOC_DAPM_PIN_SWITCH("Main AMIC"),
SOC_DAPM_PIN_SWITCH("WM1250 Input"),
SOC_DAPM_PIN_SWITCH("WM1250 Output"),
};
static struct snd_soc_dapm_widget widgets[] = {
SND_SOC_DAPM_HP("Headphone", NULL),
SND_SOC_DAPM_MIC("Headset Mic", NULL),
SND_SOC_DAPM_SPK("Main Speaker", NULL),
SND_SOC_DAPM_MIC("Main AMIC", NULL),
SND_SOC_DAPM_MIC("Main DMIC", NULL),
};
static struct snd_soc_dapm_route audio_paths[] = {
{ "IN1RN", NULL, "MICB1" },
{ "IN1RP", NULL, "MICB1" },
{ "IN1RN", NULL, "MICB2" },
{ "IN1RP", NULL, "MICB2" },
{ "MICB1", NULL, "Headset Mic", speyside_get_micbias },
{ "MICB2", NULL, "Headset Mic", speyside_get_micbias },
{ "IN1LP", NULL, "MICB2" },
{ "IN1RN", NULL, "MICB1" },
{ "MICB2", NULL, "Main AMIC" },
{ "DMIC1DAT", NULL, "MICB1" },
{ "DMIC2DAT", NULL, "MICB1" },
{ "MICB1", NULL, "Main DMIC" },
{ "Headphone", NULL, "HPOUT1L" },
{ "Headphone", NULL, "HPOUT1R" },
{ "Sub IN1", NULL, "HPOUT2L" },
{ "Sub IN2", NULL, "HPOUT2R" },
{ "Main Speaker", NULL, "Sub SPKN" },
{ "Main Speaker", NULL, "Sub SPKP" },
{ "Main Speaker", NULL, "SPKDAT" },
};
static struct snd_soc_card speyside = {
.name = "Speyside",
.dai_link = speyside_dai,
.num_links = ARRAY_SIZE(speyside_dai),
.aux_dev = speyside_aux_dev,
.num_aux_devs = ARRAY_SIZE(speyside_aux_dev),
.codec_conf = speyside_codec_conf,
.num_configs = ARRAY_SIZE(speyside_codec_conf),
.set_bias_level = speyside_set_bias_level,
.controls = controls,
.num_controls = ARRAY_SIZE(controls),
.dapm_widgets = widgets,
.num_dapm_widgets = ARRAY_SIZE(widgets),
.dapm_routes = audio_paths,
.num_dapm_routes = ARRAY_SIZE(audio_paths),
.late_probe = speyside_late_probe,
};
static __devinit int speyside_probe(struct platform_device *pdev)
{
struct snd_soc_card *card = &speyside;
int ret;
card->dev = &pdev->dev;
ret = snd_soc_register_card(card);
if (ret) {
dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
ret);
return ret;
}
return 0;
}
static int __devexit speyside_remove(struct platform_device *pdev)
{
struct snd_soc_card *card = platform_get_drvdata(pdev);
snd_soc_unregister_card(card);
return 0;
}
static struct platform_driver speyside_driver = {
.driver = {
.name = "speyside",
.owner = THIS_MODULE,
.pm = &snd_soc_pm_ops,
},
.probe = speyside_probe,
.remove = __devexit_p(speyside_remove),
};
static int __init speyside_audio_init(void)
{
return platform_driver_register(&speyside_driver);
}
module_init(speyside_audio_init);
static void __exit speyside_audio_exit(void)
{
platform_driver_unregister(&speyside_driver);
}
module_exit(speyside_audio_exit);
MODULE_DESCRIPTION("Speyside audio support");
MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:speyside");

View file

@ -86,8 +86,8 @@
#define SE (1 << 0) /* Fix the master clock */ #define SE (1 << 0) /* Fix the master clock */
/* CLK_RST */ /* CLK_RST */
#define B_CLK 0x00000010 #define CRB (1 << 4)
#define A_CLK 0x00000001 #define CRA (1 << 0)
/* IO SHIFT / MACRO */ /* IO SHIFT / MACRO */
#define BI_SHIFT 12 #define BI_SHIFT 12
@ -146,11 +146,20 @@ struct fsi_priv {
void __iomem *base; void __iomem *base;
struct fsi_master *master; struct fsi_master *master;
int chan_num;
struct fsi_stream playback; struct fsi_stream playback;
struct fsi_stream capture; struct fsi_stream capture;
int chan_num:16;
int clk_master:1;
long rate; long rate;
/* for suspend/resume */
u32 saved_do_fmt;
u32 saved_di_fmt;
u32 saved_ckg1;
u32 saved_ckg2;
u32 saved_out_sel;
}; };
struct fsi_core { struct fsi_core {
@ -171,6 +180,14 @@ struct fsi_master {
struct fsi_core *core; struct fsi_core *core;
struct sh_fsi_platform_info *info; struct sh_fsi_platform_info *info;
spinlock_t lock; spinlock_t lock;
/* for suspend/resume */
u32 saved_a_mclk;
u32 saved_b_mclk;
u32 saved_iemsk;
u32 saved_imsk;
u32 saved_clk_rst;
u32 saved_soft_rst;
}; };
/* /*
@ -244,6 +261,11 @@ static struct fsi_master *fsi_get_master(struct fsi_priv *fsi)
return fsi->master; return fsi->master;
} }
static int fsi_is_clk_master(struct fsi_priv *fsi)
{
return fsi->clk_master;
}
static int fsi_is_port_a(struct fsi_priv *fsi) static int fsi_is_port_a(struct fsi_priv *fsi)
{ {
return fsi->master->base == fsi->base; return fsi->master->base == fsi->base;
@ -535,20 +557,45 @@ static void fsi_spdif_clk_ctrl(struct fsi_priv *fsi, int enable)
} }
/* /*
* ctrl function * clock function
*/ */
#define fsi_module_init(m, d) __fsi_module_clk_ctrl(m, d, 1)
static void fsi_clk_ctrl(struct fsi_priv *fsi, int enable) #define fsi_module_kill(m, d) __fsi_module_clk_ctrl(m, d, 0)
static void __fsi_module_clk_ctrl(struct fsi_master *master,
struct device *dev,
int enable)
{ {
u32 val = fsi_is_port_a(fsi) ? (1 << 0) : (1 << 4); pm_runtime_get_sync(dev);
struct fsi_master *master = fsi_get_master(fsi);
if (enable) if (enable) {
fsi_master_mask_set(master, CLK_RST, val, val); /* enable only SR */
else fsi_master_mask_set(master, SOFT_RST, FSISR, FSISR);
fsi_master_mask_set(master, CLK_RST, val, 0); fsi_master_mask_set(master, SOFT_RST, PASR | PBSR, 0);
} else {
/* clear all registers */
fsi_master_mask_set(master, SOFT_RST, FSISR, 0);
}
pm_runtime_put_sync(dev);
} }
#define fsi_port_start(f) __fsi_port_clk_ctrl(f, 1)
#define fsi_port_stop(f) __fsi_port_clk_ctrl(f, 0)
static void __fsi_port_clk_ctrl(struct fsi_priv *fsi, int enable)
{
struct fsi_master *master = fsi_get_master(fsi);
u32 soft = fsi_is_port_a(fsi) ? PASR : PBSR;
u32 clk = fsi_is_port_a(fsi) ? CRA : CRB;
int is_master = fsi_is_clk_master(fsi);
fsi_master_mask_set(master, SOFT_RST, soft, (enable) ? soft : 0);
if (is_master)
fsi_master_mask_set(master, CLK_RST, clk, (enable) ? clk : 0);
}
/*
* ctrl function
*/
static void fsi_fifo_init(struct fsi_priv *fsi, static void fsi_fifo_init(struct fsi_priv *fsi,
int is_play, int is_play,
struct snd_soc_dai *dai) struct snd_soc_dai *dai)
@ -601,18 +648,6 @@ static void fsi_fifo_init(struct fsi_priv *fsi,
} }
} }
static void fsi_soft_all_reset(struct fsi_master *master)
{
/* port AB reset */
fsi_master_mask_set(master, SOFT_RST, PASR | PBSR, 0);
mdelay(10);
/* soft reset */
fsi_master_mask_set(master, SOFT_RST, FSISR, 0);
fsi_master_mask_set(master, SOFT_RST, FSISR, FSISR);
mdelay(10);
}
static int fsi_fifo_data_ctrl(struct fsi_priv *fsi, int stream) static int fsi_fifo_data_ctrl(struct fsi_priv *fsi, int stream)
{ {
struct snd_pcm_runtime *runtime; struct snd_pcm_runtime *runtime;
@ -793,14 +828,13 @@ static void fsi_dai_shutdown(struct snd_pcm_substream *substream,
struct fsi_priv *fsi = fsi_get_priv(substream); struct fsi_priv *fsi = fsi_get_priv(substream);
int is_play = fsi_is_play(substream); int is_play = fsi_is_play(substream);
struct fsi_master *master = fsi_get_master(fsi); struct fsi_master *master = fsi_get_master(fsi);
set_rate_func set_rate; set_rate_func set_rate = fsi_get_info_set_rate(master);
fsi_irq_disable(fsi, is_play); fsi_irq_disable(fsi, is_play);
fsi_clk_ctrl(fsi, 0);
set_rate = fsi_get_info_set_rate(master); if (fsi_is_clk_master(fsi))
if (set_rate && fsi->rate)
set_rate(dai->dev, fsi_is_port_a(fsi), fsi->rate, 0); set_rate(dai->dev, fsi_is_port_a(fsi), fsi->rate, 0);
fsi->rate = 0; fsi->rate = 0;
pm_runtime_put_sync(dai->dev); pm_runtime_put_sync(dai->dev);
@ -821,8 +855,10 @@ static int fsi_dai_trigger(struct snd_pcm_substream *substream, int cmd,
frames_to_bytes(runtime, runtime->period_size)); frames_to_bytes(runtime, runtime->period_size));
ret = is_play ? fsi_data_push(fsi) : fsi_data_pop(fsi); ret = is_play ? fsi_data_push(fsi) : fsi_data_pop(fsi);
fsi_irq_enable(fsi, is_play); fsi_irq_enable(fsi, is_play);
fsi_port_start(fsi);
break; break;
case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_STOP:
fsi_port_stop(fsi);
fsi_irq_disable(fsi, is_play); fsi_irq_disable(fsi, is_play);
fsi_stream_pop(fsi, is_play); fsi_stream_pop(fsi, is_play);
break; break;
@ -876,6 +912,8 @@ static int fsi_set_fmt_spdif(struct fsi_priv *fsi)
static int fsi_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) static int fsi_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{ {
struct fsi_priv *fsi = fsi_get_priv_frm_dai(dai); struct fsi_priv *fsi = fsi_get_priv_frm_dai(dai);
struct fsi_master *master = fsi_get_master(fsi);
set_rate_func set_rate = fsi_get_info_set_rate(master);
u32 flags = fsi_get_info_flags(fsi); u32 flags = fsi_get_info_flags(fsi);
u32 data = 0; u32 data = 0;
int ret; int ret;
@ -886,6 +924,7 @@ static int fsi_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBM_CFM: case SND_SOC_DAIFMT_CBM_CFM:
data = DIMD | DOMD; data = DIMD | DOMD;
fsi->clk_master = 1;
break; break;
case SND_SOC_DAIFMT_CBS_CFS: case SND_SOC_DAIFMT_CBS_CFS:
break; break;
@ -893,6 +932,13 @@ static int fsi_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
ret = -EINVAL; ret = -EINVAL;
goto set_fmt_exit; goto set_fmt_exit;
} }
if (fsi_is_clk_master(fsi) && !set_rate) {
dev_err(dai->dev, "platform doesn't have set_rate\n");
ret = -EINVAL;
goto set_fmt_exit;
}
fsi_reg_mask_set(fsi, CKG1, (DIMD | DOMD), data); fsi_reg_mask_set(fsi, CKG1, (DIMD | DOMD), data);
/* set format */ /* set format */
@ -919,13 +965,12 @@ static int fsi_dai_hw_params(struct snd_pcm_substream *substream,
{ {
struct fsi_priv *fsi = fsi_get_priv(substream); struct fsi_priv *fsi = fsi_get_priv(substream);
struct fsi_master *master = fsi_get_master(fsi); struct fsi_master *master = fsi_get_master(fsi);
set_rate_func set_rate; set_rate_func set_rate = fsi_get_info_set_rate(master);
int fsi_ver = master->core->ver; int fsi_ver = master->core->ver;
long rate = params_rate(params); long rate = params_rate(params);
int ret; int ret;
set_rate = fsi_get_info_set_rate(master); if (!fsi_is_clk_master(fsi))
if (!set_rate)
return 0; return 0;
ret = set_rate(dai->dev, fsi_is_port_a(fsi), rate, 1); ret = set_rate(dai->dev, fsi_is_port_a(fsi), rate, 1);
@ -987,7 +1032,6 @@ static int fsi_dai_hw_params(struct snd_pcm_substream *substream,
fsi_reg_mask_set(fsi, CKG1, (ACKMD_MASK | BPFMD_MASK) , data); fsi_reg_mask_set(fsi, CKG1, (ACKMD_MASK | BPFMD_MASK) , data);
udelay(10); udelay(10);
fsi_clk_ctrl(fsi, 1);
ret = 0; ret = 0;
} }
@ -1202,9 +1246,7 @@ static int fsi_probe(struct platform_device *pdev)
pm_runtime_enable(&pdev->dev); pm_runtime_enable(&pdev->dev);
dev_set_drvdata(&pdev->dev, master); dev_set_drvdata(&pdev->dev, master);
pm_runtime_get_sync(&pdev->dev); fsi_module_init(master, &pdev->dev);
fsi_soft_all_reset(master);
pm_runtime_put_sync(&pdev->dev);
ret = request_irq(irq, &fsi_interrupt, IRQF_DISABLED, ret = request_irq(irq, &fsi_interrupt, IRQF_DISABLED,
id_entry->name, master); id_entry->name, master);
@ -1248,6 +1290,8 @@ static int fsi_remove(struct platform_device *pdev)
master = dev_get_drvdata(&pdev->dev); master = dev_get_drvdata(&pdev->dev);
fsi_module_kill(master, &pdev->dev);
free_irq(master->irq, master); free_irq(master->irq, master);
pm_runtime_disable(&pdev->dev); pm_runtime_disable(&pdev->dev);
@ -1260,6 +1304,82 @@ static int fsi_remove(struct platform_device *pdev)
return 0; return 0;
} }
static void __fsi_suspend(struct fsi_priv *fsi,
struct device *dev,
set_rate_func set_rate)
{
fsi->saved_do_fmt = fsi_reg_read(fsi, DO_FMT);
fsi->saved_di_fmt = fsi_reg_read(fsi, DI_FMT);
fsi->saved_ckg1 = fsi_reg_read(fsi, CKG1);
fsi->saved_ckg2 = fsi_reg_read(fsi, CKG2);
fsi->saved_out_sel = fsi_reg_read(fsi, OUT_SEL);
if (fsi_is_clk_master(fsi))
set_rate(dev, fsi_is_port_a(fsi), fsi->rate, 0);
}
static void __fsi_resume(struct fsi_priv *fsi,
struct device *dev,
set_rate_func set_rate)
{
fsi_reg_write(fsi, DO_FMT, fsi->saved_do_fmt);
fsi_reg_write(fsi, DI_FMT, fsi->saved_di_fmt);
fsi_reg_write(fsi, CKG1, fsi->saved_ckg1);
fsi_reg_write(fsi, CKG2, fsi->saved_ckg2);
fsi_reg_write(fsi, OUT_SEL, fsi->saved_out_sel);
if (fsi_is_clk_master(fsi))
set_rate(dev, fsi_is_port_a(fsi), fsi->rate, 1);
}
static int fsi_suspend(struct device *dev)
{
struct fsi_master *master = dev_get_drvdata(dev);
set_rate_func set_rate = fsi_get_info_set_rate(master);
pm_runtime_get_sync(dev);
__fsi_suspend(&master->fsia, dev, set_rate);
__fsi_suspend(&master->fsib, dev, set_rate);
master->saved_a_mclk = fsi_core_read(master, a_mclk);
master->saved_b_mclk = fsi_core_read(master, b_mclk);
master->saved_iemsk = fsi_core_read(master, iemsk);
master->saved_imsk = fsi_core_read(master, imsk);
master->saved_clk_rst = fsi_master_read(master, CLK_RST);
master->saved_soft_rst = fsi_master_read(master, SOFT_RST);
fsi_module_kill(master, dev);
pm_runtime_put_sync(dev);
return 0;
}
static int fsi_resume(struct device *dev)
{
struct fsi_master *master = dev_get_drvdata(dev);
set_rate_func set_rate = fsi_get_info_set_rate(master);
pm_runtime_get_sync(dev);
fsi_module_init(master, dev);
fsi_master_mask_set(master, SOFT_RST, 0xffff, master->saved_soft_rst);
fsi_master_mask_set(master, CLK_RST, 0xffff, master->saved_clk_rst);
fsi_core_mask_set(master, a_mclk, 0xffff, master->saved_a_mclk);
fsi_core_mask_set(master, b_mclk, 0xffff, master->saved_b_mclk);
fsi_core_mask_set(master, iemsk, 0xffff, master->saved_iemsk);
fsi_core_mask_set(master, imsk, 0xffff, master->saved_imsk);
__fsi_resume(&master->fsia, dev, set_rate);
__fsi_resume(&master->fsib, dev, set_rate);
pm_runtime_put_sync(dev);
return 0;
}
static int fsi_runtime_nop(struct device *dev) static int fsi_runtime_nop(struct device *dev)
{ {
/* Runtime PM callback shared between ->runtime_suspend() /* Runtime PM callback shared between ->runtime_suspend()
@ -1273,6 +1393,8 @@ static int fsi_runtime_nop(struct device *dev)
} }
static struct dev_pm_ops fsi_pm_ops = { static struct dev_pm_ops fsi_pm_ops = {
.suspend = fsi_suspend,
.resume = fsi_resume,
.runtime_suspend = fsi_runtime_nop, .runtime_suspend = fsi_runtime_nop,
.runtime_resume = fsi_runtime_nop, .runtime_resume = fsi_runtime_nop,
}; };

View file

@ -20,40 +20,28 @@
#include <trace/events/asoc.h> #include <trace/events/asoc.h>
static unsigned int snd_soc_4_12_read(struct snd_soc_codec *codec, #ifdef CONFIG_SPI_MASTER
unsigned int reg) static int do_spi_write(void *control, const char *data, int len)
{ {
struct spi_device *spi = control;
int ret; int ret;
unsigned int val;
if (reg >= codec->driver->reg_cache_size || ret = spi_write(spi, data, len);
snd_soc_codec_volatile_register(codec, reg) ||
codec->cache_bypass) {
if (codec->cache_only)
return -1;
BUG_ON(!codec->hw_read);
return codec->hw_read(codec, reg);
}
ret = snd_soc_cache_read(codec, reg, &val);
if (ret < 0) if (ret < 0)
return -1; return ret;
return val;
return len;
} }
#endif
static int snd_soc_4_12_write(struct snd_soc_codec *codec, unsigned int reg, static int do_hw_write(struct snd_soc_codec *codec, unsigned int reg,
unsigned int value) unsigned int value, const void *data, int len)
{ {
u8 data[2];
int ret; int ret;
data[0] = (reg << 4) | ((value >> 8) & 0x000f);
data[1] = value & 0x00ff;
if (!snd_soc_codec_volatile_register(codec, reg) && if (!snd_soc_codec_volatile_register(codec, reg) &&
reg < codec->driver->reg_cache_size && reg < codec->driver->reg_cache_size &&
!codec->cache_bypass) { !codec->cache_bypass) {
ret = snd_soc_cache_write(codec, reg, value); ret = snd_soc_cache_write(codec, reg, value);
if (ret < 0) if (ret < 0)
return -1; return -1;
@ -64,8 +52,8 @@ static int snd_soc_4_12_write(struct snd_soc_codec *codec, unsigned int reg,
return 0; return 0;
} }
ret = codec->hw_write(codec->control_data, data, 2); ret = codec->hw_write(codec->control_data, data, len);
if (ret == 2) if (ret == len)
return 0; return 0;
if (ret < 0) if (ret < 0)
return ret; return ret;
@ -73,232 +61,7 @@ static int snd_soc_4_12_write(struct snd_soc_codec *codec, unsigned int reg,
return -EIO; return -EIO;
} }
#if defined(CONFIG_SPI_MASTER) static unsigned int do_hw_read(struct snd_soc_codec *codec, unsigned int reg)
static int snd_soc_4_12_spi_write(void *control_data, const char *data,
int len)
{
struct spi_device *spi = control_data;
struct spi_transfer t;
struct spi_message m;
u8 msg[2];
if (len <= 0)
return 0;
msg[0] = data[1];
msg[1] = data[0];
spi_message_init(&m);
memset(&t, 0, sizeof t);
t.tx_buf = &msg[0];
t.len = len;
spi_message_add_tail(&t, &m);
spi_sync(spi, &m);
return len;
}
#else
#define snd_soc_4_12_spi_write NULL
#endif
static unsigned int snd_soc_7_9_read(struct snd_soc_codec *codec,
unsigned int reg)
{
int ret;
unsigned int val;
if (reg >= codec->driver->reg_cache_size ||
snd_soc_codec_volatile_register(codec, reg) ||
codec->cache_bypass) {
if (codec->cache_only)
return -1;
BUG_ON(!codec->hw_read);
return codec->hw_read(codec, reg);
}
ret = snd_soc_cache_read(codec, reg, &val);
if (ret < 0)
return -1;
return val;
}
static int snd_soc_7_9_write(struct snd_soc_codec *codec, unsigned int reg,
unsigned int value)
{
u8 data[2];
int ret;
data[0] = (reg << 1) | ((value >> 8) & 0x0001);
data[1] = value & 0x00ff;
if (!snd_soc_codec_volatile_register(codec, reg) &&
reg < codec->driver->reg_cache_size &&
!codec->cache_bypass) {
ret = snd_soc_cache_write(codec, reg, value);
if (ret < 0)
return -1;
}
if (codec->cache_only) {
codec->cache_sync = 1;
return 0;
}
ret = codec->hw_write(codec->control_data, data, 2);
if (ret == 2)
return 0;
if (ret < 0)
return ret;
else
return -EIO;
}
#if defined(CONFIG_SPI_MASTER)
static int snd_soc_7_9_spi_write(void *control_data, const char *data,
int len)
{
struct spi_device *spi = control_data;
struct spi_transfer t;
struct spi_message m;
u8 msg[2];
if (len <= 0)
return 0;
msg[0] = data[0];
msg[1] = data[1];
spi_message_init(&m);
memset(&t, 0, sizeof t);
t.tx_buf = &msg[0];
t.len = len;
spi_message_add_tail(&t, &m);
spi_sync(spi, &m);
return len;
}
#else
#define snd_soc_7_9_spi_write NULL
#endif
static int snd_soc_8_8_write(struct snd_soc_codec *codec, unsigned int reg,
unsigned int value)
{
u8 data[2];
int ret;
reg &= 0xff;
data[0] = reg;
data[1] = value & 0xff;
if (!snd_soc_codec_volatile_register(codec, reg) &&
reg < codec->driver->reg_cache_size &&
!codec->cache_bypass) {
ret = snd_soc_cache_write(codec, reg, value);
if (ret < 0)
return -1;
}
if (codec->cache_only) {
codec->cache_sync = 1;
return 0;
}
if (codec->hw_write(codec->control_data, data, 2) == 2)
return 0;
else
return -EIO;
}
static unsigned int snd_soc_8_8_read(struct snd_soc_codec *codec,
unsigned int reg)
{
int ret;
unsigned int val;
reg &= 0xff;
if (reg >= codec->driver->reg_cache_size ||
snd_soc_codec_volatile_register(codec, reg) ||
codec->cache_bypass) {
if (codec->cache_only)
return -1;
BUG_ON(!codec->hw_read);
return codec->hw_read(codec, reg);
}
ret = snd_soc_cache_read(codec, reg, &val);
if (ret < 0)
return -1;
return val;
}
#if defined(CONFIG_SPI_MASTER)
static int snd_soc_8_8_spi_write(void *control_data, const char *data,
int len)
{
struct spi_device *spi = control_data;
struct spi_transfer t;
struct spi_message m;
u8 msg[2];
if (len <= 0)
return 0;
msg[0] = data[0];
msg[1] = data[1];
spi_message_init(&m);
memset(&t, 0, sizeof t);
t.tx_buf = &msg[0];
t.len = len;
spi_message_add_tail(&t, &m);
spi_sync(spi, &m);
return len;
}
#else
#define snd_soc_8_8_spi_write NULL
#endif
static int snd_soc_8_16_write(struct snd_soc_codec *codec, unsigned int reg,
unsigned int value)
{
u8 data[3];
int ret;
data[0] = reg;
data[1] = (value >> 8) & 0xff;
data[2] = value & 0xff;
if (!snd_soc_codec_volatile_register(codec, reg) &&
reg < codec->driver->reg_cache_size &&
!codec->cache_bypass) {
ret = snd_soc_cache_write(codec, reg, value);
if (ret < 0)
return -1;
}
if (codec->cache_only) {
codec->cache_sync = 1;
return 0;
}
if (codec->hw_write(codec->control_data, data, 3) == 3)
return 0;
else
return -EIO;
}
static unsigned int snd_soc_8_16_read(struct snd_soc_codec *codec,
unsigned int reg)
{ {
int ret; int ret;
unsigned int val; unsigned int val;
@ -319,65 +82,117 @@ static unsigned int snd_soc_8_16_read(struct snd_soc_codec *codec,
return val; return val;
} }
#if defined(CONFIG_SPI_MASTER) static unsigned int snd_soc_4_12_read(struct snd_soc_codec *codec,
static int snd_soc_8_16_spi_write(void *control_data, const char *data, unsigned int reg)
int len)
{ {
struct spi_device *spi = control_data; return do_hw_read(codec, reg);
struct spi_transfer t; }
struct spi_message m;
u8 msg[3]; static int snd_soc_4_12_write(struct snd_soc_codec *codec, unsigned int reg,
unsigned int value)
if (len <= 0) {
return 0; u16 data;
msg[0] = data[0]; data = cpu_to_be16((reg << 12) | (value & 0xffffff));
msg[1] = data[1];
msg[2] = data[2]; return do_hw_write(codec, reg, value, &data, 2);
}
spi_message_init(&m);
memset(&t, 0, sizeof t); static unsigned int snd_soc_7_9_read(struct snd_soc_codec *codec,
unsigned int reg)
t.tx_buf = &msg[0]; {
t.len = len; return do_hw_read(codec, reg);
}
spi_message_add_tail(&t, &m);
spi_sync(spi, &m); static int snd_soc_7_9_write(struct snd_soc_codec *codec, unsigned int reg,
unsigned int value)
return len; {
u8 data[2];
data[0] = (reg << 1) | ((value >> 8) & 0x0001);
data[1] = value & 0x00ff;
return do_hw_write(codec, reg, value, data, 2);
}
static int snd_soc_8_8_write(struct snd_soc_codec *codec, unsigned int reg,
unsigned int value)
{
u8 data[2];
reg &= 0xff;
data[0] = reg;
data[1] = value & 0xff;
return do_hw_write(codec, reg, value, data, 2);
}
static unsigned int snd_soc_8_8_read(struct snd_soc_codec *codec,
unsigned int reg)
{
return do_hw_read(codec, reg);
}
static int snd_soc_8_16_write(struct snd_soc_codec *codec, unsigned int reg,
unsigned int value)
{
u8 data[3];
data[0] = reg;
data[1] = (value >> 8) & 0xff;
data[2] = value & 0xff;
return do_hw_write(codec, reg, value, data, 3);
}
static unsigned int snd_soc_8_16_read(struct snd_soc_codec *codec,
unsigned int reg)
{
return do_hw_read(codec, reg);
} }
#else
#define snd_soc_8_16_spi_write NULL
#endif
#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE)) #if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE))
static unsigned int snd_soc_8_8_read_i2c(struct snd_soc_codec *codec, static unsigned int do_i2c_read(struct snd_soc_codec *codec,
unsigned int r) void *reg, int reglen,
void *data, int datalen)
{ {
struct i2c_msg xfer[2]; struct i2c_msg xfer[2];
u8 reg = r;
u8 data;
int ret; int ret;
struct i2c_client *client = codec->control_data; struct i2c_client *client = codec->control_data;
/* Write register */ /* Write register */
xfer[0].addr = client->addr; xfer[0].addr = client->addr;
xfer[0].flags = 0; xfer[0].flags = 0;
xfer[0].len = 1; xfer[0].len = reglen;
xfer[0].buf = &reg; xfer[0].buf = reg;
/* Read data */ /* Read data */
xfer[1].addr = client->addr; xfer[1].addr = client->addr;
xfer[1].flags = I2C_M_RD; xfer[1].flags = I2C_M_RD;
xfer[1].len = 1; xfer[1].len = datalen;
xfer[1].buf = &data; xfer[1].buf = data;
ret = i2c_transfer(client->adapter, xfer, 2); ret = i2c_transfer(client->adapter, xfer, 2);
if (ret != 2) { if (ret == 2)
dev_err(&client->dev, "i2c_transfer() returned %d\n", ret);
return 0; return 0;
} else if (ret < 0)
return ret;
else
return -EIO;
}
#endif
#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE))
static unsigned int snd_soc_8_8_read_i2c(struct snd_soc_codec *codec,
unsigned int r)
{
u8 reg = r;
u8 data;
int ret;
ret = do_i2c_read(codec, &reg, 1, &data, 1);
if (ret < 0)
return 0;
return data; return data;
} }
#else #else
@ -388,30 +203,13 @@ static unsigned int snd_soc_8_8_read_i2c(struct snd_soc_codec *codec,
static unsigned int snd_soc_8_16_read_i2c(struct snd_soc_codec *codec, static unsigned int snd_soc_8_16_read_i2c(struct snd_soc_codec *codec,
unsigned int r) unsigned int r)
{ {
struct i2c_msg xfer[2];
u8 reg = r; u8 reg = r;
u16 data; u16 data;
int ret; int ret;
struct i2c_client *client = codec->control_data;
/* Write register */ ret = do_i2c_read(codec, &reg, 1, &data, 2);
xfer[0].addr = client->addr; if (ret < 0)
xfer[0].flags = 0;
xfer[0].len = 1;
xfer[0].buf = &reg;
/* Read data */
xfer[1].addr = client->addr;
xfer[1].flags = I2C_M_RD;
xfer[1].len = 2;
xfer[1].buf = (u8 *)&data;
ret = i2c_transfer(client->adapter, xfer, 2);
if (ret != 2) {
dev_err(&client->dev, "i2c_transfer() returned %d\n", ret);
return 0; return 0;
}
return (data >> 8) | ((data & 0xff) << 8); return (data >> 8) | ((data & 0xff) << 8);
} }
#else #else
@ -422,30 +220,13 @@ static unsigned int snd_soc_8_16_read_i2c(struct snd_soc_codec *codec,
static unsigned int snd_soc_16_8_read_i2c(struct snd_soc_codec *codec, static unsigned int snd_soc_16_8_read_i2c(struct snd_soc_codec *codec,
unsigned int r) unsigned int r)
{ {
struct i2c_msg xfer[2];
u16 reg = r; u16 reg = r;
u8 data; u8 data;
int ret; int ret;
struct i2c_client *client = codec->control_data;
/* Write register */ ret = do_i2c_read(codec, &reg, 2, &data, 1);
xfer[0].addr = client->addr; if (ret < 0)
xfer[0].flags = 0;
xfer[0].len = 2;
xfer[0].buf = (u8 *)&reg;
/* Read data */
xfer[1].addr = client->addr;
xfer[1].flags = I2C_M_RD;
xfer[1].len = 1;
xfer[1].buf = &data;
ret = i2c_transfer(client->adapter, xfer, 2);
if (ret != 2) {
dev_err(&client->dev, "i2c_transfer() returned %d\n", ret);
return 0; return 0;
}
return data; return data;
} }
#else #else
@ -453,120 +234,34 @@ static unsigned int snd_soc_16_8_read_i2c(struct snd_soc_codec *codec,
#endif #endif
static unsigned int snd_soc_16_8_read(struct snd_soc_codec *codec, static unsigned int snd_soc_16_8_read(struct snd_soc_codec *codec,
unsigned int reg) unsigned int reg)
{ {
int ret; return do_hw_read(codec, reg);
unsigned int val;
reg &= 0xff;
if (reg >= codec->driver->reg_cache_size ||
snd_soc_codec_volatile_register(codec, reg) ||
codec->cache_bypass) {
if (codec->cache_only)
return -1;
BUG_ON(!codec->hw_read);
return codec->hw_read(codec, reg);
}
ret = snd_soc_cache_read(codec, reg, &val);
if (ret < 0)
return -1;
return val;
} }
static int snd_soc_16_8_write(struct snd_soc_codec *codec, unsigned int reg, static int snd_soc_16_8_write(struct snd_soc_codec *codec, unsigned int reg,
unsigned int value) unsigned int value)
{ {
u8 data[3]; u8 data[3];
int ret;
data[0] = (reg >> 8) & 0xff; data[0] = (reg >> 8) & 0xff;
data[1] = reg & 0xff; data[1] = reg & 0xff;
data[2] = value; data[2] = value;
reg &= 0xff; return do_hw_write(codec, reg, value, data, 3);
if (!snd_soc_codec_volatile_register(codec, reg) &&
reg < codec->driver->reg_cache_size &&
!codec->cache_bypass) {
ret = snd_soc_cache_write(codec, reg, value);
if (ret < 0)
return -1;
}
if (codec->cache_only) {
codec->cache_sync = 1;
return 0;
}
ret = codec->hw_write(codec->control_data, data, 3);
if (ret == 3)
return 0;
if (ret < 0)
return ret;
else
return -EIO;
} }
#if defined(CONFIG_SPI_MASTER)
static int snd_soc_16_8_spi_write(void *control_data, const char *data,
int len)
{
struct spi_device *spi = control_data;
struct spi_transfer t;
struct spi_message m;
u8 msg[3];
if (len <= 0)
return 0;
msg[0] = data[0];
msg[1] = data[1];
msg[2] = data[2];
spi_message_init(&m);
memset(&t, 0, sizeof t);
t.tx_buf = &msg[0];
t.len = len;
spi_message_add_tail(&t, &m);
spi_sync(spi, &m);
return len;
}
#else
#define snd_soc_16_8_spi_write NULL
#endif
#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE)) #if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE))
static unsigned int snd_soc_16_16_read_i2c(struct snd_soc_codec *codec, static unsigned int snd_soc_16_16_read_i2c(struct snd_soc_codec *codec,
unsigned int r) unsigned int r)
{ {
struct i2c_msg xfer[2];
u16 reg = cpu_to_be16(r); u16 reg = cpu_to_be16(r);
u16 data; u16 data;
int ret; int ret;
struct i2c_client *client = codec->control_data;
/* Write register */ ret = do_i2c_read(codec, &reg, 2, &data, 2);
xfer[0].addr = client->addr; if (ret < 0)
xfer[0].flags = 0;
xfer[0].len = 2;
xfer[0].buf = (u8 *)&reg;
/* Read data */
xfer[1].addr = client->addr;
xfer[1].flags = I2C_M_RD;
xfer[1].len = 2;
xfer[1].buf = (u8 *)&data;
ret = i2c_transfer(client->adapter, xfer, 2);
if (ret != 2) {
dev_err(&client->dev, "i2c_transfer() returned %d\n", ret);
return 0; return 0;
}
return be16_to_cpu(data); return be16_to_cpu(data);
} }
#else #else
@ -576,52 +271,59 @@ static unsigned int snd_soc_16_16_read_i2c(struct snd_soc_codec *codec,
static unsigned int snd_soc_16_16_read(struct snd_soc_codec *codec, static unsigned int snd_soc_16_16_read(struct snd_soc_codec *codec,
unsigned int reg) unsigned int reg)
{ {
int ret; return do_hw_read(codec, reg);
unsigned int val;
if (reg >= codec->driver->reg_cache_size ||
snd_soc_codec_volatile_register(codec, reg) ||
codec->cache_bypass) {
if (codec->cache_only)
return -1;
BUG_ON(!codec->hw_read);
return codec->hw_read(codec, reg);
}
ret = snd_soc_cache_read(codec, reg, &val);
if (ret < 0)
return -1;
return val;
} }
static int snd_soc_16_16_write(struct snd_soc_codec *codec, unsigned int reg, static int snd_soc_16_16_write(struct snd_soc_codec *codec, unsigned int reg,
unsigned int value) unsigned int value)
{ {
u8 data[4]; u8 data[4];
int ret;
data[0] = (reg >> 8) & 0xff; data[0] = (reg >> 8) & 0xff;
data[1] = reg & 0xff; data[1] = reg & 0xff;
data[2] = (value >> 8) & 0xff; data[2] = (value >> 8) & 0xff;
data[3] = value & 0xff; data[3] = value & 0xff;
if (!snd_soc_codec_volatile_register(codec, reg) && return do_hw_write(codec, reg, value, data, 4);
reg < codec->driver->reg_cache_size && }
!codec->cache_bypass) {
ret = snd_soc_cache_write(codec, reg, value); /* Primitive bulk write support for soc-cache. The data pointed to by
if (ret < 0) * `data' needs to already be in the form the hardware expects
return -1; * including any leading register specific data. Any data written
* through this function will not go through the cache as it only
* handles writing to volatile or out of bounds registers.
*/
static int snd_soc_hw_bulk_write_raw(struct snd_soc_codec *codec, unsigned int reg,
const void *data, size_t len)
{
int ret;
/* To ensure that we don't get out of sync with the cache, check
* whether the base register is volatile or if we've directly asked
* to bypass the cache. Out of bounds registers are considered
* volatile.
*/
if (!codec->cache_bypass
&& !snd_soc_codec_volatile_register(codec, reg)
&& reg < codec->driver->reg_cache_size)
return -EINVAL;
switch (codec->control_type) {
#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE))
case SND_SOC_I2C:
ret = i2c_master_send(codec->control_data, data, len);
break;
#endif
#if defined(CONFIG_SPI_MASTER)
case SND_SOC_SPI:
ret = spi_write(codec->control_data, data, len);
break;
#endif
default:
BUG();
} }
if (codec->cache_only) { if (ret == len)
codec->cache_sync = 1;
return 0;
}
ret = codec->hw_write(codec->control_data, data, 4);
if (ret == 4)
return 0; return 0;
if (ret < 0) if (ret < 0)
return ret; return ret;
@ -629,79 +331,40 @@ static int snd_soc_16_16_write(struct snd_soc_codec *codec, unsigned int reg,
return -EIO; return -EIO;
} }
#if defined(CONFIG_SPI_MASTER)
static int snd_soc_16_16_spi_write(void *control_data, const char *data,
int len)
{
struct spi_device *spi = control_data;
struct spi_transfer t;
struct spi_message m;
u8 msg[4];
if (len <= 0)
return 0;
msg[0] = data[0];
msg[1] = data[1];
msg[2] = data[2];
msg[3] = data[3];
spi_message_init(&m);
memset(&t, 0, sizeof t);
t.tx_buf = &msg[0];
t.len = len;
spi_message_add_tail(&t, &m);
spi_sync(spi, &m);
return len;
}
#else
#define snd_soc_16_16_spi_write NULL
#endif
static struct { static struct {
int addr_bits; int addr_bits;
int data_bits; int data_bits;
int (*write)(struct snd_soc_codec *codec, unsigned int, unsigned int); int (*write)(struct snd_soc_codec *codec, unsigned int, unsigned int);
int (*spi_write)(void *, const char *, int);
unsigned int (*read)(struct snd_soc_codec *, unsigned int); unsigned int (*read)(struct snd_soc_codec *, unsigned int);
unsigned int (*i2c_read)(struct snd_soc_codec *, unsigned int); unsigned int (*i2c_read)(struct snd_soc_codec *, unsigned int);
} io_types[] = { } io_types[] = {
{ {
.addr_bits = 4, .data_bits = 12, .addr_bits = 4, .data_bits = 12,
.write = snd_soc_4_12_write, .read = snd_soc_4_12_read, .write = snd_soc_4_12_write, .read = snd_soc_4_12_read,
.spi_write = snd_soc_4_12_spi_write,
}, },
{ {
.addr_bits = 7, .data_bits = 9, .addr_bits = 7, .data_bits = 9,
.write = snd_soc_7_9_write, .read = snd_soc_7_9_read, .write = snd_soc_7_9_write, .read = snd_soc_7_9_read,
.spi_write = snd_soc_7_9_spi_write,
}, },
{ {
.addr_bits = 8, .data_bits = 8, .addr_bits = 8, .data_bits = 8,
.write = snd_soc_8_8_write, .read = snd_soc_8_8_read, .write = snd_soc_8_8_write, .read = snd_soc_8_8_read,
.i2c_read = snd_soc_8_8_read_i2c, .i2c_read = snd_soc_8_8_read_i2c,
.spi_write = snd_soc_8_8_spi_write,
}, },
{ {
.addr_bits = 8, .data_bits = 16, .addr_bits = 8, .data_bits = 16,
.write = snd_soc_8_16_write, .read = snd_soc_8_16_read, .write = snd_soc_8_16_write, .read = snd_soc_8_16_read,
.i2c_read = snd_soc_8_16_read_i2c, .i2c_read = snd_soc_8_16_read_i2c,
.spi_write = snd_soc_8_16_spi_write,
}, },
{ {
.addr_bits = 16, .data_bits = 8, .addr_bits = 16, .data_bits = 8,
.write = snd_soc_16_8_write, .read = snd_soc_16_8_read, .write = snd_soc_16_8_write, .read = snd_soc_16_8_read,
.i2c_read = snd_soc_16_8_read_i2c, .i2c_read = snd_soc_16_8_read_i2c,
.spi_write = snd_soc_16_8_spi_write,
}, },
{ {
.addr_bits = 16, .data_bits = 16, .addr_bits = 16, .data_bits = 16,
.write = snd_soc_16_16_write, .read = snd_soc_16_16_read, .write = snd_soc_16_16_write, .read = snd_soc_16_16_read,
.i2c_read = snd_soc_16_16_read_i2c, .i2c_read = snd_soc_16_16_read_i2c,
.spi_write = snd_soc_16_16_spi_write,
}, },
}; };
@ -709,7 +372,6 @@ static struct {
* snd_soc_codec_set_cache_io: Set up standard I/O functions. * snd_soc_codec_set_cache_io: Set up standard I/O functions.
* *
* @codec: CODEC to configure. * @codec: CODEC to configure.
* @type: Type of cache.
* @addr_bits: Number of bits of register address data. * @addr_bits: Number of bits of register address data.
* @data_bits: Number of bits of data per register. * @data_bits: Number of bits of data per register.
* @control: Control bus used. * @control: Control bus used.
@ -744,6 +406,7 @@ int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec,
codec->write = io_types[i].write; codec->write = io_types[i].write;
codec->read = io_types[i].read; codec->read = io_types[i].read;
codec->bulk_write_raw = snd_soc_hw_bulk_write_raw;
switch (control) { switch (control) {
case SND_SOC_CUSTOM: case SND_SOC_CUSTOM:
@ -762,8 +425,9 @@ int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec,
break; break;
case SND_SOC_SPI: case SND_SOC_SPI:
if (io_types[i].spi_write) #ifdef CONFIG_SPI_MASTER
codec->hw_write = io_types[i].spi_write; codec->hw_write = do_spi_write;
#endif
codec->control_data = container_of(codec->dev, codec->control_data = container_of(codec->dev,
struct spi_device, struct spi_device,
@ -889,6 +553,8 @@ static int snd_soc_rbtree_cache_sync(struct snd_soc_codec *codec)
rbnode = rb_entry(node, struct snd_soc_rbtree_node, node); rbnode = rb_entry(node, struct snd_soc_rbtree_node, node);
if (rbnode->value == rbnode->defval) if (rbnode->value == rbnode->defval)
continue; continue;
WARN_ON(codec->writable_register &&
codec->writable_register(codec, rbnode->reg));
ret = snd_soc_cache_read(codec, rbnode->reg, &val); ret = snd_soc_cache_read(codec, rbnode->reg, &val);
if (ret) if (ret)
return ret; return ret;
@ -1149,6 +815,8 @@ static int snd_soc_lzo_cache_sync(struct snd_soc_codec *codec)
lzo_blocks = codec->reg_cache; lzo_blocks = codec->reg_cache;
for_each_set_bit(i, lzo_blocks[0]->sync_bmp, lzo_blocks[0]->sync_bmp_nbits) { for_each_set_bit(i, lzo_blocks[0]->sync_bmp, lzo_blocks[0]->sync_bmp_nbits) {
WARN_ON(codec->writable_register &&
codec->writable_register(codec, i));
ret = snd_soc_cache_read(codec, i, &val); ret = snd_soc_cache_read(codec, i, &val);
if (ret) if (ret)
return ret; return ret;
@ -1407,6 +1075,8 @@ static int snd_soc_flat_cache_sync(struct snd_soc_codec *codec)
codec_drv = codec->driver; codec_drv = codec->driver;
for (i = 0; i < codec_drv->reg_cache_size; ++i) { for (i = 0; i < codec_drv->reg_cache_size; ++i) {
WARN_ON(codec->writable_register &&
codec->writable_register(codec, i));
ret = snd_soc_cache_read(codec, i, &val); ret = snd_soc_cache_read(codec, i, &val);
if (ret) if (ret)
return ret; return ret;
@ -1523,7 +1193,7 @@ int snd_soc_cache_init(struct snd_soc_codec *codec)
codec->cache_ops->name, codec->name); codec->cache_ops->name, codec->name);
return codec->cache_ops->init(codec); return codec->cache_ops->init(codec);
} }
return -EINVAL; return -ENOSYS;
} }
/* /*
@ -1538,7 +1208,7 @@ int snd_soc_cache_exit(struct snd_soc_codec *codec)
codec->cache_ops->name, codec->name); codec->cache_ops->name, codec->name);
return codec->cache_ops->exit(codec); return codec->cache_ops->exit(codec);
} }
return -EINVAL; return -ENOSYS;
} }
/** /**
@ -1562,7 +1232,7 @@ int snd_soc_cache_read(struct snd_soc_codec *codec,
} }
mutex_unlock(&codec->cache_rw_mutex); mutex_unlock(&codec->cache_rw_mutex);
return -EINVAL; return -ENOSYS;
} }
EXPORT_SYMBOL_GPL(snd_soc_cache_read); EXPORT_SYMBOL_GPL(snd_soc_cache_read);
@ -1587,7 +1257,7 @@ int snd_soc_cache_write(struct snd_soc_codec *codec,
} }
mutex_unlock(&codec->cache_rw_mutex); mutex_unlock(&codec->cache_rw_mutex);
return -EINVAL; return -ENOSYS;
} }
EXPORT_SYMBOL_GPL(snd_soc_cache_write); EXPORT_SYMBOL_GPL(snd_soc_cache_write);
@ -1610,7 +1280,7 @@ int snd_soc_cache_sync(struct snd_soc_codec *codec)
} }
if (!codec->cache_ops || !codec->cache_ops->sync) if (!codec->cache_ops || !codec->cache_ops->sync)
return -EINVAL; return -ENOSYS;
if (codec->cache_ops->name) if (codec->cache_ops->name)
name = codec->cache_ops->name; name = codec->cache_ops->name;
@ -1677,3 +1347,17 @@ int snd_soc_default_readable_register(struct snd_soc_codec *codec,
return codec->driver->reg_access_default[index].read; return codec->driver->reg_access_default[index].read;
} }
EXPORT_SYMBOL_GPL(snd_soc_default_readable_register); EXPORT_SYMBOL_GPL(snd_soc_default_readable_register);
int snd_soc_default_writable_register(struct snd_soc_codec *codec,
unsigned int reg)
{
int index;
if (reg >= codec->driver->reg_cache_size)
return 1;
index = snd_soc_get_reg_access_index(codec, reg);
if (index < 0)
return 0;
return codec->driver->reg_access_default[index].write;
}
EXPORT_SYMBOL_GPL(snd_soc_default_writable_register);

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