Merge branch 'topic/asoc' into for-linus
This commit is contained in:
commit
7ec298dfef
@ -5854,7 +5854,7 @@ F: include/sound/
|
||||
F: sound/
|
||||
|
||||
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>
|
||||
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)
|
||||
@ -6105,7 +6105,7 @@ F: drivers/mmc/host/tifm_sd.c
|
||||
F: include/linux/tifm.h
|
||||
|
||||
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)
|
||||
S: Maintained
|
||||
F: sound/soc/codecs/twl4030*
|
||||
@ -6749,7 +6749,7 @@ F: drivers/scsi/vmw_pvscsi.c
|
||||
F: drivers/scsi/vmw_pvscsi.h
|
||||
|
||||
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>
|
||||
W: http://opensource.wolfsonmicro.com/node/15
|
||||
W: http://www.slimlogic.co.uk/?p=48
|
||||
|
@ -27,12 +27,14 @@ comment "Tegra board type"
|
||||
|
||||
config MACH_HARMONY
|
||||
bool "Harmony board"
|
||||
select MACH_HAS_SND_SOC_TEGRA_WM8903
|
||||
help
|
||||
Support for nVidia Harmony development platform
|
||||
|
||||
config MACH_KAEN
|
||||
bool "Kaen board"
|
||||
select MACH_SEABOARD
|
||||
select MACH_HAS_SND_SOC_TEGRA_WM8903
|
||||
help
|
||||
Support for the Kaen version of Seaboard
|
||||
|
||||
@ -43,6 +45,7 @@ config MACH_PAZ00
|
||||
|
||||
config MACH_SEABOARD
|
||||
bool "Seaboard board"
|
||||
select MACH_HAS_SND_SOC_TEGRA_WM8903
|
||||
help
|
||||
Support for nVidia Seaboard development platform. It will
|
||||
also be included for some of the derivative boards that
|
||||
|
@ -34,7 +34,7 @@
|
||||
#include <asm/mach/time.h>
|
||||
#include <asm/setup.h>
|
||||
|
||||
#include <mach/harmony_audio.h>
|
||||
#include <mach/tegra_wm8903_pdata.h>
|
||||
#include <mach/iomap.h>
|
||||
#include <mach/irqs.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_hp_det = TEGRA_GPIO_HP_DET,
|
||||
.gpio_hp_mute = -1,
|
||||
.gpio_int_mic_en = TEGRA_GPIO_INT_MIC_EN,
|
||||
.gpio_ext_mic_en = TEGRA_GPIO_EXT_MIC_EN,
|
||||
};
|
||||
|
||||
static struct platform_device harmony_audio_device = {
|
||||
.name = "tegra-snd-harmony",
|
||||
.name = "tegra-snd-wm8903",
|
||||
.id = 0,
|
||||
.dev = {
|
||||
.platform_data = &harmony_audio_pdata,
|
||||
|
@ -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.
|
||||
*
|
||||
@ -14,9 +14,10 @@
|
||||
*
|
||||
*/
|
||||
|
||||
struct harmony_audio_platform_data {
|
||||
struct tegra_wm8903_platform_data {
|
||||
int gpio_spkr_en;
|
||||
int gpio_hp_det;
|
||||
int gpio_hp_mute;
|
||||
int gpio_int_mic_en;
|
||||
int gpio_ext_mic_en;
|
||||
};
|
@ -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->rx_time_slot_status = 0; /*default AMIC*/
|
||||
card->pcm_control = sst_pmic_ops.pcm_control;
|
||||
sst_drv_ctx->scard_ops->card_status = SND_CARD_UN_INIT;
|
||||
return 0;
|
||||
} else {
|
||||
pr_err("strcmp fail %s\n", card->module_name);
|
||||
|
@ -32,6 +32,7 @@
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/firmware.h>
|
||||
#include <sound/control.h>
|
||||
#include <asm/mrst.h>
|
||||
#include <sound/pcm.h>
|
||||
@ -40,6 +41,8 @@
|
||||
#include <sound/initval.h>
|
||||
#include "intel_sst.h"
|
||||
#include "intel_sst_ioctl.h"
|
||||
#include "intel_sst_fw_ipc.h"
|
||||
#include "intel_sst_common.h"
|
||||
#include "intelmid_snd_control.h"
|
||||
#include "intelmid.h"
|
||||
|
||||
@ -802,6 +805,7 @@ static int __devinit snd_intelmad_sst_register(
|
||||
pr_err("sst card registration failed\n");
|
||||
return ret_val;
|
||||
}
|
||||
sst_drv_ctx->scard_ops->card_status = SND_CARD_UN_INIT;
|
||||
|
||||
sst_card_vendor_id = intelmaddata->sstdrv_ops->vendor_id;
|
||||
intelmaddata->pmic_status = PMIC_UNINIT;
|
||||
|
@ -32,6 +32,10 @@ struct wm8994_ldo_pdata {
|
||||
#define WM8994_EQ_REGS 20
|
||||
#define WM8958_MBC_CUTOFF_REGS 20
|
||||
#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
|
||||
@ -71,6 +75,42 @@ struct wm8958_mbc_cfg {
|
||||
const char *name;
|
||||
u16 cutoff_regs[WM8958_MBC_CUTOFF_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 {
|
||||
@ -95,6 +135,15 @@ struct wm8994_pdata {
|
||||
int num_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 */
|
||||
unsigned int lineout1_diff:1;
|
||||
unsigned int lineout2_diff:1;
|
||||
|
26
include/sound/ak4641.h
Normal file
26
include/sound/ak4641.h
Normal 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
54
include/sound/max98095.h
Normal 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
|
@ -24,7 +24,7 @@
|
||||
* SoC dynamic audio power management
|
||||
*
|
||||
* 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
|
||||
* at stream time if power is not needed for sidetone, etc.
|
||||
* 2. Platform/Machine domain - physically connected inputs and outputs
|
||||
@ -39,30 +39,30 @@
|
||||
|
||||
/* codec domain */
|
||||
#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}
|
||||
|
||||
/* platform domain */
|
||||
#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 }
|
||||
#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 }
|
||||
#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, \
|
||||
.event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD}
|
||||
#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, \
|
||||
.event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD}
|
||||
#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, \
|
||||
.event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD}
|
||||
#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, \
|
||||
.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,\
|
||||
wcontrols, wncontrols) \
|
||||
{ .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,\
|
||||
wcontrols, wncontrols) \
|
||||
{ .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, \
|
||||
wcontrols, wncontrols)\
|
||||
{ .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, \
|
||||
wcontrols, wncontrols)\
|
||||
{ .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}
|
||||
#define SND_SOC_DAPM_MICBIAS(wname, wreg, wshift, winvert) \
|
||||
{ .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) \
|
||||
{ .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) \
|
||||
{ .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) \
|
||||
{ .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) \
|
||||
{ .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}
|
||||
|
||||
/* Simplified versions of above macros, assuming wncontrols = ARRAY_SIZE(wcontrols) */
|
||||
#define SOC_PGA_ARRAY(wname, wreg, wshift, winvert,\
|
||||
wcontrols) \
|
||||
{ .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, \
|
||||
wcontrols)\
|
||||
{ .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, \
|
||||
wcontrols)\
|
||||
{ .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)}
|
||||
|
||||
/* path domain with event - event handler must return 0 for success */
|
||||
#define SND_SOC_DAPM_PGA_E(wname, wreg, wshift, winvert, wcontrols, \
|
||||
wncontrols, wevent, wflags) \
|
||||
{ .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}
|
||||
#define SND_SOC_DAPM_OUT_DRV_E(wname, wreg, wshift, winvert, wcontrols, \
|
||||
wncontrols, wevent, wflags) \
|
||||
{ .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}
|
||||
#define SND_SOC_DAPM_MIXER_E(wname, wreg, wshift, winvert, wcontrols, \
|
||||
wncontrols, wevent, wflags) \
|
||||
{ .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}
|
||||
#define SND_SOC_DAPM_MIXER_NAMED_CTL_E(wname, wreg, wshift, winvert, \
|
||||
wcontrols, wncontrols, wevent, wflags) \
|
||||
{ .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}
|
||||
#define SND_SOC_DAPM_MICBIAS_E(wname, wreg, wshift, winvert, wevent, wflags) \
|
||||
{ .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}
|
||||
#define SND_SOC_DAPM_SWITCH_E(wname, wreg, wshift, winvert, wcontrols, \
|
||||
wevent, wflags) \
|
||||
{ .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}
|
||||
#define SND_SOC_DAPM_MUX_E(wname, wreg, wshift, winvert, wcontrols, \
|
||||
wevent, wflags) \
|
||||
{ .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}
|
||||
#define SND_SOC_DAPM_VIRT_MUX_E(wname, wreg, wshift, winvert, wcontrols, \
|
||||
wevent, wflags) \
|
||||
{ .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}
|
||||
|
||||
/* additional sequencing control within an event type */
|
||||
@ -173,26 +173,26 @@
|
||||
#define SOC_PGA_E_ARRAY(wname, wreg, wshift, winvert, wcontrols, \
|
||||
wevent, wflags) \
|
||||
{ .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}
|
||||
#define SOC_MIXER_E_ARRAY(wname, wreg, wshift, winvert, wcontrols, \
|
||||
wevent, wflags) \
|
||||
{ .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}
|
||||
#define SOC_MIXER_NAMED_CTL_E_ARRAY(wname, wreg, wshift, winvert, \
|
||||
wcontrols, wevent, wflags) \
|
||||
{ .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}
|
||||
|
||||
/* events that are pre and post DAPM */
|
||||
#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, \
|
||||
.event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD}
|
||||
#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, \
|
||||
.event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD}
|
||||
|
||||
@ -232,7 +232,7 @@
|
||||
|
||||
/* generic widgets */
|
||||
#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, \
|
||||
.on_val = won_val, .off_val = woff_val, .event = dapm_reg_event, \
|
||||
.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 */
|
||||
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 */
|
||||
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 */
|
||||
int num_kcontrols;
|
||||
const struct snd_kcontrol_new *kcontrols;
|
||||
const struct snd_kcontrol_new *kcontrol_news;
|
||||
struct snd_kcontrol **kcontrols;
|
||||
|
||||
/* widget input and outputs */
|
||||
struct list_head sources;
|
||||
@ -516,4 +518,10 @@ struct snd_soc_dapm_context {
|
||||
#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
|
||||
|
@ -248,7 +248,7 @@ typedef int (*hw_write_t)(void *,const char* ,int);
|
||||
extern struct snd_ac97_bus_ops soc_ac97_ops;
|
||||
|
||||
enum snd_soc_control_type {
|
||||
SND_SOC_CUSTOM,
|
||||
SND_SOC_CUSTOM = 1,
|
||||
SND_SOC_I2C,
|
||||
SND_SOC_SPI,
|
||||
};
|
||||
@ -278,6 +278,10 @@ int snd_soc_register_codec(struct device *dev,
|
||||
void snd_soc_unregister_codec(struct device *dev);
|
||||
int snd_soc_codec_volatile_register(struct snd_soc_codec *codec,
|
||||
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 addr_bits, int data_bits,
|
||||
enum snd_soc_control_type control);
|
||||
@ -292,6 +296,8 @@ int snd_soc_default_volatile_register(struct snd_soc_codec *codec,
|
||||
unsigned int reg);
|
||||
int snd_soc_default_readable_register(struct snd_soc_codec *codec,
|
||||
unsigned int reg);
|
||||
int snd_soc_default_writable_register(struct snd_soc_codec *codec,
|
||||
unsigned int reg);
|
||||
|
||||
/* Utility functions to get clock rates from various things */
|
||||
int snd_soc_calc_frame_size(int sample_size, int channels, int tdm_slots);
|
||||
@ -523,6 +529,7 @@ struct snd_soc_codec {
|
||||
size_t reg_size; /* reg_cache_size * reg_word_size */
|
||||
int (*volatile_register)(struct snd_soc_codec *, unsigned int);
|
||||
int (*readable_register)(struct snd_soc_codec *, unsigned int);
|
||||
int (*writable_register)(struct snd_soc_codec *, unsigned int);
|
||||
|
||||
/* runtime */
|
||||
struct snd_ac97 *ac97; /* for ad-hoc ac97 devices */
|
||||
@ -539,10 +546,12 @@ struct snd_soc_codec {
|
||||
|
||||
/* codec IO */
|
||||
void *control_data; /* codec control (i2c/3wire) data */
|
||||
enum snd_soc_control_type control_type;
|
||||
hw_write_t hw_write;
|
||||
unsigned int (*hw_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 (*bulk_write_raw)(struct snd_soc_codec *, unsigned int, const void *, size_t);
|
||||
void *reg_cache;
|
||||
const void *reg_def_copy;
|
||||
const struct snd_soc_cache_ops *cache_ops;
|
||||
@ -568,7 +577,9 @@ struct snd_soc_codec_driver {
|
||||
pm_message_t state);
|
||||
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;
|
||||
int num_dapm_widgets;
|
||||
const struct snd_soc_dapm_route *dapm_routes;
|
||||
@ -587,6 +598,7 @@ struct snd_soc_codec_driver {
|
||||
size_t, unsigned int);
|
||||
int (*volatile_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_step;
|
||||
short reg_word_size;
|
||||
@ -690,6 +702,8 @@ struct snd_soc_aux_dev {
|
||||
/* SoC card */
|
||||
struct snd_soc_card {
|
||||
const char *name;
|
||||
const char *long_name;
|
||||
const char *driver_name;
|
||||
struct device *dev;
|
||||
struct snd_card *snd_card;
|
||||
struct module *owner;
|
||||
@ -737,12 +751,15 @@ struct snd_soc_card {
|
||||
struct snd_soc_pcm_runtime *rtd_aux;
|
||||
int num_aux_rtd;
|
||||
|
||||
const struct snd_kcontrol_new *controls;
|
||||
int num_controls;
|
||||
|
||||
/*
|
||||
* Card-specific routes and widgets.
|
||||
*/
|
||||
struct snd_soc_dapm_widget *dapm_widgets;
|
||||
const struct snd_soc_dapm_widget *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;
|
||||
|
||||
struct work_struct deferred_resume_work;
|
||||
@ -805,7 +822,7 @@ struct soc_enum {
|
||||
unsigned char shift_r;
|
||||
unsigned int max;
|
||||
unsigned int mask;
|
||||
const char **texts;
|
||||
const char * const *texts;
|
||||
const unsigned int *values;
|
||||
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_write(struct snd_soc_codec *codec,
|
||||
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 */
|
||||
|
||||
@ -871,6 +890,9 @@ static inline void snd_soc_initialize_card_lists(struct snd_soc_card *card)
|
||||
INIT_LIST_HEAD(&card->dapm_list);
|
||||
}
|
||||
|
||||
int snd_soc_util_init(void);
|
||||
void snd_soc_util_exit(void);
|
||||
|
||||
#include <sound/soc-dai.h>
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
|
@ -3,7 +3,7 @@
|
||||
*
|
||||
* 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
|
||||
* modify it under the terms of the GNU General Public License
|
||||
|
55
include/sound/wm8915.h
Normal file
55
include/sound/wm8915.h
Normal 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
|
@ -14,6 +14,28 @@
|
||||
/* Use to set GPIO default values to zero */
|
||||
#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 {
|
||||
int gpio_base;
|
||||
u32 gpio_init[WM8962_MAX_GPIO];
|
||||
|
@ -184,7 +184,7 @@ static struct snd_soc_dai_link at91sam9g20ek_dai = {
|
||||
.codec_dai_name = "wm8731-hifi",
|
||||
.init = at91sam9g20ek_wm8731_init,
|
||||
.platform_name = "atmel-pcm-audio",
|
||||
.codec_name = "wm8731-codec.0-001b",
|
||||
.codec_name = "wm8731.0-001b",
|
||||
.ops = &at91sam9g20ek_ops,
|
||||
};
|
||||
|
||||
|
@ -77,7 +77,7 @@ static struct snd_soc_dai_link db1200_i2s_dai = {
|
||||
.codec_dai_name = "wm8731-hifi",
|
||||
.cpu_dai_name = "au1xpsc_i2s.1",
|
||||
.platform_name = "au1xpsc-pcm.1",
|
||||
.codec_name = "wm8731-codec.0-001b",
|
||||
.codec_name = "wm8731.0-001b",
|
||||
.ops = &db1200_i2s_wm8731_ops,
|
||||
};
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
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;
|
||||
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)
|
||||
{
|
||||
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_dma_buffer *buf = &substream->dma_buffer;
|
||||
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;
|
||||
int stream;
|
||||
#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 *
|
||||
sizeof(struct ac97_frame) / 4;
|
||||
#endif
|
||||
@ -405,8 +414,6 @@ static void bf5xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
|
||||
}
|
||||
#endif
|
||||
}
|
||||
if (sport_handle)
|
||||
sport_done(sport_handle);
|
||||
}
|
||||
|
||||
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 = {
|
||||
.driver = {
|
||||
.name = "bf5xx-pcm-audio",
|
||||
.name = "bfin-ac97-pcm-audio",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
|
||||
|
@ -41,48 +41,7 @@
|
||||
* anomaly does not affect blackfin sound drivers.
|
||||
*/
|
||||
|
||||
static int *cmd_count;
|
||||
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
|
||||
};
|
||||
static struct sport_device *ac97_sport_handle;
|
||||
|
||||
void bf5xx_pcm_to_ac97(struct ac97_frame *dst, const __u16 *src,
|
||||
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)
|
||||
{
|
||||
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);
|
||||
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,
|
||||
unsigned short reg)
|
||||
{
|
||||
struct sport_device *sport_handle = ac97_sport_handle;
|
||||
struct ac97_frame out_frame[2], in_frame[2];
|
||||
|
||||
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,
|
||||
unsigned short val)
|
||||
{
|
||||
struct sport_device *sport_handle = ac97_sport_handle;
|
||||
|
||||
pr_debug("%s enter 0x%x:0x%04x\n", __func__, reg, val);
|
||||
|
||||
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)
|
||||
{
|
||||
#if defined(CONFIG_BF54x) || defined(CONFIG_BF561) || \
|
||||
(defined(BF537_FAMILY) && (CONFIG_SND_BF5XX_SPORT_NUM == 1))
|
||||
|
||||
#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));
|
||||
struct sport_device *sport_handle = ac97_sport_handle;
|
||||
u16 gpio = P_IDENT(sport_handle->pin_req[3]);
|
||||
|
||||
pr_debug("%s enter\n", __func__);
|
||||
|
||||
peripheral_free(per);
|
||||
peripheral_free_list(sport_handle->pin_req);
|
||||
gpio_request(gpio, "bf5xx-ac97");
|
||||
gpio_direction_output(gpio, 1);
|
||||
udelay(2);
|
||||
gpio_set_value(gpio, 0);
|
||||
udelay(1);
|
||||
gpio_free(gpio);
|
||||
peripheral_request(per, "soc-audio");
|
||||
#else
|
||||
pr_info("%s: Not implemented\n", __func__);
|
||||
#endif
|
||||
peripheral_request_list(sport_handle->pin_req, "soc-audio");
|
||||
}
|
||||
|
||||
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
|
||||
#endif
|
||||
|
||||
static int bf5xx_ac97_probe(struct snd_soc_dai *dai)
|
||||
{
|
||||
int ret = 0;
|
||||
cmd_count = (int *)get_zeroed_page(GFP_KERNEL);
|
||||
if (cmd_count == NULL)
|
||||
return -ENOMEM;
|
||||
static struct snd_soc_dai_driver bfin_ac97_dai = {
|
||||
.ac97_control = 1,
|
||||
.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, },
|
||||
};
|
||||
|
||||
if (peripheral_request_list(sport_req[sport_num], "soc-audio")) {
|
||||
pr_err("Requesting Peripherals failed\n");
|
||||
ret = -EFAULT;
|
||||
goto peripheral_err;
|
||||
}
|
||||
static int __devinit asoc_bfin_ac97_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct sport_device *sport_handle;
|
||||
int ret;
|
||||
|
||||
#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
|
||||
/* 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);
|
||||
#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) {
|
||||
ret = -ENODEV;
|
||||
goto sport_err;
|
||||
}
|
||||
|
||||
/*SPORT works in TDM mode to simulate AC97 transfers*/
|
||||
#if defined(CONFIG_SND_BF5XX_MULTICHAN_SUPPORT)
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
sport_config_err:
|
||||
kfree(sport_handle);
|
||||
sport_done(sport_handle);
|
||||
sport_err:
|
||||
#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
|
||||
gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM);
|
||||
gpio_err:
|
||||
#endif
|
||||
peripheral_free_list(sport_req[sport_num]);
|
||||
peripheral_err:
|
||||
free_page((unsigned long)cmd_count);
|
||||
cmd_count = NULL;
|
||||
|
||||
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);
|
||||
cmd_count = NULL;
|
||||
peripheral_free_list(sport_req[sport_num]);
|
||||
struct sport_device *sport_handle = platform_get_drvdata(pdev);
|
||||
|
||||
snd_soc_unregister_dai(&pdev->dev);
|
||||
sport_done(sport_handle);
|
||||
#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
|
||||
gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM);
|
||||
#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;
|
||||
}
|
||||
|
||||
|
@ -29,22 +29,12 @@
|
||||
#include <asm/portmux.h>
|
||||
|
||||
#include "../codecs/ad1836.h"
|
||||
#include "bf5xx-sport.h"
|
||||
|
||||
#include "bf5xx-tdm-pcm.h"
|
||||
#include "bf5xx-tdm.h"
|
||||
|
||||
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,
|
||||
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 = {
|
||||
.startup = bf5xx_ad1836_startup,
|
||||
.hw_params = bf5xx_ad1836_hw_params,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_link bf5xx_ad1836_dai = {
|
||||
.name = "ad1836",
|
||||
.stream_name = "AD1836",
|
||||
.cpu_dai_name = "bf5xx-tdm",
|
||||
.codec_dai_name = "ad1836-hifi",
|
||||
.platform_name = "bf5xx-tdm-pcm-audio",
|
||||
.codec_name = "ad1836-codec.0",
|
||||
.ops = &bf5xx_ad1836_ops,
|
||||
static struct snd_soc_dai_link bf5xx_ad1836_dai[] = {
|
||||
{
|
||||
.name = "ad1836",
|
||||
.stream_name = "AD1836",
|
||||
.cpu_dai_name = "bfin-tdm.0",
|
||||
.codec_dai_name = "ad1836-hifi",
|
||||
.platform_name = "bfin-tdm-pcm-audio",
|
||||
.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 = {
|
||||
.name = "bf5xx_ad1836",
|
||||
.dai_link = &bf5xx_ad1836_dai,
|
||||
.name = "bfin-ad1836",
|
||||
.dai_link = &bf5xx_ad1836_dai[CONFIG_SND_BF5XX_SPORT_NUM],
|
||||
.num_links = 1,
|
||||
};
|
||||
|
||||
|
@ -38,30 +38,28 @@
|
||||
#include <asm/portmux.h>
|
||||
|
||||
#include "../codecs/ad193x.h"
|
||||
#include "bf5xx-sport.h"
|
||||
|
||||
#include "bf5xx-tdm-pcm.h"
|
||||
#include "bf5xx-tdm.h"
|
||||
|
||||
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,
|
||||
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;
|
||||
unsigned int clk = 0;
|
||||
unsigned int channel_map[] = {0, 1, 2, 3, 4, 5, 6, 7};
|
||||
int ret = 0;
|
||||
|
||||
switch (params_rate(params)) {
|
||||
case 48000:
|
||||
clk = 12288000;
|
||||
break;
|
||||
}
|
||||
|
||||
/* set cpu DAI configuration */
|
||||
ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_DSP_A |
|
||||
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)
|
||||
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 */
|
||||
ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xFF, 0xFF, 8, 32);
|
||||
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 = {
|
||||
.startup = bf5xx_ad193x_startup,
|
||||
.hw_params = bf5xx_ad193x_hw_params,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_link bf5xx_ad193x_dai = {
|
||||
.name = "ad193x",
|
||||
.stream_name = "AD193X",
|
||||
.cpu_dai_name = "bf5xx-tdm",
|
||||
.codec_dai_name ="ad193x-hifi",
|
||||
.platform_name = "bf5xx-tdm-pcm-audio",
|
||||
.codec_name = "ad193x-codec.5",
|
||||
.ops = &bf5xx_ad193x_ops,
|
||||
static struct snd_soc_dai_link bf5xx_ad193x_dai[] = {
|
||||
{
|
||||
.name = "ad193x",
|
||||
.stream_name = "AD193X",
|
||||
.cpu_dai_name = "bfin-tdm.0",
|
||||
.codec_dai_name ="ad193x-hifi",
|
||||
.platform_name = "bfin-tdm-pcm-audio",
|
||||
.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 = {
|
||||
.name = "bf5xx_ad193x",
|
||||
.dai_link = &bf5xx_ad193x_dai,
|
||||
.name = "bfin-ad193x",
|
||||
.dai_link = &bf5xx_ad193x_dai[CONFIG_SND_BF5XX_SPORT_NUM],
|
||||
.num_links = 1,
|
||||
};
|
||||
|
||||
|
@ -47,39 +47,34 @@
|
||||
#include <asm/portmux.h>
|
||||
|
||||
#include "../codecs/ad1980.h"
|
||||
#include "bf5xx-sport.h"
|
||||
|
||||
#include "bf5xx-ac97-pcm.h"
|
||||
#include "bf5xx-ac97.h"
|
||||
|
||||
static struct snd_soc_card bf5xx_board;
|
||||
|
||||
static int bf5xx_board_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 struct snd_soc_ops bf5xx_board_ops = {
|
||||
.startup = bf5xx_board_startup,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_link bf5xx_board_dai = {
|
||||
.name = "AC97",
|
||||
.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_dai_link bf5xx_board_dai[] = {
|
||||
{
|
||||
.name = "AC97",
|
||||
.stream_name = "AC97 HiFi",
|
||||
.cpu_dai_name = "bfin-ac97.0",
|
||||
.codec_dai_name = "ad1980-hifi",
|
||||
.platform_name = "bfin-ac97-pcm-audio",
|
||||
.codec_name = "ad1980",
|
||||
},
|
||||
{
|
||||
.name = "AC97",
|
||||
.stream_name = "AC97 HiFi",
|
||||
.cpu_dai_name = "bfin-ac97.1",
|
||||
.codec_dai_name = "ad1980-hifi",
|
||||
.platform_name = "bfin-ac97-pcm-audio",
|
||||
.codec_name = "ad1980",
|
||||
},
|
||||
};
|
||||
|
||||
static struct snd_soc_card bf5xx_board = {
|
||||
.name = "bf5xx-board",
|
||||
.dai_link = &bf5xx_board_dai,
|
||||
.name = "bfin-ad1980",
|
||||
.dai_link = &bf5xx_board_dai[CONFIG_SND_BF5XX_SPORT_NUM],
|
||||
.num_links = 1,
|
||||
};
|
||||
|
||||
|
@ -145,16 +145,6 @@ static int bf5xx_probe(struct platform_device *pdev)
|
||||
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,
|
||||
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 = {
|
||||
.startup = bf5xx_ad73311_startup,
|
||||
.hw_params = bf5xx_ad73311_hw_params,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_link bf5xx_ad73311_dai = {
|
||||
.name = "ad73311",
|
||||
.stream_name = "AD73311",
|
||||
.cpu_dai_name = "bf5xx-i2s",
|
||||
.codec_dai_name = "ad73311-hifi",
|
||||
.platform_name = "bfin-pcm-audio",
|
||||
.codec_name = "ad73311-codec",
|
||||
.ops = &bf5xx_ad73311_ops,
|
||||
static struct snd_soc_dai_link bf5xx_ad73311_dai[] = {
|
||||
{
|
||||
.name = "ad73311",
|
||||
.stream_name = "AD73311",
|
||||
.cpu_dai_name = "bfin-i2s.0",
|
||||
.codec_dai_name = "ad73311-hifi",
|
||||
.platform_name = "bfin-i2s-pcm-audio",
|
||||
.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 = {
|
||||
.name = "bf5xx_ad73311",
|
||||
.name = "bfin-ad73311",
|
||||
.probe = bf5xx_probe,
|
||||
.dai_link = &bf5xx_ad73311_dai,
|
||||
.dai_link = &bf5xx_ad73311_dai[CONFIG_SND_BF5XX_SPORT_NUM],
|
||||
.num_links = 1,
|
||||
};
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
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_dma_buffer *buf = &substream->dma_buffer;
|
||||
int ret;
|
||||
|
||||
pr_debug("%s enter\n", __func__);
|
||||
|
||||
snd_soc_set_runtime_hwparams(substream, &bf5xx_pcm_hardware);
|
||||
|
||||
ret = snd_pcm_hw_constraint_integer(runtime, \
|
||||
@ -159,9 +164,14 @@ static int bf5xx_pcm_open(struct snd_pcm_substream *substream)
|
||||
if (ret < 0)
|
||||
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;
|
||||
else {
|
||||
} else {
|
||||
pr_err("sport_handle is NULL\n");
|
||||
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__,
|
||||
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;
|
||||
}
|
||||
|
||||
@ -239,8 +244,6 @@ static void bf5xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
|
||||
dma_free_coherent(NULL, buf->bytes, buf->area, 0);
|
||||
buf->area = NULL;
|
||||
}
|
||||
if (sport_handle)
|
||||
sport_done(sport_handle);
|
||||
}
|
||||
|
||||
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 = {
|
||||
.driver = {
|
||||
.name = "bfin-pcm-audio",
|
||||
.name = "bfin-i2s-pcm-audio",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
|
||||
|
@ -51,59 +51,24 @@ struct bf5xx_i2s_port {
|
||||
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,
|
||||
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;
|
||||
|
||||
/* interface format:support I2S,slave mode */
|
||||
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
|
||||
case SND_SOC_DAIFMT_I2S:
|
||||
bf5xx_i2s.tcr1 |= TFSR | TCKFE;
|
||||
bf5xx_i2s.rcr1 |= RFSR | RCKFE;
|
||||
bf5xx_i2s.tcr2 |= TSFSE;
|
||||
bf5xx_i2s.rcr2 |= RSFSE;
|
||||
bf5xx_i2s->tcr1 |= TFSR | TCKFE;
|
||||
bf5xx_i2s->rcr1 |= RFSR | RCKFE;
|
||||
bf5xx_i2s->tcr2 |= TSFSE;
|
||||
bf5xx_i2s->rcr2 |= RSFSE;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_DSP_A:
|
||||
bf5xx_i2s.tcr1 |= TFSR;
|
||||
bf5xx_i2s.rcr1 |= RFSR;
|
||||
bf5xx_i2s->tcr1 |= TFSR;
|
||||
bf5xx_i2s->rcr1 |= RFSR;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_LEFT_J:
|
||||
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_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;
|
||||
|
||||
bf5xx_i2s.tcr2 &= ~0x1f;
|
||||
bf5xx_i2s.rcr2 &= ~0x1f;
|
||||
bf5xx_i2s->tcr2 &= ~0x1f;
|
||||
bf5xx_i2s->rcr2 &= ~0x1f;
|
||||
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:
|
||||
bf5xx_i2s.tcr2 |= 15;
|
||||
bf5xx_i2s.rcr2 |= 15;
|
||||
bf5xx_i2s->tcr2 |= 15;
|
||||
bf5xx_i2s->rcr2 |= 15;
|
||||
sport_handle->wdsize = 2;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S24_LE:
|
||||
bf5xx_i2s.tcr2 |= 23;
|
||||
bf5xx_i2s.rcr2 |= 23;
|
||||
bf5xx_i2s->tcr2 |= 23;
|
||||
bf5xx_i2s->rcr2 |= 23;
|
||||
sport_handle->wdsize = 3;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S32_LE:
|
||||
bf5xx_i2s.tcr2 |= 31;
|
||||
bf5xx_i2s.rcr2 |= 31;
|
||||
bf5xx_i2s->tcr2 |= 31;
|
||||
bf5xx_i2s->rcr2 |= 31;
|
||||
sport_handle->wdsize = 4;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!bf5xx_i2s.configured) {
|
||||
if (!bf5xx_i2s->configured) {
|
||||
/*
|
||||
* TX and RX are not independent,they are enabled at the
|
||||
* 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.
|
||||
*/
|
||||
bf5xx_i2s.configured = 1;
|
||||
ret = sport_config_rx(sport_handle, bf5xx_i2s.rcr1,
|
||||
bf5xx_i2s.rcr2, 0, 0);
|
||||
bf5xx_i2s->configured = 1;
|
||||
ret = sport_config_rx(sport_handle, bf5xx_i2s->rcr1,
|
||||
bf5xx_i2s->rcr2, 0, 0);
|
||||
if (ret) {
|
||||
pr_err("SPORT is busy!\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
ret = sport_config_tx(sport_handle, bf5xx_i2s.tcr1,
|
||||
bf5xx_i2s.tcr2, 0, 0);
|
||||
ret = sport_config_tx(sport_handle, bf5xx_i2s->tcr1,
|
||||
bf5xx_i2s->tcr2, 0, 0);
|
||||
if (ret) {
|
||||
pr_err("SPORT is busy!\n");
|
||||
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,
|
||||
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__);
|
||||
/* No active stream, SPORT is allowed to be configured again. */
|
||||
if (!dai->active)
|
||||
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;
|
||||
bf5xx_i2s->configured = 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
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);
|
||||
|
||||
@ -235,19 +184,21 @@ static int bf5xx_i2s_suspend(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;
|
||||
|
||||
pr_debug("%s : sport %d\n", __func__, dai->id);
|
||||
|
||||
ret = sport_config_rx(sport_handle, bf5xx_i2s.rcr1,
|
||||
bf5xx_i2s.rcr2, 0, 0);
|
||||
ret = sport_config_rx(sport_handle, bf5xx_i2s->rcr1,
|
||||
bf5xx_i2s->rcr2, 0, 0);
|
||||
if (ret) {
|
||||
pr_err("SPORT is busy!\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
ret = sport_config_tx(sport_handle, bf5xx_i2s.tcr1,
|
||||
bf5xx_i2s.tcr2, 0, 0);
|
||||
ret = sport_config_tx(sport_handle, bf5xx_i2s->tcr1,
|
||||
bf5xx_i2s->tcr2, 0, 0);
|
||||
if (ret) {
|
||||
pr_err("SPORT is busy!\n");
|
||||
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_96000)
|
||||
|
||||
#define BF5XX_I2S_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |\
|
||||
SNDRV_PCM_FMTBIT_S32_LE)
|
||||
#define BF5XX_I2S_FORMATS \
|
||||
(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 = {
|
||||
.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 = {
|
||||
.probe = bf5xx_i2s_probe,
|
||||
.remove = bf5xx_i2s_remove,
|
||||
.suspend = bf5xx_i2s_suspend,
|
||||
.resume = bf5xx_i2s_resume,
|
||||
.playback = {
|
||||
@ -293,23 +245,45 @@ static struct snd_soc_dai_driver bf5xx_i2s_dai = {
|
||||
.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);
|
||||
sport_done(sport_handle);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver bfin_i2s_driver = {
|
||||
.probe = bfin_i2s_drv_probe,
|
||||
.remove = __devexit_p(bfin_i2s_drv_remove),
|
||||
|
||||
.probe = bf5xx_i2s_probe,
|
||||
.remove = __devexit_p(bf5xx_i2s_remove),
|
||||
.driver = {
|
||||
.name = "bf5xx-i2s",
|
||||
.name = "bfin-i2s",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
@ -42,8 +42,6 @@
|
||||
/* delay between frame sync pulse and first data bit in multichannel mode */
|
||||
#define FRAME_DELAY (1<<12)
|
||||
|
||||
struct sport_device *sport_handle;
|
||||
EXPORT_SYMBOL(sport_handle);
|
||||
/* note: multichannel is in units of 8 channels,
|
||||
* tdm_count is # channels NOT / 8 ! */
|
||||
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);
|
||||
|
||||
struct sport_device *sport_init(struct sport_param *param, unsigned wdsize,
|
||||
unsigned dummy_count, void *private_data)
|
||||
static int sport_config_pdev(struct platform_device *pdev, struct sport_param *param)
|
||||
{
|
||||
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;
|
||||
pr_debug("%s enter\n", __func__);
|
||||
BUG_ON(param == NULL);
|
||||
BUG_ON(wdsize == 0 || dummy_count == 0);
|
||||
sport = kmalloc(sizeof(struct sport_device), GFP_KERNEL);
|
||||
if (!sport) {
|
||||
pr_err("Failed to allocate for sport device\n");
|
||||
int ret;
|
||||
|
||||
dev_dbg(dev, "%s enter\n", __func__);
|
||||
|
||||
param.wdsize = wdsize;
|
||||
param.dummy_count = dummy_count;
|
||||
BUG_ON(param.wdsize == 0 || param.dummy_count == 0);
|
||||
|
||||
ret = sport_config_pdev(pdev, ¶m);
|
||||
if (ret)
|
||||
return NULL;
|
||||
|
||||
if (peripheral_request_list(param.pin_req, "soc-audio")) {
|
||||
dev_err(dev, "requesting Peripherals failed\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(sport, 0, sizeof(struct sport_device));
|
||||
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->private_data = private_data;
|
||||
sport = kzalloc(sizeof(*sport), GFP_KERNEL);
|
||||
if (!sport) {
|
||||
dev_err(dev, "failed to allocate for sport device\n");
|
||||
goto __init_err0;
|
||||
}
|
||||
|
||||
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) {
|
||||
pr_err("Failed to request RX dma %d\n", \
|
||||
sport->dma_rx_chan);
|
||||
dev_err(dev, "failed to request RX dma %d\n", sport->dma_rx_chan);
|
||||
goto __init_err1;
|
||||
}
|
||||
if (set_dma_callback(sport->dma_rx_chan, rx_handler, sport) != 0) {
|
||||
pr_err("Failed to request RX irq %d\n", \
|
||||
sport->dma_rx_chan);
|
||||
dev_err(dev, "failed to request RX irq %d\n", sport->dma_rx_chan);
|
||||
goto __init_err2;
|
||||
}
|
||||
|
||||
if (request_dma(sport->dma_tx_chan, "SPORT TX Data") == -EBUSY) {
|
||||
pr_err("Failed to request TX dma %d\n", \
|
||||
sport->dma_tx_chan);
|
||||
dev_err(dev, "failed to request TX dma %d\n", sport->dma_tx_chan);
|
||||
goto __init_err2;
|
||||
}
|
||||
|
||||
if (set_dma_callback(sport->dma_tx_chan, tx_handler, sport) != 0) {
|
||||
pr_err("Failed to request TX irq %d\n", \
|
||||
sport->dma_tx_chan);
|
||||
dev_err(dev, "failed to request TX irq %d\n", sport->dma_tx_chan);
|
||||
goto __init_err3;
|
||||
}
|
||||
|
||||
if (request_irq(sport->err_irq, err_handler, IRQF_SHARED, "SPORT err",
|
||||
sport) < 0) {
|
||||
pr_err("Failed to request err irq:%d\n", \
|
||||
sport->err_irq);
|
||||
dev_err(dev, "failed to request err irq %d\n", sport->err_irq);
|
||||
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->err_irq, sport->regs);
|
||||
|
||||
sport->wdsize = wdsize;
|
||||
sport->dummy_count = dummy_count;
|
||||
sport->wdsize = param.wdsize;
|
||||
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)
|
||||
sport->dummy_buf = l1_data_sram_zalloc(dummy_count * 2);
|
||||
sport->dummy_buf = l1_data_sram_zalloc(param.dummy_count * 2);
|
||||
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) {
|
||||
pr_err("Failed to allocate dummy buffer\n");
|
||||
goto __error;
|
||||
dev_err(dev, "failed to allocate dummy buffer\n");
|
||||
goto __error1;
|
||||
}
|
||||
|
||||
ret = sport_config_rx_dummy(sport);
|
||||
if (ret) {
|
||||
pr_err("Failed to config rx dummy ring\n");
|
||||
goto __error;
|
||||
dev_err(dev, "failed to config rx dummy ring\n");
|
||||
goto __error2;
|
||||
}
|
||||
ret = sport_config_tx_dummy(sport);
|
||||
if (ret) {
|
||||
pr_err("Failed to config tx dummy ring\n");
|
||||
goto __error;
|
||||
dev_err(dev, "failed to config tx dummy ring\n");
|
||||
goto __error3;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, 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);
|
||||
__init_err3:
|
||||
free_dma(sport->dma_tx_chan);
|
||||
@ -885,6 +961,8 @@ __init_err2:
|
||||
free_dma(sport->dma_rx_chan);
|
||||
__init_err1:
|
||||
kfree(sport);
|
||||
__init_err0:
|
||||
peripheral_free_list(param.pin_req);
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(sport_init);
|
||||
@ -917,8 +995,9 @@ void sport_done(struct sport_device *sport)
|
||||
free_dma(sport->dma_tx_chan);
|
||||
free_irq(sport->err_irq, sport);
|
||||
|
||||
kfree(sport->private_data);
|
||||
peripheral_free_list(sport->pin_req);
|
||||
kfree(sport);
|
||||
sport = NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(sport_done);
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* File: bf5xx_ac97_sport.h
|
||||
* File: bf5xx_sport.h
|
||||
* Based on:
|
||||
* Author: Roy Huang <roy.huang@analog.com>
|
||||
*
|
||||
@ -33,15 +33,18 @@
|
||||
#include <linux/types.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <asm/dma.h>
|
||||
#include <asm/bfin_sport.h>
|
||||
|
||||
#define DESC_ELEMENT_COUNT 9
|
||||
|
||||
struct sport_device {
|
||||
int num;
|
||||
int dma_rx_chan;
|
||||
int dma_tx_chan;
|
||||
int err_irq;
|
||||
const unsigned short *pin_req;
|
||||
struct sport_register *regs;
|
||||
|
||||
unsigned char *rx_buf;
|
||||
@ -103,17 +106,20 @@ struct sport_device {
|
||||
void *private_data;
|
||||
};
|
||||
|
||||
extern struct sport_device *sport_handle;
|
||||
|
||||
struct sport_param {
|
||||
int num;
|
||||
int dma_rx_chan;
|
||||
int dma_tx_chan;
|
||||
int err_irq;
|
||||
const unsigned short *pin_req;
|
||||
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,
|
||||
unsigned dummy_count, void *private_data);
|
||||
struct sport_device *sport_init(struct platform_device *pdev,
|
||||
unsigned int wdsize, unsigned int dummy_count, size_t priv_size);
|
||||
|
||||
void sport_done(struct sport_device *sport);
|
||||
|
||||
|
@ -44,16 +44,6 @@
|
||||
|
||||
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,
|
||||
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 = {
|
||||
.startup = bf5xx_ssm2602_startup,
|
||||
.hw_params = bf5xx_ssm2602_hw_params,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_link bf5xx_ssm2602_dai = {
|
||||
.name = "ssm2602",
|
||||
.stream_name = "SSM2602",
|
||||
.cpu_dai_name = "bf5xx-i2s",
|
||||
.codec_dai_name = "ssm2602-hifi",
|
||||
.platform_name = "bf5xx-pcm-audio",
|
||||
.codec_name = "ssm2602-codec.0-001b",
|
||||
.ops = &bf5xx_ssm2602_ops,
|
||||
static struct snd_soc_dai_link bf5xx_ssm2602_dai[] = {
|
||||
{
|
||||
.name = "ssm2602",
|
||||
.stream_name = "SSM2602",
|
||||
.cpu_dai_name = "bfin-i2s.0",
|
||||
.codec_dai_name = "ssm2602-hifi",
|
||||
.platform_name = "bfin-i2s-pcm-audio",
|
||||
.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 = {
|
||||
.name = "bf5xx_ssm2602",
|
||||
.dai_link = &bf5xx_ssm2602_dai,
|
||||
.name = "bfin-ssm2602",
|
||||
.dai_link = &bf5xx_ssm2602_dai[CONFIG_SND_BF5XX_SPORT_NUM],
|
||||
.num_links = 1,
|
||||
};
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
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_dma_buffer *buf = &substream->dma_buffer;
|
||||
|
||||
int ret = 0;
|
||||
|
||||
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)
|
||||
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;
|
||||
else {
|
||||
} else {
|
||||
pr_err("sport_handle is NULL\n");
|
||||
ret = -ENODEV;
|
||||
}
|
||||
@ -249,11 +259,6 @@ static int bf5xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
|
||||
}
|
||||
buf->bytes = size;
|
||||
|
||||
if (stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
sport_handle->tx_buf = buf->area;
|
||||
else
|
||||
sport_handle->rx_buf = buf->area;
|
||||
|
||||
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);
|
||||
buf->area = NULL;
|
||||
}
|
||||
if (sport_handle)
|
||||
sport_done(sport_handle);
|
||||
}
|
||||
|
||||
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 = {
|
||||
.driver = {
|
||||
.name = "bf5xx-tdm-pcm-audio",
|
||||
.name = "bfin-tdm-pcm-audio",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
|
||||
|
@ -46,43 +46,6 @@
|
||||
#include "bf5xx-sport.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,
|
||||
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_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;
|
||||
|
||||
bf5xx_tdm.tcr2 &= ~0x1f;
|
||||
bf5xx_tdm.rcr2 &= ~0x1f;
|
||||
bf5xx_tdm->tcr2 &= ~0x1f;
|
||||
bf5xx_tdm->rcr2 &= ~0x1f;
|
||||
switch (params_format(params)) {
|
||||
case SNDRV_PCM_FORMAT_S32_LE:
|
||||
bf5xx_tdm.tcr2 |= 31;
|
||||
bf5xx_tdm.rcr2 |= 31;
|
||||
bf5xx_tdm->tcr2 |= 31;
|
||||
bf5xx_tdm->rcr2 |= 31;
|
||||
sport_handle->wdsize = 4;
|
||||
break;
|
||||
/* at present, we only support 32bit transfer */
|
||||
@ -136,7 +101,7 @@ static int bf5xx_tdm_hw_params(struct snd_pcm_substream *substream,
|
||||
break;
|
||||
}
|
||||
|
||||
if (!bf5xx_tdm.configured) {
|
||||
if (!bf5xx_tdm->configured) {
|
||||
/*
|
||||
* TX and RX are not independent,they are enabled at the
|
||||
* 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.
|
||||
*/
|
||||
ret = sport_config_rx(sport_handle, bf5xx_tdm.rcr1,
|
||||
bf5xx_tdm.rcr2, 0, 0);
|
||||
ret = sport_config_rx(sport_handle, bf5xx_tdm->rcr1,
|
||||
bf5xx_tdm->rcr2, 0, 0);
|
||||
if (ret) {
|
||||
pr_err("SPORT is busy!\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
ret = sport_config_tx(sport_handle, bf5xx_tdm.tcr1,
|
||||
bf5xx_tdm.tcr2, 0, 0);
|
||||
ret = sport_config_tx(sport_handle, bf5xx_tdm->tcr1,
|
||||
bf5xx_tdm->tcr2, 0, 0);
|
||||
if (ret) {
|
||||
pr_err("SPORT is busy!\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
bf5xx_tdm.configured = 1;
|
||||
bf5xx_tdm->configured = 1;
|
||||
}
|
||||
|
||||
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,
|
||||
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. */
|
||||
if (!dai->active)
|
||||
bf5xx_tdm.configured = 0;
|
||||
bf5xx_tdm->configured = 0;
|
||||
}
|
||||
|
||||
static int bf5xx_tdm_set_channel_map(struct snd_soc_dai *dai,
|
||||
unsigned int tx_num, unsigned int *tx_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;
|
||||
unsigned int slot;
|
||||
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];
|
||||
if ((slot < BFIN_TDM_DAI_MAX_SLOTS) &&
|
||||
(!(tx_mapped & (1 << slot)))) {
|
||||
bf5xx_tdm.tx_map[i] = slot;
|
||||
bf5xx_tdm->tx_map[i] = slot;
|
||||
tx_mapped |= 1 << slot;
|
||||
} else
|
||||
return -EINVAL;
|
||||
@ -198,7 +168,7 @@ static int bf5xx_tdm_set_channel_map(struct snd_soc_dai *dai,
|
||||
slot = rx_slot[i];
|
||||
if ((slot < BFIN_TDM_DAI_MAX_SLOTS) &&
|
||||
(!(rx_mapped & (1 << slot)))) {
|
||||
bf5xx_tdm.rx_map[i] = slot;
|
||||
bf5xx_tdm->rx_map[i] = slot;
|
||||
rx_mapped |= 1 << slot;
|
||||
} else
|
||||
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);
|
||||
|
||||
if (!dai->active)
|
||||
return 0;
|
||||
if (dai->capture_active)
|
||||
sport_rx_stop(sport);
|
||||
if (dai->playback_active)
|
||||
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;
|
||||
}
|
||||
|
||||
@ -226,9 +198,6 @@ static int bf5xx_tdm_resume(struct snd_soc_dai *dai)
|
||||
int ret;
|
||||
struct sport_device *sport = snd_soc_dai_get_drvdata(dai);
|
||||
|
||||
if (!dai->active)
|
||||
return 0;
|
||||
|
||||
ret = sport_set_multichannel(sport, 8, 0xFF, 1);
|
||||
if (ret) {
|
||||
pr_err("SPORT is busy!\n");
|
||||
@ -247,6 +216,8 @@ static int bf5xx_tdm_resume(struct snd_soc_dai *dai)
|
||||
ret = -EBUSY;
|
||||
}
|
||||
|
||||
peripheral_request_list(sport->pin_req, "soc-audio");
|
||||
|
||||
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)
|
||||
{
|
||||
int ret = 0;
|
||||
struct sport_device *sport_handle;
|
||||
int ret;
|
||||
|
||||
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, \
|
||||
8 * sizeof(u32), NULL);
|
||||
if (!sport_handle) {
|
||||
peripheral_free_list(&sport_req[sport_num][0]);
|
||||
/* configure SPORT for TDM */
|
||||
sport_handle = sport_init(pdev, 4, 8 * sizeof(u32),
|
||||
sizeof(struct bf5xx_tdm_port));
|
||||
if (!sport_handle)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* SPORT works in TDM mode */
|
||||
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;
|
||||
}
|
||||
|
||||
sport_handle->private_data = &bf5xx_tdm;
|
||||
return 0;
|
||||
|
||||
sport_config_err:
|
||||
peripheral_free_list(&sport_req[sport_num][0]);
|
||||
sport_done(sport_handle);
|
||||
return ret;
|
||||
}
|
||||
|
||||
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);
|
||||
sport_done(sport_handle);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -120,7 +120,7 @@
|
||||
*/
|
||||
#define PM860X_DAPM_OUTPUT(wname, wevent) \
|
||||
{ .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, \
|
||||
.event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD, }
|
||||
|
||||
|
@ -16,10 +16,11 @@ config SND_SOC_ALL_CODECS
|
||||
select SND_SOC_AD1836 if SPI_MASTER
|
||||
select SND_SOC_AD193X if SND_SOC_I2C_AND_SPI
|
||||
select SND_SOC_AD1980 if SND_SOC_AC97_BUS
|
||||
select SND_SOC_AD73311
|
||||
select SND_SOC_ADS117X
|
||||
select SND_SOC_AD73311 if I2C
|
||||
select SND_SOC_AK4104 if SPI_MASTER
|
||||
select SND_SOC_AK4535 if I2C
|
||||
select SND_SOC_AK4641 if I2C
|
||||
select SND_SOC_AK4642 if I2C
|
||||
select SND_SOC_AK4671 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_LM4857 if I2C
|
||||
select SND_SOC_MAX98088 if I2C
|
||||
select SND_SOC_MAX98095 if I2C
|
||||
select SND_SOC_MAX9850 if I2C
|
||||
select SND_SOC_MAX9877 if I2C
|
||||
select SND_SOC_PCM3008
|
||||
select SND_SOC_SGTL5000 if I2C
|
||||
select SND_SOC_SN95031 if INTEL_SCU_IPC
|
||||
select SND_SOC_SPDIF
|
||||
select SND_SOC_SSM2602 if I2C
|
||||
select SND_SOC_SSM2602 if SND_SOC_I2C_AND_SPI
|
||||
select SND_SOC_STAC9766 if SND_SOC_AC97_BUS
|
||||
select SND_SOC_TLV320AIC23 if I2C
|
||||
select SND_SOC_TLV320AIC26 if SPI_MASTER
|
||||
@ -52,6 +54,7 @@ config SND_SOC_ALL_CODECS
|
||||
select SND_SOC_UDA134X
|
||||
select SND_SOC_UDA1380 if I2C
|
||||
select SND_SOC_WL1273 if MFD_WL1273_CORE
|
||||
select SND_SOC_WM1250_EV1 if I2C
|
||||
select SND_SOC_WM2000 if I2C
|
||||
select SND_SOC_WM8350 if MFD_WM8350
|
||||
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_WM8903 if I2C
|
||||
select SND_SOC_WM8904 if I2C
|
||||
select SND_SOC_WM8915 if I2C
|
||||
select SND_SOC_WM8940 if I2C
|
||||
select SND_SOC_WM8955 if I2C
|
||||
select SND_SOC_WM8960 if I2C
|
||||
@ -136,6 +140,9 @@ config SND_SOC_AK4104
|
||||
config SND_SOC_AK4535
|
||||
tristate
|
||||
|
||||
config SND_SOC_AK4641
|
||||
tristate
|
||||
|
||||
config SND_SOC_AK4642
|
||||
tristate
|
||||
|
||||
@ -187,6 +194,9 @@ config SND_SOC_DMIC
|
||||
config SND_SOC_MAX98088
|
||||
tristate
|
||||
|
||||
config SND_SOC_MAX98095
|
||||
tristate
|
||||
|
||||
config SND_SOC_MAX9850
|
||||
tristate
|
||||
|
||||
@ -241,6 +251,9 @@ config SND_SOC_UDA1380
|
||||
config SND_SOC_WL1273
|
||||
tristate
|
||||
|
||||
config SND_SOC_WM1250_EV1
|
||||
tristate
|
||||
|
||||
config SND_SOC_WM8350
|
||||
tristate
|
||||
|
||||
@ -298,6 +311,9 @@ config SND_SOC_WM8903
|
||||
config SND_SOC_WM8904
|
||||
tristate
|
||||
|
||||
config SND_SOC_WM8915
|
||||
tristate
|
||||
|
||||
config SND_SOC_WM8940
|
||||
tristate
|
||||
|
||||
|
@ -7,6 +7,7 @@ snd-soc-ad73311-objs := ad73311.o
|
||||
snd-soc-ads117x-objs := ads117x.o
|
||||
snd-soc-ak4104-objs := ak4104.o
|
||||
snd-soc-ak4535-objs := ak4535.o
|
||||
snd-soc-ak4641-objs := ak4641.o
|
||||
snd-soc-ak4642-objs := ak4642.o
|
||||
snd-soc-ak4671-objs := ak4671.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-l3-objs := l3.o
|
||||
snd-soc-max98088-objs := max98088.o
|
||||
snd-soc-max98095-objs := max98095.o
|
||||
snd-soc-max9850-objs := max9850.o
|
||||
snd-soc-pcm3008-objs := pcm3008.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-uda1380-objs := uda1380.o
|
||||
snd-soc-wl1273-objs := wl1273.o
|
||||
snd-soc-wm1250-ev1-objs := wm1250-ev1.o
|
||||
snd-soc-wm8350-objs := wm8350.o
|
||||
snd-soc-wm8400-objs := wm8400.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-wm8903-objs := wm8903.o
|
||||
snd-soc-wm8904-objs := wm8904.o
|
||||
snd-soc-wm8915-objs := wm8915.o
|
||||
snd-soc-wm8940-objs := wm8940.o
|
||||
snd-soc-wm8955-objs := wm8955.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-wm8991-objs := wm8991.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-wm9081-objs := wm9081.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_AK4104) += snd-soc-ak4104.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_AK4671) += snd-soc-ak4671.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_JZ4740_CODEC) += snd-soc-jz4740-codec.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_PCM3008) += snd-soc-pcm3008.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_UDA1380) += snd-soc-uda1380.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_WM8400) += snd-soc-wm8400.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_WM8903) += snd-soc-wm8903.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_WM8955) += snd-soc-wm8955.o
|
||||
obj-$(CONFIG_SND_SOC_WM8960) += snd-soc-wm8960.o
|
||||
|
@ -23,8 +23,7 @@
|
||||
|
||||
/* codec private data */
|
||||
struct ad193x_priv {
|
||||
enum snd_soc_control_type bus_type;
|
||||
void *control_data;
|
||||
enum snd_soc_control_type control_type;
|
||||
int sysclk;
|
||||
};
|
||||
|
||||
@ -354,14 +353,12 @@ static int ad193x_probe(struct snd_soc_codec *codec)
|
||||
struct snd_soc_dapm_context *dapm = &codec->dapm;
|
||||
int ret;
|
||||
|
||||
codec->control_data = ad193x->control_data;
|
||||
if (ad193x->bus_type == SND_SOC_I2C)
|
||||
ret = snd_soc_codec_set_cache_io(codec, 8, 8, ad193x->bus_type);
|
||||
if (ad193x->control_type == SND_SOC_I2C)
|
||||
ret = snd_soc_codec_set_cache_io(codec, 8, 8, ad193x->control_type);
|
||||
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) {
|
||||
dev_err(codec->dev, "failed to set cache I/O: %d\n",
|
||||
ret);
|
||||
dev_err(codec->dev, "failed to set cache I/O: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -408,8 +405,7 @@ static int __devinit ad193x_spi_probe(struct spi_device *spi)
|
||||
return -ENOMEM;
|
||||
|
||||
spi_set_drvdata(spi, ad193x);
|
||||
ad193x->control_data = spi;
|
||||
ad193x->bus_type = SND_SOC_SPI;
|
||||
ad193x->control_type = SND_SOC_SPI;
|
||||
|
||||
ret = snd_soc_register_codec(&spi->dev,
|
||||
&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 = {
|
||||
.driver = {
|
||||
.name = "ad193x-codec",
|
||||
.name = "ad193x",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = ad193x_spi_probe,
|
||||
@ -454,8 +450,7 @@ static int __devinit ad193x_i2c_probe(struct i2c_client *client,
|
||||
return -ENOMEM;
|
||||
|
||||
i2c_set_clientdata(client, ad193x);
|
||||
ad193x->control_data = client;
|
||||
ad193x->bus_type = SND_SOC_I2C;
|
||||
ad193x->control_type = SND_SOC_I2C;
|
||||
|
||||
ret = snd_soc_register_codec(&client->dev,
|
||||
&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 = {
|
||||
.driver = {
|
||||
.name = "ad193x-codec",
|
||||
.name = "ad193x",
|
||||
},
|
||||
.probe = ad193x_i2c_probe,
|
||||
.remove = __devexit_p(ad193x_i2c_remove),
|
||||
|
@ -266,7 +266,7 @@ static int __devexit ad1980_remove(struct platform_device *pdev)
|
||||
|
||||
static struct platform_driver ad1980_codec_driver = {
|
||||
.driver = {
|
||||
.name = "ad1980-codec",
|
||||
.name = "ad1980",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
|
||||
|
@ -55,7 +55,7 @@ static int __devexit ad73311_remove(struct platform_device *pdev)
|
||||
|
||||
static struct platform_driver ad73311_codec_driver = {
|
||||
.driver = {
|
||||
.name = "ad73311-codec",
|
||||
.name = "ad73311",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
|
||||
|
@ -230,7 +230,7 @@ static const struct snd_soc_dapm_widget ak4535_dapm_widgets[] = {
|
||||
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", "Playback Switch", "DAC"},
|
||||
{"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"},
|
||||
};
|
||||
|
||||
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,
|
||||
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,
|
||||
ARRAY_SIZE(ak4535_snd_controls));
|
||||
ak4535_add_widgets(codec);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -480,6 +467,10 @@ static struct snd_soc_codec_driver soc_codec_dev_ak4535 = {
|
||||
.reg_cache_size = ARRAY_SIZE(ak4535_reg),
|
||||
.reg_word_size = sizeof(u8),
|
||||
.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)
|
||||
|
664
sound/soc/codecs/ak4641.c
Normal file
664
sound/soc/codecs/ak4641.c
Normal 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
47
sound/soc/codecs/ak4641.h
Normal 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
|
@ -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),
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route intercon[] = {
|
||||
static const struct snd_soc_dapm_route ak4671_intercon[] = {
|
||||
{"DAC Left", "NULL", "PMPLL"},
|
||||
{"DAC Right", "NULL", "PMPLL"},
|
||||
{"ADC Left", "NULL", "PMPLL"},
|
||||
@ -433,17 +433,6 @@ static const struct snd_soc_dapm_route intercon[] = {
|
||||
{"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,
|
||||
struct snd_pcm_hw_params *params,
|
||||
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,
|
||||
ARRAY_SIZE(ak4671_snd_controls));
|
||||
ak4671_add_widgets(codec);
|
||||
|
||||
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_word_size = sizeof(u8),
|
||||
.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,
|
||||
|
@ -86,18 +86,6 @@ static const struct snd_soc_dapm_route cx20442_audio_map[] = {
|
||||
{"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,
|
||||
unsigned int reg)
|
||||
{
|
||||
@ -344,8 +332,6 @@ static int cx20442_codec_probe(struct snd_soc_codec *codec)
|
||||
return -ENOMEM;
|
||||
snd_soc_codec_set_drvdata(codec, cx20442);
|
||||
|
||||
cx20442_add_widgets(codec);
|
||||
|
||||
cx20442->control_data = NULL;
|
||||
codec->hw_write = NULL;
|
||||
codec->card->pop_time = 0;
|
||||
@ -377,6 +363,10 @@ static struct snd_soc_codec_driver cx20442_codec_dev = {
|
||||
.reg_word_size = sizeof(u8),
|
||||
.read = cx20442_read_reg_cache,
|
||||
.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)
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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)
|
||||
{
|
||||
struct snd_soc_dapm_context *dapm = &codec->dapm;
|
||||
|
||||
snd_soc_update_bits(codec, JZ4740_REG_CODEC_1,
|
||||
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);
|
||||
|
||||
return 0;
|
||||
@ -348,6 +337,13 @@ static struct snd_soc_codec_driver soc_codec_dev_jz4740_codec = {
|
||||
.reg_cache_default = jz4740_codec_regs,
|
||||
.reg_word_size = sizeof(u32),
|
||||
.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)
|
||||
|
@ -656,8 +656,6 @@ static const struct soc_enum max98088_exmode_enum =
|
||||
ARRAY_SIZE(max98088_exmode_texts),
|
||||
max98088_exmode_texts,
|
||||
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 */
|
||||
"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("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("DAI1 Filter Mode", max98088_filter_mode_enum),
|
||||
@ -808,10 +807,10 @@ static const struct snd_kcontrol_new max98088_snd_controls[] = {
|
||||
|
||||
/* Left speaker mixer switch */
|
||||
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("Right DAC1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 0, 1, 0),
|
||||
SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_2B_MIX_SPK_LEFT, 7, 1, 0),
|
||||
SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_2B_MIX_SPK_LEFT, 0, 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, 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, 7, 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("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 */
|
||||
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("Right DAC1 Switch", M98088_REG_25_MIX_HP_LEFT, 0, 1, 0),
|
||||
SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_25_MIX_HP_LEFT, 7, 1, 0),
|
||||
SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_25_MIX_HP_LEFT, 0, 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, 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, 7, 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("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 */
|
||||
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("Right DAC1 Switch", M98088_REG_28_MIX_REC_LEFT, 0, 1, 0),
|
||||
SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_28_MIX_REC_LEFT, 7, 1, 0),
|
||||
SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_28_MIX_REC_LEFT, 0, 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, 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, 7, 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("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_MUX("EX Limiter Mode", SND_SOC_NOPM, 0, 0,
|
||||
&max98088_exmode_controls),
|
||||
|
||||
SND_SOC_DAPM_OUTPUT("HPL"),
|
||||
SND_SOC_DAPM_OUTPUT("HPR"),
|
||||
SND_SOC_DAPM_OUTPUT("SPKL"),
|
||||
@ -1112,7 +1108,7 @@ static const struct snd_soc_dapm_widget max98088_dapm_widgets[] = {
|
||||
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 HP Mixer", "Left DAC1 Switch", "DACL1"},
|
||||
{"Left HP Mixer", "Left DAC2 Switch", "DACL2"},
|
||||
@ -1226,22 +1222,6 @@ static const struct snd_soc_dapm_route audio_map[] = {
|
||||
{"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 */
|
||||
static const struct {
|
||||
u32 rate;
|
||||
@ -1586,6 +1566,36 @@ static int max98088_dai2_set_fmt(struct snd_soc_dai *codec_dai,
|
||||
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)
|
||||
{
|
||||
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_fmt = max98088_dai1_set_fmt,
|
||||
.hw_params = max98088_dai1_hw_params,
|
||||
.digital_mute = max98088_dai1_digital_mute,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_ops max98088_dai2_ops = {
|
||||
.set_sysclk = max98088_dai_set_sysclk,
|
||||
.set_fmt = max98088_dai2_set_fmt,
|
||||
.hw_params = max98088_dai2_hw_params,
|
||||
.digital_mute = max98088_dai2_digital_mute,
|
||||
};
|
||||
|
||||
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_add_widgets(codec);
|
||||
snd_soc_add_controls(codec, max98088_snd_controls,
|
||||
ARRAY_SIZE(max98088_snd_controls));
|
||||
|
||||
err_access:
|
||||
return ret;
|
||||
@ -2036,6 +2049,10 @@ static struct snd_soc_codec_driver soc_codec_dev_max98088 = {
|
||||
.reg_word_size = sizeof(u8),
|
||||
.reg_cache_default = max98088_reg,
|
||||
.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,
|
||||
|
@ -133,6 +133,19 @@
|
||||
#define M98088_REC_LINEMODE (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 */
|
||||
#define M98088_MICPRE_MASK (3<<5)
|
||||
#define M98088_MICPRE_SHIFT 5
|
||||
|
2396
sound/soc/codecs/max98095.c
Normal file
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
299
sound/soc/codecs/max98095.h
Normal 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
|
@ -827,8 +827,6 @@ EXPORT_SYMBOL_GPL(sn95031_jack_detection);
|
||||
/* codec registration */
|
||||
static int sn95031_codec_probe(struct snd_soc_codec *codec)
|
||||
{
|
||||
int ret;
|
||||
|
||||
pr_debug("codec_probe called\n");
|
||||
|
||||
codec->dapm.bias_level = SND_SOC_BIAS_OFF;
|
||||
@ -879,16 +877,7 @@ static int sn95031_codec_probe(struct snd_soc_codec *codec)
|
||||
snd_soc_add_controls(codec, sn95031_snd_controls,
|
||||
ARRAY_SIZE(sn95031_snd_controls));
|
||||
|
||||
ret = snd_soc_dapm_new_controls(&codec->dapm, sn95031_dapm_widgets,
|
||||
ARRAY_SIZE(sn95031_dapm_widgets));
|
||||
if (ret)
|
||||
pr_err("soc_dapm_new_control failed %d", ret);
|
||||
ret = snd_soc_dapm_add_routes(&codec->dapm, sn95031_audio_map,
|
||||
ARRAY_SIZE(sn95031_audio_map));
|
||||
if (ret)
|
||||
pr_err("soc_dapm_add_routes failed %d", ret);
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sn95031_codec_remove(struct snd_soc_codec *codec)
|
||||
@ -905,6 +894,10 @@ struct snd_soc_codec_driver sn95031_codec = {
|
||||
.read = sn95031_read,
|
||||
.write = sn95031_write,
|
||||
.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)
|
||||
|
@ -21,7 +21,7 @@
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/initval.h>
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
#define DRV_NAME "spdif-dit"
|
||||
|
||||
#define STUB_RATES SNDRV_PCM_RATE_8000_96000
|
||||
#define STUB_FORMATS SNDRV_PCM_FMTBIT_S16_LE
|
||||
@ -56,7 +56,7 @@ static struct platform_driver spdif_dit_driver = {
|
||||
.probe = spdif_dit_probe,
|
||||
.remove = spdif_dit_remove,
|
||||
.driver = {
|
||||
.name = "spdif-dit",
|
||||
.name = DRV_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
@ -74,3 +74,7 @@ static void __exit dit_exit(void)
|
||||
module_init(dit_modinit);
|
||||
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);
|
||||
|
@ -32,6 +32,7 @@
|
||||
#include <linux/delay.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <sound/core.h>
|
||||
@ -39,18 +40,25 @@
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/initval.h>
|
||||
#include <sound/tlv.h>
|
||||
|
||||
#include "ssm2602.h"
|
||||
|
||||
#define SSM2602_VERSION "0.1"
|
||||
|
||||
enum ssm2602_type {
|
||||
SSM2602,
|
||||
SSM2604,
|
||||
};
|
||||
|
||||
/* codec private data */
|
||||
struct ssm2602_priv {
|
||||
unsigned int sysclk;
|
||||
enum snd_soc_control_type control_type;
|
||||
void *control_data;
|
||||
struct snd_pcm_substream *master_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
|
||||
*/
|
||||
static const u16 ssm2602_reg[SSM2602_CACHEREGNUM] = {
|
||||
0x0017, 0x0017, 0x0079, 0x0079,
|
||||
0x0000, 0x0000, 0x0000, 0x000a,
|
||||
0x0097, 0x0097, 0x0079, 0x0079,
|
||||
0x000a, 0x0008, 0x009f, 0x000a,
|
||||
0x0000, 0x0000
|
||||
};
|
||||
|
||||
/*
|
||||
* 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)
|
||||
#define ssm2602_reset(c) snd_soc_write(c, SSM2602_RESET, 0)
|
||||
|
||||
/*Appending several "None"s just for OSS mixer use*/
|
||||
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),
|
||||
};
|
||||
|
||||
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,
|
||||
0, 127, 0),
|
||||
SOC_DOUBLE_R("Master Playback ZC Switch", SSM2602_LOUT1V, SSM2602_ROUT1V,
|
||||
7, 1, 0),
|
||||
static const DECLARE_TLV_DB_SCALE(ssm260x_inpga_tlv, -3450, 150, 0);
|
||||
static const DECLARE_TLV_DB_SCALE(ssm260x_sidetone_tlv, -1500, 300, 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_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("Store DC Offset Switch", SSM2602_APDIGI, 4, 1, 0),
|
||||
|
||||
SOC_ENUM("Capture Source", ssm2602_enum[0]),
|
||||
|
||||
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 */
|
||||
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("Mic Sidetone Switch", SSM2602_APANA, 5, 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 */
|
||||
static const struct snd_kcontrol_new ssm2602_input_mux_controls =
|
||||
SOC_DAPM_ENUM("Input Select", ssm2602_enum[0]);
|
||||
|
||||
static const struct snd_soc_dapm_widget ssm2602_dapm_widgets[] = {
|
||||
SND_SOC_DAPM_MIXER("Output Mixer", SSM2602_PWR, 4, 1,
|
||||
&ssm2602_output_mixer_controls[0],
|
||||
ARRAY_SIZE(ssm2602_output_mixer_controls)),
|
||||
static const struct snd_soc_dapm_widget ssm260x_dapm_widgets[] = {
|
||||
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_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_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("LLINEIN"),
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route audio_conn[] = {
|
||||
/* output mixer */
|
||||
static const struct snd_soc_dapm_widget ssm2602_dapm_widgets[] = {
|
||||
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", "HiFi Playback Switch", "DAC"},
|
||||
{"Output Mixer", "Mic Sidetone Switch", "Mic Bias"},
|
||||
|
||||
/* outputs */
|
||||
{"RHPOUT", NULL, "Output Mixer"},
|
||||
{"ROUT", NULL, "Output Mixer"},
|
||||
{"LHPOUT", 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", "Mic", "Mic Bias"},
|
||||
{"ADC", NULL, "Input Mux"},
|
||||
|
||||
/* inputs */
|
||||
{"Line Input", NULL, "LLINEIN"},
|
||||
{"Line Input", NULL, "RLINEIN"},
|
||||
{"Mic Bias", NULL, "MICIN"},
|
||||
};
|
||||
|
||||
static int ssm2602_add_widgets(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct snd_soc_dapm_context *dapm = &codec->dapm;
|
||||
static const struct snd_soc_dapm_route ssm2604_routes[] = {
|
||||
{"ADC", NULL, "Line Input"},
|
||||
};
|
||||
|
||||
snd_soc_dapm_new_controls(dapm, ssm2602_dapm_widgets,
|
||||
ARRAY_SIZE(ssm2602_dapm_widgets));
|
||||
snd_soc_dapm_add_routes(dapm, audio_conn, ARRAY_SIZE(audio_conn));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct _coeff_div {
|
||||
struct ssm2602_coeff {
|
||||
u32 mclk;
|
||||
u32 rate;
|
||||
u16 fs;
|
||||
u8 sr:4;
|
||||
u8 bosr:1;
|
||||
u8 usb:1;
|
||||
u8 srate;
|
||||
};
|
||||
|
||||
/* codec mclk clock divider coefficients */
|
||||
static const struct _coeff_div coeff_div[] = {
|
||||
#define SSM2602_COEFF_SRATE(sr, bosr, usb) (((sr) << 2) | ((bosr) << 1) | (usb))
|
||||
|
||||
/* codec mclk clock coefficients */
|
||||
static const struct ssm2602_coeff ssm2602_coeff_table[] = {
|
||||
/* 48k */
|
||||
{12288000, 48000, 256, 0x0, 0x0, 0x0},
|
||||
{18432000, 48000, 384, 0x0, 0x1, 0x0},
|
||||
{12000000, 48000, 250, 0x0, 0x0, 0x1},
|
||||
{12288000, 48000, SSM2602_COEFF_SRATE(0x0, 0x0, 0x0)},
|
||||
{18432000, 48000, SSM2602_COEFF_SRATE(0x0, 0x1, 0x0)},
|
||||
{12000000, 48000, SSM2602_COEFF_SRATE(0x0, 0x0, 0x1)},
|
||||
|
||||
/* 32k */
|
||||
{12288000, 32000, 384, 0x6, 0x0, 0x0},
|
||||
{18432000, 32000, 576, 0x6, 0x1, 0x0},
|
||||
{12000000, 32000, 375, 0x6, 0x0, 0x1},
|
||||
{12288000, 32000, SSM2602_COEFF_SRATE(0x6, 0x0, 0x0)},
|
||||
{18432000, 32000, SSM2602_COEFF_SRATE(0x6, 0x1, 0x0)},
|
||||
{12000000, 32000, SSM2602_COEFF_SRATE(0x6, 0x0, 0x1)},
|
||||
|
||||
/* 8k */
|
||||
{12288000, 8000, 1536, 0x3, 0x0, 0x0},
|
||||
{18432000, 8000, 2304, 0x3, 0x1, 0x0},
|
||||
{11289600, 8000, 1408, 0xb, 0x0, 0x0},
|
||||
{16934400, 8000, 2112, 0xb, 0x1, 0x0},
|
||||
{12000000, 8000, 1500, 0x3, 0x0, 0x1},
|
||||
{12288000, 8000, SSM2602_COEFF_SRATE(0x3, 0x0, 0x0)},
|
||||
{18432000, 8000, SSM2602_COEFF_SRATE(0x3, 0x1, 0x0)},
|
||||
{11289600, 8000, SSM2602_COEFF_SRATE(0xb, 0x0, 0x0)},
|
||||
{16934400, 8000, SSM2602_COEFF_SRATE(0xb, 0x1, 0x0)},
|
||||
{12000000, 8000, SSM2602_COEFF_SRATE(0x3, 0x0, 0x1)},
|
||||
|
||||
/* 96k */
|
||||
{12288000, 96000, 128, 0x7, 0x0, 0x0},
|
||||
{18432000, 96000, 192, 0x7, 0x1, 0x0},
|
||||
{12000000, 96000, 125, 0x7, 0x0, 0x1},
|
||||
{12288000, 96000, SSM2602_COEFF_SRATE(0x7, 0x0, 0x0)},
|
||||
{18432000, 96000, SSM2602_COEFF_SRATE(0x7, 0x1, 0x0)},
|
||||
{12000000, 96000, SSM2602_COEFF_SRATE(0x7, 0x0, 0x1)},
|
||||
|
||||
/* 44.1k */
|
||||
{11289600, 44100, 256, 0x8, 0x0, 0x0},
|
||||
{16934400, 44100, 384, 0x8, 0x1, 0x0},
|
||||
{12000000, 44100, 272, 0x8, 0x1, 0x1},
|
||||
{11289600, 44100, SSM2602_COEFF_SRATE(0x8, 0x0, 0x0)},
|
||||
{16934400, 44100, SSM2602_COEFF_SRATE(0x8, 0x1, 0x0)},
|
||||
{12000000, 44100, SSM2602_COEFF_SRATE(0x8, 0x1, 0x1)},
|
||||
|
||||
/* 88.2k */
|
||||
{11289600, 88200, 128, 0xf, 0x0, 0x0},
|
||||
{16934400, 88200, 192, 0xf, 0x1, 0x0},
|
||||
{12000000, 88200, 136, 0xf, 0x1, 0x1},
|
||||
{11289600, 88200, SSM2602_COEFF_SRATE(0xf, 0x0, 0x0)},
|
||||
{16934400, 88200, SSM2602_COEFF_SRATE(0xf, 0x1, 0x0)},
|
||||
{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;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(coeff_div); i++) {
|
||||
if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk)
|
||||
return i;
|
||||
for (i = 0; i < ARRAY_SIZE(ssm2602_coeff_table); i++) {
|
||||
if (ssm2602_coeff_table[i].rate == rate &&
|
||||
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,
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
u16 srate;
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_codec *codec = rtd->codec;
|
||||
struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
|
||||
struct i2c_client *i2c = codec->control_data;
|
||||
u16 iface = ssm2602_read_reg_cache(codec, SSM2602_IFACE) & 0xfff3;
|
||||
int i = get_coeff(ssm2602->sysclk, params_rate(params));
|
||||
u16 iface = snd_soc_read(codec, SSM2602_IFACE) & 0xfff3;
|
||||
int srate = ssm2602_get_coeff(ssm2602->sysclk, params_rate(params));
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/*no match is found*/
|
||||
if (i == ARRAY_SIZE(coeff_div))
|
||||
return -EINVAL;
|
||||
if (srate < 0)
|
||||
return srate;
|
||||
|
||||
srate = (coeff_div[i].sr << 2) |
|
||||
(coeff_div[i].bosr << 1) | coeff_div[i].usb;
|
||||
|
||||
ssm2602_write(codec, SSM2602_ACTIVE, 0);
|
||||
ssm2602_write(codec, SSM2602_SRATE, srate);
|
||||
snd_soc_write(codec, SSM2602_SRATE, srate);
|
||||
|
||||
/* bit size */
|
||||
switch (params_format(params)) {
|
||||
@ -311,8 +284,7 @@ static int ssm2602_hw_params(struct snd_pcm_substream *substream,
|
||||
iface |= 0x000c;
|
||||
break;
|
||||
}
|
||||
ssm2602_write(codec, SSM2602_IFACE, iface);
|
||||
ssm2602_write(codec, SSM2602_ACTIVE, ACTIVE_ACTIVATE_CODEC);
|
||||
snd_soc_write(codec, SSM2602_IFACE, iface);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -354,17 +326,6 @@ static int ssm2602_startup(struct snd_pcm_substream *substream,
|
||||
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,
|
||||
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 ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
/* deactivate */
|
||||
if (!codec->active)
|
||||
ssm2602_write(codec, SSM2602_ACTIVE, 0);
|
||||
|
||||
if (ssm2602->master_substream == substream)
|
||||
ssm2602->master_substream = ssm2602->slave_substream;
|
||||
|
||||
ssm2602->slave_substream = NULL;
|
||||
}
|
||||
|
||||
|
||||
static int ssm2602_mute(struct snd_soc_dai *dai, int mute)
|
||||
{
|
||||
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)
|
||||
ssm2602_write(codec, SSM2602_APDIGI,
|
||||
snd_soc_write(codec, SSM2602_APDIGI,
|
||||
mute_reg | APDIGI_ENABLE_DAC_MUTE);
|
||||
else
|
||||
ssm2602_write(codec, SSM2602_APDIGI, mute_reg);
|
||||
snd_soc_write(codec, SSM2602_APDIGI, mute_reg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -466,30 +424,29 @@ static int ssm2602_set_dai_fmt(struct snd_soc_dai *codec_dai,
|
||||
}
|
||||
|
||||
/* set iface */
|
||||
ssm2602_write(codec, SSM2602_IFACE, iface);
|
||||
snd_soc_write(codec, SSM2602_IFACE, iface);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ssm2602_set_bias_level(struct snd_soc_codec *codec,
|
||||
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) {
|
||||
case SND_SOC_BIAS_ON:
|
||||
/* vref/mid, osc on, dac unmute */
|
||||
ssm2602_write(codec, SSM2602_PWR, reg);
|
||||
snd_soc_write(codec, SSM2602_PWR, reg);
|
||||
break;
|
||||
case SND_SOC_BIAS_PREPARE:
|
||||
break;
|
||||
case SND_SOC_BIAS_STANDBY:
|
||||
/* 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;
|
||||
case SND_SOC_BIAS_OFF:
|
||||
/* everything off, dac mute, inactive */
|
||||
ssm2602_write(codec, SSM2602_ACTIVE, 0);
|
||||
ssm2602_write(codec, SSM2602_PWR, 0xffff);
|
||||
snd_soc_write(codec, SSM2602_PWR, 0xffff);
|
||||
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 = {
|
||||
.startup = ssm2602_startup,
|
||||
.prepare = ssm2602_pcm_prepare,
|
||||
.hw_params = ssm2602_hw_params,
|
||||
.shutdown = ssm2602_shutdown,
|
||||
.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)
|
||||
{
|
||||
int i;
|
||||
u8 data[2];
|
||||
u16 *cache = codec->reg_cache;
|
||||
snd_soc_cache_sync(codec);
|
||||
|
||||
/* 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);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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);
|
||||
int ret = 0, reg;
|
||||
int ret, reg;
|
||||
|
||||
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 */
|
||||
reg = ssm2602_read_reg_cache(codec, SSM2602_LINVOL);
|
||||
ssm2602_write(codec, SSM2602_LINVOL, reg | LINVOL_LRIN_BOTH);
|
||||
reg = ssm2602_read_reg_cache(codec, SSM2602_RINVOL);
|
||||
ssm2602_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);
|
||||
reg = snd_soc_read(codec, SSM2602_LINVOL);
|
||||
snd_soc_write(codec, SSM2602_LINVOL, reg | LINVOL_LRIN_BOTH);
|
||||
reg = snd_soc_read(codec, SSM2602_RINVOL);
|
||||
snd_soc_write(codec, SSM2602_RINVOL, reg | RINVOL_RLIN_BOTH);
|
||||
/*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);
|
||||
ssm2602_write(codec, SSM2602_PWR, 0);
|
||||
|
||||
snd_soc_add_controls(codec, ssm2602_snd_controls,
|
||||
ARRAY_SIZE(ssm2602_snd_controls));
|
||||
ssm2602_add_widgets(codec);
|
||||
switch (ssm2602->type) {
|
||||
case SSM2602:
|
||||
ret = ssm2602_probe(codec);
|
||||
break;
|
||||
case SSM2604:
|
||||
ret = ssm2604_probe(codec);
|
||||
break;
|
||||
}
|
||||
|
||||
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 = {
|
||||
.probe = ssm2602_probe,
|
||||
.probe = ssm260x_probe,
|
||||
.remove = ssm2602_remove,
|
||||
.suspend = ssm2602_suspend,
|
||||
.resume = ssm2602_resume,
|
||||
.read = ssm2602_read_reg_cache,
|
||||
.write = ssm2602_write,
|
||||
.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_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)
|
||||
/*
|
||||
* ssm2602 2 wire address is determined by GPIO5
|
||||
@ -614,7 +650,7 @@ static struct snd_soc_codec_driver soc_codec_dev_ssm2602 = {
|
||||
* low = 0x1a
|
||||
* 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)
|
||||
{
|
||||
struct ssm2602_priv *ssm2602;
|
||||
@ -625,8 +661,8 @@ static int ssm2602_i2c_probe(struct i2c_client *i2c,
|
||||
return -ENOMEM;
|
||||
|
||||
i2c_set_clientdata(i2c, ssm2602);
|
||||
ssm2602->control_data = i2c;
|
||||
ssm2602->control_type = SND_SOC_I2C;
|
||||
ssm2602->type = id->driver_data;
|
||||
|
||||
ret = snd_soc_register_codec(&i2c->dev,
|
||||
&soc_codec_dev_ssm2602, &ssm2602_dai, 1);
|
||||
@ -635,7 +671,7 @@ static int ssm2602_i2c_probe(struct i2c_client *i2c,
|
||||
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);
|
||||
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[] = {
|
||||
{ "ssm2602", 0 },
|
||||
{ "ssm2602", SSM2602 },
|
||||
{ "ssm2603", SSM2602 },
|
||||
{ "ssm2604", SSM2604 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, ssm2602_i2c_id);
|
||||
@ -651,11 +689,11 @@ MODULE_DEVICE_TABLE(i2c, ssm2602_i2c_id);
|
||||
/* corgi i2c codec control layer */
|
||||
static struct i2c_driver ssm2602_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "ssm2602-codec",
|
||||
.name = "ssm2602",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = ssm2602_i2c_probe,
|
||||
.remove = ssm2602_i2c_remove,
|
||||
.remove = __devexit_p(ssm2602_i2c_remove),
|
||||
.id_table = ssm2602_i2c_id,
|
||||
};
|
||||
#endif
|
||||
@ -664,25 +702,35 @@ static struct i2c_driver ssm2602_i2c_driver = {
|
||||
static int __init ssm2602_modinit(void)
|
||||
{
|
||||
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)
|
||||
ret = i2c_add_driver(&ssm2602_i2c_driver);
|
||||
if (ret != 0) {
|
||||
printk(KERN_ERR "Failed to register SSM2602 I2C driver: %d\n",
|
||||
ret);
|
||||
}
|
||||
if (ret)
|
||||
return ret;
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
}
|
||||
module_init(ssm2602_modinit);
|
||||
|
||||
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)
|
||||
i2c_del_driver(&ssm2602_i2c_driver);
|
||||
#endif
|
||||
}
|
||||
module_exit(ssm2602_exit);
|
||||
|
||||
MODULE_DESCRIPTION("ASoC ssm2602 driver");
|
||||
MODULE_DESCRIPTION("ASoC SSM2602/SSM2603/SSM2604 driver");
|
||||
MODULE_AUTHOR("Cliff Cai");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
@ -117,11 +117,5 @@
|
||||
#define SSM2602_CACHEREGNUM 10
|
||||
|
||||
#define SSM2602_SYSCLK 0
|
||||
#define SSM2602_DAI 0
|
||||
|
||||
struct ssm2602_setup_data {
|
||||
int i2c_bus;
|
||||
unsigned short i2c_address;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -212,7 +212,7 @@ static const struct snd_soc_dapm_widget tlv320aic23_dapm_widgets[] = {
|
||||
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", "Line Bypass Switch", "Line Input"},
|
||||
{"Output Mixer", "Playback Switch", "DAC"},
|
||||
@ -388,18 +388,6 @@ static int set_sample_rate_control(struct snd_soc_codec *codec, int mclk,
|
||||
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,
|
||||
struct snd_pcm_hw_params *params,
|
||||
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,
|
||||
ARRAY_SIZE(tlv320aic23_snd_controls));
|
||||
tlv320aic23_add_widgets(codec);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -698,6 +685,10 @@ static struct snd_soc_codec_driver soc_codec_dev_tlv320aic23 = {
|
||||
.read = tlv320aic23_read_reg_cache,
|
||||
.write = tlv320aic23_write,
|
||||
.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)
|
||||
|
@ -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,
|
||||
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 *)kcontrol->private_value;
|
||||
unsigned int reg = mc->reg;
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
@ -587,6 +587,9 @@ static const struct snd_soc_dapm_widget dac33_dapm_widgets[] = {
|
||||
SND_SOC_DAPM_SUPPLY("Right DAC Power",
|
||||
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_POST("Post Playback", dac33_playback_event),
|
||||
};
|
||||
@ -619,6 +622,9 @@ static const struct snd_soc_dapm_route audio_map[] = {
|
||||
/* output */
|
||||
{"LEFT_LO", NULL, "Output Left 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)
|
||||
@ -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,
|
||||
enum snd_soc_bias_level level)
|
||||
{
|
||||
struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
|
||||
int ret;
|
||||
|
||||
switch (level) {
|
||||
case SND_SOC_BIAS_ON:
|
||||
if (!dac33->substream)
|
||||
dac33_soft_power(codec, 1);
|
||||
break;
|
||||
case SND_SOC_BIAS_PREPARE:
|
||||
break;
|
||||
@ -943,8 +946,8 @@ static int dac33_prepare_chip(struct snd_pcm_substream *substream)
|
||||
/* Write registers 0x08 and 0x09 (MSB, LSB) */
|
||||
dac33_write16(codec, DAC33_INT_OSC_FREQ_RAT_A, oscset);
|
||||
|
||||
/* calib time: 128 is a nice number ;) */
|
||||
dac33_write(codec, DAC33_CALIB_TIME, 128);
|
||||
/* OSC calibration time */
|
||||
dac33_write(codec, DAC33_CALIB_TIME, 96);
|
||||
|
||||
/* adjustment treshold & step */
|
||||
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_AUTHOR("Peter Ujfalusi <peter.ujfalusi@nokia.com>");
|
||||
MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
|
@ -3,7 +3,7 @@
|
||||
*
|
||||
* 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
|
||||
* 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);
|
||||
}
|
||||
|
||||
MODULE_AUTHOR("Peter Ujfalusi");
|
||||
MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>");
|
||||
MODULE_DESCRIPTION("TPA6130A2 Headphone amplifier driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
*
|
||||
* 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
|
||||
* modify it under the terms of the GNU General Public License
|
||||
|
@ -960,9 +960,9 @@ static DECLARE_TLV_DB_SCALE(mic_amp_tlv, -600, 600, 0);
|
||||
|
||||
/*
|
||||
* 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:
|
||||
@ -1049,7 +1049,7 @@ static const struct snd_kcontrol_new twl6040_snd_controls[] = {
|
||||
|
||||
/* AFM gains */
|
||||
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 */
|
||||
SOC_TWL6040_DOUBLE_TLV("Headset Playback Volume",
|
||||
|
@ -601,9 +601,7 @@ static struct snd_soc_codec_driver soc_codec_dev_uda134x = {
|
||||
.reg_cache_step = 1,
|
||||
.read = uda134x_read_reg_cache,
|
||||
.write = uda134x_write,
|
||||
#ifdef POWER_OFF_ON_STANDBY
|
||||
.set_bias_level = uda134x_set_bias_level,
|
||||
#endif
|
||||
};
|
||||
|
||||
static int __devinit uda134x_codec_probe(struct platform_device *pdev)
|
||||
|
108
sound/soc/codecs/wm1250-ev1.c
Normal file
108
sound/soc/codecs/wm1250-ev1.c
Normal 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");
|
@ -77,7 +77,7 @@ SND_SOC_DAPM_OUTPUT("ROUT"),
|
||||
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", "Line Bypass Switch", "Line Input"},
|
||||
{"Output Mixer", "HiFi Playback Switch", "DAC"},
|
||||
@ -89,17 +89,6 @@ static const struct snd_soc_dapm_route intercon[] = {
|
||||
{"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 {
|
||||
u32 mclk;
|
||||
u32 rate;
|
||||
@ -398,7 +387,6 @@ static int wm8711_probe(struct snd_soc_codec *codec)
|
||||
|
||||
snd_soc_add_controls(codec, wm8711_snd_controls,
|
||||
ARRAY_SIZE(wm8711_snd_controls));
|
||||
wm8711_add_widgets(codec);
|
||||
|
||||
return ret;
|
||||
|
||||
@ -420,6 +408,10 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8711 = {
|
||||
.reg_cache_size = ARRAY_SIZE(wm8711_reg),
|
||||
.reg_word_size = sizeof(u16),
|
||||
.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)
|
||||
|
@ -65,22 +65,11 @@ SND_SOC_DAPM_OUTPUT("VOUTL"),
|
||||
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"},
|
||||
{"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)
|
||||
{
|
||||
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,
|
||||
ARRAY_SIZE(wm8728_snd_controls));
|
||||
wm8728_add_widgets(codec);
|
||||
|
||||
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_word_size = sizeof(u16),
|
||||
.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)
|
||||
|
@ -201,7 +201,7 @@ static int wm8731_check_osc(struct snd_soc_dapm_widget *source,
|
||||
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},
|
||||
{"ADC", NULL, "OSC", wm8731_check_osc},
|
||||
|
||||
@ -227,17 +227,6 @@ static const struct snd_soc_dapm_route intercon[] = {
|
||||
{"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 {
|
||||
u32 mclk;
|
||||
u32 rate;
|
||||
@ -599,7 +588,6 @@ static int wm8731_probe(struct snd_soc_codec *codec)
|
||||
|
||||
snd_soc_add_controls(codec, wm8731_snd_controls,
|
||||
ARRAY_SIZE(wm8731_snd_controls));
|
||||
wm8731_add_widgets(codec);
|
||||
|
||||
/* Regulators will have been enabled by bias management */
|
||||
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_word_size = sizeof(u16),
|
||||
.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)
|
||||
@ -667,7 +659,7 @@ static int __devexit wm8731_spi_remove(struct spi_device *spi)
|
||||
|
||||
static struct spi_driver wm8731_spi_driver = {
|
||||
.driver = {
|
||||
.name = "wm8731-codec",
|
||||
.name = "wm8731",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = wm8731_spi_probe,
|
||||
@ -711,7 +703,7 @@ MODULE_DEVICE_TABLE(i2c, wm8731_i2c_id);
|
||||
|
||||
static struct i2c_driver wm8731_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "wm8731-codec",
|
||||
.name = "wm8731",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = wm8731_i2c_probe,
|
||||
|
@ -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,
|
||||
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 wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec);
|
||||
u16 reg;
|
||||
@ -634,6 +635,13 @@ static const struct soc_enum lsidetone_enum =
|
||||
static const struct soc_enum rsidetone_enum =
|
||||
SOC_ENUM_SINGLE(WM8903_DAC_DIGITAL_0, 0, 3, sidetone_text);
|
||||
|
||||
static const char *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[] = {
|
||||
"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_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_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 =
|
||||
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 =
|
||||
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("IN3L"),
|
||||
SND_SOC_DAPM_INPUT("IN3R"),
|
||||
SND_SOC_DAPM_INPUT("DMICDAT"),
|
||||
|
||||
SND_SOC_DAPM_OUTPUT("HPOUTL"),
|
||||
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("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("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),
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route intercon[] = {
|
||||
static const struct snd_soc_dapm_route wm8903_intercon[] = {
|
||||
|
||||
{ "CLK_DSP", 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" },
|
||||
{ "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", "Right", "ADCR" },
|
||||
|
||||
@ -988,9 +1008,9 @@ static const struct snd_soc_dapm_route intercon[] = {
|
||||
{ "AIFTXL", NULL, "Left Capture Mux" },
|
||||
{ "AIFTXR", NULL, "Right Capture Mux" },
|
||||
|
||||
{ "ADCL", NULL, "Left Input PGA" },
|
||||
{ "ADCL", NULL, "Left ADC Input" },
|
||||
{ "ADCL", NULL, "CLK_DSP" },
|
||||
{ "ADCR", NULL, "Right Input PGA" },
|
||||
{ "ADCR", NULL, "Right ADC Input" },
|
||||
{ "ADCR", NULL, "CLK_DSP" },
|
||||
|
||||
{ "Left Playback Mux", "Left", "AIFRXL" },
|
||||
@ -1087,17 +1107,6 @@ static const struct snd_soc_dapm_route intercon[] = {
|
||||
{ "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,
|
||||
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,
|
||||
ARRAY_SIZE(wm8903_snd_controls));
|
||||
wm8903_add_widgets(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,
|
||||
.volatile_register = wm8903_volatile_register,
|
||||
.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)
|
||||
|
2931
sound/soc/codecs/wm8915.c
Normal file
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
3717
sound/soc/codecs/wm8915.h
Normal file
File diff suppressed because it is too large
Load Diff
1051
sound/soc/codecs/wm8958-dsp2.c
Normal file
1051
sound/soc/codecs/wm8958-dsp2.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -58,6 +58,7 @@ struct wm8962_priv {
|
||||
int bclk; /* Desired BCLK */
|
||||
int lrclk;
|
||||
|
||||
struct completion fll_lock;
|
||||
int fll_src;
|
||||
int fll_fref;
|
||||
int fll_fout;
|
||||
@ -2038,6 +2039,13 @@ static int wm8962_put_spk_sw(struct snd_kcontrol *kcontrol,
|
||||
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[] = {
|
||||
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),
|
||||
SOC_DOUBLE_R("Capture ZC Switch", WM8962_LEFT_INPUT_VOLUME,
|
||||
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,
|
||||
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("IN4R"),
|
||||
SND_SOC_DAPM_INPUT("Beep"),
|
||||
SND_SOC_DAPM_INPUT("DMICDAT"),
|
||||
|
||||
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,
|
||||
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("ADCR", "Capture", WM8962_PWR_MGMT_1, 2, 0),
|
||||
|
||||
@ -2563,13 +2577,17 @@ static const struct snd_soc_dapm_route wm8962_intercon[] = {
|
||||
|
||||
{ "MICBIAS", NULL, "SYSCLK" },
|
||||
|
||||
{ "DMIC", NULL, "DMICDAT" },
|
||||
|
||||
{ "ADCL", NULL, "SYSCLK" },
|
||||
{ "ADCL", NULL, "TOCLK" },
|
||||
{ "ADCL", NULL, "MIXINL" },
|
||||
{ "ADCL", NULL, "DMIC" },
|
||||
|
||||
{ "ADCR", NULL, "SYSCLK" },
|
||||
{ "ADCR", NULL, "TOCLK" },
|
||||
{ "ADCR", NULL, "MIXINR" },
|
||||
{ "ADCR", NULL, "DMIC" },
|
||||
|
||||
{ "STL", "Left", "ADCL" },
|
||||
{ "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:
|
||||
wm8962->sysclk = WM8962_SYSCLK_FLL;
|
||||
src = 1 << WM8962_SYSCLK_SRC_SHIFT;
|
||||
WARN_ON(freq != wm8962->fll_fout);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
@ -3172,12 +3189,12 @@ static int fll_factors(struct _fll_div *fll_div, unsigned int Fref,
|
||||
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)
|
||||
{
|
||||
struct snd_soc_codec *codec = dai->codec;
|
||||
struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
|
||||
struct _fll_div fll_div;
|
||||
unsigned long timeout;
|
||||
int ret;
|
||||
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);
|
||||
|
||||
/* 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_fout = Fout;
|
||||
wm8962->fll_src = source;
|
||||
@ -3274,7 +3296,6 @@ static struct snd_soc_dai_ops wm8962_dai_ops = {
|
||||
.hw_params = wm8962_hw_params,
|
||||
.set_sysclk = wm8962_set_dai_sysclk,
|
||||
.set_fmt = wm8962_set_dai_fmt,
|
||||
.set_pll = wm8962_set_fll,
|
||||
.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 &= ~mask;
|
||||
|
||||
if (active & WM8962_FLL_LOCK_EINT) {
|
||||
dev_dbg(codec->dev, "FLL locked\n");
|
||||
complete(&wm8962->fll_lock);
|
||||
}
|
||||
|
||||
if (active & WM8962_FIFOS_ERR_EINT)
|
||||
dev_err(codec->dev, "FIFO error\n");
|
||||
|
||||
@ -3709,9 +3735,11 @@ static int wm8962_probe(struct snd_soc_codec *codec)
|
||||
dev);
|
||||
u16 *reg_cache = codec->reg_cache;
|
||||
int i, trigger, irq_pol;
|
||||
bool dmicclk, dmicdat;
|
||||
|
||||
wm8962->codec = codec;
|
||||
INIT_DELAYED_WORK(&wm8962->mic_work, wm8962_mic_work);
|
||||
init_completion(&wm8962->fll_lock);
|
||||
|
||||
codec->cache_sync = 1;
|
||||
codec->dapm.idle_bias_off = 1;
|
||||
@ -3845,6 +3873,29 @@ static int wm8962_probe(struct snd_soc_codec *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_gpio(codec);
|
||||
|
||||
@ -3868,9 +3919,10 @@ static int wm8962_probe(struct snd_soc_codec *codec)
|
||||
i2c->irq, ret);
|
||||
/* Non-fatal */
|
||||
} else {
|
||||
/* Enable error reporting IRQs by default */
|
||||
/* Enable some IRQs by default */
|
||||
snd_soc_update_bits(codec,
|
||||
WM8962_INTERRUPT_STATUS_2_MASK,
|
||||
WM8962_FLL_LOCK_EINT |
|
||||
WM8962_TEMP_SHUT_EINT |
|
||||
WM8962_FIFOS_ERR_EINT, 0);
|
||||
}
|
||||
@ -3918,6 +3970,7 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8962 = {
|
||||
.reg_cache_default = wm8962_reg,
|
||||
.volatile_register = wm8962_volatile_register,
|
||||
.readable_register = wm8962_readable_register,
|
||||
.set_pll = wm8962_set_fll,
|
||||
};
|
||||
|
||||
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
|
||||
|
@ -718,7 +718,8 @@ static int clk_sys_event(struct snd_soc_dapm_widget *w,
|
||||
static int class_w_put(struct snd_kcontrol *kcontrol,
|
||||
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 wm8993_priv *wm8993 = snd_soc_codec_get_drvdata(codec);
|
||||
int ret;
|
||||
|
@ -38,12 +38,6 @@
|
||||
#include "wm8994.h"
|
||||
#include "wm_hubs.h"
|
||||
|
||||
struct fll_config {
|
||||
int src;
|
||||
int in;
|
||||
int out;
|
||||
};
|
||||
|
||||
#define WM8994_NUM_DRC 3
|
||||
#define WM8994_NUM_EQ 3
|
||||
|
||||
@ -59,63 +53,11 @@ static int wm8994_retune_mobile_base[] = {
|
||||
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)
|
||||
{
|
||||
struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
|
||||
struct wm8994 *control = wm8994->control_data;
|
||||
|
||||
switch (reg) {
|
||||
case WM8994_GPIO_1:
|
||||
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_RAW_STATUS_2:
|
||||
return 1;
|
||||
|
||||
case WM8958_DSP2_PROGRAM:
|
||||
case WM8958_DSP2_CONFIG:
|
||||
case WM8958_DSP2_EXECCONTROL:
|
||||
if (control->type == WM8958)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -574,215 +525,6 @@ static const struct soc_enum dac_osr =
|
||||
static const struct soc_enum adc_osr =
|
||||
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[] = {
|
||||
SOC_DOUBLE_R_TLV("AIF1ADC1 Volume", WM8994_AIF1_ADC1_LEFT_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[] = {
|
||||
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,
|
||||
@ -1032,6 +771,9 @@ static int late_enable_ev(struct snd_soc_dapm_widget *w,
|
||||
break;
|
||||
}
|
||||
|
||||
/* We may also have postponed startup of DSP, handle that. */
|
||||
wm8958_aif_ev(w, kcontrol, event);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1135,7 +877,8 @@ static const char *hp_mux_text[] = {
|
||||
static int wm8994_put_hp_enum(struct snd_kcontrol *kcontrol,
|
||||
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;
|
||||
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,
|
||||
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;
|
||||
int ret;
|
||||
|
||||
@ -2180,6 +1924,8 @@ static int wm8994_set_bias_level(struct snd_soc_codec *codec,
|
||||
WM8994_VMID_BUF_ENA |
|
||||
WM8994_VMID_RAMP_MASK, 0);
|
||||
|
||||
wm8994->cur_fw = NULL;
|
||||
|
||||
pm_runtime_put(codec->dev);
|
||||
}
|
||||
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)
|
||||
{
|
||||
struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
|
||||
struct wm8994 *control = codec->control_data;
|
||||
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++) {
|
||||
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);
|
||||
if (ret < 0)
|
||||
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)
|
||||
{
|
||||
struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
|
||||
struct wm8994 *control = codec->control_data;
|
||||
int i, ret;
|
||||
unsigned int val, mask;
|
||||
|
||||
@ -2729,6 +2487,19 @@ static int wm8994_resume(struct snd_soc_codec *codec)
|
||||
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;
|
||||
}
|
||||
#else
|
||||
@ -2862,34 +2633,6 @@ static void wm8994_handle_pdata(struct wm8994_priv *wm8994)
|
||||
dev_dbg(codec->dev, "%d ReTune Mobile configurations\n",
|
||||
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)
|
||||
wm8994_handle_retune_mobile_pdata(wm8994);
|
||||
else
|
||||
@ -3343,14 +3086,23 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
|
||||
case WM8958:
|
||||
snd_soc_add_controls(codec, wm8958_snd_controls,
|
||||
ARRAY_SIZE(wm8958_snd_controls));
|
||||
snd_soc_dapm_new_controls(dapm, wm8994_lateclk_widgets,
|
||||
ARRAY_SIZE(wm8994_lateclk_widgets));
|
||||
snd_soc_dapm_new_controls(dapm, wm8994_adc_widgets,
|
||||
ARRAY_SIZE(wm8994_adc_widgets));
|
||||
snd_soc_dapm_new_controls(dapm, wm8994_dac_widgets,
|
||||
ARRAY_SIZE(wm8994_dac_widgets));
|
||||
snd_soc_dapm_new_controls(dapm, wm8958_dapm_widgets,
|
||||
ARRAY_SIZE(wm8958_dapm_widgets));
|
||||
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;
|
||||
}
|
||||
|
||||
@ -3374,10 +3126,19 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
|
||||
}
|
||||
break;
|
||||
case WM8958:
|
||||
snd_soc_dapm_add_routes(dapm, wm8994_lateclk_intercon,
|
||||
ARRAY_SIZE(wm8994_lateclk_intercon));
|
||||
snd_soc_dapm_add_routes(dapm, wm8958_intercon,
|
||||
ARRAY_SIZE(wm8958_intercon));
|
||||
if (wm8994->revision < 1) {
|
||||
snd_soc_dapm_add_routes(dapm, wm8994_revd_intercon,
|
||||
ARRAY_SIZE(wm8994_revd_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;
|
||||
}
|
||||
|
||||
@ -3420,6 +3181,12 @@ static int wm8994_codec_remove(struct snd_soc_codec *codec)
|
||||
free_irq(wm8994->micdet_irq, wm8994);
|
||||
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->drc_texts);
|
||||
kfree(wm8994);
|
||||
|
@ -10,6 +10,9 @@
|
||||
#define _WM8994_H
|
||||
|
||||
#include <sound/soc.h>
|
||||
#include <linux/firmware.h>
|
||||
|
||||
#include "wm_hubs.h"
|
||||
|
||||
/* Sources for AIF1/2 SYSCLK - use with set_dai_sysclk() */
|
||||
#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 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
|
||||
|
@ -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,
|
||||
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;
|
||||
int ret;
|
||||
|
||||
w = snd_kcontrol_chip(kcontrol);
|
||||
codec = w->codec;
|
||||
ret = snd_soc_dapm_put_volsw(kcontrol, ucontrol);
|
||||
wm8995_update_class_w(codec);
|
||||
|
@ -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
|
||||
* controls.
|
||||
*/
|
||||
static const struct snd_soc_dapm_route audio_map[] = {
|
||||
static const struct snd_soc_dapm_route wm9705_audio_map[] = {
|
||||
/* HP mixer */
|
||||
{"HP Mixer", "PCBeep Playback Switch", "PCBEEP 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"},
|
||||
};
|
||||
|
||||
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. */
|
||||
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,
|
||||
ARRAY_SIZE(wm9705_snd_ac97_controls));
|
||||
wm9705_add_widgets(codec);
|
||||
|
||||
return 0;
|
||||
|
||||
@ -390,6 +378,10 @@ static struct snd_soc_codec_driver soc_codec_dev_wm9705 = {
|
||||
.reg_word_size = sizeof(u16),
|
||||
.reg_cache_step = 2,
|
||||
.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)
|
||||
|
@ -332,7 +332,7 @@ SND_SOC_DAPM_INPUT("MIC1"),
|
||||
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 */
|
||||
{"AC97 Mixer", NULL, "Left DAC"},
|
||||
{"AC97 Mixer", NULL, "Right DAC"},
|
||||
@ -429,17 +429,6 @@ static const struct snd_soc_dapm_route audio_map[] = {
|
||||
{"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,
|
||||
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);
|
||||
snd_soc_add_controls(codec, wm9712_snd_ac97_controls,
|
||||
ARRAY_SIZE(wm9712_snd_ac97_controls));
|
||||
wm9712_add_widgets(codec);
|
||||
|
||||
return 0;
|
||||
|
||||
@ -678,6 +666,10 @@ static struct snd_soc_codec_driver soc_codec_dev_wm9712 = {
|
||||
.reg_word_size = sizeof(u16),
|
||||
.reg_cache_step = 2,
|
||||
.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)
|
||||
|
@ -487,7 +487,7 @@ SND_SOC_DAPM_INPUT("MIC2B"),
|
||||
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", "Beep Playback Switch", "PCBEEP"},
|
||||
{"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"},
|
||||
};
|
||||
|
||||
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,
|
||||
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,
|
||||
ARRAY_SIZE(wm9713_snd_ac97_controls));
|
||||
wm9713_add_widgets(codec);
|
||||
|
||||
return 0;
|
||||
|
||||
@ -1262,6 +1249,10 @@ static struct snd_soc_codec_driver soc_codec_dev_wm9713 = {
|
||||
.reg_word_size = sizeof(u16),
|
||||
.reg_cache_step = 2,
|
||||
.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)
|
||||
|
@ -787,17 +787,17 @@ static const struct snd_soc_dapm_route analogue_routes[] = {
|
||||
static const struct snd_soc_dapm_route lineout1_diff_routes[] = {
|
||||
{ "LINEOUT1 Mixer", "IN1L Switch", "IN1L 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" },
|
||||
{ "LINEOUT1P Driver", NULL, "LINEOUT1 Mixer" },
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route lineout1_se_routes[] = {
|
||||
{ "LINEOUT1N Mixer", "Left Output Switch", "Left Output Mixer" },
|
||||
{ "LINEOUT1N Mixer", "Right Output Switch", "Left Output Mixer" },
|
||||
{ "LINEOUT1N Mixer", "Left Output Switch", "Left Output PGA" },
|
||||
{ "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" },
|
||||
{ "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[] = {
|
||||
{ "LINEOUT2 Mixer", "IN2L Switch", "IN2L 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" },
|
||||
{ "LINEOUT2P Driver", NULL, "LINEOUT2 Mixer" },
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route lineout2_se_routes[] = {
|
||||
{ "LINEOUT2N Mixer", "Left Output Switch", "Left Output Mixer" },
|
||||
{ "LINEOUT2N Mixer", "Right Output Switch", "Left Output Mixer" },
|
||||
{ "LINEOUT2N Mixer", "Left Output Switch", "Left Output PGA" },
|
||||
{ "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" },
|
||||
{ "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,
|
||||
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,
|
||||
WM8993_SPKOUT_VU, WM8993_SPKOUT_VU);
|
||||
|
||||
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,
|
||||
WM8993_HPOUT1_VU | WM8993_HPOUT1R_ZC,
|
||||
WM8993_HPOUT1_VU | WM8993_HPOUT1R_ZC);
|
||||
|
||||
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,
|
||||
WM8993_MIXOUTR_ZC | WM8993_MIXOUT_VU,
|
||||
WM8993_MIXOUTR_ZC | WM8993_MIXOUT_VU);
|
||||
|
@ -909,6 +909,7 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
|
||||
dma_data = &dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK];
|
||||
dma_data->asp_chan_q = pdata->asp_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 +
|
||||
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->asp_chan_q = pdata->asp_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 +
|
||||
mem->start);
|
||||
|
||||
|
@ -667,12 +667,6 @@ static int imx_ssi_probe(struct platform_device *pdev)
|
||||
if (res)
|
||||
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);
|
||||
|
||||
ret = snd_soc_register_dai(&pdev->dev, dai);
|
||||
|
@ -133,7 +133,7 @@ static void jz4740_i2s_shutdown(struct snd_pcm_substream *substream,
|
||||
struct jz4740_i2s *i2s = snd_soc_dai_get_drvdata(dai);
|
||||
uint32_t conf;
|
||||
|
||||
if (!dai->active)
|
||||
if (dai->active)
|
||||
return;
|
||||
|
||||
conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF);
|
||||
|
@ -27,11 +27,7 @@
|
||||
static int qi_lb60_spk_event(struct snd_soc_dapm_widget *widget,
|
||||
struct snd_kcontrol *ctrl, int event)
|
||||
{
|
||||
int on = 0;
|
||||
if (event & SND_SOC_DAPM_POST_PMU)
|
||||
on = 1;
|
||||
else if (event & SND_SOC_DAPM_PRE_PMD)
|
||||
on = 0;
|
||||
int on = !SND_SOC_DAPM_EVENT_OFF(event);
|
||||
|
||||
gpio_set_value(QI_LB60_SND_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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@ -93,10 +83,20 @@ static struct snd_soc_card qi_lb60 = {
|
||||
.name = "QI LB60",
|
||||
.dai_link = &qi_lb60_dai,
|
||||
.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 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)
|
||||
{
|
||||
int ret;
|
||||
@ -106,23 +106,12 @@ static int __init qi_lb60_init(void)
|
||||
if (!qi_lb60_snd_device)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = gpio_request(QI_LB60_SND_GPIO, "SND");
|
||||
ret = gpio_request_array(qi_lb60_gpios, ARRAY_SIZE(qi_lb60_gpios));
|
||||
if (ret) {
|
||||
pr_err("qi_lb60 snd: Failed to request SND GPIO(%d): %d\n",
|
||||
QI_LB60_SND_GPIO, ret);
|
||||
pr_err("qi_lb60 snd: Failed to request gpios: %d\n", ret);
|
||||
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);
|
||||
|
||||
ret = platform_device_add(qi_lb60_snd_device);
|
||||
@ -135,10 +124,8 @@ static int __init qi_lb60_init(void)
|
||||
|
||||
err_unset_pdata:
|
||||
platform_set_drvdata(qi_lb60_snd_device, NULL);
|
||||
/*err_gpio_free_amp:*/
|
||||
gpio_free(QI_LB60_AMP_GPIO);
|
||||
err_gpio_free_snd:
|
||||
gpio_free(QI_LB60_SND_GPIO);
|
||||
/*err_gpio_free_array:*/
|
||||
gpio_free_array(qi_lb60_gpios, ARRAY_SIZE(qi_lb60_gpios));
|
||||
err_device_put:
|
||||
platform_device_put(qi_lb60_snd_device);
|
||||
|
||||
@ -148,9 +135,8 @@ module_init(qi_lb60_init);
|
||||
|
||||
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);
|
||||
gpio_free_array(qi_lb60_gpios, ARRAY_SIZE(qi_lb60_gpios));
|
||||
}
|
||||
module_exit(qi_lb60_exit);
|
||||
|
||||
|
@ -249,10 +249,13 @@ static int sst_platform_open(struct snd_pcm_substream *substream)
|
||||
return -ENOMEM;
|
||||
}
|
||||
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 */
|
||||
ret_val = register_sst_card(stream->sstdrv_ops);
|
||||
if (ret_val) {
|
||||
pr_err("sst: sst card registration failed\n");
|
||||
kfree(stream->sstdrv_ops);
|
||||
kfree(stream);
|
||||
return ret_val;
|
||||
}
|
||||
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;
|
||||
if (str_id)
|
||||
ret_val = stream->sstdrv_ops->pcm_control->close(str_id);
|
||||
unregister_sst_card(stream->sstdrv_ops);
|
||||
kfree(stream->sstdrv_ops);
|
||||
kfree(stream);
|
||||
return ret_val;
|
||||
@ -376,6 +380,11 @@ static int sst_platform_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
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 = {
|
||||
.open = sst_platform_open,
|
||||
.close = sst_platform_close,
|
||||
@ -384,6 +393,7 @@ static struct snd_pcm_ops sst_platform_ops = {
|
||||
.trigger = sst_platform_pcm_trigger,
|
||||
.pointer = sst_platform_pcm_pointer,
|
||||
.hw_params = sst_platform_pcm_hw_params,
|
||||
.hw_free = sst_platform_pcm_hw_free,
|
||||
};
|
||||
|
||||
static void sst_pcm_free(struct snd_pcm *pcm)
|
||||
|
@ -4,7 +4,7 @@
|
||||
* Copyright (C) 2008 Nokia Corporation
|
||||
*
|
||||
* 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
|
||||
* 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)
|
||||
* 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
|
||||
* 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:
|
||||
return -EINVAL;
|
||||
}
|
||||
if (cpu_is_omap343x()) {
|
||||
if (cpu_is_omap34xx()) {
|
||||
dma_data->set_threshold = omap_mcbsp_set_threshold;
|
||||
/* TODO: Currently, MODE_ELEMENT == MODE_FRAME */
|
||||
if (omap_mcbsp_get_dma_op_mode(bus_id) ==
|
||||
|
@ -4,7 +4,7 @@
|
||||
* Copyright (C) 2008 Nokia Corporation
|
||||
*
|
||||
* 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
|
||||
* modify it under the terms of the GNU General Public License
|
||||
|
@ -4,7 +4,7 @@
|
||||
* Copyright (C) 2008 Nokia Corporation
|
||||
*
|
||||
* 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
|
||||
* 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_INTERLEAVED |
|
||||
SNDRV_PCM_INFO_PAUSE |
|
||||
SNDRV_PCM_INFO_RESUME,
|
||||
SNDRV_PCM_INFO_RESUME |
|
||||
SNDRV_PCM_INFO_NO_PERIOD_WAKEUP,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE |
|
||||
SNDRV_PCM_FMTBIT_S32_LE,
|
||||
.period_bytes_min = 32,
|
||||
@ -195,7 +196,7 @@ static int omap_pcm_prepare(struct snd_pcm_substream *substream)
|
||||
if ((cpu_is_omap1510()))
|
||||
omap_enable_dma_irq(prtd->dma_ch, OMAP_DMA_FRAME_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);
|
||||
|
||||
if (!(cpu_class_is_omap1())) {
|
||||
|
@ -4,7 +4,7 @@
|
||||
* Copyright (C) 2008 Nokia Corporation
|
||||
*
|
||||
* 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
|
||||
* modify it under the terms of the GNU General Public License
|
||||
|
@ -3,7 +3,7 @@
|
||||
*
|
||||
* 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>
|
||||
* Jarkko Nikula <jhnikula@gmail.com>
|
||||
*
|
||||
|
@ -155,6 +155,15 @@ config SND_SOC_RAUMFELD
|
||||
help
|
||||
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
|
||||
tristate "SoC Audio support for HTC Magician"
|
||||
depends on SND_PXA2XX_SOC && MACH_MAGICIAN
|
||||
|
@ -22,6 +22,7 @@ snd-soc-palm27x-objs := palm27x.o
|
||||
snd-soc-saarb-objs := saarb.o
|
||||
snd-soc-tavorevb3-objs := tavorevb3.o
|
||||
snd-soc-zylonite-objs := zylonite.o
|
||||
snd-soc-hx4700-objs := hx4700.o
|
||||
snd-soc-magician-objs := magician.o
|
||||
snd-soc-mioa701-objs := mioa701_wm9713.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_EM_X270) += snd-soc-em-x270.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_MIOA701) += snd-soc-mioa701.o
|
||||
obj-$(CONFIG_SND_PXA2XX_SOC_Z2) += snd-soc-z2.o
|
||||
|
@ -310,7 +310,7 @@ static struct snd_soc_dai_link corgi_dai = {
|
||||
.cpu_dai_name = "pxa2xx-i2s",
|
||||
.codec_dai_name = "wm8731-hifi",
|
||||
.platform_name = "pxa-pcm-audio",
|
||||
.codec_name = "wm8731-codec.0-001b",
|
||||
.codec_name = "wm8731.0-001b",
|
||||
.init = corgi_wm8731_init,
|
||||
.ops = &corgi_ops,
|
||||
};
|
||||
|
255
sound/soc/pxa/hx4700.c
Normal file
255
sound/soc/pxa/hx4700.c
Normal 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");
|
@ -276,7 +276,7 @@ static struct snd_soc_dai_link poodle_dai = {
|
||||
.cpu_dai_name = "pxa2xx-i2s",
|
||||
.codec_dai_name = "wm8731-hifi",
|
||||
.platform_name = "pxa-pcm-audio",
|
||||
.codec_name = "wm8731-codec.0-001b",
|
||||
.codec_name = "wm8731.0-001b",
|
||||
.init = poodle_wm8731_init,
|
||||
.ops = &poodle_ops,
|
||||
};
|
||||
|
@ -42,6 +42,7 @@
|
||||
|
||||
static int spitz_jack_func;
|
||||
static int spitz_spk_func;
|
||||
static int spitz_mic_gpio;
|
||||
|
||||
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,
|
||||
struct snd_kcontrol *k, int event)
|
||||
{
|
||||
if (machine_is_borzoi() || machine_is_spitz())
|
||||
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));
|
||||
|
||||
gpio_set_value_cansleep(spitz_mic_gpio, SND_SOC_DAPM_EVENT_ON(event));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -339,22 +333,45 @@ static int __init spitz_init(void)
|
||||
if (!(machine_is_spitz() || machine_is_borzoi() || machine_is_akita()))
|
||||
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);
|
||||
if (!spitz_snd_device)
|
||||
return -ENOMEM;
|
||||
if (!spitz_snd_device) {
|
||||
ret = -ENOMEM;
|
||||
goto err2;
|
||||
}
|
||||
|
||||
platform_set_drvdata(spitz_snd_device, &snd_soc_spitz);
|
||||
|
||||
ret = platform_device_add(spitz_snd_device);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static void __exit spitz_exit(void)
|
||||
{
|
||||
platform_device_unregister(spitz_snd_device);
|
||||
gpio_free(spitz_mic_gpio);
|
||||
}
|
||||
|
||||
module_init(spitz_init);
|
||||
|
@ -162,3 +162,18 @@ config SND_SOC_SAMSUNG_SMDK_SPDIF
|
||||
select SND_SAMSUNG_SPDIF
|
||||
help
|
||||
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
|
||||
|
@ -34,6 +34,8 @@ snd-soc-smdk-wm9713-objs := smdk_wm9713.o
|
||||
snd-soc-s3c64xx-smartq-wm8987-objs := smartq_wm8987.o
|
||||
snd-soc-goni-wm8994-objs := goni_wm8994.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_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_SAMSUNG_SMDK_SPDIF) += snd-soc-smdk-spdif.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
|
||||
|
@ -246,7 +246,6 @@ static struct snd_soc_dai_link goni_dai[] = {
|
||||
.stream_name = "Voice",
|
||||
.cpu_dai_name = "goni-voice-dai",
|
||||
.codec_dai_name = "wm8994-aif2",
|
||||
.platform_name = "samsung-audio",
|
||||
.codec_name = "wm8994-codec.0-001a",
|
||||
.ops = &goni_voice_ops,
|
||||
},
|
||||
|
@ -432,7 +432,6 @@ static struct snd_soc_dai_link neo1973_dai[] = {
|
||||
{ /* Voice via BT */
|
||||
.name = "Bluetooth",
|
||||
.stream_name = "Voice",
|
||||
.platform_name = "samsung-audio",
|
||||
.cpu_dai_name = "dfbmcs320-pcm",
|
||||
.codec_dai_name = "wm8753-voice",
|
||||
.codec_name = "wm8753-codec.0-001a",
|
||||
|
206
sound/soc/samsung/smdk_wm8580pcm.c
Normal file
206
sound/soc/samsung/smdk_wm8580pcm.c
Normal 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");
|
332
sound/soc/samsung/speyside.c
Normal file
332
sound/soc/samsung/speyside.c
Normal 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");
|
@ -86,8 +86,8 @@
|
||||
#define SE (1 << 0) /* Fix the master clock */
|
||||
|
||||
/* CLK_RST */
|
||||
#define B_CLK 0x00000010
|
||||
#define A_CLK 0x00000001
|
||||
#define CRB (1 << 4)
|
||||
#define CRA (1 << 0)
|
||||
|
||||
/* IO SHIFT / MACRO */
|
||||
#define BI_SHIFT 12
|
||||
@ -146,11 +146,20 @@ struct fsi_priv {
|
||||
void __iomem *base;
|
||||
struct fsi_master *master;
|
||||
|
||||
int chan_num;
|
||||
struct fsi_stream playback;
|
||||
struct fsi_stream capture;
|
||||
|
||||
int chan_num:16;
|
||||
int clk_master:1;
|
||||
|
||||
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 {
|
||||
@ -171,6 +180,14 @@ struct fsi_master {
|
||||
struct fsi_core *core;
|
||||
struct sh_fsi_platform_info *info;
|
||||
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;
|
||||
}
|
||||
|
||||
static int fsi_is_clk_master(struct fsi_priv *fsi)
|
||||
{
|
||||
return fsi->clk_master;
|
||||
}
|
||||
|
||||
static int fsi_is_port_a(struct fsi_priv *fsi)
|
||||
{
|
||||
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
|
||||
*/
|
||||
|
||||
static void fsi_clk_ctrl(struct fsi_priv *fsi, int enable)
|
||||
#define fsi_module_init(m, d) __fsi_module_clk_ctrl(m, d, 1)
|
||||
#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);
|
||||
struct fsi_master *master = fsi_get_master(fsi);
|
||||
pm_runtime_get_sync(dev);
|
||||
|
||||
if (enable)
|
||||
fsi_master_mask_set(master, CLK_RST, val, val);
|
||||
else
|
||||
fsi_master_mask_set(master, CLK_RST, val, 0);
|
||||
if (enable) {
|
||||
/* enable only SR */
|
||||
fsi_master_mask_set(master, SOFT_RST, FSISR, FSISR);
|
||||
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,
|
||||
int is_play,
|
||||
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)
|
||||
{
|
||||
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);
|
||||
int is_play = fsi_is_play(substream);
|
||||
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_clk_ctrl(fsi, 0);
|
||||
|
||||
set_rate = fsi_get_info_set_rate(master);
|
||||
if (set_rate && fsi->rate)
|
||||
if (fsi_is_clk_master(fsi))
|
||||
set_rate(dai->dev, fsi_is_port_a(fsi), fsi->rate, 0);
|
||||
|
||||
fsi->rate = 0;
|
||||
|
||||
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));
|
||||
ret = is_play ? fsi_data_push(fsi) : fsi_data_pop(fsi);
|
||||
fsi_irq_enable(fsi, is_play);
|
||||
fsi_port_start(fsi);
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
fsi_port_stop(fsi);
|
||||
fsi_irq_disable(fsi, is_play);
|
||||
fsi_stream_pop(fsi, is_play);
|
||||
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)
|
||||
{
|
||||
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 data = 0;
|
||||
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) {
|
||||
case SND_SOC_DAIFMT_CBM_CFM:
|
||||
data = DIMD | DOMD;
|
||||
fsi->clk_master = 1;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_CBS_CFS:
|
||||
break;
|
||||
@ -893,6 +932,13 @@ static int fsi_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
|
||||
ret = -EINVAL;
|
||||
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);
|
||||
|
||||
/* 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_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;
|
||||
long rate = params_rate(params);
|
||||
int ret;
|
||||
|
||||
set_rate = fsi_get_info_set_rate(master);
|
||||
if (!set_rate)
|
||||
if (!fsi_is_clk_master(fsi))
|
||||
return 0;
|
||||
|
||||
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);
|
||||
udelay(10);
|
||||
fsi_clk_ctrl(fsi, 1);
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
@ -1202,9 +1246,7 @@ static int fsi_probe(struct platform_device *pdev)
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
dev_set_drvdata(&pdev->dev, master);
|
||||
|
||||
pm_runtime_get_sync(&pdev->dev);
|
||||
fsi_soft_all_reset(master);
|
||||
pm_runtime_put_sync(&pdev->dev);
|
||||
fsi_module_init(master, &pdev->dev);
|
||||
|
||||
ret = request_irq(irq, &fsi_interrupt, IRQF_DISABLED,
|
||||
id_entry->name, master);
|
||||
@ -1248,6 +1290,8 @@ static int fsi_remove(struct platform_device *pdev)
|
||||
|
||||
master = dev_get_drvdata(&pdev->dev);
|
||||
|
||||
fsi_module_kill(master, &pdev->dev);
|
||||
|
||||
free_irq(master->irq, master);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
||||
@ -1260,6 +1304,82 @@ static int fsi_remove(struct platform_device *pdev)
|
||||
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)
|
||||
{
|
||||
/* 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 = {
|
||||
.suspend = fsi_suspend,
|
||||
.resume = fsi_resume,
|
||||
.runtime_suspend = fsi_runtime_nop,
|
||||
.runtime_resume = fsi_runtime_nop,
|
||||
};
|
||||
|
@ -20,40 +20,28 @@
|
||||
|
||||
#include <trace/events/asoc.h>
|
||||
|
||||
static unsigned int snd_soc_4_12_read(struct snd_soc_codec *codec,
|
||||
unsigned int reg)
|
||||
#ifdef CONFIG_SPI_MASTER
|
||||
static int do_spi_write(void *control, const char *data, int len)
|
||||
{
|
||||
struct spi_device *spi = control;
|
||||
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);
|
||||
ret = spi_write(spi, data, len);
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
return val;
|
||||
return ret;
|
||||
|
||||
return len;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int snd_soc_4_12_write(struct snd_soc_codec *codec, unsigned int reg,
|
||||
unsigned int value)
|
||||
static int do_hw_write(struct snd_soc_codec *codec, unsigned int reg,
|
||||
unsigned int value, const void *data, int len)
|
||||
{
|
||||
u8 data[2];
|
||||
int ret;
|
||||
|
||||
data[0] = (reg << 4) | ((value >> 8) & 0x000f);
|
||||
data[1] = value & 0x00ff;
|
||||
|
||||
if (!snd_soc_codec_volatile_register(codec, reg) &&
|
||||
reg < codec->driver->reg_cache_size &&
|
||||
!codec->cache_bypass) {
|
||||
reg < codec->driver->reg_cache_size &&
|
||||
!codec->cache_bypass) {
|
||||
ret = snd_soc_cache_write(codec, reg, value);
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
@ -64,8 +52,8 @@ static int snd_soc_4_12_write(struct snd_soc_codec *codec, unsigned int reg,
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = codec->hw_write(codec->control_data, data, 2);
|
||||
if (ret == 2)
|
||||
ret = codec->hw_write(codec->control_data, data, len);
|
||||
if (ret == len)
|
||||
return 0;
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
@ -73,232 +61,7 @@ static int snd_soc_4_12_write(struct snd_soc_codec *codec, unsigned int reg,
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_SPI_MASTER)
|
||||
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)
|
||||
static unsigned int do_hw_read(struct snd_soc_codec *codec, unsigned int reg)
|
||||
{
|
||||
int ret;
|
||||
unsigned int val;
|
||||
@ -319,65 +82,117 @@ static unsigned int snd_soc_8_16_read(struct snd_soc_codec *codec,
|
||||
return val;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_SPI_MASTER)
|
||||
static int snd_soc_8_16_spi_write(void *control_data, const char *data,
|
||||
int len)
|
||||
static unsigned int snd_soc_4_12_read(struct snd_soc_codec *codec,
|
||||
unsigned int reg)
|
||||
{
|
||||
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;
|
||||
return do_hw_read(codec, reg);
|
||||
}
|
||||
|
||||
static int snd_soc_4_12_write(struct snd_soc_codec *codec, unsigned int reg,
|
||||
unsigned int value)
|
||||
{
|
||||
u16 data;
|
||||
|
||||
data = cpu_to_be16((reg << 12) | (value & 0xffffff));
|
||||
|
||||
return do_hw_write(codec, reg, value, &data, 2);
|
||||
}
|
||||
|
||||
static unsigned int snd_soc_7_9_read(struct snd_soc_codec *codec,
|
||||
unsigned int reg)
|
||||
{
|
||||
return do_hw_read(codec, reg);
|
||||
}
|
||||
|
||||
static int snd_soc_7_9_write(struct snd_soc_codec *codec, unsigned int reg,
|
||||
unsigned int value)
|
||||
{
|
||||
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))
|
||||
static unsigned int snd_soc_8_8_read_i2c(struct snd_soc_codec *codec,
|
||||
unsigned int r)
|
||||
static unsigned int do_i2c_read(struct snd_soc_codec *codec,
|
||||
void *reg, int reglen,
|
||||
void *data, int datalen)
|
||||
{
|
||||
struct i2c_msg xfer[2];
|
||||
u8 reg = r;
|
||||
u8 data;
|
||||
int ret;
|
||||
struct i2c_client *client = codec->control_data;
|
||||
|
||||
/* Write register */
|
||||
xfer[0].addr = client->addr;
|
||||
xfer[0].flags = 0;
|
||||
xfer[0].len = 1;
|
||||
xfer[0].buf = ®
|
||||
xfer[0].len = reglen;
|
||||
xfer[0].buf = reg;
|
||||
|
||||
/* Read data */
|
||||
xfer[1].addr = client->addr;
|
||||
xfer[1].flags = I2C_M_RD;
|
||||
xfer[1].len = 1;
|
||||
xfer[1].buf = &data;
|
||||
xfer[1].len = datalen;
|
||||
xfer[1].buf = data;
|
||||
|
||||
ret = i2c_transfer(client->adapter, xfer, 2);
|
||||
if (ret != 2) {
|
||||
dev_err(&client->dev, "i2c_transfer() returned %d\n", ret);
|
||||
if (ret == 2)
|
||||
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, ®, 1, &data, 1);
|
||||
if (ret < 0)
|
||||
return 0;
|
||||
return data;
|
||||
}
|
||||
#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,
|
||||
unsigned int r)
|
||||
{
|
||||
struct i2c_msg xfer[2];
|
||||
u8 reg = r;
|
||||
u16 data;
|
||||
int ret;
|
||||
struct i2c_client *client = codec->control_data;
|
||||
|
||||
/* Write register */
|
||||
xfer[0].addr = client->addr;
|
||||
xfer[0].flags = 0;
|
||||
xfer[0].len = 1;
|
||||
xfer[0].buf = ®
|
||||
|
||||
/* 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);
|
||||
ret = do_i2c_read(codec, ®, 1, &data, 2);
|
||||
if (ret < 0)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (data >> 8) | ((data & 0xff) << 8);
|
||||
}
|
||||
#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,
|
||||
unsigned int r)
|
||||
{
|
||||
struct i2c_msg xfer[2];
|
||||
u16 reg = r;
|
||||
u8 data;
|
||||
int ret;
|
||||
struct i2c_client *client = codec->control_data;
|
||||
|
||||
/* Write register */
|
||||
xfer[0].addr = client->addr;
|
||||
xfer[0].flags = 0;
|
||||
xfer[0].len = 2;
|
||||
xfer[0].buf = (u8 *)®
|
||||
|
||||
/* 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);
|
||||
ret = do_i2c_read(codec, ®, 2, &data, 1);
|
||||
if (ret < 0)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
#else
|
||||
@ -453,120 +234,34 @@ static unsigned int snd_soc_16_8_read_i2c(struct snd_soc_codec *codec,
|
||||
#endif
|
||||
|
||||
static unsigned int snd_soc_16_8_read(struct snd_soc_codec *codec,
|
||||
unsigned int reg)
|
||||
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;
|
||||
return do_hw_read(codec, 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];
|
||||
int ret;
|
||||
|
||||
data[0] = (reg >> 8) & 0xff;
|
||||
data[1] = reg & 0xff;
|
||||
data[2] = value;
|
||||
|
||||
reg &= 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;
|
||||
}
|
||||
|
||||
ret = codec->hw_write(codec->control_data, data, 3);
|
||||
if (ret == 3)
|
||||
return 0;
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
else
|
||||
return -EIO;
|
||||
return do_hw_write(codec, reg, value, data, 3);
|
||||
}
|
||||
|
||||
#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))
|
||||
static unsigned int snd_soc_16_16_read_i2c(struct snd_soc_codec *codec,
|
||||
unsigned int r)
|
||||
{
|
||||
struct i2c_msg xfer[2];
|
||||
u16 reg = cpu_to_be16(r);
|
||||
u16 data;
|
||||
int ret;
|
||||
struct i2c_client *client = codec->control_data;
|
||||
|
||||
/* Write register */
|
||||
xfer[0].addr = client->addr;
|
||||
xfer[0].flags = 0;
|
||||
xfer[0].len = 2;
|
||||
xfer[0].buf = (u8 *)®
|
||||
|
||||
/* 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);
|
||||
ret = do_i2c_read(codec, ®, 2, &data, 2);
|
||||
if (ret < 0)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return be16_to_cpu(data);
|
||||
}
|
||||
#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,
|
||||
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;
|
||||
return do_hw_read(codec, reg);
|
||||
}
|
||||
|
||||
static int snd_soc_16_16_write(struct snd_soc_codec *codec, unsigned int reg,
|
||||
unsigned int value)
|
||||
{
|
||||
u8 data[4];
|
||||
int ret;
|
||||
|
||||
data[0] = (reg >> 8) & 0xff;
|
||||
data[1] = reg & 0xff;
|
||||
data[2] = (value >> 8) & 0xff;
|
||||
data[3] = 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;
|
||||
return do_hw_write(codec, reg, value, data, 4);
|
||||
}
|
||||
|
||||
/* Primitive bulk write support for soc-cache. The data pointed to by
|
||||
* `data' needs to already be in the form the hardware expects
|
||||
* 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) {
|
||||
codec->cache_sync = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = codec->hw_write(codec->control_data, data, 4);
|
||||
if (ret == 4)
|
||||
if (ret == len)
|
||||
return 0;
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
@ -629,79 +331,40 @@ static int snd_soc_16_16_write(struct snd_soc_codec *codec, unsigned int reg,
|
||||
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 {
|
||||
int addr_bits;
|
||||
int data_bits;
|
||||
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 (*i2c_read)(struct snd_soc_codec *, unsigned int);
|
||||
} io_types[] = {
|
||||
{
|
||||
.addr_bits = 4, .data_bits = 12,
|
||||
.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,
|
||||
.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,
|
||||
.write = snd_soc_8_8_write, .read = snd_soc_8_8_read,
|
||||
.i2c_read = snd_soc_8_8_read_i2c,
|
||||
.spi_write = snd_soc_8_8_spi_write,
|
||||
},
|
||||
{
|
||||
.addr_bits = 8, .data_bits = 16,
|
||||
.write = snd_soc_8_16_write, .read = snd_soc_8_16_read,
|
||||
.i2c_read = snd_soc_8_16_read_i2c,
|
||||
.spi_write = snd_soc_8_16_spi_write,
|
||||
},
|
||||
{
|
||||
.addr_bits = 16, .data_bits = 8,
|
||||
.write = snd_soc_16_8_write, .read = snd_soc_16_8_read,
|
||||
.i2c_read = snd_soc_16_8_read_i2c,
|
||||
.spi_write = snd_soc_16_8_spi_write,
|
||||
},
|
||||
{
|
||||
.addr_bits = 16, .data_bits = 16,
|
||||
.write = snd_soc_16_16_write, .read = snd_soc_16_16_read,
|
||||
.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.
|
||||
*
|
||||
* @codec: CODEC to configure.
|
||||
* @type: Type of cache.
|
||||
* @addr_bits: Number of bits of register address data.
|
||||
* @data_bits: Number of bits of data per register.
|
||||
* @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->read = io_types[i].read;
|
||||
codec->bulk_write_raw = snd_soc_hw_bulk_write_raw;
|
||||
|
||||
switch (control) {
|
||||
case SND_SOC_CUSTOM:
|
||||
@ -762,8 +425,9 @@ int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec,
|
||||
break;
|
||||
|
||||
case SND_SOC_SPI:
|
||||
if (io_types[i].spi_write)
|
||||
codec->hw_write = io_types[i].spi_write;
|
||||
#ifdef CONFIG_SPI_MASTER
|
||||
codec->hw_write = do_spi_write;
|
||||
#endif
|
||||
|
||||
codec->control_data = container_of(codec->dev,
|
||||
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);
|
||||
if (rbnode->value == rbnode->defval)
|
||||
continue;
|
||||
WARN_ON(codec->writable_register &&
|
||||
codec->writable_register(codec, rbnode->reg));
|
||||
ret = snd_soc_cache_read(codec, rbnode->reg, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
@ -1149,6 +815,8 @@ static int snd_soc_lzo_cache_sync(struct snd_soc_codec *codec)
|
||||
|
||||
lzo_blocks = codec->reg_cache;
|
||||
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);
|
||||
if (ret)
|
||||
return ret;
|
||||
@ -1407,6 +1075,8 @@ static int snd_soc_flat_cache_sync(struct snd_soc_codec *codec)
|
||||
|
||||
codec_drv = codec->driver;
|
||||
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);
|
||||
if (ret)
|
||||
return ret;
|
||||
@ -1523,7 +1193,7 @@ int snd_soc_cache_init(struct snd_soc_codec *codec)
|
||||
codec->cache_ops->name, codec->name);
|
||||
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);
|
||||
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);
|
||||
return -EINVAL;
|
||||
return -ENOSYS;
|
||||
}
|
||||
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);
|
||||
return -EINVAL;
|
||||
return -ENOSYS;
|
||||
}
|
||||
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)
|
||||
return -EINVAL;
|
||||
return -ENOSYS;
|
||||
|
||||
if (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;
|
||||
}
|
||||
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
Loading…
x
Reference in New Issue
Block a user