Merge branch 'topic/asoc' into to-push
This commit is contained in:
commit
5c8261e44e
139 changed files with 12688 additions and 4423 deletions
|
@ -9,7 +9,7 @@ the audio subsystem with the kernel as a platform device and is represented by
|
|||
the following struct:-
|
||||
|
||||
/* SoC machine */
|
||||
struct snd_soc_machine {
|
||||
struct snd_soc_card {
|
||||
char *name;
|
||||
|
||||
int (*probe)(struct platform_device *pdev);
|
||||
|
@ -67,10 +67,10 @@ static struct snd_soc_dai_link corgi_dai = {
|
|||
.ops = &corgi_ops,
|
||||
};
|
||||
|
||||
struct snd_soc_machine then sets up the machine with it's DAIs. e.g.
|
||||
struct snd_soc_card then sets up the machine with it's DAIs. e.g.
|
||||
|
||||
/* corgi audio machine driver */
|
||||
static struct snd_soc_machine snd_soc_machine_corgi = {
|
||||
static struct snd_soc_card snd_soc_corgi = {
|
||||
.name = "Corgi",
|
||||
.dai_link = &corgi_dai,
|
||||
.num_links = 1,
|
||||
|
@ -90,7 +90,7 @@ static struct wm8731_setup_data corgi_wm8731_setup = {
|
|||
|
||||
/* corgi audio subsystem */
|
||||
static struct snd_soc_device corgi_snd_devdata = {
|
||||
.machine = &snd_soc_machine_corgi,
|
||||
.machine = &snd_soc_corgi,
|
||||
.platform = &pxa2xx_soc_platform,
|
||||
.codec_dev = &soc_codec_dev_wm8731,
|
||||
.codec_data = &corgi_wm8731_setup,
|
||||
|
|
|
@ -3977,7 +3977,7 @@ M: tiwai@suse.de
|
|||
L: alsa-devel@alsa-project.org (subscribers-only)
|
||||
S: Maintained
|
||||
|
||||
SOUND - SOC LAYER / DYNAMIC AUDIO POWER MANAGEMENT
|
||||
SOUND - SOC LAYER / DYNAMIC AUDIO POWER MANAGEMENT (ASoC)
|
||||
P: Liam Girdwood
|
||||
M: lrg@slimlogic.co.uk
|
||||
P: Mark Brown
|
||||
|
|
13
arch/arm/mach-pxa/include/mach/palmasoc.h
Normal file
13
arch/arm/mach-pxa/include/mach/palmasoc.h
Normal file
|
@ -0,0 +1,13 @@
|
|||
#ifndef _INCLUDE_PALMASOC_H_
|
||||
#define _INCLUDE_PALMASOC_H_
|
||||
struct palm27x_asoc_info {
|
||||
int jack_gpio;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_SND_PXA2XX_SOC_PALM27X
|
||||
void __init palm27x_asoc_set_pdata(struct palm27x_asoc_info *data);
|
||||
#else
|
||||
static inline void palm27x_asoc_set_pdata(struct palm27x_asoc_info *data) {}
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* audio.h -- Audio Driver for Wolfson WM8350 PMIC
|
||||
*
|
||||
* Copyright 2007 Wolfson Microelectronics PLC
|
||||
* Copyright 2007, 2008 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
|
||||
|
@ -70,9 +70,9 @@
|
|||
#define WM8350_CODEC_ISEL_0_5 3 /* x0.5 */
|
||||
|
||||
#define WM8350_VMID_OFF 0
|
||||
#define WM8350_VMID_500K 1
|
||||
#define WM8350_VMID_100K 2
|
||||
#define WM8350_VMID_10K 3
|
||||
#define WM8350_VMID_300K 1
|
||||
#define WM8350_VMID_50K 2
|
||||
#define WM8350_VMID_5K 3
|
||||
|
||||
/*
|
||||
* R40 (0x28) - Clock Control 1
|
||||
|
@ -591,8 +591,38 @@
|
|||
#define WM8350_IRQ_CODEC_MICSCD 41
|
||||
#define WM8350_IRQ_CODEC_MICD 42
|
||||
|
||||
/*
|
||||
* WM8350 Platform data.
|
||||
*
|
||||
* This must be initialised per platform for best audio performance.
|
||||
* Please see WM8350 datasheet for information.
|
||||
*/
|
||||
struct wm8350_audio_platform_data {
|
||||
int vmid_discharge_msecs; /* VMID --> OFF discharge time */
|
||||
int drain_msecs; /* OFF drain time */
|
||||
int cap_discharge_msecs; /* Cap ON (from OFF) discharge time */
|
||||
int vmid_charge_msecs; /* vmid power up time */
|
||||
u32 vmid_s_curve:2; /* vmid enable s curve speed */
|
||||
u32 dis_out4:2; /* out4 discharge speed */
|
||||
u32 dis_out3:2; /* out3 discharge speed */
|
||||
u32 dis_out2:2; /* out2 discharge speed */
|
||||
u32 dis_out1:2; /* out1 discharge speed */
|
||||
u32 vroi_out4:1; /* out4 tie off */
|
||||
u32 vroi_out3:1; /* out3 tie off */
|
||||
u32 vroi_out2:1; /* out2 tie off */
|
||||
u32 vroi_out1:1; /* out1 tie off */
|
||||
u32 vroi_enable:1; /* enable tie off */
|
||||
u32 codec_current_on:2; /* current level ON */
|
||||
u32 codec_current_standby:2; /* current level STANDBY */
|
||||
u32 codec_current_charge:2; /* codec current @ vmid charge */
|
||||
};
|
||||
|
||||
struct snd_soc_codec;
|
||||
|
||||
struct wm8350_codec {
|
||||
struct platform_device *pdev;
|
||||
struct snd_soc_codec *codec;
|
||||
struct wm8350_audio_platform_data *platform_data;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
18
include/sound/l3.h
Normal file
18
include/sound/l3.h
Normal file
|
@ -0,0 +1,18 @@
|
|||
#ifndef _L3_H_
|
||||
#define _L3_H_ 1
|
||||
|
||||
struct l3_pins {
|
||||
void (*setdat)(int);
|
||||
void (*setclk)(int);
|
||||
void (*setmode)(int);
|
||||
int data_hold;
|
||||
int data_setup;
|
||||
int clock_high;
|
||||
int mode_hold;
|
||||
int mode;
|
||||
int mode_setup;
|
||||
};
|
||||
|
||||
int l3_write(struct l3_pins *adap, u8 addr, u8 *data, int len);
|
||||
|
||||
#endif
|
14
include/sound/s3c24xx_uda134x.h
Normal file
14
include/sound/s3c24xx_uda134x.h
Normal file
|
@ -0,0 +1,14 @@
|
|||
#ifndef _S3C24XX_UDA134X_H_
|
||||
#define _S3C24XX_UDA134X_H_ 1
|
||||
|
||||
#include <sound/uda134x.h>
|
||||
|
||||
struct s3c24xx_uda134x_platform_data {
|
||||
int l3_clk;
|
||||
int l3_mode;
|
||||
int l3_data;
|
||||
void (*power) (int);
|
||||
int model;
|
||||
};
|
||||
|
||||
#endif
|
231
include/sound/soc-dai.h
Normal file
231
include/sound/soc-dai.h
Normal file
|
@ -0,0 +1,231 @@
|
|||
/*
|
||||
* linux/sound/soc-dai.h -- ALSA SoC Layer
|
||||
*
|
||||
* Copyright: 2005-2008 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.
|
||||
*
|
||||
* Digital Audio Interface (DAI) API.
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_SND_SOC_DAI_H
|
||||
#define __LINUX_SND_SOC_DAI_H
|
||||
|
||||
|
||||
#include <linux/list.h>
|
||||
|
||||
struct snd_pcm_substream;
|
||||
|
||||
/*
|
||||
* DAI hardware audio formats.
|
||||
*
|
||||
* Describes the physical PCM data formating and clocking. Add new formats
|
||||
* to the end.
|
||||
*/
|
||||
#define SND_SOC_DAIFMT_I2S 0 /* I2S mode */
|
||||
#define SND_SOC_DAIFMT_RIGHT_J 1 /* Right Justified mode */
|
||||
#define SND_SOC_DAIFMT_LEFT_J 2 /* Left Justified mode */
|
||||
#define SND_SOC_DAIFMT_DSP_A 3 /* L data msb after FRM LRC */
|
||||
#define SND_SOC_DAIFMT_DSP_B 4 /* L data msb during FRM LRC */
|
||||
#define SND_SOC_DAIFMT_AC97 5 /* AC97 */
|
||||
|
||||
/* left and right justified also known as MSB and LSB respectively */
|
||||
#define SND_SOC_DAIFMT_MSB SND_SOC_DAIFMT_LEFT_J
|
||||
#define SND_SOC_DAIFMT_LSB SND_SOC_DAIFMT_RIGHT_J
|
||||
|
||||
/*
|
||||
* DAI Clock gating.
|
||||
*
|
||||
* DAI bit clocks can be be gated (disabled) when not the DAI is not
|
||||
* sending or receiving PCM data in a frame. This can be used to save power.
|
||||
*/
|
||||
#define SND_SOC_DAIFMT_CONT (0 << 4) /* continuous clock */
|
||||
#define SND_SOC_DAIFMT_GATED (1 << 4) /* clock is gated */
|
||||
|
||||
/*
|
||||
* DAI Left/Right Clocks.
|
||||
*
|
||||
* Specifies whether the DAI can support different samples for similtanious
|
||||
* playback and capture. This usually requires a seperate physical frame
|
||||
* clock for playback and capture.
|
||||
*/
|
||||
#define SND_SOC_DAIFMT_SYNC (0 << 5) /* Tx FRM = Rx FRM */
|
||||
#define SND_SOC_DAIFMT_ASYNC (1 << 5) /* Tx FRM ~ Rx FRM */
|
||||
|
||||
/*
|
||||
* TDM
|
||||
*
|
||||
* Time Division Multiplexing. Allows PCM data to be multplexed with other
|
||||
* data on the DAI.
|
||||
*/
|
||||
#define SND_SOC_DAIFMT_TDM (1 << 6)
|
||||
|
||||
/*
|
||||
* DAI hardware signal inversions.
|
||||
*
|
||||
* Specifies whether the DAI can also support inverted clocks for the specified
|
||||
* format.
|
||||
*/
|
||||
#define SND_SOC_DAIFMT_NB_NF (0 << 8) /* normal bit clock + frame */
|
||||
#define SND_SOC_DAIFMT_NB_IF (1 << 8) /* normal bclk + inv frm */
|
||||
#define SND_SOC_DAIFMT_IB_NF (2 << 8) /* invert bclk + nor frm */
|
||||
#define SND_SOC_DAIFMT_IB_IF (3 << 8) /* invert bclk + frm */
|
||||
|
||||
/*
|
||||
* DAI hardware clock masters.
|
||||
*
|
||||
* This is wrt the codec, the inverse is true for the interface
|
||||
* i.e. if the codec is clk and frm master then the interface is
|
||||
* clk and frame slave.
|
||||
*/
|
||||
#define SND_SOC_DAIFMT_CBM_CFM (0 << 12) /* codec clk & frm master */
|
||||
#define SND_SOC_DAIFMT_CBS_CFM (1 << 12) /* codec clk slave & frm master */
|
||||
#define SND_SOC_DAIFMT_CBM_CFS (2 << 12) /* codec clk master & frame slave */
|
||||
#define SND_SOC_DAIFMT_CBS_CFS (3 << 12) /* codec clk & frm slave */
|
||||
|
||||
#define SND_SOC_DAIFMT_FORMAT_MASK 0x000f
|
||||
#define SND_SOC_DAIFMT_CLOCK_MASK 0x00f0
|
||||
#define SND_SOC_DAIFMT_INV_MASK 0x0f00
|
||||
#define SND_SOC_DAIFMT_MASTER_MASK 0xf000
|
||||
|
||||
/*
|
||||
* Master Clock Directions
|
||||
*/
|
||||
#define SND_SOC_CLOCK_IN 0
|
||||
#define SND_SOC_CLOCK_OUT 1
|
||||
|
||||
struct snd_soc_dai_ops;
|
||||
struct snd_soc_dai;
|
||||
struct snd_ac97_bus_ops;
|
||||
|
||||
/* Digital Audio Interface registration */
|
||||
int snd_soc_register_dai(struct snd_soc_dai *dai);
|
||||
void snd_soc_unregister_dai(struct snd_soc_dai *dai);
|
||||
int snd_soc_register_dais(struct snd_soc_dai *dai, size_t count);
|
||||
void snd_soc_unregister_dais(struct snd_soc_dai *dai, size_t count);
|
||||
|
||||
/* Digital Audio Interface clocking API.*/
|
||||
int snd_soc_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id,
|
||||
unsigned int freq, int dir);
|
||||
|
||||
int snd_soc_dai_set_clkdiv(struct snd_soc_dai *dai,
|
||||
int div_id, int div);
|
||||
|
||||
int snd_soc_dai_set_pll(struct snd_soc_dai *dai,
|
||||
int pll_id, unsigned int freq_in, unsigned int freq_out);
|
||||
|
||||
/* Digital Audio interface formatting */
|
||||
int snd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt);
|
||||
|
||||
int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai,
|
||||
unsigned int mask, int slots);
|
||||
|
||||
int snd_soc_dai_set_tristate(struct snd_soc_dai *dai, int tristate);
|
||||
|
||||
/* Digital Audio Interface mute */
|
||||
int snd_soc_dai_digital_mute(struct snd_soc_dai *dai, int mute);
|
||||
|
||||
/*
|
||||
* Digital Audio Interface.
|
||||
*
|
||||
* Describes the Digital Audio Interface in terms of it's ALSA, DAI and AC97
|
||||
* operations an capabilities. Codec and platfom drivers will register a this
|
||||
* structure for every DAI they have.
|
||||
*
|
||||
* This structure covers the clocking, formating and ALSA operations for each
|
||||
* interface a
|
||||
*/
|
||||
struct snd_soc_dai_ops {
|
||||
/*
|
||||
* DAI clocking configuration, all optional.
|
||||
* Called by soc_card drivers, normally in their hw_params.
|
||||
*/
|
||||
int (*set_sysclk)(struct snd_soc_dai *dai,
|
||||
int clk_id, unsigned int freq, int dir);
|
||||
int (*set_pll)(struct snd_soc_dai *dai,
|
||||
int pll_id, unsigned int freq_in, unsigned int freq_out);
|
||||
int (*set_clkdiv)(struct snd_soc_dai *dai, int div_id, int div);
|
||||
|
||||
/*
|
||||
* DAI format configuration
|
||||
* Called by soc_card drivers, normally in their hw_params.
|
||||
*/
|
||||
int (*set_fmt)(struct snd_soc_dai *dai, unsigned int fmt);
|
||||
int (*set_tdm_slot)(struct snd_soc_dai *dai,
|
||||
unsigned int mask, int slots);
|
||||
int (*set_tristate)(struct snd_soc_dai *dai, int tristate);
|
||||
|
||||
/*
|
||||
* DAI digital mute - optional.
|
||||
* Called by soc-core to minimise any pops.
|
||||
*/
|
||||
int (*digital_mute)(struct snd_soc_dai *dai, int mute);
|
||||
|
||||
/*
|
||||
* ALSA PCM audio operations - all optional.
|
||||
* Called by soc-core during audio PCM operations.
|
||||
*/
|
||||
int (*startup)(struct snd_pcm_substream *,
|
||||
struct snd_soc_dai *);
|
||||
void (*shutdown)(struct snd_pcm_substream *,
|
||||
struct snd_soc_dai *);
|
||||
int (*hw_params)(struct snd_pcm_substream *,
|
||||
struct snd_pcm_hw_params *, struct snd_soc_dai *);
|
||||
int (*hw_free)(struct snd_pcm_substream *,
|
||||
struct snd_soc_dai *);
|
||||
int (*prepare)(struct snd_pcm_substream *,
|
||||
struct snd_soc_dai *);
|
||||
int (*trigger)(struct snd_pcm_substream *, int,
|
||||
struct snd_soc_dai *);
|
||||
};
|
||||
|
||||
/*
|
||||
* Digital Audio Interface runtime data.
|
||||
*
|
||||
* Holds runtime data for a DAI.
|
||||
*/
|
||||
struct snd_soc_dai {
|
||||
/* DAI description */
|
||||
char *name;
|
||||
unsigned int id;
|
||||
int ac97_control;
|
||||
|
||||
struct device *dev;
|
||||
|
||||
/* DAI callbacks */
|
||||
int (*probe)(struct platform_device *pdev,
|
||||
struct snd_soc_dai *dai);
|
||||
void (*remove)(struct platform_device *pdev,
|
||||
struct snd_soc_dai *dai);
|
||||
int (*suspend)(struct snd_soc_dai *dai);
|
||||
int (*resume)(struct snd_soc_dai *dai);
|
||||
|
||||
/* ops */
|
||||
struct snd_soc_dai_ops ops;
|
||||
|
||||
/* DAI capabilities */
|
||||
struct snd_soc_pcm_stream capture;
|
||||
struct snd_soc_pcm_stream playback;
|
||||
|
||||
/* DAI runtime info */
|
||||
struct snd_pcm_runtime *runtime;
|
||||
struct snd_soc_codec *codec;
|
||||
unsigned int active;
|
||||
unsigned char pop_wait:1;
|
||||
void *dma_data;
|
||||
|
||||
/* DAI private data */
|
||||
void *private_data;
|
||||
|
||||
/* parent codec/platform */
|
||||
union {
|
||||
struct snd_soc_codec *codec;
|
||||
struct snd_soc_platform *platform;
|
||||
};
|
||||
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -221,8 +221,6 @@ int snd_soc_dapm_new_controls(struct snd_soc_codec *codec,
|
|||
int num);
|
||||
|
||||
/* dapm path setup */
|
||||
int __deprecated snd_soc_dapm_connect_input(struct snd_soc_codec *codec,
|
||||
const char *sink_name, const char *control_name, const char *src_name);
|
||||
int snd_soc_dapm_new_widgets(struct snd_soc_codec *codec);
|
||||
void snd_soc_dapm_free(struct snd_soc_device *socdev);
|
||||
int snd_soc_dapm_add_routes(struct snd_soc_codec *codec,
|
||||
|
|
|
@ -21,8 +21,6 @@
|
|||
#include <sound/control.h>
|
||||
#include <sound/ac97_codec.h>
|
||||
|
||||
#define SND_SOC_VERSION "0.13.2"
|
||||
|
||||
/*
|
||||
* Convenience kcontrol builders
|
||||
*/
|
||||
|
@ -145,105 +143,31 @@ enum snd_soc_bias_level {
|
|||
SND_SOC_BIAS_OFF,
|
||||
};
|
||||
|
||||
/*
|
||||
* Digital Audio Interface (DAI) types
|
||||
*/
|
||||
#define SND_SOC_DAI_AC97 0x1
|
||||
#define SND_SOC_DAI_I2S 0x2
|
||||
#define SND_SOC_DAI_PCM 0x4
|
||||
#define SND_SOC_DAI_AC97_BUS 0x8 /* for custom i.e. non ac97_codec.c */
|
||||
|
||||
/*
|
||||
* DAI hardware audio formats
|
||||
*/
|
||||
#define SND_SOC_DAIFMT_I2S 0 /* I2S mode */
|
||||
#define SND_SOC_DAIFMT_RIGHT_J 1 /* Right justified mode */
|
||||
#define SND_SOC_DAIFMT_LEFT_J 2 /* Left Justified mode */
|
||||
#define SND_SOC_DAIFMT_DSP_A 3 /* L data msb after FRM or LRC */
|
||||
#define SND_SOC_DAIFMT_DSP_B 4 /* L data msb during FRM or LRC */
|
||||
#define SND_SOC_DAIFMT_AC97 5 /* AC97 */
|
||||
|
||||
#define SND_SOC_DAIFMT_MSB SND_SOC_DAIFMT_LEFT_J
|
||||
#define SND_SOC_DAIFMT_LSB SND_SOC_DAIFMT_RIGHT_J
|
||||
|
||||
/*
|
||||
* DAI Gating
|
||||
*/
|
||||
#define SND_SOC_DAIFMT_CONT (0 << 4) /* continuous clock */
|
||||
#define SND_SOC_DAIFMT_GATED (1 << 4) /* clock is gated when not Tx/Rx */
|
||||
|
||||
/*
|
||||
* DAI Sync
|
||||
* Synchronous LR (Left Right) clocks and Frame signals.
|
||||
*/
|
||||
#define SND_SOC_DAIFMT_SYNC (0 << 5) /* Tx FRM = Rx FRM */
|
||||
#define SND_SOC_DAIFMT_ASYNC (1 << 5) /* Tx FRM ~ Rx FRM */
|
||||
|
||||
/*
|
||||
* TDM
|
||||
*/
|
||||
#define SND_SOC_DAIFMT_TDM (1 << 6)
|
||||
|
||||
/*
|
||||
* DAI hardware signal inversions
|
||||
*/
|
||||
#define SND_SOC_DAIFMT_NB_NF (0 << 8) /* normal bclk + frm */
|
||||
#define SND_SOC_DAIFMT_NB_IF (1 << 8) /* normal bclk + inv frm */
|
||||
#define SND_SOC_DAIFMT_IB_NF (2 << 8) /* invert bclk + nor frm */
|
||||
#define SND_SOC_DAIFMT_IB_IF (3 << 8) /* invert bclk + frm */
|
||||
|
||||
/*
|
||||
* DAI hardware clock masters
|
||||
* This is wrt the codec, the inverse is true for the interface
|
||||
* i.e. if the codec is clk and frm master then the interface is
|
||||
* clk and frame slave.
|
||||
*/
|
||||
#define SND_SOC_DAIFMT_CBM_CFM (0 << 12) /* codec clk & frm master */
|
||||
#define SND_SOC_DAIFMT_CBS_CFM (1 << 12) /* codec clk slave & frm master */
|
||||
#define SND_SOC_DAIFMT_CBM_CFS (2 << 12) /* codec clk master & frame slave */
|
||||
#define SND_SOC_DAIFMT_CBS_CFS (3 << 12) /* codec clk & frm slave */
|
||||
|
||||
#define SND_SOC_DAIFMT_FORMAT_MASK 0x000f
|
||||
#define SND_SOC_DAIFMT_CLOCK_MASK 0x00f0
|
||||
#define SND_SOC_DAIFMT_INV_MASK 0x0f00
|
||||
#define SND_SOC_DAIFMT_MASTER_MASK 0xf000
|
||||
|
||||
|
||||
/*
|
||||
* Master Clock Directions
|
||||
*/
|
||||
#define SND_SOC_CLOCK_IN 0
|
||||
#define SND_SOC_CLOCK_OUT 1
|
||||
|
||||
/*
|
||||
* AC97 codec ID's bitmask
|
||||
*/
|
||||
#define SND_SOC_DAI_AC97_ID0 (1 << 0)
|
||||
#define SND_SOC_DAI_AC97_ID1 (1 << 1)
|
||||
#define SND_SOC_DAI_AC97_ID2 (1 << 2)
|
||||
#define SND_SOC_DAI_AC97_ID3 (1 << 3)
|
||||
|
||||
struct snd_soc_device;
|
||||
struct snd_soc_pcm_stream;
|
||||
struct snd_soc_ops;
|
||||
struct snd_soc_dai_mode;
|
||||
struct snd_soc_pcm_runtime;
|
||||
struct snd_soc_dai;
|
||||
struct snd_soc_platform;
|
||||
struct snd_soc_codec;
|
||||
struct snd_soc_machine_config;
|
||||
struct soc_enum;
|
||||
struct snd_soc_ac97_ops;
|
||||
struct snd_soc_clock_info;
|
||||
|
||||
typedef int (*hw_write_t)(void *,const char* ,int);
|
||||
typedef int (*hw_read_t)(void *,char* ,int);
|
||||
|
||||
extern struct snd_ac97_bus_ops soc_ac97_ops;
|
||||
|
||||
int snd_soc_register_platform(struct snd_soc_platform *platform);
|
||||
void snd_soc_unregister_platform(struct snd_soc_platform *platform);
|
||||
int snd_soc_register_codec(struct snd_soc_codec *codec);
|
||||
void snd_soc_unregister_codec(struct snd_soc_codec *codec);
|
||||
|
||||
/* pcm <-> DAI connect */
|
||||
void snd_soc_free_pcms(struct snd_soc_device *socdev);
|
||||
int snd_soc_new_pcms(struct snd_soc_device *socdev, int idx, const char *xid);
|
||||
int snd_soc_register_card(struct snd_soc_device *socdev);
|
||||
int snd_soc_init_card(struct snd_soc_device *socdev);
|
||||
|
||||
/* set runtime hw params */
|
||||
int snd_soc_set_runtime_hwparams(struct snd_pcm_substream *substream,
|
||||
|
@ -263,27 +187,6 @@ int snd_soc_new_ac97_codec(struct snd_soc_codec *codec,
|
|||
struct snd_ac97_bus_ops *ops, int num);
|
||||
void snd_soc_free_ac97_codec(struct snd_soc_codec *codec);
|
||||
|
||||
/* Digital Audio Interface clocking API.*/
|
||||
int snd_soc_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id,
|
||||
unsigned int freq, int dir);
|
||||
|
||||
int snd_soc_dai_set_clkdiv(struct snd_soc_dai *dai,
|
||||
int div_id, int div);
|
||||
|
||||
int snd_soc_dai_set_pll(struct snd_soc_dai *dai,
|
||||
int pll_id, unsigned int freq_in, unsigned int freq_out);
|
||||
|
||||
/* Digital Audio interface formatting */
|
||||
int snd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt);
|
||||
|
||||
int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai,
|
||||
unsigned int mask, int slots);
|
||||
|
||||
int snd_soc_dai_set_tristate(struct snd_soc_dai *dai, int tristate);
|
||||
|
||||
/* Digital Audio Interface mute */
|
||||
int snd_soc_dai_digital_mute(struct snd_soc_dai *dai, int mute);
|
||||
|
||||
/*
|
||||
*Controls
|
||||
*/
|
||||
|
@ -341,66 +244,14 @@ struct snd_soc_ops {
|
|||
int (*trigger)(struct snd_pcm_substream *, int);
|
||||
};
|
||||
|
||||
/* ASoC DAI ops */
|
||||
struct snd_soc_dai_ops {
|
||||
/* DAI clocking configuration */
|
||||
int (*set_sysclk)(struct snd_soc_dai *dai,
|
||||
int clk_id, unsigned int freq, int dir);
|
||||
int (*set_pll)(struct snd_soc_dai *dai,
|
||||
int pll_id, unsigned int freq_in, unsigned int freq_out);
|
||||
int (*set_clkdiv)(struct snd_soc_dai *dai, int div_id, int div);
|
||||
|
||||
/* DAI format configuration */
|
||||
int (*set_fmt)(struct snd_soc_dai *dai, unsigned int fmt);
|
||||
int (*set_tdm_slot)(struct snd_soc_dai *dai,
|
||||
unsigned int mask, int slots);
|
||||
int (*set_tristate)(struct snd_soc_dai *dai, int tristate);
|
||||
|
||||
/* digital mute */
|
||||
int (*digital_mute)(struct snd_soc_dai *dai, int mute);
|
||||
};
|
||||
|
||||
/* SoC DAI (Digital Audio Interface) */
|
||||
struct snd_soc_dai {
|
||||
/* DAI description */
|
||||
char *name;
|
||||
unsigned int id;
|
||||
unsigned char type;
|
||||
|
||||
/* DAI callbacks */
|
||||
int (*probe)(struct platform_device *pdev,
|
||||
struct snd_soc_dai *dai);
|
||||
void (*remove)(struct platform_device *pdev,
|
||||
struct snd_soc_dai *dai);
|
||||
int (*suspend)(struct platform_device *pdev,
|
||||
struct snd_soc_dai *dai);
|
||||
int (*resume)(struct platform_device *pdev,
|
||||
struct snd_soc_dai *dai);
|
||||
|
||||
/* ops */
|
||||
struct snd_soc_ops ops;
|
||||
struct snd_soc_dai_ops dai_ops;
|
||||
|
||||
/* DAI capabilities */
|
||||
struct snd_soc_pcm_stream capture;
|
||||
struct snd_soc_pcm_stream playback;
|
||||
|
||||
/* DAI runtime info */
|
||||
struct snd_pcm_runtime *runtime;
|
||||
struct snd_soc_codec *codec;
|
||||
unsigned int active;
|
||||
unsigned char pop_wait:1;
|
||||
void *dma_data;
|
||||
|
||||
/* DAI private data */
|
||||
void *private_data;
|
||||
};
|
||||
|
||||
/* SoC Audio Codec */
|
||||
struct snd_soc_codec {
|
||||
char *name;
|
||||
struct module *owner;
|
||||
struct mutex mutex;
|
||||
struct device *dev;
|
||||
|
||||
struct list_head list;
|
||||
|
||||
/* callbacks */
|
||||
int (*set_bias_level)(struct snd_soc_codec *,
|
||||
|
@ -426,6 +277,7 @@ struct snd_soc_codec {
|
|||
short reg_cache_step;
|
||||
|
||||
/* dapm */
|
||||
u32 pop_time;
|
||||
struct list_head dapm_widgets;
|
||||
struct list_head dapm_paths;
|
||||
enum snd_soc_bias_level bias_level;
|
||||
|
@ -435,6 +287,11 @@ struct snd_soc_codec {
|
|||
/* codec DAI's */
|
||||
struct snd_soc_dai *dai;
|
||||
unsigned int num_dai;
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
struct dentry *debugfs_reg;
|
||||
struct dentry *debugfs_pop_time;
|
||||
#endif
|
||||
};
|
||||
|
||||
/* codec device */
|
||||
|
@ -448,13 +305,12 @@ struct snd_soc_codec_device {
|
|||
/* SoC platform interface */
|
||||
struct snd_soc_platform {
|
||||
char *name;
|
||||
struct list_head list;
|
||||
|
||||
int (*probe)(struct platform_device *pdev);
|
||||
int (*remove)(struct platform_device *pdev);
|
||||
int (*suspend)(struct platform_device *pdev,
|
||||
struct snd_soc_dai *dai);
|
||||
int (*resume)(struct platform_device *pdev,
|
||||
struct snd_soc_dai *dai);
|
||||
int (*suspend)(struct snd_soc_dai *dai);
|
||||
int (*resume)(struct snd_soc_dai *dai);
|
||||
|
||||
/* pcm creation and destruction */
|
||||
int (*pcm_new)(struct snd_card *, struct snd_soc_dai *,
|
||||
|
@ -484,9 +340,14 @@ struct snd_soc_dai_link {
|
|||
struct snd_pcm *pcm;
|
||||
};
|
||||
|
||||
/* SoC machine */
|
||||
struct snd_soc_machine {
|
||||
/* SoC card */
|
||||
struct snd_soc_card {
|
||||
char *name;
|
||||
struct device *dev;
|
||||
|
||||
struct list_head list;
|
||||
|
||||
int instantiated;
|
||||
|
||||
int (*probe)(struct platform_device *pdev);
|
||||
int (*remove)(struct platform_device *pdev);
|
||||
|
@ -499,23 +360,26 @@ struct snd_soc_machine {
|
|||
int (*resume_post)(struct platform_device *pdev);
|
||||
|
||||
/* callbacks */
|
||||
int (*set_bias_level)(struct snd_soc_machine *,
|
||||
int (*set_bias_level)(struct snd_soc_card *,
|
||||
enum snd_soc_bias_level level);
|
||||
|
||||
/* CPU <--> Codec DAI links */
|
||||
struct snd_soc_dai_link *dai_link;
|
||||
int num_links;
|
||||
|
||||
struct snd_soc_device *socdev;
|
||||
|
||||
struct snd_soc_platform *platform;
|
||||
struct delayed_work delayed_work;
|
||||
struct work_struct deferred_resume_work;
|
||||
};
|
||||
|
||||
/* SoC Device - the audio subsystem */
|
||||
struct snd_soc_device {
|
||||
struct device *dev;
|
||||
struct snd_soc_machine *machine;
|
||||
struct snd_soc_platform *platform;
|
||||
struct snd_soc_card *card;
|
||||
struct snd_soc_codec *codec;
|
||||
struct snd_soc_codec_device *codec_dev;
|
||||
struct delayed_work delayed_work;
|
||||
struct work_struct deferred_resume_work;
|
||||
void *codec_data;
|
||||
};
|
||||
|
||||
|
@ -542,4 +406,6 @@ struct soc_enum {
|
|||
void *dapm;
|
||||
};
|
||||
|
||||
#include <sound/soc-dai.h>
|
||||
|
||||
#endif
|
||||
|
|
26
include/sound/uda134x.h
Normal file
26
include/sound/uda134x.h
Normal file
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* uda134x.h -- UDA134x ALSA SoC Codec driver
|
||||
*
|
||||
* Copyright 2007 Dension Audio Systems Ltd.
|
||||
* Author: Zoltan Devai
|
||||
*
|
||||
* 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 _UDA134X_H
|
||||
#define _UDA134X_H
|
||||
|
||||
#include <sound/l3.h>
|
||||
|
||||
struct uda134x_platform_data {
|
||||
struct l3_pins l3;
|
||||
void (*power) (int);
|
||||
int model;
|
||||
#define UDA134X_UDA1340 1
|
||||
#define UDA134X_UDA1341 2
|
||||
#define UDA134X_UDA1344 3
|
||||
};
|
||||
|
||||
#endif /* _UDA134X_H */
|
|
@ -22,17 +22,16 @@ if SND_SOC
|
|||
config SND_SOC_AC97_BUS
|
||||
bool
|
||||
|
||||
# All the supported Soc's
|
||||
source "sound/soc/at32/Kconfig"
|
||||
source "sound/soc/at91/Kconfig"
|
||||
# All the supported SoCs
|
||||
source "sound/soc/atmel/Kconfig"
|
||||
source "sound/soc/au1x/Kconfig"
|
||||
source "sound/soc/blackfin/Kconfig"
|
||||
source "sound/soc/davinci/Kconfig"
|
||||
source "sound/soc/fsl/Kconfig"
|
||||
source "sound/soc/omap/Kconfig"
|
||||
source "sound/soc/pxa/Kconfig"
|
||||
source "sound/soc/s3c24xx/Kconfig"
|
||||
source "sound/soc/sh/Kconfig"
|
||||
source "sound/soc/fsl/Kconfig"
|
||||
source "sound/soc/davinci/Kconfig"
|
||||
source "sound/soc/omap/Kconfig"
|
||||
source "sound/soc/blackfin/Kconfig"
|
||||
|
||||
# Supported codecs
|
||||
source "sound/soc/codecs/Kconfig"
|
||||
|
|
|
@ -1,5 +1,13 @@
|
|||
snd-soc-core-objs := soc-core.o soc-dapm.o
|
||||
|
||||
obj-$(CONFIG_SND_SOC) += snd-soc-core.o
|
||||
obj-$(CONFIG_SND_SOC) += codecs/ at32/ at91/ pxa/ s3c24xx/ sh/ fsl/ davinci/
|
||||
obj-$(CONFIG_SND_SOC) += omap/ au1x/ blackfin/
|
||||
obj-$(CONFIG_SND_SOC) += codecs/
|
||||
obj-$(CONFIG_SND_SOC) += atmel/
|
||||
obj-$(CONFIG_SND_SOC) += au1x/
|
||||
obj-$(CONFIG_SND_SOC) += blackfin/
|
||||
obj-$(CONFIG_SND_SOC) += davinci/
|
||||
obj-$(CONFIG_SND_SOC) += fsl/
|
||||
obj-$(CONFIG_SND_SOC) += omap/
|
||||
obj-$(CONFIG_SND_SOC) += pxa/
|
||||
obj-$(CONFIG_SND_SOC) += s3c24xx/
|
||||
obj-$(CONFIG_SND_SOC) += sh/
|
||||
|
|
|
@ -1,34 +0,0 @@
|
|||
config SND_AT32_SOC
|
||||
tristate "SoC Audio for the Atmel AT32 System-on-a-Chip"
|
||||
depends on AVR32 && SND_SOC
|
||||
help
|
||||
Say Y or M if you want to add support for codecs attached to
|
||||
the AT32 SSC interface. You will also need to
|
||||
to select the audio interfaces to support below.
|
||||
|
||||
|
||||
config SND_AT32_SOC_SSC
|
||||
tristate
|
||||
|
||||
|
||||
|
||||
config SND_AT32_SOC_PLAYPAQ
|
||||
tristate "SoC Audio support for PlayPaq with WM8510"
|
||||
depends on SND_AT32_SOC && BOARD_PLAYPAQ
|
||||
select SND_AT32_SOC_SSC
|
||||
select SND_SOC_WM8510
|
||||
help
|
||||
Say Y or M here if you want to add support for SoC audio
|
||||
on the LRS PlayPaq.
|
||||
|
||||
|
||||
|
||||
config SND_AT32_SOC_PLAYPAQ_SLAVE
|
||||
bool "Run CODEC on PlayPaq in slave mode"
|
||||
depends on SND_AT32_SOC_PLAYPAQ
|
||||
default n
|
||||
help
|
||||
Say Y if you want to run with the AT32 SSC generating the BCLK
|
||||
and FRAME signals on the PlayPaq. Unless you want to play
|
||||
with the AT32 as the SSC master, you probably want to say N here,
|
||||
as this will give you better sound quality.
|
|
@ -1,11 +0,0 @@
|
|||
# AT32 Platform Support
|
||||
snd-soc-at32-objs := at32-pcm.o
|
||||
snd-soc-at32-ssc-objs := at32-ssc.o
|
||||
|
||||
obj-$(CONFIG_SND_AT32_SOC) += snd-soc-at32.o
|
||||
obj-$(CONFIG_SND_AT32_SOC_SSC) += snd-soc-at32-ssc.o
|
||||
|
||||
# AT32 Machine Support
|
||||
snd-soc-playpaq-objs := playpaq_wm8510.o
|
||||
|
||||
obj-$(CONFIG_SND_AT32_SOC_PLAYPAQ) += snd-soc-playpaq.o
|
|
@ -1,492 +0,0 @@
|
|||
/* sound/soc/at32/at32-pcm.c
|
||||
* ASoC PCM interface for Atmel AT32 SoC
|
||||
*
|
||||
* Copyright (C) 2008 Long Range Systems
|
||||
* Geoffrey Wossum <gwossum@acm.org>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Note that this is basically a port of the sound/soc/at91-pcm.c to
|
||||
* the AVR32 kernel. Thanks to Frank Mandarino for that code.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/atmel_pdc.h>
|
||||
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
|
||||
#include "at32-pcm.h"
|
||||
|
||||
|
||||
|
||||
/*--------------------------------------------------------------------------*\
|
||||
* Hardware definition
|
||||
\*--------------------------------------------------------------------------*/
|
||||
/* TODO: These values were taken from the AT91 platform driver, check
|
||||
* them against real values for AT32
|
||||
*/
|
||||
static const struct snd_pcm_hardware at32_pcm_hardware = {
|
||||
.info = (SNDRV_PCM_INFO_MMAP |
|
||||
SNDRV_PCM_INFO_MMAP_VALID |
|
||||
SNDRV_PCM_INFO_INTERLEAVED |
|
||||
SNDRV_PCM_INFO_BLOCK_TRANSFER |
|
||||
SNDRV_PCM_INFO_PAUSE),
|
||||
|
||||
.formats = SNDRV_PCM_FMTBIT_S16,
|
||||
.period_bytes_min = 32,
|
||||
.period_bytes_max = 8192, /* 512 frames * 16 bytes / frame */
|
||||
.periods_min = 2,
|
||||
.periods_max = 1024,
|
||||
.buffer_bytes_max = 32 * 1024,
|
||||
};
|
||||
|
||||
|
||||
|
||||
/*--------------------------------------------------------------------------*\
|
||||
* Data types
|
||||
\*--------------------------------------------------------------------------*/
|
||||
struct at32_runtime_data {
|
||||
struct at32_pcm_dma_params *params;
|
||||
dma_addr_t dma_buffer; /* physical address of DMA buffer */
|
||||
dma_addr_t dma_buffer_end; /* first address beyond DMA buffer */
|
||||
size_t period_size;
|
||||
|
||||
dma_addr_t period_ptr; /* physical address of next period */
|
||||
int periods; /* period index of period_ptr */
|
||||
|
||||
/* Save PDC registers (for power management) */
|
||||
u32 pdc_xpr_save;
|
||||
u32 pdc_xcr_save;
|
||||
u32 pdc_xnpr_save;
|
||||
u32 pdc_xncr_save;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/*--------------------------------------------------------------------------*\
|
||||
* Helper functions
|
||||
\*--------------------------------------------------------------------------*/
|
||||
static int at32_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
|
||||
{
|
||||
struct snd_pcm_substream *substream = pcm->streams[stream].substream;
|
||||
struct snd_dma_buffer *dmabuf = &substream->dma_buffer;
|
||||
size_t size = at32_pcm_hardware.buffer_bytes_max;
|
||||
|
||||
dmabuf->dev.type = SNDRV_DMA_TYPE_DEV;
|
||||
dmabuf->dev.dev = pcm->card->dev;
|
||||
dmabuf->private_data = NULL;
|
||||
dmabuf->area = dma_alloc_coherent(pcm->card->dev, size,
|
||||
&dmabuf->addr, GFP_KERNEL);
|
||||
pr_debug("at32_pcm: preallocate_dma_buffer: "
|
||||
"area=%p, addr=%p, size=%ld\n",
|
||||
(void *)dmabuf->area, (void *)dmabuf->addr, size);
|
||||
|
||||
if (!dmabuf->area)
|
||||
return -ENOMEM;
|
||||
|
||||
dmabuf->bytes = size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*--------------------------------------------------------------------------*\
|
||||
* ISR
|
||||
\*--------------------------------------------------------------------------*/
|
||||
static void at32_pcm_dma_irq(u32 ssc_sr, struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *rtd = substream->runtime;
|
||||
struct at32_runtime_data *prtd = rtd->private_data;
|
||||
struct at32_pcm_dma_params *params = prtd->params;
|
||||
static int count;
|
||||
|
||||
count++;
|
||||
if (ssc_sr & params->mask->ssc_endbuf) {
|
||||
pr_warning("at32-pcm: buffer %s on %s (SSC_SR=%#x, count=%d)\n",
|
||||
substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
|
||||
"underrun" : "overrun", params->name, ssc_sr, count);
|
||||
|
||||
/* re-start the PDC */
|
||||
ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
|
||||
params->mask->pdc_disable);
|
||||
prtd->period_ptr += prtd->period_size;
|
||||
if (prtd->period_ptr >= prtd->dma_buffer_end)
|
||||
prtd->period_ptr = prtd->dma_buffer;
|
||||
|
||||
|
||||
ssc_writex(params->ssc->regs, params->pdc->xpr,
|
||||
prtd->period_ptr);
|
||||
ssc_writex(params->ssc->regs, params->pdc->xcr,
|
||||
prtd->period_size / params->pdc_xfer_size);
|
||||
ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
|
||||
params->mask->pdc_enable);
|
||||
}
|
||||
|
||||
|
||||
if (ssc_sr & params->mask->ssc_endx) {
|
||||
/* Load the PDC next pointer and counter registers */
|
||||
prtd->period_ptr += prtd->period_size;
|
||||
if (prtd->period_ptr >= prtd->dma_buffer_end)
|
||||
prtd->period_ptr = prtd->dma_buffer;
|
||||
ssc_writex(params->ssc->regs, params->pdc->xnpr,
|
||||
prtd->period_ptr);
|
||||
ssc_writex(params->ssc->regs, params->pdc->xncr,
|
||||
prtd->period_size / params->pdc_xfer_size);
|
||||
}
|
||||
|
||||
|
||||
snd_pcm_period_elapsed(substream);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*--------------------------------------------------------------------------*\
|
||||
* PCM operations
|
||||
\*--------------------------------------------------------------------------*/
|
||||
static int at32_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct at32_runtime_data *prtd = runtime->private_data;
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
|
||||
/* this may get called several times by oss emulation
|
||||
* with different params
|
||||
*/
|
||||
snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
|
||||
runtime->dma_bytes = params_buffer_bytes(params);
|
||||
|
||||
prtd->params = rtd->dai->cpu_dai->dma_data;
|
||||
prtd->params->dma_intr_handler = at32_pcm_dma_irq;
|
||||
|
||||
prtd->dma_buffer = runtime->dma_addr;
|
||||
prtd->dma_buffer_end = runtime->dma_addr + runtime->dma_bytes;
|
||||
prtd->period_size = params_period_bytes(params);
|
||||
|
||||
pr_debug("hw_params: DMA for %s initialized "
|
||||
"(dma_bytes=%ld, period_size=%ld)\n",
|
||||
prtd->params->name, runtime->dma_bytes, prtd->period_size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int at32_pcm_hw_free(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct at32_runtime_data *prtd = substream->runtime->private_data;
|
||||
struct at32_pcm_dma_params *params = prtd->params;
|
||||
|
||||
if (params != NULL) {
|
||||
ssc_writex(params->ssc->regs, SSC_PDC_PTCR,
|
||||
params->mask->pdc_disable);
|
||||
prtd->params->dma_intr_handler = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int at32_pcm_prepare(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct at32_runtime_data *prtd = substream->runtime->private_data;
|
||||
struct at32_pcm_dma_params *params = prtd->params;
|
||||
|
||||
ssc_writex(params->ssc->regs, SSC_IDR,
|
||||
params->mask->ssc_endx | params->mask->ssc_endbuf);
|
||||
ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
|
||||
params->mask->pdc_disable);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int at32_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
{
|
||||
struct snd_pcm_runtime *rtd = substream->runtime;
|
||||
struct at32_runtime_data *prtd = rtd->private_data;
|
||||
struct at32_pcm_dma_params *params = prtd->params;
|
||||
int ret = 0;
|
||||
|
||||
pr_debug("at32_pcm_trigger: buffer_size = %ld, "
|
||||
"dma_area = %p, dma_bytes = %ld\n",
|
||||
rtd->buffer_size, rtd->dma_area, rtd->dma_bytes);
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
prtd->period_ptr = prtd->dma_buffer;
|
||||
|
||||
ssc_writex(params->ssc->regs, params->pdc->xpr,
|
||||
prtd->period_ptr);
|
||||
ssc_writex(params->ssc->regs, params->pdc->xcr,
|
||||
prtd->period_size / params->pdc_xfer_size);
|
||||
|
||||
prtd->period_ptr += prtd->period_size;
|
||||
ssc_writex(params->ssc->regs, params->pdc->xnpr,
|
||||
prtd->period_ptr);
|
||||
ssc_writex(params->ssc->regs, params->pdc->xncr,
|
||||
prtd->period_size / params->pdc_xfer_size);
|
||||
|
||||
pr_debug("trigger: period_ptr=%lx, xpr=%x, "
|
||||
"xcr=%d, xnpr=%x, xncr=%d\n",
|
||||
(unsigned long)prtd->period_ptr,
|
||||
ssc_readx(params->ssc->regs, params->pdc->xpr),
|
||||
ssc_readx(params->ssc->regs, params->pdc->xcr),
|
||||
ssc_readx(params->ssc->regs, params->pdc->xnpr),
|
||||
ssc_readx(params->ssc->regs, params->pdc->xncr));
|
||||
|
||||
ssc_writex(params->ssc->regs, SSC_IER,
|
||||
params->mask->ssc_endx | params->mask->ssc_endbuf);
|
||||
ssc_writex(params->ssc->regs, SSC_PDC_PTCR,
|
||||
params->mask->pdc_enable);
|
||||
|
||||
pr_debug("sr=%x, imr=%x\n",
|
||||
ssc_readx(params->ssc->regs, SSC_SR),
|
||||
ssc_readx(params->ssc->regs, SSC_IER));
|
||||
break; /* SNDRV_PCM_TRIGGER_START */
|
||||
|
||||
|
||||
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
|
||||
params->mask->pdc_disable);
|
||||
break;
|
||||
|
||||
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
|
||||
params->mask->pdc_enable);
|
||||
break;
|
||||
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static snd_pcm_uframes_t at32_pcm_pointer(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct at32_runtime_data *prtd = runtime->private_data;
|
||||
struct at32_pcm_dma_params *params = prtd->params;
|
||||
dma_addr_t ptr;
|
||||
snd_pcm_uframes_t x;
|
||||
|
||||
ptr = (dma_addr_t) ssc_readx(params->ssc->regs, params->pdc->xpr);
|
||||
x = bytes_to_frames(runtime, ptr - prtd->dma_buffer);
|
||||
|
||||
if (x == runtime->buffer_size)
|
||||
x = 0;
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int at32_pcm_open(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct at32_runtime_data *prtd;
|
||||
int ret = 0;
|
||||
|
||||
snd_soc_set_runtime_hwparams(substream, &at32_pcm_hardware);
|
||||
|
||||
/* ensure that buffer size is a multiple of period size */
|
||||
ret = snd_pcm_hw_constraint_integer(runtime,
|
||||
SNDRV_PCM_HW_PARAM_PERIODS);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
prtd = kzalloc(sizeof(*prtd), GFP_KERNEL);
|
||||
if (prtd == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
runtime->private_data = prtd;
|
||||
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int at32_pcm_close(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct at32_runtime_data *prtd = substream->runtime->private_data;
|
||||
|
||||
kfree(prtd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int at32_pcm_mmap(struct snd_pcm_substream *substream,
|
||||
struct vm_area_struct *vma)
|
||||
{
|
||||
return remap_pfn_range(vma, vma->vm_start,
|
||||
substream->dma_buffer.addr >> PAGE_SHIFT,
|
||||
vma->vm_end - vma->vm_start, vma->vm_page_prot);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static struct snd_pcm_ops at32_pcm_ops = {
|
||||
.open = at32_pcm_open,
|
||||
.close = at32_pcm_close,
|
||||
.ioctl = snd_pcm_lib_ioctl,
|
||||
.hw_params = at32_pcm_hw_params,
|
||||
.hw_free = at32_pcm_hw_free,
|
||||
.prepare = at32_pcm_prepare,
|
||||
.trigger = at32_pcm_trigger,
|
||||
.pointer = at32_pcm_pointer,
|
||||
.mmap = at32_pcm_mmap,
|
||||
};
|
||||
|
||||
|
||||
|
||||
/*--------------------------------------------------------------------------*\
|
||||
* ASoC platform driver
|
||||
\*--------------------------------------------------------------------------*/
|
||||
static u64 at32_pcm_dmamask = 0xffffffff;
|
||||
|
||||
static int at32_pcm_new(struct snd_card *card,
|
||||
struct snd_soc_dai *dai,
|
||||
struct snd_pcm *pcm)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (!card->dev->dma_mask)
|
||||
card->dev->dma_mask = &at32_pcm_dmamask;
|
||||
if (!card->dev->coherent_dma_mask)
|
||||
card->dev->coherent_dma_mask = 0xffffffff;
|
||||
|
||||
if (dai->playback.channels_min) {
|
||||
ret = at32_pcm_preallocate_dma_buffer(
|
||||
pcm, SNDRV_PCM_STREAM_PLAYBACK);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (dai->capture.channels_min) {
|
||||
pr_debug("at32-pcm: Allocating PCM capture DMA buffer\n");
|
||||
ret = at32_pcm_preallocate_dma_buffer(
|
||||
pcm, SNDRV_PCM_STREAM_CAPTURE);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void at32_pcm_free_dma_buffers(struct snd_pcm *pcm)
|
||||
{
|
||||
struct snd_pcm_substream *substream;
|
||||
struct snd_dma_buffer *buf;
|
||||
int stream;
|
||||
|
||||
for (stream = 0; stream < 2; stream++) {
|
||||
substream = pcm->streams[stream].substream;
|
||||
if (substream == NULL)
|
||||
continue;
|
||||
|
||||
buf = &substream->dma_buffer;
|
||||
if (!buf->area)
|
||||
continue;
|
||||
dma_free_coherent(pcm->card->dev, buf->bytes,
|
||||
buf->area, buf->addr);
|
||||
buf->area = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int at32_pcm_suspend(struct platform_device *pdev,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = dai->runtime;
|
||||
struct at32_runtime_data *prtd;
|
||||
struct at32_pcm_dma_params *params;
|
||||
|
||||
if (runtime == NULL)
|
||||
return 0;
|
||||
prtd = runtime->private_data;
|
||||
params = prtd->params;
|
||||
|
||||
/* Disable the PDC and save the PDC registers */
|
||||
ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
|
||||
params->mask->pdc_disable);
|
||||
|
||||
prtd->pdc_xpr_save = ssc_readx(params->ssc->regs, params->pdc->xpr);
|
||||
prtd->pdc_xcr_save = ssc_readx(params->ssc->regs, params->pdc->xcr);
|
||||
prtd->pdc_xnpr_save = ssc_readx(params->ssc->regs, params->pdc->xnpr);
|
||||
prtd->pdc_xncr_save = ssc_readx(params->ssc->regs, params->pdc->xncr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int at32_pcm_resume(struct platform_device *pdev,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = dai->runtime;
|
||||
struct at32_runtime_data *prtd;
|
||||
struct at32_pcm_dma_params *params;
|
||||
|
||||
if (runtime == NULL)
|
||||
return 0;
|
||||
prtd = runtime->private_data;
|
||||
params = prtd->params;
|
||||
|
||||
/* Restore the PDC registers and enable the PDC */
|
||||
ssc_writex(params->ssc->regs, params->pdc->xpr, prtd->pdc_xpr_save);
|
||||
ssc_writex(params->ssc->regs, params->pdc->xcr, prtd->pdc_xcr_save);
|
||||
ssc_writex(params->ssc->regs, params->pdc->xnpr, prtd->pdc_xnpr_save);
|
||||
ssc_writex(params->ssc->regs, params->pdc->xncr, prtd->pdc_xncr_save);
|
||||
|
||||
ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR, params->mask->pdc_enable);
|
||||
return 0;
|
||||
}
|
||||
#else /* CONFIG_PM */
|
||||
# define at32_pcm_suspend NULL
|
||||
# define at32_pcm_resume NULL
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
|
||||
|
||||
struct snd_soc_platform at32_soc_platform = {
|
||||
.name = "at32-audio",
|
||||
.pcm_ops = &at32_pcm_ops,
|
||||
.pcm_new = at32_pcm_new,
|
||||
.pcm_free = at32_pcm_free_dma_buffers,
|
||||
.suspend = at32_pcm_suspend,
|
||||
.resume = at32_pcm_resume,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(at32_soc_platform);
|
||||
|
||||
|
||||
|
||||
MODULE_AUTHOR("Geoffrey Wossum <gwossum@acm.org>");
|
||||
MODULE_DESCRIPTION("Atmel AT32 PCM module");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -1,79 +0,0 @@
|
|||
/* sound/soc/at32/at32-pcm.h
|
||||
* ASoC PCM interface for Atmel AT32 SoC
|
||||
*
|
||||
* Copyright (C) 2008 Long Range Systems
|
||||
* Geoffrey Wossum <gwossum@acm.org>
|
||||
*
|
||||
* 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 __SOUND_SOC_AT32_AT32_PCM_H
|
||||
#define __SOUND_SOC_AT32_AT32_PCM_H __FILE__
|
||||
|
||||
#include <linux/atmel-ssc.h>
|
||||
|
||||
|
||||
/*
|
||||
* Registers and status bits that are required by the PCM driver
|
||||
* TODO: Is ptcr really used?
|
||||
*/
|
||||
struct at32_pdc_regs {
|
||||
u32 xpr; /* PDC RX/TX pointer */
|
||||
u32 xcr; /* PDC RX/TX counter */
|
||||
u32 xnpr; /* PDC next RX/TX pointer */
|
||||
u32 xncr; /* PDC next RX/TX counter */
|
||||
u32 ptcr; /* PDC transfer control */
|
||||
};
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* SSC mask info
|
||||
*/
|
||||
struct at32_ssc_mask {
|
||||
u32 ssc_enable; /* SSC RX/TX enable */
|
||||
u32 ssc_disable; /* SSC RX/TX disable */
|
||||
u32 ssc_endx; /* SSC ENDTX or ENDRX */
|
||||
u32 ssc_endbuf; /* SSC TXBUFF or RXBUFF */
|
||||
u32 pdc_enable; /* PDC RX/TX enable */
|
||||
u32 pdc_disable; /* PDC RX/TX disable */
|
||||
};
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* This structure, shared between the PCM driver and the interface,
|
||||
* contains all information required by the PCM driver to perform the
|
||||
* PDC DMA operation. All fields except dma_intr_handler() are initialized
|
||||
* by the interface. The dms_intr_handler() pointer is set by the PCM
|
||||
* driver and called by the interface SSC interrupt handler if it is
|
||||
* non-NULL.
|
||||
*/
|
||||
struct at32_pcm_dma_params {
|
||||
char *name; /* stream identifier */
|
||||
int pdc_xfer_size; /* PDC counter increment in bytes */
|
||||
struct ssc_device *ssc; /* SSC device for stream */
|
||||
struct at32_pdc_regs *pdc; /* PDC register info */
|
||||
struct at32_ssc_mask *mask; /* SSC mask info */
|
||||
struct snd_pcm_substream *substream;
|
||||
void (*dma_intr_handler) (u32, struct snd_pcm_substream *);
|
||||
};
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* The AT32 ASoC platform driver
|
||||
*/
|
||||
extern struct snd_soc_platform at32_soc_platform;
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* SSC register access (since ssc_writel() / ssc_readl() require literal name)
|
||||
*/
|
||||
#define ssc_readx(base, reg) (__raw_readl((base) + (reg)))
|
||||
#define ssc_writex(base, reg, value) __raw_writel((value), (base) + (reg))
|
||||
|
||||
#endif /* __SOUND_SOC_AT32_AT32_PCM_H */
|
|
@ -1,849 +0,0 @@
|
|||
/* sound/soc/at32/at32-ssc.c
|
||||
* ASoC platform driver for AT32 using SSC as DAI
|
||||
*
|
||||
* Copyright (C) 2008 Long Range Systems
|
||||
* Geoffrey Wossum <gwossum@acm.org>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Note that this is basically a port of the sound/soc/at91-ssc.c to
|
||||
* the AVR32 kernel. Thanks to Frank Mandarino for that code.
|
||||
*/
|
||||
|
||||
/* #define DEBUG */
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/atmel_pdc.h>
|
||||
#include <linux/atmel-ssc.h>
|
||||
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/initval.h>
|
||||
#include <sound/soc.h>
|
||||
|
||||
#include "at32-pcm.h"
|
||||
#include "at32-ssc.h"
|
||||
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*\
|
||||
* Constants
|
||||
\*-------------------------------------------------------------------------*/
|
||||
#define NUM_SSC_DEVICES 3
|
||||
|
||||
/*
|
||||
* SSC direction masks
|
||||
*/
|
||||
#define SSC_DIR_MASK_UNUSED 0
|
||||
#define SSC_DIR_MASK_PLAYBACK 1
|
||||
#define SSC_DIR_MASK_CAPTURE 2
|
||||
|
||||
/*
|
||||
* SSC register values that Atmel left out of <linux/atmel-ssc.h>. These
|
||||
* are expected to be used with SSC_BF
|
||||
*/
|
||||
/* START bit field values */
|
||||
#define SSC_START_CONTINUOUS 0
|
||||
#define SSC_START_TX_RX 1
|
||||
#define SSC_START_LOW_RF 2
|
||||
#define SSC_START_HIGH_RF 3
|
||||
#define SSC_START_FALLING_RF 4
|
||||
#define SSC_START_RISING_RF 5
|
||||
#define SSC_START_LEVEL_RF 6
|
||||
#define SSC_START_EDGE_RF 7
|
||||
#define SSS_START_COMPARE_0 8
|
||||
|
||||
/* CKI bit field values */
|
||||
#define SSC_CKI_FALLING 0
|
||||
#define SSC_CKI_RISING 1
|
||||
|
||||
/* CKO bit field values */
|
||||
#define SSC_CKO_NONE 0
|
||||
#define SSC_CKO_CONTINUOUS 1
|
||||
#define SSC_CKO_TRANSFER 2
|
||||
|
||||
/* CKS bit field values */
|
||||
#define SSC_CKS_DIV 0
|
||||
#define SSC_CKS_CLOCK 1
|
||||
#define SSC_CKS_PIN 2
|
||||
|
||||
/* FSEDGE bit field values */
|
||||
#define SSC_FSEDGE_POSITIVE 0
|
||||
#define SSC_FSEDGE_NEGATIVE 1
|
||||
|
||||
/* FSOS bit field values */
|
||||
#define SSC_FSOS_NONE 0
|
||||
#define SSC_FSOS_NEGATIVE 1
|
||||
#define SSC_FSOS_POSITIVE 2
|
||||
#define SSC_FSOS_LOW 3
|
||||
#define SSC_FSOS_HIGH 4
|
||||
#define SSC_FSOS_TOGGLE 5
|
||||
|
||||
#define START_DELAY 1
|
||||
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*\
|
||||
* Module data
|
||||
\*-------------------------------------------------------------------------*/
|
||||
/*
|
||||
* SSC PDC registered required by the PCM DMA engine
|
||||
*/
|
||||
static struct at32_pdc_regs pdc_tx_reg = {
|
||||
.xpr = SSC_PDC_TPR,
|
||||
.xcr = SSC_PDC_TCR,
|
||||
.xnpr = SSC_PDC_TNPR,
|
||||
.xncr = SSC_PDC_TNCR,
|
||||
};
|
||||
|
||||
|
||||
|
||||
static struct at32_pdc_regs pdc_rx_reg = {
|
||||
.xpr = SSC_PDC_RPR,
|
||||
.xcr = SSC_PDC_RCR,
|
||||
.xnpr = SSC_PDC_RNPR,
|
||||
.xncr = SSC_PDC_RNCR,
|
||||
};
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* SSC and PDC status bits for transmit and receive
|
||||
*/
|
||||
static struct at32_ssc_mask ssc_tx_mask = {
|
||||
.ssc_enable = SSC_BIT(CR_TXEN),
|
||||
.ssc_disable = SSC_BIT(CR_TXDIS),
|
||||
.ssc_endx = SSC_BIT(SR_ENDTX),
|
||||
.ssc_endbuf = SSC_BIT(SR_TXBUFE),
|
||||
.pdc_enable = SSC_BIT(PDC_PTCR_TXTEN),
|
||||
.pdc_disable = SSC_BIT(PDC_PTCR_TXTDIS),
|
||||
};
|
||||
|
||||
|
||||
|
||||
static struct at32_ssc_mask ssc_rx_mask = {
|
||||
.ssc_enable = SSC_BIT(CR_RXEN),
|
||||
.ssc_disable = SSC_BIT(CR_RXDIS),
|
||||
.ssc_endx = SSC_BIT(SR_ENDRX),
|
||||
.ssc_endbuf = SSC_BIT(SR_RXBUFF),
|
||||
.pdc_enable = SSC_BIT(PDC_PTCR_RXTEN),
|
||||
.pdc_disable = SSC_BIT(PDC_PTCR_RXTDIS),
|
||||
};
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* DMA parameters for each SSC
|
||||
*/
|
||||
static struct at32_pcm_dma_params ssc_dma_params[NUM_SSC_DEVICES][2] = {
|
||||
{
|
||||
{
|
||||
.name = "SSC0 PCM out",
|
||||
.pdc = &pdc_tx_reg,
|
||||
.mask = &ssc_tx_mask,
|
||||
},
|
||||
{
|
||||
.name = "SSC0 PCM in",
|
||||
.pdc = &pdc_rx_reg,
|
||||
.mask = &ssc_rx_mask,
|
||||
},
|
||||
},
|
||||
{
|
||||
{
|
||||
.name = "SSC1 PCM out",
|
||||
.pdc = &pdc_tx_reg,
|
||||
.mask = &ssc_tx_mask,
|
||||
},
|
||||
{
|
||||
.name = "SSC1 PCM in",
|
||||
.pdc = &pdc_rx_reg,
|
||||
.mask = &ssc_rx_mask,
|
||||
},
|
||||
},
|
||||
{
|
||||
{
|
||||
.name = "SSC2 PCM out",
|
||||
.pdc = &pdc_tx_reg,
|
||||
.mask = &ssc_tx_mask,
|
||||
},
|
||||
{
|
||||
.name = "SSC2 PCM in",
|
||||
.pdc = &pdc_rx_reg,
|
||||
.mask = &ssc_rx_mask,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
static struct at32_ssc_info ssc_info[NUM_SSC_DEVICES] = {
|
||||
{
|
||||
.name = "ssc0",
|
||||
.lock = __SPIN_LOCK_UNLOCKED(ssc_info[0].lock),
|
||||
.dir_mask = SSC_DIR_MASK_UNUSED,
|
||||
.initialized = 0,
|
||||
},
|
||||
{
|
||||
.name = "ssc1",
|
||||
.lock = __SPIN_LOCK_UNLOCKED(ssc_info[1].lock),
|
||||
.dir_mask = SSC_DIR_MASK_UNUSED,
|
||||
.initialized = 0,
|
||||
},
|
||||
{
|
||||
.name = "ssc2",
|
||||
.lock = __SPIN_LOCK_UNLOCKED(ssc_info[2].lock),
|
||||
.dir_mask = SSC_DIR_MASK_UNUSED,
|
||||
.initialized = 0,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*\
|
||||
* ISR
|
||||
\*-------------------------------------------------------------------------*/
|
||||
/*
|
||||
* SSC interrupt handler. Passes PDC interrupts to the DMA interrupt
|
||||
* handler in the PCM driver.
|
||||
*/
|
||||
static irqreturn_t at32_ssc_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct at32_ssc_info *ssc_p = dev_id;
|
||||
struct at32_pcm_dma_params *dma_params;
|
||||
u32 ssc_sr;
|
||||
u32 ssc_substream_mask;
|
||||
int i;
|
||||
|
||||
ssc_sr = (ssc_readl(ssc_p->ssc->regs, SR) &
|
||||
ssc_readl(ssc_p->ssc->regs, IMR));
|
||||
|
||||
/*
|
||||
* Loop through substreams attached to this SSC. If a DMA-related
|
||||
* interrupt occured on that substream, call the DMA interrupt
|
||||
* handler function, if one has been registered in the dma_param
|
||||
* structure by the PCM driver.
|
||||
*/
|
||||
for (i = 0; i < ARRAY_SIZE(ssc_p->dma_params); i++) {
|
||||
dma_params = ssc_p->dma_params[i];
|
||||
|
||||
if ((dma_params != NULL) &&
|
||||
(dma_params->dma_intr_handler != NULL)) {
|
||||
ssc_substream_mask = (dma_params->mask->ssc_endx |
|
||||
dma_params->mask->ssc_endbuf);
|
||||
if (ssc_sr & ssc_substream_mask) {
|
||||
dma_params->dma_intr_handler(ssc_sr,
|
||||
dma_params->
|
||||
substream);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*\
|
||||
* DAI functions
|
||||
\*-------------------------------------------------------------------------*/
|
||||
/*
|
||||
* Startup. Only that one substream allowed in each direction.
|
||||
*/
|
||||
static int at32_ssc_startup(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct at32_ssc_info *ssc_p = &ssc_info[rtd->dai->cpu_dai->id];
|
||||
int dir_mask;
|
||||
|
||||
dir_mask = ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
|
||||
SSC_DIR_MASK_PLAYBACK : SSC_DIR_MASK_CAPTURE);
|
||||
|
||||
spin_lock_irq(&ssc_p->lock);
|
||||
if (ssc_p->dir_mask & dir_mask) {
|
||||
spin_unlock_irq(&ssc_p->lock);
|
||||
return -EBUSY;
|
||||
}
|
||||
ssc_p->dir_mask |= dir_mask;
|
||||
spin_unlock_irq(&ssc_p->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Shutdown. Clear DMA parameters and shutdown the SSC if there
|
||||
* are no other substreams open.
|
||||
*/
|
||||
static void at32_ssc_shutdown(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct at32_ssc_info *ssc_p = &ssc_info[rtd->dai->cpu_dai->id];
|
||||
struct at32_pcm_dma_params *dma_params;
|
||||
int dir_mask;
|
||||
|
||||
dma_params = ssc_p->dma_params[substream->stream];
|
||||
|
||||
if (dma_params != NULL) {
|
||||
ssc_writel(dma_params->ssc->regs, CR,
|
||||
dma_params->mask->ssc_disable);
|
||||
pr_debug("%s disabled SSC_SR=0x%08x\n",
|
||||
(substream->stream ? "receiver" : "transmit"),
|
||||
ssc_readl(ssc_p->ssc->regs, SR));
|
||||
|
||||
dma_params->ssc = NULL;
|
||||
dma_params->substream = NULL;
|
||||
ssc_p->dma_params[substream->stream] = NULL;
|
||||
}
|
||||
|
||||
|
||||
dir_mask = 1 << substream->stream;
|
||||
spin_lock_irq(&ssc_p->lock);
|
||||
ssc_p->dir_mask &= ~dir_mask;
|
||||
if (!ssc_p->dir_mask) {
|
||||
/* Shutdown the SSC clock */
|
||||
pr_debug("at32-ssc: Stopping user %d clock\n",
|
||||
ssc_p->ssc->user);
|
||||
clk_disable(ssc_p->ssc->clk);
|
||||
|
||||
if (ssc_p->initialized) {
|
||||
free_irq(ssc_p->ssc->irq, ssc_p);
|
||||
ssc_p->initialized = 0;
|
||||
}
|
||||
|
||||
/* Reset the SSC */
|
||||
ssc_writel(ssc_p->ssc->regs, CR, SSC_BIT(CR_SWRST));
|
||||
|
||||
/* clear the SSC dividers */
|
||||
ssc_p->cmr_div = 0;
|
||||
ssc_p->tcmr_period = 0;
|
||||
ssc_p->rcmr_period = 0;
|
||||
}
|
||||
spin_unlock_irq(&ssc_p->lock);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Set the SSC system clock rate
|
||||
*/
|
||||
static int at32_ssc_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
|
||||
int clk_id, unsigned int freq, int dir)
|
||||
{
|
||||
/* TODO: What the heck do I do here? */
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Record DAI format for use by hw_params()
|
||||
*/
|
||||
static int at32_ssc_set_dai_fmt(struct snd_soc_dai *cpu_dai,
|
||||
unsigned int fmt)
|
||||
{
|
||||
struct at32_ssc_info *ssc_p = &ssc_info[cpu_dai->id];
|
||||
|
||||
ssc_p->daifmt = fmt;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Record SSC clock dividers for use in hw_params()
|
||||
*/
|
||||
static int at32_ssc_set_dai_clkdiv(struct snd_soc_dai *cpu_dai,
|
||||
int div_id, int div)
|
||||
{
|
||||
struct at32_ssc_info *ssc_p = &ssc_info[cpu_dai->id];
|
||||
|
||||
switch (div_id) {
|
||||
case AT32_SSC_CMR_DIV:
|
||||
/*
|
||||
* The same master clock divider is used for both
|
||||
* transmit and receive, so if a value has already
|
||||
* been set, it must match this value
|
||||
*/
|
||||
if (ssc_p->cmr_div == 0)
|
||||
ssc_p->cmr_div = div;
|
||||
else if (div != ssc_p->cmr_div)
|
||||
return -EBUSY;
|
||||
break;
|
||||
|
||||
case AT32_SSC_TCMR_PERIOD:
|
||||
ssc_p->tcmr_period = div;
|
||||
break;
|
||||
|
||||
case AT32_SSC_RCMR_PERIOD:
|
||||
ssc_p->rcmr_period = div;
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Configure the SSC
|
||||
*/
|
||||
static int at32_ssc_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
int id = rtd->dai->cpu_dai->id;
|
||||
struct at32_ssc_info *ssc_p = &ssc_info[id];
|
||||
struct at32_pcm_dma_params *dma_params;
|
||||
int channels, bits;
|
||||
u32 tfmr, rfmr, tcmr, rcmr;
|
||||
int start_event;
|
||||
int ret;
|
||||
|
||||
|
||||
/*
|
||||
* Currently, there is only one set of dma_params for each direction.
|
||||
* If more are added, this code will have to be changed to select
|
||||
* the proper set
|
||||
*/
|
||||
dma_params = &ssc_dma_params[id][substream->stream];
|
||||
dma_params->ssc = ssc_p->ssc;
|
||||
dma_params->substream = substream;
|
||||
|
||||
ssc_p->dma_params[substream->stream] = dma_params;
|
||||
|
||||
|
||||
/*
|
||||
* The cpu_dai->dma_data field is only used to communicate the
|
||||
* appropriate DMA parameters to the PCM driver's hw_params()
|
||||
* function. It should not be used for other purposes as it
|
||||
* is common to all substreams.
|
||||
*/
|
||||
rtd->dai->cpu_dai->dma_data = dma_params;
|
||||
|
||||
channels = params_channels(params);
|
||||
|
||||
|
||||
/*
|
||||
* Determine sample size in bits and the PDC increment
|
||||
*/
|
||||
switch (params_format(params)) {
|
||||
case SNDRV_PCM_FORMAT_S8:
|
||||
bits = 8;
|
||||
dma_params->pdc_xfer_size = 1;
|
||||
break;
|
||||
|
||||
case SNDRV_PCM_FORMAT_S16:
|
||||
bits = 16;
|
||||
dma_params->pdc_xfer_size = 2;
|
||||
break;
|
||||
|
||||
case SNDRV_PCM_FORMAT_S24:
|
||||
bits = 24;
|
||||
dma_params->pdc_xfer_size = 4;
|
||||
break;
|
||||
|
||||
case SNDRV_PCM_FORMAT_S32:
|
||||
bits = 32;
|
||||
dma_params->pdc_xfer_size = 4;
|
||||
break;
|
||||
|
||||
default:
|
||||
pr_warning("at32-ssc: Unsupported PCM format %d",
|
||||
params_format(params));
|
||||
return -EINVAL;
|
||||
}
|
||||
pr_debug("at32-ssc: bits = %d, pdc_xfer_size = %d, channels = %d\n",
|
||||
bits, dma_params->pdc_xfer_size, channels);
|
||||
|
||||
|
||||
/*
|
||||
* The SSC only supports up to 16-bit samples in I2S format, due
|
||||
* to the size of the Frame Mode Register FSLEN field.
|
||||
*/
|
||||
if ((ssc_p->daifmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_I2S)
|
||||
if (bits > 16) {
|
||||
pr_warning("at32-ssc: "
|
||||
"sample size %d is too large for I2S\n",
|
||||
bits);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Compute the SSC register settings
|
||||
*/
|
||||
switch (ssc_p->daifmt & (SND_SOC_DAIFMT_FORMAT_MASK |
|
||||
SND_SOC_DAIFMT_MASTER_MASK)) {
|
||||
case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS:
|
||||
/*
|
||||
* I2S format, SSC provides BCLK and LRS clocks.
|
||||
*
|
||||
* The SSC transmit and receive clocks are generated from the
|
||||
* MCK divider, and the BCLK signal is output on the SSC TK line
|
||||
*/
|
||||
pr_debug("at32-ssc: SSC mode is I2S BCLK / FRAME master\n");
|
||||
rcmr = (SSC_BF(RCMR_PERIOD, ssc_p->rcmr_period) |
|
||||
SSC_BF(RCMR_STTDLY, START_DELAY) |
|
||||
SSC_BF(RCMR_START, SSC_START_FALLING_RF) |
|
||||
SSC_BF(RCMR_CKI, SSC_CKI_RISING) |
|
||||
SSC_BF(RCMR_CKO, SSC_CKO_NONE) |
|
||||
SSC_BF(RCMR_CKS, SSC_CKS_DIV));
|
||||
|
||||
rfmr = (SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE) |
|
||||
SSC_BF(RFMR_FSOS, SSC_FSOS_NEGATIVE) |
|
||||
SSC_BF(RFMR_FSLEN, bits - 1) |
|
||||
SSC_BF(RFMR_DATNB, channels - 1) |
|
||||
SSC_BIT(RFMR_MSBF) | SSC_BF(RFMR_DATLEN, bits - 1));
|
||||
|
||||
tcmr = (SSC_BF(TCMR_PERIOD, ssc_p->tcmr_period) |
|
||||
SSC_BF(TCMR_STTDLY, START_DELAY) |
|
||||
SSC_BF(TCMR_START, SSC_START_FALLING_RF) |
|
||||
SSC_BF(TCMR_CKI, SSC_CKI_FALLING) |
|
||||
SSC_BF(TCMR_CKO, SSC_CKO_CONTINUOUS) |
|
||||
SSC_BF(TCMR_CKS, SSC_CKS_DIV));
|
||||
|
||||
tfmr = (SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_POSITIVE) |
|
||||
SSC_BF(TFMR_FSOS, SSC_FSOS_NEGATIVE) |
|
||||
SSC_BF(TFMR_FSLEN, bits - 1) |
|
||||
SSC_BF(TFMR_DATNB, channels - 1) | SSC_BIT(TFMR_MSBF) |
|
||||
SSC_BF(TFMR_DATLEN, bits - 1));
|
||||
break;
|
||||
|
||||
|
||||
case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM:
|
||||
/*
|
||||
* I2S format, CODEC supplies BCLK and LRC clock.
|
||||
*
|
||||
* The SSC transmit clock is obtained from the BCLK signal
|
||||
* on the TK line, and the SSC receive clock is generated from
|
||||
* the transmit clock.
|
||||
*
|
||||
* For single channel data, one sample is transferred on the
|
||||
* falling edge of the LRC clock. For two channel data, one
|
||||
* sample is transferred on both edges of the LRC clock.
|
||||
*/
|
||||
pr_debug("at32-ssc: SSC mode is I2S BCLK / FRAME slave\n");
|
||||
start_event = ((channels == 1) ?
|
||||
SSC_START_FALLING_RF : SSC_START_EDGE_RF);
|
||||
|
||||
rcmr = (SSC_BF(RCMR_STTDLY, START_DELAY) |
|
||||
SSC_BF(RCMR_START, start_event) |
|
||||
SSC_BF(RCMR_CKI, SSC_CKI_RISING) |
|
||||
SSC_BF(RCMR_CKO, SSC_CKO_NONE) |
|
||||
SSC_BF(RCMR_CKS, SSC_CKS_CLOCK));
|
||||
|
||||
rfmr = (SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE) |
|
||||
SSC_BF(RFMR_FSOS, SSC_FSOS_NONE) |
|
||||
SSC_BIT(RFMR_MSBF) | SSC_BF(RFMR_DATLEN, bits - 1));
|
||||
|
||||
tcmr = (SSC_BF(TCMR_STTDLY, START_DELAY) |
|
||||
SSC_BF(TCMR_START, start_event) |
|
||||
SSC_BF(TCMR_CKI, SSC_CKI_FALLING) |
|
||||
SSC_BF(TCMR_CKO, SSC_CKO_NONE) |
|
||||
SSC_BF(TCMR_CKS, SSC_CKS_PIN));
|
||||
|
||||
tfmr = (SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_POSITIVE) |
|
||||
SSC_BF(TFMR_FSOS, SSC_FSOS_NONE) |
|
||||
SSC_BIT(TFMR_MSBF) | SSC_BF(TFMR_DATLEN, bits - 1));
|
||||
break;
|
||||
|
||||
|
||||
case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBS_CFS:
|
||||
/*
|
||||
* DSP/PCM Mode A format, SSC provides BCLK and LRC clocks.
|
||||
*
|
||||
* The SSC transmit and receive clocks are generated from the
|
||||
* MCK divider, and the BCLK signal is output on the SSC TK line
|
||||
*/
|
||||
pr_debug("at32-ssc: SSC mode is DSP A BCLK / FRAME master\n");
|
||||
rcmr = (SSC_BF(RCMR_PERIOD, ssc_p->rcmr_period) |
|
||||
SSC_BF(RCMR_STTDLY, 1) |
|
||||
SSC_BF(RCMR_START, SSC_START_RISING_RF) |
|
||||
SSC_BF(RCMR_CKI, SSC_CKI_RISING) |
|
||||
SSC_BF(RCMR_CKO, SSC_CKO_NONE) |
|
||||
SSC_BF(RCMR_CKS, SSC_CKS_DIV));
|
||||
|
||||
rfmr = (SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE) |
|
||||
SSC_BF(RFMR_FSOS, SSC_FSOS_POSITIVE) |
|
||||
SSC_BF(RFMR_DATNB, channels - 1) |
|
||||
SSC_BIT(RFMR_MSBF) | SSC_BF(RFMR_DATLEN, bits - 1));
|
||||
|
||||
tcmr = (SSC_BF(TCMR_PERIOD, ssc_p->tcmr_period) |
|
||||
SSC_BF(TCMR_STTDLY, 1) |
|
||||
SSC_BF(TCMR_START, SSC_START_RISING_RF) |
|
||||
SSC_BF(TCMR_CKI, SSC_CKI_RISING) |
|
||||
SSC_BF(TCMR_CKO, SSC_CKO_CONTINUOUS) |
|
||||
SSC_BF(TCMR_CKS, SSC_CKS_DIV));
|
||||
|
||||
tfmr = (SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_POSITIVE) |
|
||||
SSC_BF(TFMR_FSOS, SSC_FSOS_POSITIVE) |
|
||||
SSC_BF(TFMR_DATNB, channels - 1) |
|
||||
SSC_BIT(TFMR_MSBF) | SSC_BF(TFMR_DATLEN, bits - 1));
|
||||
break;
|
||||
|
||||
|
||||
case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBM_CFM:
|
||||
default:
|
||||
pr_warning("at32-ssc: unsupported DAI format 0x%x\n",
|
||||
ssc_p->daifmt);
|
||||
return -EINVAL;
|
||||
break;
|
||||
}
|
||||
pr_debug("at32-ssc: RCMR=%08x RFMR=%08x TCMR=%08x TFMR=%08x\n",
|
||||
rcmr, rfmr, tcmr, tfmr);
|
||||
|
||||
|
||||
if (!ssc_p->initialized) {
|
||||
/* enable peripheral clock */
|
||||
pr_debug("at32-ssc: Starting clock\n");
|
||||
clk_enable(ssc_p->ssc->clk);
|
||||
|
||||
/* Reset the SSC and its PDC registers */
|
||||
ssc_writel(ssc_p->ssc->regs, CR, SSC_BIT(CR_SWRST));
|
||||
|
||||
ssc_writel(ssc_p->ssc->regs, PDC_RPR, 0);
|
||||
ssc_writel(ssc_p->ssc->regs, PDC_RCR, 0);
|
||||
ssc_writel(ssc_p->ssc->regs, PDC_RNPR, 0);
|
||||
ssc_writel(ssc_p->ssc->regs, PDC_RNCR, 0);
|
||||
|
||||
ssc_writel(ssc_p->ssc->regs, PDC_TPR, 0);
|
||||
ssc_writel(ssc_p->ssc->regs, PDC_TCR, 0);
|
||||
ssc_writel(ssc_p->ssc->regs, PDC_TNPR, 0);
|
||||
ssc_writel(ssc_p->ssc->regs, PDC_TNCR, 0);
|
||||
|
||||
ret = request_irq(ssc_p->ssc->irq, at32_ssc_interrupt, 0,
|
||||
ssc_p->name, ssc_p);
|
||||
if (ret < 0) {
|
||||
pr_warning("at32-ssc: request irq failed (%d)\n", ret);
|
||||
pr_debug("at32-ssc: Stopping clock\n");
|
||||
clk_disable(ssc_p->ssc->clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ssc_p->initialized = 1;
|
||||
}
|
||||
|
||||
/* Set SSC clock mode register */
|
||||
ssc_writel(ssc_p->ssc->regs, CMR, ssc_p->cmr_div);
|
||||
|
||||
/* set receive clock mode and format */
|
||||
ssc_writel(ssc_p->ssc->regs, RCMR, rcmr);
|
||||
ssc_writel(ssc_p->ssc->regs, RFMR, rfmr);
|
||||
|
||||
/* set transmit clock mode and format */
|
||||
ssc_writel(ssc_p->ssc->regs, TCMR, tcmr);
|
||||
ssc_writel(ssc_p->ssc->regs, TFMR, tfmr);
|
||||
|
||||
pr_debug("at32-ssc: SSC initialized\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int at32_ssc_prepare(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct at32_ssc_info *ssc_p = &ssc_info[rtd->dai->cpu_dai->id];
|
||||
struct at32_pcm_dma_params *dma_params;
|
||||
|
||||
dma_params = ssc_p->dma_params[substream->stream];
|
||||
|
||||
ssc_writel(dma_params->ssc->regs, CR, dma_params->mask->ssc_enable);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int at32_ssc_suspend(struct platform_device *pdev,
|
||||
struct snd_soc_dai *cpu_dai)
|
||||
{
|
||||
struct at32_ssc_info *ssc_p;
|
||||
|
||||
if (!cpu_dai->active)
|
||||
return 0;
|
||||
|
||||
ssc_p = &ssc_info[cpu_dai->id];
|
||||
|
||||
/* Save the status register before disabling transmit and receive */
|
||||
ssc_p->ssc_state.ssc_sr = ssc_readl(ssc_p->ssc->regs, SR);
|
||||
ssc_writel(ssc_p->ssc->regs, CR, SSC_BIT(CR_TXDIS) | SSC_BIT(CR_RXDIS));
|
||||
|
||||
/* Save the current interrupt mask, then disable unmasked interrupts */
|
||||
ssc_p->ssc_state.ssc_imr = ssc_readl(ssc_p->ssc->regs, IMR);
|
||||
ssc_writel(ssc_p->ssc->regs, IDR, ssc_p->ssc_state.ssc_imr);
|
||||
|
||||
ssc_p->ssc_state.ssc_cmr = ssc_readl(ssc_p->ssc->regs, CMR);
|
||||
ssc_p->ssc_state.ssc_rcmr = ssc_readl(ssc_p->ssc->regs, RCMR);
|
||||
ssc_p->ssc_state.ssc_rfmr = ssc_readl(ssc_p->ssc->regs, RFMR);
|
||||
ssc_p->ssc_state.ssc_tcmr = ssc_readl(ssc_p->ssc->regs, TCMR);
|
||||
ssc_p->ssc_state.ssc_tfmr = ssc_readl(ssc_p->ssc->regs, TFMR);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int at32_ssc_resume(struct platform_device *pdev,
|
||||
struct snd_soc_dai *cpu_dai)
|
||||
{
|
||||
struct at32_ssc_info *ssc_p;
|
||||
u32 cr;
|
||||
|
||||
if (!cpu_dai->active)
|
||||
return 0;
|
||||
|
||||
ssc_p = &ssc_info[cpu_dai->id];
|
||||
|
||||
/* restore SSC register settings */
|
||||
ssc_writel(ssc_p->ssc->regs, TFMR, ssc_p->ssc_state.ssc_tfmr);
|
||||
ssc_writel(ssc_p->ssc->regs, TCMR, ssc_p->ssc_state.ssc_tcmr);
|
||||
ssc_writel(ssc_p->ssc->regs, RFMR, ssc_p->ssc_state.ssc_rfmr);
|
||||
ssc_writel(ssc_p->ssc->regs, RCMR, ssc_p->ssc_state.ssc_rcmr);
|
||||
ssc_writel(ssc_p->ssc->regs, CMR, ssc_p->ssc_state.ssc_cmr);
|
||||
|
||||
/* re-enable interrupts */
|
||||
ssc_writel(ssc_p->ssc->regs, IER, ssc_p->ssc_state.ssc_imr);
|
||||
|
||||
/* Re-enable recieve and transmit as appropriate */
|
||||
cr = 0;
|
||||
cr |=
|
||||
(ssc_p->ssc_state.ssc_sr & SSC_BIT(SR_RXEN)) ? SSC_BIT(CR_RXEN) : 0;
|
||||
cr |=
|
||||
(ssc_p->ssc_state.ssc_sr & SSC_BIT(SR_TXEN)) ? SSC_BIT(CR_TXEN) : 0;
|
||||
ssc_writel(ssc_p->ssc->regs, CR, cr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else /* CONFIG_PM */
|
||||
# define at32_ssc_suspend NULL
|
||||
# define at32_ssc_resume NULL
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
|
||||
#define AT32_SSC_RATES \
|
||||
(SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \
|
||||
SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
|
||||
SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
|
||||
|
||||
|
||||
#define AT32_SSC_FORMATS \
|
||||
(SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16 | \
|
||||
SNDRV_PCM_FMTBIT_S24 | SNDRV_PCM_FMTBIT_S32)
|
||||
|
||||
|
||||
struct snd_soc_dai at32_ssc_dai[NUM_SSC_DEVICES] = {
|
||||
{
|
||||
.name = "at32-ssc0",
|
||||
.id = 0,
|
||||
.type = SND_SOC_DAI_PCM,
|
||||
.suspend = at32_ssc_suspend,
|
||||
.resume = at32_ssc_resume,
|
||||
.playback = {
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.rates = AT32_SSC_RATES,
|
||||
.formats = AT32_SSC_FORMATS,
|
||||
},
|
||||
.capture = {
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.rates = AT32_SSC_RATES,
|
||||
.formats = AT32_SSC_FORMATS,
|
||||
},
|
||||
.ops = {
|
||||
.startup = at32_ssc_startup,
|
||||
.shutdown = at32_ssc_shutdown,
|
||||
.prepare = at32_ssc_prepare,
|
||||
.hw_params = at32_ssc_hw_params,
|
||||
},
|
||||
.dai_ops = {
|
||||
.set_sysclk = at32_ssc_set_dai_sysclk,
|
||||
.set_fmt = at32_ssc_set_dai_fmt,
|
||||
.set_clkdiv = at32_ssc_set_dai_clkdiv,
|
||||
},
|
||||
.private_data = &ssc_info[0],
|
||||
},
|
||||
{
|
||||
.name = "at32-ssc1",
|
||||
.id = 1,
|
||||
.type = SND_SOC_DAI_PCM,
|
||||
.suspend = at32_ssc_suspend,
|
||||
.resume = at32_ssc_resume,
|
||||
.playback = {
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.rates = AT32_SSC_RATES,
|
||||
.formats = AT32_SSC_FORMATS,
|
||||
},
|
||||
.capture = {
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.rates = AT32_SSC_RATES,
|
||||
.formats = AT32_SSC_FORMATS,
|
||||
},
|
||||
.ops = {
|
||||
.startup = at32_ssc_startup,
|
||||
.shutdown = at32_ssc_shutdown,
|
||||
.prepare = at32_ssc_prepare,
|
||||
.hw_params = at32_ssc_hw_params,
|
||||
},
|
||||
.dai_ops = {
|
||||
.set_sysclk = at32_ssc_set_dai_sysclk,
|
||||
.set_fmt = at32_ssc_set_dai_fmt,
|
||||
.set_clkdiv = at32_ssc_set_dai_clkdiv,
|
||||
},
|
||||
.private_data = &ssc_info[1],
|
||||
},
|
||||
{
|
||||
.name = "at32-ssc2",
|
||||
.id = 2,
|
||||
.type = SND_SOC_DAI_PCM,
|
||||
.suspend = at32_ssc_suspend,
|
||||
.resume = at32_ssc_resume,
|
||||
.playback = {
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.rates = AT32_SSC_RATES,
|
||||
.formats = AT32_SSC_FORMATS,
|
||||
},
|
||||
.capture = {
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.rates = AT32_SSC_RATES,
|
||||
.formats = AT32_SSC_FORMATS,
|
||||
},
|
||||
.ops = {
|
||||
.startup = at32_ssc_startup,
|
||||
.shutdown = at32_ssc_shutdown,
|
||||
.prepare = at32_ssc_prepare,
|
||||
.hw_params = at32_ssc_hw_params,
|
||||
},
|
||||
.dai_ops = {
|
||||
.set_sysclk = at32_ssc_set_dai_sysclk,
|
||||
.set_fmt = at32_ssc_set_dai_fmt,
|
||||
.set_clkdiv = at32_ssc_set_dai_clkdiv,
|
||||
},
|
||||
.private_data = &ssc_info[2],
|
||||
},
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(at32_ssc_dai);
|
||||
|
||||
|
||||
MODULE_AUTHOR("Geoffrey Wossum <gwossum@acm.org>");
|
||||
MODULE_DESCRIPTION("AT32 SSC ASoC Interface");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -1,59 +0,0 @@
|
|||
/* sound/soc/at32/at32-ssc.h
|
||||
* ASoC SSC interface for Atmel AT32 SoC
|
||||
*
|
||||
* Copyright (C) 2008 Long Range Systems
|
||||
* Geoffrey Wossum <gwossum@acm.org>
|
||||
*
|
||||
* 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 __SOUND_SOC_AT32_AT32_SSC_H
|
||||
#define __SOUND_SOC_AT32_AT32_SSC_H __FILE__
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/atmel-ssc.h>
|
||||
|
||||
#include "at32-pcm.h"
|
||||
|
||||
|
||||
|
||||
struct at32_ssc_state {
|
||||
u32 ssc_cmr;
|
||||
u32 ssc_rcmr;
|
||||
u32 ssc_rfmr;
|
||||
u32 ssc_tcmr;
|
||||
u32 ssc_tfmr;
|
||||
u32 ssc_sr;
|
||||
u32 ssc_imr;
|
||||
};
|
||||
|
||||
|
||||
|
||||
struct at32_ssc_info {
|
||||
char *name;
|
||||
struct ssc_device *ssc;
|
||||
spinlock_t lock; /* lock for dir_mask */
|
||||
unsigned short dir_mask; /* 0=unused, 1=playback, 2=capture */
|
||||
unsigned short initialized; /* true if SSC has been initialized */
|
||||
unsigned short daifmt;
|
||||
unsigned short cmr_div;
|
||||
unsigned short tcmr_period;
|
||||
unsigned short rcmr_period;
|
||||
struct at32_pcm_dma_params *dma_params[2];
|
||||
struct at32_ssc_state ssc_state;
|
||||
};
|
||||
|
||||
|
||||
/* SSC divider ids */
|
||||
#define AT32_SSC_CMR_DIV 0 /* MCK divider for BCLK */
|
||||
#define AT32_SSC_TCMR_PERIOD 1 /* BCLK divider for transmit FS */
|
||||
#define AT32_SSC_RCMR_PERIOD 2 /* BCLK divider for receive FS */
|
||||
|
||||
|
||||
extern struct snd_soc_dai at32_ssc_dai[];
|
||||
|
||||
|
||||
|
||||
#endif /* __SOUND_SOC_AT32_AT32_SSC_H */
|
|
@ -1,10 +0,0 @@
|
|||
config SND_AT91_SOC
|
||||
tristate "SoC Audio for the Atmel AT91 System-on-Chip"
|
||||
depends on ARCH_AT91
|
||||
help
|
||||
Say Y or M if you want to add support for codecs attached to
|
||||
the AT91 SSC interface. You will also need
|
||||
to select the audio interfaces to support below.
|
||||
|
||||
config SND_AT91_SOC_SSC
|
||||
tristate
|
|
@ -1,6 +0,0 @@
|
|||
# AT91 Platform Support
|
||||
snd-soc-at91-objs := at91-pcm.o
|
||||
snd-soc-at91-ssc-objs := at91-ssc.o
|
||||
|
||||
obj-$(CONFIG_SND_AT91_SOC) += snd-soc-at91.o
|
||||
obj-$(CONFIG_SND_AT91_SOC_SSC) += snd-soc-at91-ssc.o
|
|
@ -1,434 +0,0 @@
|
|||
/*
|
||||
* at91-pcm.c -- ALSA PCM interface for the Atmel AT91 SoC
|
||||
*
|
||||
* Author: Frank Mandarino <fmandarino@endrelia.com>
|
||||
* Endrelia Technologies Inc.
|
||||
* Created: Mar 3, 2006
|
||||
*
|
||||
* Based on pxa2xx-pcm.c by:
|
||||
*
|
||||
* Author: Nicolas Pitre
|
||||
* Created: Nov 30, 2004
|
||||
* Copyright: (C) 2004 MontaVista Software, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/atmel_pdc.h>
|
||||
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
|
||||
#include <mach/hardware.h>
|
||||
#include <mach/at91_ssc.h>
|
||||
|
||||
#include "at91-pcm.h"
|
||||
|
||||
#if 0
|
||||
#define DBG(x...) printk(KERN_INFO "at91-pcm: " x)
|
||||
#else
|
||||
#define DBG(x...)
|
||||
#endif
|
||||
|
||||
static const struct snd_pcm_hardware at91_pcm_hardware = {
|
||||
.info = SNDRV_PCM_INFO_MMAP |
|
||||
SNDRV_PCM_INFO_MMAP_VALID |
|
||||
SNDRV_PCM_INFO_INTERLEAVED |
|
||||
SNDRV_PCM_INFO_PAUSE,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
.period_bytes_min = 32,
|
||||
.period_bytes_max = 8192,
|
||||
.periods_min = 2,
|
||||
.periods_max = 1024,
|
||||
.buffer_bytes_max = 32 * 1024,
|
||||
};
|
||||
|
||||
struct at91_runtime_data {
|
||||
struct at91_pcm_dma_params *params;
|
||||
dma_addr_t dma_buffer; /* physical address of dma buffer */
|
||||
dma_addr_t dma_buffer_end; /* first address beyond DMA buffer */
|
||||
size_t period_size;
|
||||
dma_addr_t period_ptr; /* physical address of next period */
|
||||
u32 pdc_xpr_save; /* PDC register save */
|
||||
u32 pdc_xcr_save;
|
||||
u32 pdc_xnpr_save;
|
||||
u32 pdc_xncr_save;
|
||||
};
|
||||
|
||||
static void at91_pcm_dma_irq(u32 ssc_sr,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct at91_runtime_data *prtd = substream->runtime->private_data;
|
||||
struct at91_pcm_dma_params *params = prtd->params;
|
||||
static int count = 0;
|
||||
|
||||
count++;
|
||||
|
||||
if (ssc_sr & params->mask->ssc_endbuf) {
|
||||
|
||||
printk(KERN_WARNING
|
||||
"at91-pcm: buffer %s on %s (SSC_SR=%#x, count=%d)\n",
|
||||
substream->stream == SNDRV_PCM_STREAM_PLAYBACK
|
||||
? "underrun" : "overrun",
|
||||
params->name, ssc_sr, count);
|
||||
|
||||
/* re-start the PDC */
|
||||
at91_ssc_write(params->ssc_base + ATMEL_PDC_PTCR, params->mask->pdc_disable);
|
||||
|
||||
prtd->period_ptr += prtd->period_size;
|
||||
if (prtd->period_ptr >= prtd->dma_buffer_end) {
|
||||
prtd->period_ptr = prtd->dma_buffer;
|
||||
}
|
||||
|
||||
at91_ssc_write(params->ssc_base + params->pdc->xpr, prtd->period_ptr);
|
||||
at91_ssc_write(params->ssc_base + params->pdc->xcr,
|
||||
prtd->period_size / params->pdc_xfer_size);
|
||||
|
||||
at91_ssc_write(params->ssc_base + ATMEL_PDC_PTCR, params->mask->pdc_enable);
|
||||
}
|
||||
|
||||
if (ssc_sr & params->mask->ssc_endx) {
|
||||
|
||||
/* Load the PDC next pointer and counter registers */
|
||||
prtd->period_ptr += prtd->period_size;
|
||||
if (prtd->period_ptr >= prtd->dma_buffer_end) {
|
||||
prtd->period_ptr = prtd->dma_buffer;
|
||||
}
|
||||
at91_ssc_write(params->ssc_base + params->pdc->xnpr,
|
||||
prtd->period_ptr);
|
||||
at91_ssc_write(params->ssc_base + params->pdc->xncr,
|
||||
prtd->period_size / params->pdc_xfer_size);
|
||||
}
|
||||
|
||||
snd_pcm_period_elapsed(substream);
|
||||
}
|
||||
|
||||
static int at91_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct at91_runtime_data *prtd = runtime->private_data;
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
|
||||
/* this may get called several times by oss emulation
|
||||
* with different params */
|
||||
|
||||
snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
|
||||
runtime->dma_bytes = params_buffer_bytes(params);
|
||||
|
||||
prtd->params = rtd->dai->cpu_dai->dma_data;
|
||||
prtd->params->dma_intr_handler = at91_pcm_dma_irq;
|
||||
|
||||
prtd->dma_buffer = runtime->dma_addr;
|
||||
prtd->dma_buffer_end = runtime->dma_addr + runtime->dma_bytes;
|
||||
prtd->period_size = params_period_bytes(params);
|
||||
|
||||
DBG("hw_params: DMA for %s initialized (dma_bytes=%d, period_size=%d)\n",
|
||||
prtd->params->name, runtime->dma_bytes, prtd->period_size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int at91_pcm_hw_free(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct at91_runtime_data *prtd = substream->runtime->private_data;
|
||||
struct at91_pcm_dma_params *params = prtd->params;
|
||||
|
||||
if (params != NULL) {
|
||||
at91_ssc_write(params->ssc_base + ATMEL_PDC_PTCR, params->mask->pdc_disable);
|
||||
prtd->params->dma_intr_handler = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int at91_pcm_prepare(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct at91_runtime_data *prtd = substream->runtime->private_data;
|
||||
struct at91_pcm_dma_params *params = prtd->params;
|
||||
|
||||
at91_ssc_write(params->ssc_base + AT91_SSC_IDR,
|
||||
params->mask->ssc_endx | params->mask->ssc_endbuf);
|
||||
|
||||
at91_ssc_write(params->ssc_base + ATMEL_PDC_PTCR, params->mask->pdc_disable);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int at91_pcm_trigger(struct snd_pcm_substream *substream,
|
||||
int cmd)
|
||||
{
|
||||
struct at91_runtime_data *prtd = substream->runtime->private_data;
|
||||
struct at91_pcm_dma_params *params = prtd->params;
|
||||
int ret = 0;
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
prtd->period_ptr = prtd->dma_buffer;
|
||||
|
||||
at91_ssc_write(params->ssc_base + params->pdc->xpr, prtd->period_ptr);
|
||||
at91_ssc_write(params->ssc_base + params->pdc->xcr,
|
||||
prtd->period_size / params->pdc_xfer_size);
|
||||
|
||||
prtd->period_ptr += prtd->period_size;
|
||||
at91_ssc_write(params->ssc_base + params->pdc->xnpr, prtd->period_ptr);
|
||||
at91_ssc_write(params->ssc_base + params->pdc->xncr,
|
||||
prtd->period_size / params->pdc_xfer_size);
|
||||
|
||||
DBG("trigger: period_ptr=%lx, xpr=%lx, xcr=%ld, xnpr=%lx, xncr=%ld\n",
|
||||
(unsigned long) prtd->period_ptr,
|
||||
at91_ssc_read(params->ssc_base + params->pdc->xpr),
|
||||
at91_ssc_read(params->ssc_base + params->pdc->xcr),
|
||||
at91_ssc_read(params->ssc_base + params->pdc->xnpr),
|
||||
at91_ssc_read(params->ssc_base + params->pdc->xncr));
|
||||
|
||||
at91_ssc_write(params->ssc_base + AT91_SSC_IER,
|
||||
params->mask->ssc_endx | params->mask->ssc_endbuf);
|
||||
|
||||
at91_ssc_write(params->ssc_base + ATMEL_PDC_PTCR,
|
||||
params->mask->pdc_enable);
|
||||
|
||||
DBG("sr=%lx imr=%lx\n",
|
||||
at91_ssc_read(params->ssc_base + AT91_SSC_SR),
|
||||
at91_ssc_read(params->ssc_base + AT91_SSC_IMR));
|
||||
break;
|
||||
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
at91_ssc_write(params->ssc_base + ATMEL_PDC_PTCR, params->mask->pdc_disable);
|
||||
break;
|
||||
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
at91_ssc_write(params->ssc_base + ATMEL_PDC_PTCR, params->mask->pdc_enable);
|
||||
break;
|
||||
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static snd_pcm_uframes_t at91_pcm_pointer(
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct at91_runtime_data *prtd = runtime->private_data;
|
||||
struct at91_pcm_dma_params *params = prtd->params;
|
||||
dma_addr_t ptr;
|
||||
snd_pcm_uframes_t x;
|
||||
|
||||
ptr = (dma_addr_t) at91_ssc_read(params->ssc_base + params->pdc->xpr);
|
||||
x = bytes_to_frames(runtime, ptr - prtd->dma_buffer);
|
||||
|
||||
if (x == runtime->buffer_size)
|
||||
x = 0;
|
||||
return x;
|
||||
}
|
||||
|
||||
static int at91_pcm_open(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct at91_runtime_data *prtd;
|
||||
int ret = 0;
|
||||
|
||||
snd_soc_set_runtime_hwparams(substream, &at91_pcm_hardware);
|
||||
|
||||
/* ensure that buffer size is a multiple of period size */
|
||||
ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
prtd = kzalloc(sizeof(struct at91_runtime_data), GFP_KERNEL);
|
||||
if (prtd == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
runtime->private_data = prtd;
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int at91_pcm_close(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct at91_runtime_data *prtd = substream->runtime->private_data;
|
||||
|
||||
kfree(prtd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int at91_pcm_mmap(struct snd_pcm_substream *substream,
|
||||
struct vm_area_struct *vma)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
|
||||
return dma_mmap_writecombine(substream->pcm->card->dev, vma,
|
||||
runtime->dma_area,
|
||||
runtime->dma_addr,
|
||||
runtime->dma_bytes);
|
||||
}
|
||||
|
||||
struct snd_pcm_ops at91_pcm_ops = {
|
||||
.open = at91_pcm_open,
|
||||
.close = at91_pcm_close,
|
||||
.ioctl = snd_pcm_lib_ioctl,
|
||||
.hw_params = at91_pcm_hw_params,
|
||||
.hw_free = at91_pcm_hw_free,
|
||||
.prepare = at91_pcm_prepare,
|
||||
.trigger = at91_pcm_trigger,
|
||||
.pointer = at91_pcm_pointer,
|
||||
.mmap = at91_pcm_mmap,
|
||||
};
|
||||
|
||||
static int at91_pcm_preallocate_dma_buffer(struct snd_pcm *pcm,
|
||||
int stream)
|
||||
{
|
||||
struct snd_pcm_substream *substream = pcm->streams[stream].substream;
|
||||
struct snd_dma_buffer *buf = &substream->dma_buffer;
|
||||
size_t size = at91_pcm_hardware.buffer_bytes_max;
|
||||
|
||||
buf->dev.type = SNDRV_DMA_TYPE_DEV;
|
||||
buf->dev.dev = pcm->card->dev;
|
||||
buf->private_data = NULL;
|
||||
buf->area = dma_alloc_writecombine(pcm->card->dev, size,
|
||||
&buf->addr, GFP_KERNEL);
|
||||
|
||||
DBG("preallocate_dma_buffer: area=%p, addr=%p, size=%d\n",
|
||||
(void *) buf->area,
|
||||
(void *) buf->addr,
|
||||
size);
|
||||
|
||||
if (!buf->area)
|
||||
return -ENOMEM;
|
||||
|
||||
buf->bytes = size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u64 at91_pcm_dmamask = 0xffffffff;
|
||||
|
||||
static int at91_pcm_new(struct snd_card *card,
|
||||
struct snd_soc_dai *dai, struct snd_pcm *pcm)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (!card->dev->dma_mask)
|
||||
card->dev->dma_mask = &at91_pcm_dmamask;
|
||||
if (!card->dev->coherent_dma_mask)
|
||||
card->dev->coherent_dma_mask = 0xffffffff;
|
||||
|
||||
if (dai->playback.channels_min) {
|
||||
ret = at91_pcm_preallocate_dma_buffer(pcm,
|
||||
SNDRV_PCM_STREAM_PLAYBACK);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (dai->capture.channels_min) {
|
||||
ret = at91_pcm_preallocate_dma_buffer(pcm,
|
||||
SNDRV_PCM_STREAM_CAPTURE);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void at91_pcm_free_dma_buffers(struct snd_pcm *pcm)
|
||||
{
|
||||
struct snd_pcm_substream *substream;
|
||||
struct snd_dma_buffer *buf;
|
||||
int stream;
|
||||
|
||||
for (stream = 0; stream < 2; stream++) {
|
||||
substream = pcm->streams[stream].substream;
|
||||
if (!substream)
|
||||
continue;
|
||||
|
||||
buf = &substream->dma_buffer;
|
||||
if (!buf->area)
|
||||
continue;
|
||||
|
||||
dma_free_writecombine(pcm->card->dev, buf->bytes,
|
||||
buf->area, buf->addr);
|
||||
buf->area = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int at91_pcm_suspend(struct platform_device *pdev,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = dai->runtime;
|
||||
struct at91_runtime_data *prtd;
|
||||
struct at91_pcm_dma_params *params;
|
||||
|
||||
if (!runtime)
|
||||
return 0;
|
||||
|
||||
prtd = runtime->private_data;
|
||||
params = prtd->params;
|
||||
|
||||
/* disable the PDC and save the PDC registers */
|
||||
|
||||
at91_ssc_write(params->ssc_base + ATMEL_PDC_PTCR, params->mask->pdc_disable);
|
||||
|
||||
prtd->pdc_xpr_save = at91_ssc_read(params->ssc_base + params->pdc->xpr);
|
||||
prtd->pdc_xcr_save = at91_ssc_read(params->ssc_base + params->pdc->xcr);
|
||||
prtd->pdc_xnpr_save = at91_ssc_read(params->ssc_base + params->pdc->xnpr);
|
||||
prtd->pdc_xncr_save = at91_ssc_read(params->ssc_base + params->pdc->xncr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int at91_pcm_resume(struct platform_device *pdev,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = dai->runtime;
|
||||
struct at91_runtime_data *prtd;
|
||||
struct at91_pcm_dma_params *params;
|
||||
|
||||
if (!runtime)
|
||||
return 0;
|
||||
|
||||
prtd = runtime->private_data;
|
||||
params = prtd->params;
|
||||
|
||||
/* restore the PDC registers and enable the PDC */
|
||||
at91_ssc_write(params->ssc_base + params->pdc->xpr, prtd->pdc_xpr_save);
|
||||
at91_ssc_write(params->ssc_base + params->pdc->xcr, prtd->pdc_xcr_save);
|
||||
at91_ssc_write(params->ssc_base + params->pdc->xnpr, prtd->pdc_xnpr_save);
|
||||
at91_ssc_write(params->ssc_base + params->pdc->xncr, prtd->pdc_xncr_save);
|
||||
|
||||
at91_ssc_write(params->ssc_base + ATMEL_PDC_PTCR, params->mask->pdc_enable);
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#define at91_pcm_suspend NULL
|
||||
#define at91_pcm_resume NULL
|
||||
#endif
|
||||
|
||||
struct snd_soc_platform at91_soc_platform = {
|
||||
.name = "at91-audio",
|
||||
.pcm_ops = &at91_pcm_ops,
|
||||
.pcm_new = at91_pcm_new,
|
||||
.pcm_free = at91_pcm_free_dma_buffers,
|
||||
.suspend = at91_pcm_suspend,
|
||||
.resume = at91_pcm_resume,
|
||||
};
|
||||
|
||||
EXPORT_SYMBOL_GPL(at91_soc_platform);
|
||||
|
||||
MODULE_AUTHOR("Frank Mandarino <fmandarino@endrelia.com>");
|
||||
MODULE_DESCRIPTION("Atmel AT91 PCM module");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -1,72 +0,0 @@
|
|||
/*
|
||||
* at91-pcm.h - ALSA PCM interface for the Atmel AT91 SoC
|
||||
*
|
||||
* Author: Frank Mandarino <fmandarino@endrelia.com>
|
||||
* Endrelia Technologies Inc.
|
||||
* Created: Mar 3, 2006
|
||||
*
|
||||
* Based on pxa2xx-pcm.h by:
|
||||
*
|
||||
* Author: Nicolas Pitre
|
||||
* Created: Nov 30, 2004
|
||||
* Copyright: MontaVista Software, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef _AT91_PCM_H
|
||||
#define _AT91_PCM_H
|
||||
|
||||
#include <mach/hardware.h>
|
||||
|
||||
struct at91_ssc_periph {
|
||||
void __iomem *base;
|
||||
u32 pid;
|
||||
};
|
||||
|
||||
/*
|
||||
* Registers and status bits that are required by the PCM driver.
|
||||
*/
|
||||
struct at91_pdc_regs {
|
||||
unsigned int xpr; /* PDC recv/trans pointer */
|
||||
unsigned int xcr; /* PDC recv/trans counter */
|
||||
unsigned int xnpr; /* PDC next recv/trans pointer */
|
||||
unsigned int xncr; /* PDC next recv/trans counter */
|
||||
unsigned int ptcr; /* PDC transfer control */
|
||||
};
|
||||
|
||||
struct at91_ssc_mask {
|
||||
u32 ssc_enable; /* SSC recv/trans enable */
|
||||
u32 ssc_disable; /* SSC recv/trans disable */
|
||||
u32 ssc_endx; /* SSC ENDTX or ENDRX */
|
||||
u32 ssc_endbuf; /* SSC TXBUFE or RXBUFF */
|
||||
u32 pdc_enable; /* PDC recv/trans enable */
|
||||
u32 pdc_disable; /* PDC recv/trans disable */
|
||||
};
|
||||
|
||||
/*
|
||||
* This structure, shared between the PCM driver and the interface,
|
||||
* contains all information required by the PCM driver to perform the
|
||||
* PDC DMA operation. All fields except dma_intr_handler() are initialized
|
||||
* by the interface. The dms_intr_handler() pointer is set by the PCM
|
||||
* driver and called by the interface SSC interrupt handler if it is
|
||||
* non-NULL.
|
||||
*/
|
||||
struct at91_pcm_dma_params {
|
||||
char *name; /* stream identifier */
|
||||
int pdc_xfer_size; /* PDC counter increment in bytes */
|
||||
void __iomem *ssc_base; /* SSC base address */
|
||||
struct at91_pdc_regs *pdc; /* PDC receive or transmit registers */
|
||||
struct at91_ssc_mask *mask;/* SSC & PDC status bits */
|
||||
struct snd_pcm_substream *substream;
|
||||
void (*dma_intr_handler)(u32, struct snd_pcm_substream *);
|
||||
};
|
||||
|
||||
extern struct snd_soc_platform at91_soc_platform;
|
||||
|
||||
#define at91_ssc_read(a) ((unsigned long) __raw_readl(a))
|
||||
#define at91_ssc_write(a,v) __raw_writel((v),(a))
|
||||
|
||||
#endif /* _AT91_PCM_H */
|
|
@ -1,791 +0,0 @@
|
|||
/*
|
||||
* at91-ssc.c -- ALSA SoC AT91 SSC Audio Layer Platform driver
|
||||
*
|
||||
* Author: Frank Mandarino <fmandarino@endrelia.com>
|
||||
* Endrelia Technologies Inc.
|
||||
*
|
||||
* Based on pxa2xx Platform drivers by
|
||||
* Liam Girdwood <lrg@slimlogic.co.uk>
|
||||
*
|
||||
* 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/interrupt.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/atmel_pdc.h>
|
||||
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/initval.h>
|
||||
#include <sound/soc.h>
|
||||
|
||||
#include <mach/hardware.h>
|
||||
#include <mach/at91_pmc.h>
|
||||
#include <mach/at91_ssc.h>
|
||||
|
||||
#include "at91-pcm.h"
|
||||
#include "at91-ssc.h"
|
||||
|
||||
#if 0
|
||||
#define DBG(x...) printk(KERN_DEBUG "at91-ssc:" x)
|
||||
#else
|
||||
#define DBG(x...)
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_ARCH_AT91SAM9260) || defined(CONFIG_ARCH_AT91SAM9G20)
|
||||
#define NUM_SSC_DEVICES 1
|
||||
#else
|
||||
#define NUM_SSC_DEVICES 3
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* SSC PDC registers required by the PCM DMA engine.
|
||||
*/
|
||||
static struct at91_pdc_regs pdc_tx_reg = {
|
||||
.xpr = ATMEL_PDC_TPR,
|
||||
.xcr = ATMEL_PDC_TCR,
|
||||
.xnpr = ATMEL_PDC_TNPR,
|
||||
.xncr = ATMEL_PDC_TNCR,
|
||||
};
|
||||
|
||||
static struct at91_pdc_regs pdc_rx_reg = {
|
||||
.xpr = ATMEL_PDC_RPR,
|
||||
.xcr = ATMEL_PDC_RCR,
|
||||
.xnpr = ATMEL_PDC_RNPR,
|
||||
.xncr = ATMEL_PDC_RNCR,
|
||||
};
|
||||
|
||||
/*
|
||||
* SSC & PDC status bits for transmit and receive.
|
||||
*/
|
||||
static struct at91_ssc_mask ssc_tx_mask = {
|
||||
.ssc_enable = AT91_SSC_TXEN,
|
||||
.ssc_disable = AT91_SSC_TXDIS,
|
||||
.ssc_endx = AT91_SSC_ENDTX,
|
||||
.ssc_endbuf = AT91_SSC_TXBUFE,
|
||||
.pdc_enable = ATMEL_PDC_TXTEN,
|
||||
.pdc_disable = ATMEL_PDC_TXTDIS,
|
||||
};
|
||||
|
||||
static struct at91_ssc_mask ssc_rx_mask = {
|
||||
.ssc_enable = AT91_SSC_RXEN,
|
||||
.ssc_disable = AT91_SSC_RXDIS,
|
||||
.ssc_endx = AT91_SSC_ENDRX,
|
||||
.ssc_endbuf = AT91_SSC_RXBUFF,
|
||||
.pdc_enable = ATMEL_PDC_RXTEN,
|
||||
.pdc_disable = ATMEL_PDC_RXTDIS,
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* DMA parameters.
|
||||
*/
|
||||
static struct at91_pcm_dma_params ssc_dma_params[NUM_SSC_DEVICES][2] = {
|
||||
{{
|
||||
.name = "SSC0 PCM out",
|
||||
.pdc = &pdc_tx_reg,
|
||||
.mask = &ssc_tx_mask,
|
||||
},
|
||||
{
|
||||
.name = "SSC0 PCM in",
|
||||
.pdc = &pdc_rx_reg,
|
||||
.mask = &ssc_rx_mask,
|
||||
}},
|
||||
#if NUM_SSC_DEVICES == 3
|
||||
{{
|
||||
.name = "SSC1 PCM out",
|
||||
.pdc = &pdc_tx_reg,
|
||||
.mask = &ssc_tx_mask,
|
||||
},
|
||||
{
|
||||
.name = "SSC1 PCM in",
|
||||
.pdc = &pdc_rx_reg,
|
||||
.mask = &ssc_rx_mask,
|
||||
}},
|
||||
{{
|
||||
.name = "SSC2 PCM out",
|
||||
.pdc = &pdc_tx_reg,
|
||||
.mask = &ssc_tx_mask,
|
||||
},
|
||||
{
|
||||
.name = "SSC2 PCM in",
|
||||
.pdc = &pdc_rx_reg,
|
||||
.mask = &ssc_rx_mask,
|
||||
}},
|
||||
#endif
|
||||
};
|
||||
|
||||
struct at91_ssc_state {
|
||||
u32 ssc_cmr;
|
||||
u32 ssc_rcmr;
|
||||
u32 ssc_rfmr;
|
||||
u32 ssc_tcmr;
|
||||
u32 ssc_tfmr;
|
||||
u32 ssc_sr;
|
||||
u32 ssc_imr;
|
||||
};
|
||||
|
||||
static struct at91_ssc_info {
|
||||
char *name;
|
||||
struct at91_ssc_periph ssc;
|
||||
spinlock_t lock; /* lock for dir_mask */
|
||||
unsigned short dir_mask; /* 0=unused, 1=playback, 2=capture */
|
||||
unsigned short initialized; /* 1=SSC has been initialized */
|
||||
unsigned short daifmt;
|
||||
unsigned short cmr_div;
|
||||
unsigned short tcmr_period;
|
||||
unsigned short rcmr_period;
|
||||
struct at91_pcm_dma_params *dma_params[2];
|
||||
struct at91_ssc_state ssc_state;
|
||||
|
||||
} ssc_info[NUM_SSC_DEVICES] = {
|
||||
{
|
||||
.name = "ssc0",
|
||||
.lock = __SPIN_LOCK_UNLOCKED(ssc_info[0].lock),
|
||||
.dir_mask = 0,
|
||||
.initialized = 0,
|
||||
},
|
||||
#if NUM_SSC_DEVICES == 3
|
||||
{
|
||||
.name = "ssc1",
|
||||
.lock = __SPIN_LOCK_UNLOCKED(ssc_info[1].lock),
|
||||
.dir_mask = 0,
|
||||
.initialized = 0,
|
||||
},
|
||||
{
|
||||
.name = "ssc2",
|
||||
.lock = __SPIN_LOCK_UNLOCKED(ssc_info[2].lock),
|
||||
.dir_mask = 0,
|
||||
.initialized = 0,
|
||||
},
|
||||
#endif
|
||||
};
|
||||
|
||||
static unsigned int at91_ssc_sysclk;
|
||||
|
||||
/*
|
||||
* SSC interrupt handler. Passes PDC interrupts to the DMA
|
||||
* interrupt handler in the PCM driver.
|
||||
*/
|
||||
static irqreturn_t at91_ssc_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct at91_ssc_info *ssc_p = dev_id;
|
||||
struct at91_pcm_dma_params *dma_params;
|
||||
u32 ssc_sr;
|
||||
int i;
|
||||
|
||||
ssc_sr = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_SR)
|
||||
& at91_ssc_read(ssc_p->ssc.base + AT91_SSC_IMR);
|
||||
|
||||
/*
|
||||
* Loop through the substreams attached to this SSC. If
|
||||
* a DMA-related interrupt occurred on that substream, call
|
||||
* the DMA interrupt handler function, if one has been
|
||||
* registered in the dma_params structure by the PCM driver.
|
||||
*/
|
||||
for (i = 0; i < ARRAY_SIZE(ssc_p->dma_params); i++) {
|
||||
dma_params = ssc_p->dma_params[i];
|
||||
|
||||
if (dma_params != NULL && dma_params->dma_intr_handler != NULL &&
|
||||
(ssc_sr &
|
||||
(dma_params->mask->ssc_endx | dma_params->mask->ssc_endbuf)))
|
||||
|
||||
dma_params->dma_intr_handler(ssc_sr, dma_params->substream);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/*
|
||||
* Startup. Only that one substream allowed in each direction.
|
||||
*/
|
||||
static int at91_ssc_startup(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct at91_ssc_info *ssc_p = &ssc_info[rtd->dai->cpu_dai->id];
|
||||
int dir_mask;
|
||||
|
||||
DBG("ssc_startup: SSC_SR=0x%08lx\n",
|
||||
at91_ssc_read(ssc_p->ssc.base + AT91_SSC_SR));
|
||||
dir_mask = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0x1 : 0x2;
|
||||
|
||||
spin_lock_irq(&ssc_p->lock);
|
||||
if (ssc_p->dir_mask & dir_mask) {
|
||||
spin_unlock_irq(&ssc_p->lock);
|
||||
return -EBUSY;
|
||||
}
|
||||
ssc_p->dir_mask |= dir_mask;
|
||||
spin_unlock_irq(&ssc_p->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Shutdown. Clear DMA parameters and shutdown the SSC if there
|
||||
* are no other substreams open.
|
||||
*/
|
||||
static void at91_ssc_shutdown(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct at91_ssc_info *ssc_p = &ssc_info[rtd->dai->cpu_dai->id];
|
||||
struct at91_pcm_dma_params *dma_params;
|
||||
int dir, dir_mask;
|
||||
|
||||
dir = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0 : 1;
|
||||
dma_params = ssc_p->dma_params[dir];
|
||||
|
||||
if (dma_params != NULL) {
|
||||
at91_ssc_write(dma_params->ssc_base + AT91_SSC_CR,
|
||||
dma_params->mask->ssc_disable);
|
||||
DBG("%s disabled SSC_SR=0x%08lx\n", (dir ? "receive" : "transmit"),
|
||||
at91_ssc_read(ssc_p->ssc.base + AT91_SSC_SR));
|
||||
|
||||
dma_params->ssc_base = NULL;
|
||||
dma_params->substream = NULL;
|
||||
ssc_p->dma_params[dir] = NULL;
|
||||
}
|
||||
|
||||
dir_mask = 1 << dir;
|
||||
|
||||
spin_lock_irq(&ssc_p->lock);
|
||||
ssc_p->dir_mask &= ~dir_mask;
|
||||
if (!ssc_p->dir_mask) {
|
||||
/* Shutdown the SSC clock. */
|
||||
DBG("Stopping pid %d clock\n", ssc_p->ssc.pid);
|
||||
at91_sys_write(AT91_PMC_PCDR, 1<<ssc_p->ssc.pid);
|
||||
|
||||
if (ssc_p->initialized) {
|
||||
free_irq(ssc_p->ssc.pid, ssc_p);
|
||||
ssc_p->initialized = 0;
|
||||
}
|
||||
|
||||
/* Reset the SSC */
|
||||
at91_ssc_write(ssc_p->ssc.base + AT91_SSC_CR, AT91_SSC_SWRST);
|
||||
|
||||
/* Clear the SSC dividers */
|
||||
ssc_p->cmr_div = ssc_p->tcmr_period = ssc_p->rcmr_period = 0;
|
||||
}
|
||||
spin_unlock_irq(&ssc_p->lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* Record the SSC system clock rate.
|
||||
*/
|
||||
static int at91_ssc_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
|
||||
int clk_id, unsigned int freq, int dir)
|
||||
{
|
||||
/*
|
||||
* The only clock supplied to the SSC is the AT91 master clock,
|
||||
* which is only used if the SSC is generating BCLK and/or
|
||||
* LRC clocks.
|
||||
*/
|
||||
switch (clk_id) {
|
||||
case AT91_SYSCLK_MCK:
|
||||
at91_ssc_sysclk = freq;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Record the DAI format for use in hw_params().
|
||||
*/
|
||||
static int at91_ssc_set_dai_fmt(struct snd_soc_dai *cpu_dai,
|
||||
unsigned int fmt)
|
||||
{
|
||||
struct at91_ssc_info *ssc_p = &ssc_info[cpu_dai->id];
|
||||
|
||||
ssc_p->daifmt = fmt;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Record SSC clock dividers for use in hw_params().
|
||||
*/
|
||||
static int at91_ssc_set_dai_clkdiv(struct snd_soc_dai *cpu_dai,
|
||||
int div_id, int div)
|
||||
{
|
||||
struct at91_ssc_info *ssc_p = &ssc_info[cpu_dai->id];
|
||||
|
||||
switch (div_id) {
|
||||
case AT91SSC_CMR_DIV:
|
||||
/*
|
||||
* The same master clock divider is used for both
|
||||
* transmit and receive, so if a value has already
|
||||
* been set, it must match this value.
|
||||
*/
|
||||
if (ssc_p->cmr_div == 0)
|
||||
ssc_p->cmr_div = div;
|
||||
else
|
||||
if (div != ssc_p->cmr_div)
|
||||
return -EBUSY;
|
||||
break;
|
||||
|
||||
case AT91SSC_TCMR_PERIOD:
|
||||
ssc_p->tcmr_period = div;
|
||||
break;
|
||||
|
||||
case AT91SSC_RCMR_PERIOD:
|
||||
ssc_p->rcmr_period = div;
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Configure the SSC.
|
||||
*/
|
||||
static int at91_ssc_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
int id = rtd->dai->cpu_dai->id;
|
||||
struct at91_ssc_info *ssc_p = &ssc_info[id];
|
||||
struct at91_pcm_dma_params *dma_params;
|
||||
int dir, channels, bits;
|
||||
u32 tfmr, rfmr, tcmr, rcmr;
|
||||
int start_event;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Currently, there is only one set of dma params for
|
||||
* each direction. If more are added, this code will
|
||||
* have to be changed to select the proper set.
|
||||
*/
|
||||
dir = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0 : 1;
|
||||
|
||||
dma_params = &ssc_dma_params[id][dir];
|
||||
dma_params->ssc_base = ssc_p->ssc.base;
|
||||
dma_params->substream = substream;
|
||||
|
||||
ssc_p->dma_params[dir] = dma_params;
|
||||
|
||||
/*
|
||||
* The cpu_dai->dma_data field is only used to communicate the
|
||||
* appropriate DMA parameters to the pcm driver hw_params()
|
||||
* function. It should not be used for other purposes
|
||||
* as it is common to all substreams.
|
||||
*/
|
||||
rtd->dai->cpu_dai->dma_data = dma_params;
|
||||
|
||||
channels = params_channels(params);
|
||||
|
||||
/*
|
||||
* Determine sample size in bits and the PDC increment.
|
||||
*/
|
||||
switch(params_format(params)) {
|
||||
case SNDRV_PCM_FORMAT_S8:
|
||||
bits = 8;
|
||||
dma_params->pdc_xfer_size = 1;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S16_LE:
|
||||
bits = 16;
|
||||
dma_params->pdc_xfer_size = 2;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S24_LE:
|
||||
bits = 24;
|
||||
dma_params->pdc_xfer_size = 4;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S32_LE:
|
||||
bits = 32;
|
||||
dma_params->pdc_xfer_size = 4;
|
||||
break;
|
||||
default:
|
||||
printk(KERN_WARNING "at91-ssc: unsupported PCM format\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* The SSC only supports up to 16-bit samples in I2S format, due
|
||||
* to the size of the Frame Mode Register FSLEN field.
|
||||
*/
|
||||
if ((ssc_p->daifmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_I2S
|
||||
&& bits > 16) {
|
||||
printk(KERN_WARNING
|
||||
"at91-ssc: sample size %d is too large for I2S\n", bits);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Compute SSC register settings.
|
||||
*/
|
||||
switch (ssc_p->daifmt
|
||||
& (SND_SOC_DAIFMT_FORMAT_MASK | SND_SOC_DAIFMT_MASTER_MASK)) {
|
||||
|
||||
case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS:
|
||||
/*
|
||||
* I2S format, SSC provides BCLK and LRC clocks.
|
||||
*
|
||||
* The SSC transmit and receive clocks are generated from the
|
||||
* MCK divider, and the BCLK signal is output on the SSC TK line.
|
||||
*/
|
||||
rcmr = (( ssc_p->rcmr_period << 24) & AT91_SSC_PERIOD)
|
||||
| (( 1 << 16) & AT91_SSC_STTDLY)
|
||||
| (( AT91_SSC_START_FALLING_RF ) & AT91_SSC_START)
|
||||
| (( AT91_SSC_CK_RISING ) & AT91_SSC_CKI)
|
||||
| (( AT91_SSC_CKO_NONE ) & AT91_SSC_CKO)
|
||||
| (( AT91_SSC_CKS_DIV ) & AT91_SSC_CKS);
|
||||
|
||||
rfmr = (( AT91_SSC_FSEDGE_POSITIVE ) & AT91_SSC_FSEDGE)
|
||||
| (( AT91_SSC_FSOS_NEGATIVE ) & AT91_SSC_FSOS)
|
||||
| (((bits - 1) << 16) & AT91_SSC_FSLEN)
|
||||
| (((channels - 1) << 8) & AT91_SSC_DATNB)
|
||||
| (( 1 << 7) & AT91_SSC_MSBF)
|
||||
| (( 0 << 5) & AT91_SSC_LOOP)
|
||||
| (((bits - 1) << 0) & AT91_SSC_DATALEN);
|
||||
|
||||
tcmr = (( ssc_p->tcmr_period << 24) & AT91_SSC_PERIOD)
|
||||
| (( 1 << 16) & AT91_SSC_STTDLY)
|
||||
| (( AT91_SSC_START_FALLING_RF ) & AT91_SSC_START)
|
||||
| (( AT91_SSC_CKI_FALLING ) & AT91_SSC_CKI)
|
||||
| (( AT91_SSC_CKO_CONTINUOUS ) & AT91_SSC_CKO)
|
||||
| (( AT91_SSC_CKS_DIV ) & AT91_SSC_CKS);
|
||||
|
||||
tfmr = (( AT91_SSC_FSEDGE_POSITIVE ) & AT91_SSC_FSEDGE)
|
||||
| (( 0 << 23) & AT91_SSC_FSDEN)
|
||||
| (( AT91_SSC_FSOS_NEGATIVE ) & AT91_SSC_FSOS)
|
||||
| (((bits - 1) << 16) & AT91_SSC_FSLEN)
|
||||
| (((channels - 1) << 8) & AT91_SSC_DATNB)
|
||||
| (( 1 << 7) & AT91_SSC_MSBF)
|
||||
| (( 0 << 5) & AT91_SSC_DATDEF)
|
||||
| (((bits - 1) << 0) & AT91_SSC_DATALEN);
|
||||
break;
|
||||
|
||||
case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM:
|
||||
/*
|
||||
* I2S format, CODEC supplies BCLK and LRC clocks.
|
||||
*
|
||||
* The SSC transmit clock is obtained from the BCLK signal on
|
||||
* on the TK line, and the SSC receive clock is generated from the
|
||||
* transmit clock.
|
||||
*
|
||||
* For single channel data, one sample is transferred on the falling
|
||||
* edge of the LRC clock. For two channel data, one sample is
|
||||
* transferred on both edges of the LRC clock.
|
||||
*/
|
||||
start_event = channels == 1
|
||||
? AT91_SSC_START_FALLING_RF
|
||||
: AT91_SSC_START_EDGE_RF;
|
||||
|
||||
rcmr = (( 0 << 24) & AT91_SSC_PERIOD)
|
||||
| (( 1 << 16) & AT91_SSC_STTDLY)
|
||||
| (( start_event ) & AT91_SSC_START)
|
||||
| (( AT91_SSC_CK_RISING ) & AT91_SSC_CKI)
|
||||
| (( AT91_SSC_CKO_NONE ) & AT91_SSC_CKO)
|
||||
| (( AT91_SSC_CKS_CLOCK ) & AT91_SSC_CKS);
|
||||
|
||||
rfmr = (( AT91_SSC_FSEDGE_POSITIVE ) & AT91_SSC_FSEDGE)
|
||||
| (( AT91_SSC_FSOS_NONE ) & AT91_SSC_FSOS)
|
||||
| (( 0 << 16) & AT91_SSC_FSLEN)
|
||||
| (( 0 << 8) & AT91_SSC_DATNB)
|
||||
| (( 1 << 7) & AT91_SSC_MSBF)
|
||||
| (( 0 << 5) & AT91_SSC_LOOP)
|
||||
| (((bits - 1) << 0) & AT91_SSC_DATALEN);
|
||||
|
||||
tcmr = (( 0 << 24) & AT91_SSC_PERIOD)
|
||||
| (( 1 << 16) & AT91_SSC_STTDLY)
|
||||
| (( start_event ) & AT91_SSC_START)
|
||||
| (( AT91_SSC_CKI_FALLING ) & AT91_SSC_CKI)
|
||||
| (( AT91_SSC_CKO_NONE ) & AT91_SSC_CKO)
|
||||
| (( AT91_SSC_CKS_PIN ) & AT91_SSC_CKS);
|
||||
|
||||
tfmr = (( AT91_SSC_FSEDGE_POSITIVE ) & AT91_SSC_FSEDGE)
|
||||
| (( 0 << 23) & AT91_SSC_FSDEN)
|
||||
| (( AT91_SSC_FSOS_NONE ) & AT91_SSC_FSOS)
|
||||
| (( 0 << 16) & AT91_SSC_FSLEN)
|
||||
| (( 0 << 8) & AT91_SSC_DATNB)
|
||||
| (( 1 << 7) & AT91_SSC_MSBF)
|
||||
| (( 0 << 5) & AT91_SSC_DATDEF)
|
||||
| (((bits - 1) << 0) & AT91_SSC_DATALEN);
|
||||
break;
|
||||
|
||||
case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBS_CFS:
|
||||
/*
|
||||
* DSP/PCM Mode A format, SSC provides BCLK and LRC clocks.
|
||||
*
|
||||
* The SSC transmit and receive clocks are generated from the
|
||||
* MCK divider, and the BCLK signal is output on the SSC TK line.
|
||||
*/
|
||||
rcmr = (( ssc_p->rcmr_period << 24) & AT91_SSC_PERIOD)
|
||||
| (( 1 << 16) & AT91_SSC_STTDLY)
|
||||
| (( AT91_SSC_START_RISING_RF ) & AT91_SSC_START)
|
||||
| (( AT91_SSC_CK_RISING ) & AT91_SSC_CKI)
|
||||
| (( AT91_SSC_CKO_NONE ) & AT91_SSC_CKO)
|
||||
| (( AT91_SSC_CKS_DIV ) & AT91_SSC_CKS);
|
||||
|
||||
rfmr = (( AT91_SSC_FSEDGE_POSITIVE ) & AT91_SSC_FSEDGE)
|
||||
| (( AT91_SSC_FSOS_POSITIVE ) & AT91_SSC_FSOS)
|
||||
| (( 0 << 16) & AT91_SSC_FSLEN)
|
||||
| (((channels - 1) << 8) & AT91_SSC_DATNB)
|
||||
| (( 1 << 7) & AT91_SSC_MSBF)
|
||||
| (( 0 << 5) & AT91_SSC_LOOP)
|
||||
| (((bits - 1) << 0) & AT91_SSC_DATALEN);
|
||||
|
||||
tcmr = (( ssc_p->tcmr_period << 24) & AT91_SSC_PERIOD)
|
||||
| (( 1 << 16) & AT91_SSC_STTDLY)
|
||||
| (( AT91_SSC_START_RISING_RF ) & AT91_SSC_START)
|
||||
| (( AT91_SSC_CK_RISING ) & AT91_SSC_CKI)
|
||||
| (( AT91_SSC_CKO_CONTINUOUS ) & AT91_SSC_CKO)
|
||||
| (( AT91_SSC_CKS_DIV ) & AT91_SSC_CKS);
|
||||
|
||||
tfmr = (( AT91_SSC_FSEDGE_POSITIVE ) & AT91_SSC_FSEDGE)
|
||||
| (( 0 << 23) & AT91_SSC_FSDEN)
|
||||
| (( AT91_SSC_FSOS_POSITIVE ) & AT91_SSC_FSOS)
|
||||
| (( 0 << 16) & AT91_SSC_FSLEN)
|
||||
| (((channels - 1) << 8) & AT91_SSC_DATNB)
|
||||
| (( 1 << 7) & AT91_SSC_MSBF)
|
||||
| (( 0 << 5) & AT91_SSC_DATDEF)
|
||||
| (((bits - 1) << 0) & AT91_SSC_DATALEN);
|
||||
|
||||
|
||||
|
||||
break;
|
||||
|
||||
case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBM_CFM:
|
||||
default:
|
||||
printk(KERN_WARNING "at91-ssc: unsupported DAI format 0x%x.\n",
|
||||
ssc_p->daifmt);
|
||||
return -EINVAL;
|
||||
break;
|
||||
}
|
||||
DBG("RCMR=%08x RFMR=%08x TCMR=%08x TFMR=%08x\n", rcmr, rfmr, tcmr, tfmr);
|
||||
|
||||
if (!ssc_p->initialized) {
|
||||
|
||||
/* Enable PMC peripheral clock for this SSC */
|
||||
DBG("Starting pid %d clock\n", ssc_p->ssc.pid);
|
||||
at91_sys_write(AT91_PMC_PCER, 1<<ssc_p->ssc.pid);
|
||||
|
||||
/* Reset the SSC and its PDC registers */
|
||||
at91_ssc_write(ssc_p->ssc.base + AT91_SSC_CR, AT91_SSC_SWRST);
|
||||
|
||||
at91_ssc_write(ssc_p->ssc.base + ATMEL_PDC_RPR, 0);
|
||||
at91_ssc_write(ssc_p->ssc.base + ATMEL_PDC_RCR, 0);
|
||||
at91_ssc_write(ssc_p->ssc.base + ATMEL_PDC_RNPR, 0);
|
||||
at91_ssc_write(ssc_p->ssc.base + ATMEL_PDC_RNCR, 0);
|
||||
at91_ssc_write(ssc_p->ssc.base + ATMEL_PDC_TPR, 0);
|
||||
at91_ssc_write(ssc_p->ssc.base + ATMEL_PDC_TCR, 0);
|
||||
at91_ssc_write(ssc_p->ssc.base + ATMEL_PDC_TNPR, 0);
|
||||
at91_ssc_write(ssc_p->ssc.base + ATMEL_PDC_TNCR, 0);
|
||||
|
||||
if ((ret = request_irq(ssc_p->ssc.pid, at91_ssc_interrupt,
|
||||
0, ssc_p->name, ssc_p)) < 0) {
|
||||
printk(KERN_WARNING "at91-ssc: request_irq failure\n");
|
||||
|
||||
DBG("Stopping pid %d clock\n", ssc_p->ssc.pid);
|
||||
at91_sys_write(AT91_PMC_PCDR, 1<<ssc_p->ssc.pid);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ssc_p->initialized = 1;
|
||||
}
|
||||
|
||||
/* set SSC clock mode register */
|
||||
at91_ssc_write(ssc_p->ssc.base + AT91_SSC_CMR, ssc_p->cmr_div);
|
||||
|
||||
/* set receive clock mode and format */
|
||||
at91_ssc_write(ssc_p->ssc.base + AT91_SSC_RCMR, rcmr);
|
||||
at91_ssc_write(ssc_p->ssc.base + AT91_SSC_RFMR, rfmr);
|
||||
|
||||
/* set transmit clock mode and format */
|
||||
at91_ssc_write(ssc_p->ssc.base + AT91_SSC_TCMR, tcmr);
|
||||
at91_ssc_write(ssc_p->ssc.base + AT91_SSC_TFMR, tfmr);
|
||||
|
||||
DBG("hw_params: SSC initialized\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int at91_ssc_prepare(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct at91_ssc_info *ssc_p = &ssc_info[rtd->dai->cpu_dai->id];
|
||||
struct at91_pcm_dma_params *dma_params;
|
||||
int dir;
|
||||
|
||||
dir = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0 : 1;
|
||||
dma_params = ssc_p->dma_params[dir];
|
||||
|
||||
at91_ssc_write(dma_params->ssc_base + AT91_SSC_CR,
|
||||
dma_params->mask->ssc_enable);
|
||||
|
||||
DBG("%s enabled SSC_SR=0x%08lx\n", dir ? "receive" : "transmit",
|
||||
at91_ssc_read(dma_params->ssc_base + AT91_SSC_SR));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int at91_ssc_suspend(struct platform_device *pdev,
|
||||
struct snd_soc_dai *cpu_dai)
|
||||
{
|
||||
struct at91_ssc_info *ssc_p;
|
||||
|
||||
if(!cpu_dai->active)
|
||||
return 0;
|
||||
|
||||
ssc_p = &ssc_info[cpu_dai->id];
|
||||
|
||||
/* Save the status register before disabling transmit and receive. */
|
||||
ssc_p->ssc_state.ssc_sr = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_SR);
|
||||
at91_ssc_write(ssc_p->ssc.base + AT91_SSC_CR,
|
||||
AT91_SSC_TXDIS | AT91_SSC_RXDIS);
|
||||
|
||||
/* Save the current interrupt mask, then disable unmasked interrupts. */
|
||||
ssc_p->ssc_state.ssc_imr = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_IMR);
|
||||
at91_ssc_write(ssc_p->ssc.base + AT91_SSC_IDR, ssc_p->ssc_state.ssc_imr);
|
||||
|
||||
ssc_p->ssc_state.ssc_cmr = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_CMR);
|
||||
ssc_p->ssc_state.ssc_rcmr = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_RCMR);
|
||||
ssc_p->ssc_state.ssc_rfmr = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_RFMR);
|
||||
ssc_p->ssc_state.ssc_tcmr = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_TCMR);
|
||||
ssc_p->ssc_state.ssc_tfmr = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_TFMR);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int at91_ssc_resume(struct platform_device *pdev,
|
||||
struct snd_soc_dai *cpu_dai)
|
||||
{
|
||||
struct at91_ssc_info *ssc_p;
|
||||
|
||||
if(!cpu_dai->active)
|
||||
return 0;
|
||||
|
||||
ssc_p = &ssc_info[cpu_dai->id];
|
||||
|
||||
at91_ssc_write(ssc_p->ssc.base + AT91_SSC_TFMR, ssc_p->ssc_state.ssc_tfmr);
|
||||
at91_ssc_write(ssc_p->ssc.base + AT91_SSC_TCMR, ssc_p->ssc_state.ssc_tcmr);
|
||||
at91_ssc_write(ssc_p->ssc.base + AT91_SSC_RFMR, ssc_p->ssc_state.ssc_rfmr);
|
||||
at91_ssc_write(ssc_p->ssc.base + AT91_SSC_RCMR, ssc_p->ssc_state.ssc_rcmr);
|
||||
at91_ssc_write(ssc_p->ssc.base + AT91_SSC_CMR, ssc_p->ssc_state.ssc_cmr);
|
||||
|
||||
at91_ssc_write(ssc_p->ssc.base + AT91_SSC_IER, ssc_p->ssc_state.ssc_imr);
|
||||
|
||||
at91_ssc_write(ssc_p->ssc.base + AT91_SSC_CR,
|
||||
((ssc_p->ssc_state.ssc_sr & AT91_SSC_RXENA) ? AT91_SSC_RXEN : 0) |
|
||||
((ssc_p->ssc_state.ssc_sr & AT91_SSC_TXENA) ? AT91_SSC_TXEN : 0));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else
|
||||
#define at91_ssc_suspend NULL
|
||||
#define at91_ssc_resume NULL
|
||||
#endif
|
||||
|
||||
#define AT91_SSC_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
|
||||
SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\
|
||||
SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\
|
||||
SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |\
|
||||
SNDRV_PCM_RATE_96000)
|
||||
|
||||
#define AT91_SSC_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE |\
|
||||
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
|
||||
|
||||
struct snd_soc_dai at91_ssc_dai[NUM_SSC_DEVICES] = {
|
||||
{ .name = "at91-ssc0",
|
||||
.id = 0,
|
||||
.type = SND_SOC_DAI_PCM,
|
||||
.suspend = at91_ssc_suspend,
|
||||
.resume = at91_ssc_resume,
|
||||
.playback = {
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.rates = AT91_SSC_RATES,
|
||||
.formats = AT91_SSC_FORMATS,},
|
||||
.capture = {
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.rates = AT91_SSC_RATES,
|
||||
.formats = AT91_SSC_FORMATS,},
|
||||
.ops = {
|
||||
.startup = at91_ssc_startup,
|
||||
.shutdown = at91_ssc_shutdown,
|
||||
.prepare = at91_ssc_prepare,
|
||||
.hw_params = at91_ssc_hw_params,},
|
||||
.dai_ops = {
|
||||
.set_sysclk = at91_ssc_set_dai_sysclk,
|
||||
.set_fmt = at91_ssc_set_dai_fmt,
|
||||
.set_clkdiv = at91_ssc_set_dai_clkdiv,},
|
||||
.private_data = &ssc_info[0].ssc,
|
||||
},
|
||||
#if NUM_SSC_DEVICES == 3
|
||||
{ .name = "at91-ssc1",
|
||||
.id = 1,
|
||||
.type = SND_SOC_DAI_PCM,
|
||||
.suspend = at91_ssc_suspend,
|
||||
.resume = at91_ssc_resume,
|
||||
.playback = {
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.rates = AT91_SSC_RATES,
|
||||
.formats = AT91_SSC_FORMATS,},
|
||||
.capture = {
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.rates = AT91_SSC_RATES,
|
||||
.formats = AT91_SSC_FORMATS,},
|
||||
.ops = {
|
||||
.startup = at91_ssc_startup,
|
||||
.shutdown = at91_ssc_shutdown,
|
||||
.prepare = at91_ssc_prepare,
|
||||
.hw_params = at91_ssc_hw_params,},
|
||||
.dai_ops = {
|
||||
.set_sysclk = at91_ssc_set_dai_sysclk,
|
||||
.set_fmt = at91_ssc_set_dai_fmt,
|
||||
.set_clkdiv = at91_ssc_set_dai_clkdiv,},
|
||||
.private_data = &ssc_info[1].ssc,
|
||||
},
|
||||
{ .name = "at91-ssc2",
|
||||
.id = 2,
|
||||
.type = SND_SOC_DAI_PCM,
|
||||
.suspend = at91_ssc_suspend,
|
||||
.resume = at91_ssc_resume,
|
||||
.playback = {
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.rates = AT91_SSC_RATES,
|
||||
.formats = AT91_SSC_FORMATS,},
|
||||
.capture = {
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.rates = AT91_SSC_RATES,
|
||||
.formats = AT91_SSC_FORMATS,},
|
||||
.ops = {
|
||||
.startup = at91_ssc_startup,
|
||||
.shutdown = at91_ssc_shutdown,
|
||||
.prepare = at91_ssc_prepare,
|
||||
.hw_params = at91_ssc_hw_params,},
|
||||
.dai_ops = {
|
||||
.set_sysclk = at91_ssc_set_dai_sysclk,
|
||||
.set_fmt = at91_ssc_set_dai_fmt,
|
||||
.set_clkdiv = at91_ssc_set_dai_clkdiv,},
|
||||
.private_data = &ssc_info[2].ssc,
|
||||
},
|
||||
#endif
|
||||
};
|
||||
|
||||
EXPORT_SYMBOL_GPL(at91_ssc_dai);
|
||||
|
||||
/* Module information */
|
||||
MODULE_AUTHOR("Frank Mandarino, fmandarino@endrelia.com, www.endrelia.com");
|
||||
MODULE_DESCRIPTION("AT91 SSC ASoC Interface");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -1,27 +0,0 @@
|
|||
/*
|
||||
* at91-ssc.h - ALSA SSC interface for the Atmel AT91 SoC
|
||||
*
|
||||
* Author: Frank Mandarino <fmandarino@endrelia.com>
|
||||
* Endrelia Technologies Inc.
|
||||
* Created: Jan 9, 2007
|
||||
*
|
||||
* 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 _AT91_SSC_H
|
||||
#define _AT91_SSC_H
|
||||
|
||||
/* SSC system clock ids */
|
||||
#define AT91_SYSCLK_MCK 0 /* SSC uses AT91 MCK as system clock */
|
||||
|
||||
/* SSC divider ids */
|
||||
#define AT91SSC_CMR_DIV 0 /* MCK divider for BCLK */
|
||||
#define AT91SSC_TCMR_PERIOD 1 /* BCLK divider for transmit FS */
|
||||
#define AT91SSC_RCMR_PERIOD 2 /* BCLK divider for receive FS */
|
||||
|
||||
extern struct snd_soc_dai at91_ssc_dai[];
|
||||
|
||||
#endif /* _AT91_SSC_H */
|
||||
|
43
sound/soc/atmel/Kconfig
Normal file
43
sound/soc/atmel/Kconfig
Normal file
|
@ -0,0 +1,43 @@
|
|||
config SND_ATMEL_SOC
|
||||
tristate "SoC Audio for the Atmel System-on-Chip"
|
||||
depends on ARCH_AT91 || AVR32
|
||||
help
|
||||
Say Y or M if you want to add support for codecs attached to
|
||||
the ATMEL SSC interface. You will also need
|
||||
to select the audio interfaces to support below.
|
||||
|
||||
config SND_ATMEL_SOC_SSC
|
||||
tristate
|
||||
depends on SND_ATMEL_SOC
|
||||
help
|
||||
Say Y or M if you want to add support for codecs the
|
||||
ATMEL SSC interface. You will also needs to select the individual
|
||||
machine drivers to support below.
|
||||
|
||||
config SND_AT91_SOC_SAM9G20_WM8731
|
||||
tristate "SoC Audio support for WM8731-based At91sam9g20 evaluation board"
|
||||
depends on ATMEL_SSC && ARCH_AT91SAM9G20 && SND_ATMEL_SOC
|
||||
select SND_ATMEL_SOC_SSC
|
||||
select SND_SOC_WM8731
|
||||
help
|
||||
Say Y if you want to add support for SoC audio on WM8731-based
|
||||
AT91sam9g20 evaluation board.
|
||||
|
||||
config SND_AT32_SOC_PLAYPAQ
|
||||
tristate "SoC Audio support for PlayPaq with WM8510"
|
||||
depends on SND_ATMEL_SOC && BOARD_PLAYPAQ
|
||||
select SND_ATMEL_SOC_SSC
|
||||
select SND_SOC_WM8510
|
||||
help
|
||||
Say Y or M here if you want to add support for SoC audio
|
||||
on the LRS PlayPaq.
|
||||
|
||||
config SND_AT32_SOC_PLAYPAQ_SLAVE
|
||||
bool "Run CODEC on PlayPaq in slave mode"
|
||||
depends on SND_AT32_SOC_PLAYPAQ
|
||||
default n
|
||||
help
|
||||
Say Y if you want to run with the AT32 SSC generating the BCLK
|
||||
and FRAME signals on the PlayPaq. Unless you want to play
|
||||
with the AT32 as the SSC master, you probably want to say N here,
|
||||
as this will give you better sound quality.
|
15
sound/soc/atmel/Makefile
Normal file
15
sound/soc/atmel/Makefile
Normal file
|
@ -0,0 +1,15 @@
|
|||
# AT91 Platform Support
|
||||
snd-soc-atmel-pcm-objs := atmel-pcm.o
|
||||
snd-soc-atmel_ssc_dai-objs := atmel_ssc_dai.o
|
||||
|
||||
obj-$(CONFIG_SND_ATMEL_SOC) += snd-soc-atmel-pcm.o
|
||||
obj-$(CONFIG_SND_ATMEL_SOC_SSC) += snd-soc-atmel_ssc_dai.o
|
||||
|
||||
# AT91 Machine Support
|
||||
snd-soc-sam9g20-wm8731-objs := sam9g20_wm8731.o
|
||||
|
||||
# AT32 Machine Support
|
||||
snd-soc-playpaq-objs := playpaq_wm8510.o
|
||||
|
||||
obj-$(CONFIG_SND_AT91_SOC_SAM9G20_WM8731) += snd-soc-sam9g20-wm8731.o
|
||||
obj-$(CONFIG_SND_AT32_SOC_PLAYPAQ) += snd-soc-playpaq.o
|
494
sound/soc/atmel/atmel-pcm.c
Normal file
494
sound/soc/atmel/atmel-pcm.c
Normal file
|
@ -0,0 +1,494 @@
|
|||
/*
|
||||
* atmel-pcm.c -- ALSA PCM interface for the Atmel atmel SoC.
|
||||
*
|
||||
* Copyright (C) 2005 SAN People
|
||||
* Copyright (C) 2008 Atmel
|
||||
*
|
||||
* Authors: Sedji Gaouaou <sedji.gaouaou@atmel.com>
|
||||
*
|
||||
* Based on at91-pcm. by:
|
||||
* Frank Mandarino <fmandarino@endrelia.com>
|
||||
* Copyright 2006 Endrelia Technologies Inc.
|
||||
*
|
||||
* Based on pxa2xx-pcm.c by:
|
||||
*
|
||||
* Author: Nicolas Pitre
|
||||
* Created: Nov 30, 2004
|
||||
* Copyright: (C) 2004 MontaVista Software, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/atmel_pdc.h>
|
||||
#include <linux/atmel-ssc.h>
|
||||
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
|
||||
#include <mach/hardware.h>
|
||||
|
||||
#include "atmel-pcm.h"
|
||||
|
||||
|
||||
/*--------------------------------------------------------------------------*\
|
||||
* Hardware definition
|
||||
\*--------------------------------------------------------------------------*/
|
||||
/* TODO: These values were taken from the AT91 platform driver, check
|
||||
* them against real values for AT32
|
||||
*/
|
||||
static const struct snd_pcm_hardware atmel_pcm_hardware = {
|
||||
.info = SNDRV_PCM_INFO_MMAP |
|
||||
SNDRV_PCM_INFO_MMAP_VALID |
|
||||
SNDRV_PCM_INFO_INTERLEAVED |
|
||||
SNDRV_PCM_INFO_PAUSE,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
.period_bytes_min = 32,
|
||||
.period_bytes_max = 8192,
|
||||
.periods_min = 2,
|
||||
.periods_max = 1024,
|
||||
.buffer_bytes_max = 32 * 1024,
|
||||
};
|
||||
|
||||
|
||||
/*--------------------------------------------------------------------------*\
|
||||
* Data types
|
||||
\*--------------------------------------------------------------------------*/
|
||||
struct atmel_runtime_data {
|
||||
struct atmel_pcm_dma_params *params;
|
||||
dma_addr_t dma_buffer; /* physical address of dma buffer */
|
||||
dma_addr_t dma_buffer_end; /* first address beyond DMA buffer */
|
||||
size_t period_size;
|
||||
|
||||
dma_addr_t period_ptr; /* physical address of next period */
|
||||
int periods; /* period index of period_ptr */
|
||||
|
||||
/* PDC register save */
|
||||
u32 pdc_xpr_save;
|
||||
u32 pdc_xcr_save;
|
||||
u32 pdc_xnpr_save;
|
||||
u32 pdc_xncr_save;
|
||||
};
|
||||
|
||||
|
||||
/*--------------------------------------------------------------------------*\
|
||||
* Helper functions
|
||||
\*--------------------------------------------------------------------------*/
|
||||
static int atmel_pcm_preallocate_dma_buffer(struct snd_pcm *pcm,
|
||||
int stream)
|
||||
{
|
||||
struct snd_pcm_substream *substream = pcm->streams[stream].substream;
|
||||
struct snd_dma_buffer *buf = &substream->dma_buffer;
|
||||
size_t size = atmel_pcm_hardware.buffer_bytes_max;
|
||||
|
||||
buf->dev.type = SNDRV_DMA_TYPE_DEV;
|
||||
buf->dev.dev = pcm->card->dev;
|
||||
buf->private_data = NULL;
|
||||
buf->area = dma_alloc_coherent(pcm->card->dev, size,
|
||||
&buf->addr, GFP_KERNEL);
|
||||
pr_debug("atmel-pcm:"
|
||||
"preallocate_dma_buffer: area=%p, addr=%p, size=%d\n",
|
||||
(void *) buf->area,
|
||||
(void *) buf->addr,
|
||||
size);
|
||||
|
||||
if (!buf->area)
|
||||
return -ENOMEM;
|
||||
|
||||
buf->bytes = size;
|
||||
return 0;
|
||||
}
|
||||
/*--------------------------------------------------------------------------*\
|
||||
* ISR
|
||||
\*--------------------------------------------------------------------------*/
|
||||
static void atmel_pcm_dma_irq(u32 ssc_sr,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct atmel_runtime_data *prtd = substream->runtime->private_data;
|
||||
struct atmel_pcm_dma_params *params = prtd->params;
|
||||
static int count;
|
||||
|
||||
count++;
|
||||
|
||||
if (ssc_sr & params->mask->ssc_endbuf) {
|
||||
pr_warning("atmel-pcm: buffer %s on %s"
|
||||
" (SSC_SR=%#x, count=%d)\n",
|
||||
substream->stream == SNDRV_PCM_STREAM_PLAYBACK
|
||||
? "underrun" : "overrun",
|
||||
params->name, ssc_sr, count);
|
||||
|
||||
/* re-start the PDC */
|
||||
ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
|
||||
params->mask->pdc_disable);
|
||||
prtd->period_ptr += prtd->period_size;
|
||||
if (prtd->period_ptr >= prtd->dma_buffer_end)
|
||||
prtd->period_ptr = prtd->dma_buffer;
|
||||
|
||||
ssc_writex(params->ssc->regs, params->pdc->xpr,
|
||||
prtd->period_ptr);
|
||||
ssc_writex(params->ssc->regs, params->pdc->xcr,
|
||||
prtd->period_size / params->pdc_xfer_size);
|
||||
ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
|
||||
params->mask->pdc_enable);
|
||||
}
|
||||
|
||||
if (ssc_sr & params->mask->ssc_endx) {
|
||||
/* Load the PDC next pointer and counter registers */
|
||||
prtd->period_ptr += prtd->period_size;
|
||||
if (prtd->period_ptr >= prtd->dma_buffer_end)
|
||||
prtd->period_ptr = prtd->dma_buffer;
|
||||
|
||||
ssc_writex(params->ssc->regs, params->pdc->xnpr,
|
||||
prtd->period_ptr);
|
||||
ssc_writex(params->ssc->regs, params->pdc->xncr,
|
||||
prtd->period_size / params->pdc_xfer_size);
|
||||
}
|
||||
|
||||
snd_pcm_period_elapsed(substream);
|
||||
}
|
||||
|
||||
|
||||
/*--------------------------------------------------------------------------*\
|
||||
* PCM operations
|
||||
\*--------------------------------------------------------------------------*/
|
||||
static int atmel_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct atmel_runtime_data *prtd = runtime->private_data;
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
|
||||
/* this may get called several times by oss emulation
|
||||
* with different params */
|
||||
|
||||
snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
|
||||
runtime->dma_bytes = params_buffer_bytes(params);
|
||||
|
||||
prtd->params = rtd->dai->cpu_dai->dma_data;
|
||||
prtd->params->dma_intr_handler = atmel_pcm_dma_irq;
|
||||
|
||||
prtd->dma_buffer = runtime->dma_addr;
|
||||
prtd->dma_buffer_end = runtime->dma_addr + runtime->dma_bytes;
|
||||
prtd->period_size = params_period_bytes(params);
|
||||
|
||||
pr_debug("atmel-pcm: "
|
||||
"hw_params: DMA for %s initialized "
|
||||
"(dma_bytes=%u, period_size=%u)\n",
|
||||
prtd->params->name,
|
||||
runtime->dma_bytes,
|
||||
prtd->period_size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int atmel_pcm_hw_free(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct atmel_runtime_data *prtd = substream->runtime->private_data;
|
||||
struct atmel_pcm_dma_params *params = prtd->params;
|
||||
|
||||
if (params != NULL) {
|
||||
ssc_writex(params->ssc->regs, SSC_PDC_PTCR,
|
||||
params->mask->pdc_disable);
|
||||
prtd->params->dma_intr_handler = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int atmel_pcm_prepare(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct atmel_runtime_data *prtd = substream->runtime->private_data;
|
||||
struct atmel_pcm_dma_params *params = prtd->params;
|
||||
|
||||
ssc_writex(params->ssc->regs, SSC_IDR,
|
||||
params->mask->ssc_endx | params->mask->ssc_endbuf);
|
||||
ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
|
||||
params->mask->pdc_disable);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int atmel_pcm_trigger(struct snd_pcm_substream *substream,
|
||||
int cmd)
|
||||
{
|
||||
struct snd_pcm_runtime *rtd = substream->runtime;
|
||||
struct atmel_runtime_data *prtd = rtd->private_data;
|
||||
struct atmel_pcm_dma_params *params = prtd->params;
|
||||
int ret = 0;
|
||||
|
||||
pr_debug("atmel-pcm:buffer_size = %ld,"
|
||||
"dma_area = %p, dma_bytes = %u\n",
|
||||
rtd->buffer_size, rtd->dma_area, rtd->dma_bytes);
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
prtd->period_ptr = prtd->dma_buffer;
|
||||
|
||||
ssc_writex(params->ssc->regs, params->pdc->xpr,
|
||||
prtd->period_ptr);
|
||||
ssc_writex(params->ssc->regs, params->pdc->xcr,
|
||||
prtd->period_size / params->pdc_xfer_size);
|
||||
|
||||
prtd->period_ptr += prtd->period_size;
|
||||
ssc_writex(params->ssc->regs, params->pdc->xnpr,
|
||||
prtd->period_ptr);
|
||||
ssc_writex(params->ssc->regs, params->pdc->xncr,
|
||||
prtd->period_size / params->pdc_xfer_size);
|
||||
|
||||
pr_debug("atmel-pcm: trigger: "
|
||||
"period_ptr=%lx, xpr=%u, "
|
||||
"xcr=%u, xnpr=%u, xncr=%u\n",
|
||||
(unsigned long)prtd->period_ptr,
|
||||
ssc_readx(params->ssc->regs, params->pdc->xpr),
|
||||
ssc_readx(params->ssc->regs, params->pdc->xcr),
|
||||
ssc_readx(params->ssc->regs, params->pdc->xnpr),
|
||||
ssc_readx(params->ssc->regs, params->pdc->xncr));
|
||||
|
||||
ssc_writex(params->ssc->regs, SSC_IER,
|
||||
params->mask->ssc_endx | params->mask->ssc_endbuf);
|
||||
ssc_writex(params->ssc->regs, SSC_PDC_PTCR,
|
||||
params->mask->pdc_enable);
|
||||
|
||||
pr_debug("sr=%u imr=%u\n",
|
||||
ssc_readx(params->ssc->regs, SSC_SR),
|
||||
ssc_readx(params->ssc->regs, SSC_IER));
|
||||
break; /* SNDRV_PCM_TRIGGER_START */
|
||||
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
|
||||
params->mask->pdc_disable);
|
||||
break;
|
||||
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
|
||||
params->mask->pdc_enable);
|
||||
break;
|
||||
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static snd_pcm_uframes_t atmel_pcm_pointer(
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct atmel_runtime_data *prtd = runtime->private_data;
|
||||
struct atmel_pcm_dma_params *params = prtd->params;
|
||||
dma_addr_t ptr;
|
||||
snd_pcm_uframes_t x;
|
||||
|
||||
ptr = (dma_addr_t) ssc_readx(params->ssc->regs, params->pdc->xpr);
|
||||
x = bytes_to_frames(runtime, ptr - prtd->dma_buffer);
|
||||
|
||||
if (x == runtime->buffer_size)
|
||||
x = 0;
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
static int atmel_pcm_open(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct atmel_runtime_data *prtd;
|
||||
int ret = 0;
|
||||
|
||||
snd_soc_set_runtime_hwparams(substream, &atmel_pcm_hardware);
|
||||
|
||||
/* ensure that buffer size is a multiple of period size */
|
||||
ret = snd_pcm_hw_constraint_integer(runtime,
|
||||
SNDRV_PCM_HW_PARAM_PERIODS);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
prtd = kzalloc(sizeof(struct atmel_runtime_data), GFP_KERNEL);
|
||||
if (prtd == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
runtime->private_data = prtd;
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int atmel_pcm_close(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct atmel_runtime_data *prtd = substream->runtime->private_data;
|
||||
|
||||
kfree(prtd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int atmel_pcm_mmap(struct snd_pcm_substream *substream,
|
||||
struct vm_area_struct *vma)
|
||||
{
|
||||
return remap_pfn_range(vma, vma->vm_start,
|
||||
substream->dma_buffer.addr >> PAGE_SHIFT,
|
||||
vma->vm_end - vma->vm_start, vma->vm_page_prot);
|
||||
}
|
||||
|
||||
struct snd_pcm_ops atmel_pcm_ops = {
|
||||
.open = atmel_pcm_open,
|
||||
.close = atmel_pcm_close,
|
||||
.ioctl = snd_pcm_lib_ioctl,
|
||||
.hw_params = atmel_pcm_hw_params,
|
||||
.hw_free = atmel_pcm_hw_free,
|
||||
.prepare = atmel_pcm_prepare,
|
||||
.trigger = atmel_pcm_trigger,
|
||||
.pointer = atmel_pcm_pointer,
|
||||
.mmap = atmel_pcm_mmap,
|
||||
};
|
||||
|
||||
|
||||
/*--------------------------------------------------------------------------*\
|
||||
* ASoC platform driver
|
||||
\*--------------------------------------------------------------------------*/
|
||||
static u64 atmel_pcm_dmamask = 0xffffffff;
|
||||
|
||||
static int atmel_pcm_new(struct snd_card *card,
|
||||
struct snd_soc_dai *dai, struct snd_pcm *pcm)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (!card->dev->dma_mask)
|
||||
card->dev->dma_mask = &atmel_pcm_dmamask;
|
||||
if (!card->dev->coherent_dma_mask)
|
||||
card->dev->coherent_dma_mask = 0xffffffff;
|
||||
|
||||
if (dai->playback.channels_min) {
|
||||
ret = atmel_pcm_preallocate_dma_buffer(pcm,
|
||||
SNDRV_PCM_STREAM_PLAYBACK);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (dai->capture.channels_min) {
|
||||
pr_debug("at32-pcm:"
|
||||
"Allocating PCM capture DMA buffer\n");
|
||||
ret = atmel_pcm_preallocate_dma_buffer(pcm,
|
||||
SNDRV_PCM_STREAM_CAPTURE);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void atmel_pcm_free_dma_buffers(struct snd_pcm *pcm)
|
||||
{
|
||||
struct snd_pcm_substream *substream;
|
||||
struct snd_dma_buffer *buf;
|
||||
int stream;
|
||||
|
||||
for (stream = 0; stream < 2; stream++) {
|
||||
substream = pcm->streams[stream].substream;
|
||||
if (!substream)
|
||||
continue;
|
||||
|
||||
buf = &substream->dma_buffer;
|
||||
if (!buf->area)
|
||||
continue;
|
||||
dma_free_coherent(pcm->card->dev, buf->bytes,
|
||||
buf->area, buf->addr);
|
||||
buf->area = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int atmel_pcm_suspend(struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = dai->runtime;
|
||||
struct atmel_runtime_data *prtd;
|
||||
struct atmel_pcm_dma_params *params;
|
||||
|
||||
if (!runtime)
|
||||
return 0;
|
||||
|
||||
prtd = runtime->private_data;
|
||||
params = prtd->params;
|
||||
|
||||
/* disable the PDC and save the PDC registers */
|
||||
|
||||
ssc_writel(params->ssc->regs, PDC_PTCR, params->mask->pdc_disable);
|
||||
|
||||
prtd->pdc_xpr_save = ssc_readx(params->ssc->regs, params->pdc->xpr);
|
||||
prtd->pdc_xcr_save = ssc_readx(params->ssc->regs, params->pdc->xcr);
|
||||
prtd->pdc_xnpr_save = ssc_readx(params->ssc->regs, params->pdc->xnpr);
|
||||
prtd->pdc_xncr_save = ssc_readx(params->ssc->regs, params->pdc->xncr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int atmel_pcm_resume(struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = dai->runtime;
|
||||
struct atmel_runtime_data *prtd;
|
||||
struct atmel_pcm_dma_params *params;
|
||||
|
||||
if (!runtime)
|
||||
return 0;
|
||||
|
||||
prtd = runtime->private_data;
|
||||
params = prtd->params;
|
||||
|
||||
/* restore the PDC registers and enable the PDC */
|
||||
ssc_writex(params->ssc->regs, params->pdc->xpr, prtd->pdc_xpr_save);
|
||||
ssc_writex(params->ssc->regs, params->pdc->xcr, prtd->pdc_xcr_save);
|
||||
ssc_writex(params->ssc->regs, params->pdc->xnpr, prtd->pdc_xnpr_save);
|
||||
ssc_writex(params->ssc->regs, params->pdc->xncr, prtd->pdc_xncr_save);
|
||||
|
||||
ssc_writel(params->ssc->regs, PDC_PTCR, params->mask->pdc_enable);
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#define atmel_pcm_suspend NULL
|
||||
#define atmel_pcm_resume NULL
|
||||
#endif
|
||||
|
||||
struct snd_soc_platform atmel_soc_platform = {
|
||||
.name = "atmel-audio",
|
||||
.pcm_ops = &atmel_pcm_ops,
|
||||
.pcm_new = atmel_pcm_new,
|
||||
.pcm_free = atmel_pcm_free_dma_buffers,
|
||||
.suspend = atmel_pcm_suspend,
|
||||
.resume = atmel_pcm_resume,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(atmel_soc_platform);
|
||||
|
||||
static int __init atmel_pcm_modinit(void)
|
||||
{
|
||||
return snd_soc_register_platform(&atmel_soc_platform);
|
||||
}
|
||||
module_init(atmel_pcm_modinit);
|
||||
|
||||
static void __exit atmel_pcm_modexit(void)
|
||||
{
|
||||
snd_soc_unregister_platform(&atmel_soc_platform);
|
||||
}
|
||||
module_exit(atmel_pcm_modexit);
|
||||
|
||||
MODULE_AUTHOR("Sedji Gaouaou <sedji.gaouaou@atmel.com>");
|
||||
MODULE_DESCRIPTION("Atmel PCM module");
|
||||
MODULE_LICENSE("GPL");
|
86
sound/soc/atmel/atmel-pcm.h
Normal file
86
sound/soc/atmel/atmel-pcm.h
Normal file
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
* at91-pcm.h - ALSA PCM interface for the Atmel AT91 SoC.
|
||||
*
|
||||
* Copyright (C) 2005 SAN People
|
||||
* Copyright (C) 2008 Atmel
|
||||
*
|
||||
* Authors: Sedji Gaouaou <sedji.gaouaou@atmel.com>
|
||||
*
|
||||
* Based on at91-pcm. by:
|
||||
* Frank Mandarino <fmandarino@endrelia.com>
|
||||
* Copyright 2006 Endrelia Technologies Inc.
|
||||
*
|
||||
* Based on pxa2xx-pcm.c by:
|
||||
*
|
||||
* Author: Nicolas Pitre
|
||||
* Created: Nov 30, 2004
|
||||
* Copyright: (C) 2004 MontaVista Software, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _ATMEL_PCM_H
|
||||
#define _ATMEL_PCM_H
|
||||
|
||||
#include <linux/atmel-ssc.h>
|
||||
|
||||
/*
|
||||
* Registers and status bits that are required by the PCM driver.
|
||||
*/
|
||||
struct atmel_pdc_regs {
|
||||
unsigned int xpr; /* PDC recv/trans pointer */
|
||||
unsigned int xcr; /* PDC recv/trans counter */
|
||||
unsigned int xnpr; /* PDC next recv/trans pointer */
|
||||
unsigned int xncr; /* PDC next recv/trans counter */
|
||||
unsigned int ptcr; /* PDC transfer control */
|
||||
};
|
||||
|
||||
struct atmel_ssc_mask {
|
||||
u32 ssc_enable; /* SSC recv/trans enable */
|
||||
u32 ssc_disable; /* SSC recv/trans disable */
|
||||
u32 ssc_endx; /* SSC ENDTX or ENDRX */
|
||||
u32 ssc_endbuf; /* SSC TXBUFE or RXBUFF */
|
||||
u32 pdc_enable; /* PDC recv/trans enable */
|
||||
u32 pdc_disable; /* PDC recv/trans disable */
|
||||
};
|
||||
|
||||
/*
|
||||
* This structure, shared between the PCM driver and the interface,
|
||||
* contains all information required by the PCM driver to perform the
|
||||
* PDC DMA operation. All fields except dma_intr_handler() are initialized
|
||||
* by the interface. The dms_intr_handler() pointer is set by the PCM
|
||||
* driver and called by the interface SSC interrupt handler if it is
|
||||
* non-NULL.
|
||||
*/
|
||||
struct atmel_pcm_dma_params {
|
||||
char *name; /* stream identifier */
|
||||
int pdc_xfer_size; /* PDC counter increment in bytes */
|
||||
struct ssc_device *ssc; /* SSC device for stream */
|
||||
struct atmel_pdc_regs *pdc; /* PDC receive or transmit registers */
|
||||
struct atmel_ssc_mask *mask; /* SSC & PDC status bits */
|
||||
struct snd_pcm_substream *substream;
|
||||
void (*dma_intr_handler)(u32, struct snd_pcm_substream *);
|
||||
};
|
||||
|
||||
extern struct snd_soc_platform atmel_soc_platform;
|
||||
|
||||
|
||||
/*
|
||||
* SSC register access (since ssc_writel() / ssc_readl() require literal name)
|
||||
*/
|
||||
#define ssc_readx(base, reg) (__raw_readl((base) + (reg)))
|
||||
#define ssc_writex(base, reg, value) __raw_writel((value), (base) + (reg))
|
||||
|
||||
#endif /* _ATMEL_PCM_H */
|
790
sound/soc/atmel/atmel_ssc_dai.c
Normal file
790
sound/soc/atmel/atmel_ssc_dai.c
Normal file
|
@ -0,0 +1,790 @@
|
|||
/*
|
||||
* atmel_ssc_dai.c -- ALSA SoC ATMEL SSC Audio Layer Platform driver
|
||||
*
|
||||
* Copyright (C) 2005 SAN People
|
||||
* Copyright (C) 2008 Atmel
|
||||
*
|
||||
* Author: Sedji Gaouaou <sedji.gaouaou@atmel.com>
|
||||
* ATMEL CORP.
|
||||
*
|
||||
* Based on at91-ssc.c by
|
||||
* Frank Mandarino <fmandarino@endrelia.com>
|
||||
* Based on pxa2xx Platform drivers by
|
||||
* Liam Girdwood <liam.girdwood@wolfsonmicro.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/atmel_pdc.h>
|
||||
|
||||
#include <linux/atmel-ssc.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/initval.h>
|
||||
#include <sound/soc.h>
|
||||
|
||||
#include <mach/hardware.h>
|
||||
|
||||
#include "atmel-pcm.h"
|
||||
#include "atmel_ssc_dai.h"
|
||||
|
||||
|
||||
#if defined(CONFIG_ARCH_AT91SAM9260) || defined(CONFIG_ARCH_AT91SAM9G20)
|
||||
#define NUM_SSC_DEVICES 1
|
||||
#else
|
||||
#define NUM_SSC_DEVICES 3
|
||||
#endif
|
||||
|
||||
/*
|
||||
* SSC PDC registers required by the PCM DMA engine.
|
||||
*/
|
||||
static struct atmel_pdc_regs pdc_tx_reg = {
|
||||
.xpr = ATMEL_PDC_TPR,
|
||||
.xcr = ATMEL_PDC_TCR,
|
||||
.xnpr = ATMEL_PDC_TNPR,
|
||||
.xncr = ATMEL_PDC_TNCR,
|
||||
};
|
||||
|
||||
static struct atmel_pdc_regs pdc_rx_reg = {
|
||||
.xpr = ATMEL_PDC_RPR,
|
||||
.xcr = ATMEL_PDC_RCR,
|
||||
.xnpr = ATMEL_PDC_RNPR,
|
||||
.xncr = ATMEL_PDC_RNCR,
|
||||
};
|
||||
|
||||
/*
|
||||
* SSC & PDC status bits for transmit and receive.
|
||||
*/
|
||||
static struct atmel_ssc_mask ssc_tx_mask = {
|
||||
.ssc_enable = SSC_BIT(CR_TXEN),
|
||||
.ssc_disable = SSC_BIT(CR_TXDIS),
|
||||
.ssc_endx = SSC_BIT(SR_ENDTX),
|
||||
.ssc_endbuf = SSC_BIT(SR_TXBUFE),
|
||||
.pdc_enable = ATMEL_PDC_TXTEN,
|
||||
.pdc_disable = ATMEL_PDC_TXTDIS,
|
||||
};
|
||||
|
||||
static struct atmel_ssc_mask ssc_rx_mask = {
|
||||
.ssc_enable = SSC_BIT(CR_RXEN),
|
||||
.ssc_disable = SSC_BIT(CR_RXDIS),
|
||||
.ssc_endx = SSC_BIT(SR_ENDRX),
|
||||
.ssc_endbuf = SSC_BIT(SR_RXBUFF),
|
||||
.pdc_enable = ATMEL_PDC_RXTEN,
|
||||
.pdc_disable = ATMEL_PDC_RXTDIS,
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* DMA parameters.
|
||||
*/
|
||||
static struct atmel_pcm_dma_params ssc_dma_params[NUM_SSC_DEVICES][2] = {
|
||||
{{
|
||||
.name = "SSC0 PCM out",
|
||||
.pdc = &pdc_tx_reg,
|
||||
.mask = &ssc_tx_mask,
|
||||
},
|
||||
{
|
||||
.name = "SSC0 PCM in",
|
||||
.pdc = &pdc_rx_reg,
|
||||
.mask = &ssc_rx_mask,
|
||||
} },
|
||||
#if NUM_SSC_DEVICES == 3
|
||||
{{
|
||||
.name = "SSC1 PCM out",
|
||||
.pdc = &pdc_tx_reg,
|
||||
.mask = &ssc_tx_mask,
|
||||
},
|
||||
{
|
||||
.name = "SSC1 PCM in",
|
||||
.pdc = &pdc_rx_reg,
|
||||
.mask = &ssc_rx_mask,
|
||||
} },
|
||||
{{
|
||||
.name = "SSC2 PCM out",
|
||||
.pdc = &pdc_tx_reg,
|
||||
.mask = &ssc_tx_mask,
|
||||
},
|
||||
{
|
||||
.name = "SSC2 PCM in",
|
||||
.pdc = &pdc_rx_reg,
|
||||
.mask = &ssc_rx_mask,
|
||||
} },
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
static struct atmel_ssc_info ssc_info[NUM_SSC_DEVICES] = {
|
||||
{
|
||||
.name = "ssc0",
|
||||
.lock = __SPIN_LOCK_UNLOCKED(ssc_info[0].lock),
|
||||
.dir_mask = SSC_DIR_MASK_UNUSED,
|
||||
.initialized = 0,
|
||||
},
|
||||
#if NUM_SSC_DEVICES == 3
|
||||
{
|
||||
.name = "ssc1",
|
||||
.lock = __SPIN_LOCK_UNLOCKED(ssc_info[1].lock),
|
||||
.dir_mask = SSC_DIR_MASK_UNUSED,
|
||||
.initialized = 0,
|
||||
},
|
||||
{
|
||||
.name = "ssc2",
|
||||
.lock = __SPIN_LOCK_UNLOCKED(ssc_info[2].lock),
|
||||
.dir_mask = SSC_DIR_MASK_UNUSED,
|
||||
.initialized = 0,
|
||||
},
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* SSC interrupt handler. Passes PDC interrupts to the DMA
|
||||
* interrupt handler in the PCM driver.
|
||||
*/
|
||||
static irqreturn_t atmel_ssc_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct atmel_ssc_info *ssc_p = dev_id;
|
||||
struct atmel_pcm_dma_params *dma_params;
|
||||
u32 ssc_sr;
|
||||
u32 ssc_substream_mask;
|
||||
int i;
|
||||
|
||||
ssc_sr = (unsigned long)ssc_readl(ssc_p->ssc->regs, SR)
|
||||
& (unsigned long)ssc_readl(ssc_p->ssc->regs, IMR);
|
||||
|
||||
/*
|
||||
* Loop through the substreams attached to this SSC. If
|
||||
* a DMA-related interrupt occurred on that substream, call
|
||||
* the DMA interrupt handler function, if one has been
|
||||
* registered in the dma_params structure by the PCM driver.
|
||||
*/
|
||||
for (i = 0; i < ARRAY_SIZE(ssc_p->dma_params); i++) {
|
||||
dma_params = ssc_p->dma_params[i];
|
||||
|
||||
if ((dma_params != NULL) &&
|
||||
(dma_params->dma_intr_handler != NULL)) {
|
||||
ssc_substream_mask = (dma_params->mask->ssc_endx |
|
||||
dma_params->mask->ssc_endbuf);
|
||||
if (ssc_sr & ssc_substream_mask) {
|
||||
dma_params->dma_intr_handler(ssc_sr,
|
||||
dma_params->
|
||||
substream);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*\
|
||||
* DAI functions
|
||||
\*-------------------------------------------------------------------------*/
|
||||
/*
|
||||
* Startup. Only that one substream allowed in each direction.
|
||||
*/
|
||||
static int atmel_ssc_startup(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
|
||||
struct atmel_ssc_info *ssc_p = &ssc_info[rtd->dai->cpu_dai->id];
|
||||
int dir_mask;
|
||||
|
||||
pr_debug("atmel_ssc_startup: SSC_SR=0x%u\n",
|
||||
ssc_readl(ssc_p->ssc->regs, SR));
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
dir_mask = SSC_DIR_MASK_PLAYBACK;
|
||||
else
|
||||
dir_mask = SSC_DIR_MASK_CAPTURE;
|
||||
|
||||
spin_lock_irq(&ssc_p->lock);
|
||||
if (ssc_p->dir_mask & dir_mask) {
|
||||
spin_unlock_irq(&ssc_p->lock);
|
||||
return -EBUSY;
|
||||
}
|
||||
ssc_p->dir_mask |= dir_mask;
|
||||
spin_unlock_irq(&ssc_p->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Shutdown. Clear DMA parameters and shutdown the SSC if there
|
||||
* are no other substreams open.
|
||||
*/
|
||||
static void atmel_ssc_shutdown(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
|
||||
struct atmel_ssc_info *ssc_p = &ssc_info[rtd->dai->cpu_dai->id];
|
||||
struct atmel_pcm_dma_params *dma_params;
|
||||
int dir, dir_mask;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
dir = 0;
|
||||
else
|
||||
dir = 1;
|
||||
|
||||
dma_params = ssc_p->dma_params[dir];
|
||||
|
||||
if (dma_params != NULL) {
|
||||
ssc_writel(ssc_p->ssc->regs, CR, dma_params->mask->ssc_disable);
|
||||
pr_debug("atmel_ssc_shutdown: %s disabled SSC_SR=0x%08x\n",
|
||||
(dir ? "receive" : "transmit"),
|
||||
ssc_readl(ssc_p->ssc->regs, SR));
|
||||
|
||||
dma_params->ssc = NULL;
|
||||
dma_params->substream = NULL;
|
||||
ssc_p->dma_params[dir] = NULL;
|
||||
}
|
||||
|
||||
dir_mask = 1 << dir;
|
||||
|
||||
spin_lock_irq(&ssc_p->lock);
|
||||
ssc_p->dir_mask &= ~dir_mask;
|
||||
if (!ssc_p->dir_mask) {
|
||||
if (ssc_p->initialized) {
|
||||
/* Shutdown the SSC clock. */
|
||||
pr_debug("atmel_ssc_dau: Stopping clock\n");
|
||||
clk_disable(ssc_p->ssc->clk);
|
||||
|
||||
free_irq(ssc_p->ssc->irq, ssc_p);
|
||||
ssc_p->initialized = 0;
|
||||
}
|
||||
|
||||
/* Reset the SSC */
|
||||
ssc_writel(ssc_p->ssc->regs, CR, SSC_BIT(CR_SWRST));
|
||||
/* Clear the SSC dividers */
|
||||
ssc_p->cmr_div = ssc_p->tcmr_period = ssc_p->rcmr_period = 0;
|
||||
}
|
||||
spin_unlock_irq(&ssc_p->lock);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Record the DAI format for use in hw_params().
|
||||
*/
|
||||
static int atmel_ssc_set_dai_fmt(struct snd_soc_dai *cpu_dai,
|
||||
unsigned int fmt)
|
||||
{
|
||||
struct atmel_ssc_info *ssc_p = &ssc_info[cpu_dai->id];
|
||||
|
||||
ssc_p->daifmt = fmt;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Record SSC clock dividers for use in hw_params().
|
||||
*/
|
||||
static int atmel_ssc_set_dai_clkdiv(struct snd_soc_dai *cpu_dai,
|
||||
int div_id, int div)
|
||||
{
|
||||
struct atmel_ssc_info *ssc_p = &ssc_info[cpu_dai->id];
|
||||
|
||||
switch (div_id) {
|
||||
case ATMEL_SSC_CMR_DIV:
|
||||
/*
|
||||
* The same master clock divider is used for both
|
||||
* transmit and receive, so if a value has already
|
||||
* been set, it must match this value.
|
||||
*/
|
||||
if (ssc_p->cmr_div == 0)
|
||||
ssc_p->cmr_div = div;
|
||||
else
|
||||
if (div != ssc_p->cmr_div)
|
||||
return -EBUSY;
|
||||
break;
|
||||
|
||||
case ATMEL_SSC_TCMR_PERIOD:
|
||||
ssc_p->tcmr_period = div;
|
||||
break;
|
||||
|
||||
case ATMEL_SSC_RCMR_PERIOD:
|
||||
ssc_p->rcmr_period = div;
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Configure the SSC.
|
||||
*/
|
||||
static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
|
||||
int id = rtd->dai->cpu_dai->id;
|
||||
struct atmel_ssc_info *ssc_p = &ssc_info[id];
|
||||
struct atmel_pcm_dma_params *dma_params;
|
||||
int dir, channels, bits;
|
||||
u32 tfmr, rfmr, tcmr, rcmr;
|
||||
int start_event;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Currently, there is only one set of dma params for
|
||||
* each direction. If more are added, this code will
|
||||
* have to be changed to select the proper set.
|
||||
*/
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
dir = 0;
|
||||
else
|
||||
dir = 1;
|
||||
|
||||
dma_params = &ssc_dma_params[id][dir];
|
||||
dma_params->ssc = ssc_p->ssc;
|
||||
dma_params->substream = substream;
|
||||
|
||||
ssc_p->dma_params[dir] = dma_params;
|
||||
|
||||
/*
|
||||
* The cpu_dai->dma_data field is only used to communicate the
|
||||
* appropriate DMA parameters to the pcm driver hw_params()
|
||||
* function. It should not be used for other purposes
|
||||
* as it is common to all substreams.
|
||||
*/
|
||||
rtd->dai->cpu_dai->dma_data = dma_params;
|
||||
|
||||
channels = params_channels(params);
|
||||
|
||||
/*
|
||||
* Determine sample size in bits and the PDC increment.
|
||||
*/
|
||||
switch (params_format(params)) {
|
||||
case SNDRV_PCM_FORMAT_S8:
|
||||
bits = 8;
|
||||
dma_params->pdc_xfer_size = 1;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S16_LE:
|
||||
bits = 16;
|
||||
dma_params->pdc_xfer_size = 2;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S24_LE:
|
||||
bits = 24;
|
||||
dma_params->pdc_xfer_size = 4;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S32_LE:
|
||||
bits = 32;
|
||||
dma_params->pdc_xfer_size = 4;
|
||||
break;
|
||||
default:
|
||||
printk(KERN_WARNING "atmel_ssc_dai: unsupported PCM format");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* The SSC only supports up to 16-bit samples in I2S format, due
|
||||
* to the size of the Frame Mode Register FSLEN field.
|
||||
*/
|
||||
if ((ssc_p->daifmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_I2S
|
||||
&& bits > 16) {
|
||||
printk(KERN_WARNING
|
||||
"atmel_ssc_dai: sample size %d"
|
||||
"is too large for I2S\n", bits);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Compute SSC register settings.
|
||||
*/
|
||||
switch (ssc_p->daifmt
|
||||
& (SND_SOC_DAIFMT_FORMAT_MASK | SND_SOC_DAIFMT_MASTER_MASK)) {
|
||||
|
||||
case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS:
|
||||
/*
|
||||
* I2S format, SSC provides BCLK and LRC clocks.
|
||||
*
|
||||
* The SSC transmit and receive clocks are generated
|
||||
* from the MCK divider, and the BCLK signal
|
||||
* is output on the SSC TK line.
|
||||
*/
|
||||
rcmr = SSC_BF(RCMR_PERIOD, ssc_p->rcmr_period)
|
||||
| SSC_BF(RCMR_STTDLY, START_DELAY)
|
||||
| SSC_BF(RCMR_START, SSC_START_FALLING_RF)
|
||||
| SSC_BF(RCMR_CKI, SSC_CKI_RISING)
|
||||
| SSC_BF(RCMR_CKO, SSC_CKO_NONE)
|
||||
| SSC_BF(RCMR_CKS, SSC_CKS_DIV);
|
||||
|
||||
rfmr = SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
|
||||
| SSC_BF(RFMR_FSOS, SSC_FSOS_NEGATIVE)
|
||||
| SSC_BF(RFMR_FSLEN, (bits - 1))
|
||||
| SSC_BF(RFMR_DATNB, (channels - 1))
|
||||
| SSC_BIT(RFMR_MSBF)
|
||||
| SSC_BF(RFMR_LOOP, 0)
|
||||
| SSC_BF(RFMR_DATLEN, (bits - 1));
|
||||
|
||||
tcmr = SSC_BF(TCMR_PERIOD, ssc_p->tcmr_period)
|
||||
| SSC_BF(TCMR_STTDLY, START_DELAY)
|
||||
| SSC_BF(TCMR_START, SSC_START_FALLING_RF)
|
||||
| SSC_BF(TCMR_CKI, SSC_CKI_FALLING)
|
||||
| SSC_BF(TCMR_CKO, SSC_CKO_CONTINUOUS)
|
||||
| SSC_BF(TCMR_CKS, SSC_CKS_DIV);
|
||||
|
||||
tfmr = SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
|
||||
| SSC_BF(TFMR_FSDEN, 0)
|
||||
| SSC_BF(TFMR_FSOS, SSC_FSOS_NEGATIVE)
|
||||
| SSC_BF(TFMR_FSLEN, (bits - 1))
|
||||
| SSC_BF(TFMR_DATNB, (channels - 1))
|
||||
| SSC_BIT(TFMR_MSBF)
|
||||
| SSC_BF(TFMR_DATDEF, 0)
|
||||
| SSC_BF(TFMR_DATLEN, (bits - 1));
|
||||
break;
|
||||
|
||||
case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM:
|
||||
/*
|
||||
* I2S format, CODEC supplies BCLK and LRC clocks.
|
||||
*
|
||||
* The SSC transmit clock is obtained from the BCLK signal on
|
||||
* on the TK line, and the SSC receive clock is
|
||||
* generated from the transmit clock.
|
||||
*
|
||||
* For single channel data, one sample is transferred
|
||||
* on the falling edge of the LRC clock.
|
||||
* For two channel data, one sample is
|
||||
* transferred on both edges of the LRC clock.
|
||||
*/
|
||||
start_event = ((channels == 1)
|
||||
? SSC_START_FALLING_RF
|
||||
: SSC_START_EDGE_RF);
|
||||
|
||||
rcmr = SSC_BF(RCMR_PERIOD, 0)
|
||||
| SSC_BF(RCMR_STTDLY, START_DELAY)
|
||||
| SSC_BF(RCMR_START, start_event)
|
||||
| SSC_BF(RCMR_CKI, SSC_CKI_RISING)
|
||||
| SSC_BF(RCMR_CKO, SSC_CKO_NONE)
|
||||
| SSC_BF(RCMR_CKS, SSC_CKS_CLOCK);
|
||||
|
||||
rfmr = SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
|
||||
| SSC_BF(RFMR_FSOS, SSC_FSOS_NONE)
|
||||
| SSC_BF(RFMR_FSLEN, 0)
|
||||
| SSC_BF(RFMR_DATNB, 0)
|
||||
| SSC_BIT(RFMR_MSBF)
|
||||
| SSC_BF(RFMR_LOOP, 0)
|
||||
| SSC_BF(RFMR_DATLEN, (bits - 1));
|
||||
|
||||
tcmr = SSC_BF(TCMR_PERIOD, 0)
|
||||
| SSC_BF(TCMR_STTDLY, START_DELAY)
|
||||
| SSC_BF(TCMR_START, start_event)
|
||||
| SSC_BF(TCMR_CKI, SSC_CKI_FALLING)
|
||||
| SSC_BF(TCMR_CKO, SSC_CKO_NONE)
|
||||
| SSC_BF(TCMR_CKS, SSC_CKS_PIN);
|
||||
|
||||
tfmr = SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
|
||||
| SSC_BF(TFMR_FSDEN, 0)
|
||||
| SSC_BF(TFMR_FSOS, SSC_FSOS_NONE)
|
||||
| SSC_BF(TFMR_FSLEN, 0)
|
||||
| SSC_BF(TFMR_DATNB, 0)
|
||||
| SSC_BIT(TFMR_MSBF)
|
||||
| SSC_BF(TFMR_DATDEF, 0)
|
||||
| SSC_BF(TFMR_DATLEN, (bits - 1));
|
||||
break;
|
||||
|
||||
case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBS_CFS:
|
||||
/*
|
||||
* DSP/PCM Mode A format, SSC provides BCLK and LRC clocks.
|
||||
*
|
||||
* The SSC transmit and receive clocks are generated from the
|
||||
* MCK divider, and the BCLK signal is output
|
||||
* on the SSC TK line.
|
||||
*/
|
||||
rcmr = SSC_BF(RCMR_PERIOD, ssc_p->rcmr_period)
|
||||
| SSC_BF(RCMR_STTDLY, 1)
|
||||
| SSC_BF(RCMR_START, SSC_START_RISING_RF)
|
||||
| SSC_BF(RCMR_CKI, SSC_CKI_RISING)
|
||||
| SSC_BF(RCMR_CKO, SSC_CKO_NONE)
|
||||
| SSC_BF(RCMR_CKS, SSC_CKS_DIV);
|
||||
|
||||
rfmr = SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
|
||||
| SSC_BF(RFMR_FSOS, SSC_FSOS_POSITIVE)
|
||||
| SSC_BF(RFMR_FSLEN, 0)
|
||||
| SSC_BF(RFMR_DATNB, (channels - 1))
|
||||
| SSC_BIT(RFMR_MSBF)
|
||||
| SSC_BF(RFMR_LOOP, 0)
|
||||
| SSC_BF(RFMR_DATLEN, (bits - 1));
|
||||
|
||||
tcmr = SSC_BF(TCMR_PERIOD, ssc_p->tcmr_period)
|
||||
| SSC_BF(TCMR_STTDLY, 1)
|
||||
| SSC_BF(TCMR_START, SSC_START_RISING_RF)
|
||||
| SSC_BF(TCMR_CKI, SSC_CKI_RISING)
|
||||
| SSC_BF(TCMR_CKO, SSC_CKO_CONTINUOUS)
|
||||
| SSC_BF(TCMR_CKS, SSC_CKS_DIV);
|
||||
|
||||
tfmr = SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
|
||||
| SSC_BF(TFMR_FSDEN, 0)
|
||||
| SSC_BF(TFMR_FSOS, SSC_FSOS_POSITIVE)
|
||||
| SSC_BF(TFMR_FSLEN, 0)
|
||||
| SSC_BF(TFMR_DATNB, (channels - 1))
|
||||
| SSC_BIT(TFMR_MSBF)
|
||||
| SSC_BF(TFMR_DATDEF, 0)
|
||||
| SSC_BF(TFMR_DATLEN, (bits - 1));
|
||||
break;
|
||||
|
||||
case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBM_CFM:
|
||||
default:
|
||||
printk(KERN_WARNING "atmel_ssc_dai: unsupported DAI format 0x%x\n",
|
||||
ssc_p->daifmt);
|
||||
return -EINVAL;
|
||||
break;
|
||||
}
|
||||
pr_debug("atmel_ssc_hw_params: "
|
||||
"RCMR=%08x RFMR=%08x TCMR=%08x TFMR=%08x\n",
|
||||
rcmr, rfmr, tcmr, tfmr);
|
||||
|
||||
if (!ssc_p->initialized) {
|
||||
|
||||
/* Enable PMC peripheral clock for this SSC */
|
||||
pr_debug("atmel_ssc_dai: Starting clock\n");
|
||||
clk_enable(ssc_p->ssc->clk);
|
||||
|
||||
/* Reset the SSC and its PDC registers */
|
||||
ssc_writel(ssc_p->ssc->regs, CR, SSC_BIT(CR_SWRST));
|
||||
|
||||
ssc_writel(ssc_p->ssc->regs, PDC_RPR, 0);
|
||||
ssc_writel(ssc_p->ssc->regs, PDC_RCR, 0);
|
||||
ssc_writel(ssc_p->ssc->regs, PDC_RNPR, 0);
|
||||
ssc_writel(ssc_p->ssc->regs, PDC_RNCR, 0);
|
||||
|
||||
ssc_writel(ssc_p->ssc->regs, PDC_TPR, 0);
|
||||
ssc_writel(ssc_p->ssc->regs, PDC_TCR, 0);
|
||||
ssc_writel(ssc_p->ssc->regs, PDC_TNPR, 0);
|
||||
ssc_writel(ssc_p->ssc->regs, PDC_TNCR, 0);
|
||||
|
||||
ret = request_irq(ssc_p->ssc->irq, atmel_ssc_interrupt, 0,
|
||||
ssc_p->name, ssc_p);
|
||||
if (ret < 0) {
|
||||
printk(KERN_WARNING
|
||||
"atmel_ssc_dai: request_irq failure\n");
|
||||
pr_debug("Atmel_ssc_dai: Stoping clock\n");
|
||||
clk_disable(ssc_p->ssc->clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ssc_p->initialized = 1;
|
||||
}
|
||||
|
||||
/* set SSC clock mode register */
|
||||
ssc_writel(ssc_p->ssc->regs, CMR, ssc_p->cmr_div);
|
||||
|
||||
/* set receive clock mode and format */
|
||||
ssc_writel(ssc_p->ssc->regs, RCMR, rcmr);
|
||||
ssc_writel(ssc_p->ssc->regs, RFMR, rfmr);
|
||||
|
||||
/* set transmit clock mode and format */
|
||||
ssc_writel(ssc_p->ssc->regs, TCMR, tcmr);
|
||||
ssc_writel(ssc_p->ssc->regs, TFMR, tfmr);
|
||||
|
||||
pr_debug("atmel_ssc_dai,hw_params: SSC initialized\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int atmel_ssc_prepare(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
|
||||
struct atmel_ssc_info *ssc_p = &ssc_info[rtd->dai->cpu_dai->id];
|
||||
struct atmel_pcm_dma_params *dma_params;
|
||||
int dir;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
dir = 0;
|
||||
else
|
||||
dir = 1;
|
||||
|
||||
dma_params = ssc_p->dma_params[dir];
|
||||
|
||||
ssc_writel(ssc_p->ssc->regs, CR, dma_params->mask->ssc_enable);
|
||||
|
||||
pr_debug("%s enabled SSC_SR=0x%08x\n",
|
||||
dir ? "receive" : "transmit",
|
||||
ssc_readl(ssc_p->ssc->regs, SR));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int atmel_ssc_suspend(struct snd_soc_dai *cpu_dai)
|
||||
{
|
||||
struct atmel_ssc_info *ssc_p;
|
||||
|
||||
if (!cpu_dai->active)
|
||||
return 0;
|
||||
|
||||
ssc_p = &ssc_info[cpu_dai->id];
|
||||
|
||||
/* Save the status register before disabling transmit and receive */
|
||||
ssc_p->ssc_state.ssc_sr = ssc_readl(ssc_p->ssc->regs, SR);
|
||||
ssc_writel(ssc_p->ssc->regs, CR, SSC_BIT(CR_TXDIS) | SSC_BIT(CR_RXDIS));
|
||||
|
||||
/* Save the current interrupt mask, then disable unmasked interrupts */
|
||||
ssc_p->ssc_state.ssc_imr = ssc_readl(ssc_p->ssc->regs, IMR);
|
||||
ssc_writel(ssc_p->ssc->regs, IDR, ssc_p->ssc_state.ssc_imr);
|
||||
|
||||
ssc_p->ssc_state.ssc_cmr = ssc_readl(ssc_p->ssc->regs, CMR);
|
||||
ssc_p->ssc_state.ssc_rcmr = ssc_readl(ssc_p->ssc->regs, RCMR);
|
||||
ssc_p->ssc_state.ssc_rfmr = ssc_readl(ssc_p->ssc->regs, RFMR);
|
||||
ssc_p->ssc_state.ssc_tcmr = ssc_readl(ssc_p->ssc->regs, TCMR);
|
||||
ssc_p->ssc_state.ssc_tfmr = ssc_readl(ssc_p->ssc->regs, TFMR);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int atmel_ssc_resume(struct snd_soc_dai *cpu_dai)
|
||||
{
|
||||
struct atmel_ssc_info *ssc_p;
|
||||
u32 cr;
|
||||
|
||||
if (!cpu_dai->active)
|
||||
return 0;
|
||||
|
||||
ssc_p = &ssc_info[cpu_dai->id];
|
||||
|
||||
/* restore SSC register settings */
|
||||
ssc_writel(ssc_p->ssc->regs, TFMR, ssc_p->ssc_state.ssc_tfmr);
|
||||
ssc_writel(ssc_p->ssc->regs, TCMR, ssc_p->ssc_state.ssc_tcmr);
|
||||
ssc_writel(ssc_p->ssc->regs, RFMR, ssc_p->ssc_state.ssc_rfmr);
|
||||
ssc_writel(ssc_p->ssc->regs, RCMR, ssc_p->ssc_state.ssc_rcmr);
|
||||
ssc_writel(ssc_p->ssc->regs, CMR, ssc_p->ssc_state.ssc_cmr);
|
||||
|
||||
/* re-enable interrupts */
|
||||
ssc_writel(ssc_p->ssc->regs, IER, ssc_p->ssc_state.ssc_imr);
|
||||
|
||||
/* Re-enable recieve and transmit as appropriate */
|
||||
cr = 0;
|
||||
cr |=
|
||||
(ssc_p->ssc_state.ssc_sr & SSC_BIT(SR_RXEN)) ? SSC_BIT(CR_RXEN) : 0;
|
||||
cr |=
|
||||
(ssc_p->ssc_state.ssc_sr & SSC_BIT(SR_TXEN)) ? SSC_BIT(CR_TXEN) : 0;
|
||||
ssc_writel(ssc_p->ssc->regs, CR, cr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else /* CONFIG_PM */
|
||||
# define atmel_ssc_suspend NULL
|
||||
# define atmel_ssc_resume NULL
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
|
||||
#define ATMEL_SSC_RATES (SNDRV_PCM_RATE_8000_96000)
|
||||
|
||||
#define ATMEL_SSC_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE |\
|
||||
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
|
||||
|
||||
struct snd_soc_dai atmel_ssc_dai[NUM_SSC_DEVICES] = {
|
||||
{ .name = "atmel-ssc0",
|
||||
.id = 0,
|
||||
.suspend = atmel_ssc_suspend,
|
||||
.resume = atmel_ssc_resume,
|
||||
.playback = {
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.rates = ATMEL_SSC_RATES,
|
||||
.formats = ATMEL_SSC_FORMATS,},
|
||||
.capture = {
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.rates = ATMEL_SSC_RATES,
|
||||
.formats = ATMEL_SSC_FORMATS,},
|
||||
.ops = {
|
||||
.startup = atmel_ssc_startup,
|
||||
.shutdown = atmel_ssc_shutdown,
|
||||
.prepare = atmel_ssc_prepare,
|
||||
.hw_params = atmel_ssc_hw_params,
|
||||
.set_fmt = atmel_ssc_set_dai_fmt,
|
||||
.set_clkdiv = atmel_ssc_set_dai_clkdiv,},
|
||||
.private_data = &ssc_info[0],
|
||||
},
|
||||
#if NUM_SSC_DEVICES == 3
|
||||
{ .name = "atmel-ssc1",
|
||||
.id = 1,
|
||||
.suspend = atmel_ssc_suspend,
|
||||
.resume = atmel_ssc_resume,
|
||||
.playback = {
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.rates = ATMEL_SSC_RATES,
|
||||
.formats = ATMEL_SSC_FORMATS,},
|
||||
.capture = {
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.rates = ATMEL_SSC_RATES,
|
||||
.formats = ATMEL_SSC_FORMATS,},
|
||||
.ops = {
|
||||
.startup = atmel_ssc_startup,
|
||||
.shutdown = atmel_ssc_shutdown,
|
||||
.prepare = atmel_ssc_prepare,
|
||||
.hw_params = atmel_ssc_hw_params,
|
||||
.set_fmt = atmel_ssc_set_dai_fmt,
|
||||
.set_clkdiv = atmel_ssc_set_dai_clkdiv,},
|
||||
.private_data = &ssc_info[1],
|
||||
},
|
||||
{ .name = "atmel-ssc2",
|
||||
.id = 2,
|
||||
.suspend = atmel_ssc_suspend,
|
||||
.resume = atmel_ssc_resume,
|
||||
.playback = {
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.rates = ATMEL_SSC_RATES,
|
||||
.formats = ATMEL_SSC_FORMATS,},
|
||||
.capture = {
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.rates = ATMEL_SSC_RATES,
|
||||
.formats = ATMEL_SSC_FORMATS,},
|
||||
.ops = {
|
||||
.startup = atmel_ssc_startup,
|
||||
.shutdown = atmel_ssc_shutdown,
|
||||
.prepare = atmel_ssc_prepare,
|
||||
.hw_params = atmel_ssc_hw_params,
|
||||
.set_fmt = atmel_ssc_set_dai_fmt,
|
||||
.set_clkdiv = atmel_ssc_set_dai_clkdiv,},
|
||||
.private_data = &ssc_info[2],
|
||||
},
|
||||
#endif
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(atmel_ssc_dai);
|
||||
|
||||
static int __init atmel_ssc_modinit(void)
|
||||
{
|
||||
return snd_soc_register_dais(atmel_ssc_dai, ARRAY_SIZE(atmel_ssc_dai));
|
||||
}
|
||||
module_init(atmel_ssc_modinit);
|
||||
|
||||
static void __exit atmel_ssc_modexit(void)
|
||||
{
|
||||
snd_soc_unregister_dais(atmel_ssc_dai, ARRAY_SIZE(atmel_ssc_dai));
|
||||
}
|
||||
module_exit(atmel_ssc_modexit);
|
||||
|
||||
/* Module information */
|
||||
MODULE_AUTHOR("Sedji Gaouaou, sedji.gaouaou@atmel.com, www.atmel.com");
|
||||
MODULE_DESCRIPTION("ATMEL SSC ASoC Interface");
|
||||
MODULE_LICENSE("GPL");
|
121
sound/soc/atmel/atmel_ssc_dai.h
Normal file
121
sound/soc/atmel/atmel_ssc_dai.h
Normal file
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
* atmel_ssc_dai.h - ALSA SSC interface for the Atmel SoC
|
||||
*
|
||||
* Copyright (C) 2005 SAN People
|
||||
* Copyright (C) 2008 Atmel
|
||||
*
|
||||
* Author: Sedji Gaouaou <sedji.gaouaou@atmel.com>
|
||||
* ATMEL CORP.
|
||||
*
|
||||
* Based on at91-ssc.c by
|
||||
* Frank Mandarino <fmandarino@endrelia.com>
|
||||
* Based on pxa2xx Platform drivers by
|
||||
* Liam Girdwood <liam.girdwood@wolfsonmicro.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _ATMEL_SSC_DAI_H
|
||||
#define _ATMEL_SSC_DAI_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/atmel-ssc.h>
|
||||
|
||||
#include "atmel-pcm.h"
|
||||
|
||||
/* SSC system clock ids */
|
||||
#define ATMEL_SYSCLK_MCK 0 /* SSC uses AT91 MCK as system clock */
|
||||
|
||||
/* SSC divider ids */
|
||||
#define ATMEL_SSC_CMR_DIV 0 /* MCK divider for BCLK */
|
||||
#define ATMEL_SSC_TCMR_PERIOD 1 /* BCLK divider for transmit FS */
|
||||
#define ATMEL_SSC_RCMR_PERIOD 2 /* BCLK divider for receive FS */
|
||||
/*
|
||||
* SSC direction masks
|
||||
*/
|
||||
#define SSC_DIR_MASK_UNUSED 0
|
||||
#define SSC_DIR_MASK_PLAYBACK 1
|
||||
#define SSC_DIR_MASK_CAPTURE 2
|
||||
|
||||
/*
|
||||
* SSC register values that Atmel left out of <linux/atmel-ssc.h>. These
|
||||
* are expected to be used with SSC_BF
|
||||
*/
|
||||
/* START bit field values */
|
||||
#define SSC_START_CONTINUOUS 0
|
||||
#define SSC_START_TX_RX 1
|
||||
#define SSC_START_LOW_RF 2
|
||||
#define SSC_START_HIGH_RF 3
|
||||
#define SSC_START_FALLING_RF 4
|
||||
#define SSC_START_RISING_RF 5
|
||||
#define SSC_START_LEVEL_RF 6
|
||||
#define SSC_START_EDGE_RF 7
|
||||
#define SSS_START_COMPARE_0 8
|
||||
|
||||
/* CKI bit field values */
|
||||
#define SSC_CKI_FALLING 0
|
||||
#define SSC_CKI_RISING 1
|
||||
|
||||
/* CKO bit field values */
|
||||
#define SSC_CKO_NONE 0
|
||||
#define SSC_CKO_CONTINUOUS 1
|
||||
#define SSC_CKO_TRANSFER 2
|
||||
|
||||
/* CKS bit field values */
|
||||
#define SSC_CKS_DIV 0
|
||||
#define SSC_CKS_CLOCK 1
|
||||
#define SSC_CKS_PIN 2
|
||||
|
||||
/* FSEDGE bit field values */
|
||||
#define SSC_FSEDGE_POSITIVE 0
|
||||
#define SSC_FSEDGE_NEGATIVE 1
|
||||
|
||||
/* FSOS bit field values */
|
||||
#define SSC_FSOS_NONE 0
|
||||
#define SSC_FSOS_NEGATIVE 1
|
||||
#define SSC_FSOS_POSITIVE 2
|
||||
#define SSC_FSOS_LOW 3
|
||||
#define SSC_FSOS_HIGH 4
|
||||
#define SSC_FSOS_TOGGLE 5
|
||||
|
||||
#define START_DELAY 1
|
||||
|
||||
struct atmel_ssc_state {
|
||||
u32 ssc_cmr;
|
||||
u32 ssc_rcmr;
|
||||
u32 ssc_rfmr;
|
||||
u32 ssc_tcmr;
|
||||
u32 ssc_tfmr;
|
||||
u32 ssc_sr;
|
||||
u32 ssc_imr;
|
||||
};
|
||||
|
||||
|
||||
struct atmel_ssc_info {
|
||||
char *name;
|
||||
struct ssc_device *ssc;
|
||||
spinlock_t lock; /* lock for dir_mask */
|
||||
unsigned short dir_mask; /* 0=unused, 1=playback, 2=capture */
|
||||
unsigned short initialized; /* true if SSC has been initialized */
|
||||
unsigned short daifmt;
|
||||
unsigned short cmr_div;
|
||||
unsigned short tcmr_period;
|
||||
unsigned short rcmr_period;
|
||||
struct atmel_pcm_dma_params *dma_params[2];
|
||||
struct atmel_ssc_state ssc_state;
|
||||
};
|
||||
extern struct snd_soc_dai atmel_ssc_dai[];
|
||||
|
||||
#endif /* _AT91_SSC_DAI_H */
|
|
@ -22,7 +22,6 @@
|
|||
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/clk.h>
|
||||
|
@ -40,8 +39,8 @@
|
|||
#include <mach/portmux.h>
|
||||
|
||||
#include "../codecs/wm8510.h"
|
||||
#include "at32-pcm.h"
|
||||
#include "at32-ssc.h"
|
||||
#include "atmel-pcm.h"
|
||||
#include "atmel_ssc_dai.h"
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*\
|
||||
|
@ -362,8 +361,9 @@ static struct snd_soc_dai_link playpaq_wm8510_dai = {
|
|||
|
||||
|
||||
|
||||
static struct snd_soc_machine snd_soc_machine_playpaq = {
|
||||
static struct snd_soc_card snd_soc_playpaq = {
|
||||
.name = "LRS_PlayPaq_WM8510",
|
||||
.platform = &at32_soc_platform,
|
||||
.dai_link = &playpaq_wm8510_dai,
|
||||
.num_links = 1,
|
||||
};
|
||||
|
@ -378,8 +378,7 @@ static struct wm8510_setup_data playpaq_wm8510_setup = {
|
|||
|
||||
|
||||
static struct snd_soc_device playpaq_wm8510_snd_devdata = {
|
||||
.machine = &snd_soc_machine_playpaq,
|
||||
.platform = &at32_soc_platform,
|
||||
.card = &snd_soc_playpaq,
|
||||
.codec_dev = &soc_codec_dev_wm8510,
|
||||
.codec_data = &playpaq_wm8510_setup,
|
||||
};
|
328
sound/soc/atmel/sam9g20_wm8731.c
Normal file
328
sound/soc/atmel/sam9g20_wm8731.c
Normal file
|
@ -0,0 +1,328 @@
|
|||
/*
|
||||
* sam9g20_wm8731 -- SoC audio for AT91SAM9G20-based
|
||||
* ATMEL AT91SAM9G20ek board.
|
||||
*
|
||||
* Copyright (C) 2005 SAN People
|
||||
* Copyright (C) 2008 Atmel
|
||||
*
|
||||
* Authors: Sedji Gaouaou <sedji.gaouaou@atmel.com>
|
||||
*
|
||||
* Based on ati_b1_wm8731.c by:
|
||||
* Frank Mandarino <fmandarino@endrelia.com>
|
||||
* Copyright 2006 Endrelia Technologies Inc.
|
||||
* Based on corgi.c by:
|
||||
* Copyright 2005 Wolfson Microelectronics PLC.
|
||||
* Copyright 2005 Openedhand 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include <linux/atmel-ssc.h>
|
||||
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/soc-dapm.h>
|
||||
|
||||
#include <mach/hardware.h>
|
||||
#include <mach/gpio.h>
|
||||
|
||||
#include "../codecs/wm8731.h"
|
||||
#include "atmel-pcm.h"
|
||||
#include "atmel_ssc_dai.h"
|
||||
|
||||
|
||||
static int at91sam9g20ek_startup(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
|
||||
struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
|
||||
int ret;
|
||||
|
||||
/* codec system clock is supplied by PCK0, set to 12MHz */
|
||||
ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK,
|
||||
12000000, SND_SOC_CLOCK_IN);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void at91sam9g20ek_shutdown(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
|
||||
|
||||
dev_dbg(rtd->socdev->dev, "shutdown");
|
||||
}
|
||||
|
||||
static int at91sam9g20ek_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->dai->codec_dai;
|
||||
struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
|
||||
struct atmel_ssc_info *ssc_p = cpu_dai->private_data;
|
||||
struct ssc_device *ssc = ssc_p->ssc;
|
||||
int ret;
|
||||
|
||||
unsigned int rate;
|
||||
int cmr_div, period;
|
||||
|
||||
if (ssc == NULL) {
|
||||
printk(KERN_INFO "at91sam9g20ek_hw_params: ssc is NULL!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* set codec DAI configuration */
|
||||
ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
|
||||
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_I2S |
|
||||
SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* The SSC clock dividers depend on the sample rate. The CMR.DIV
|
||||
* field divides the system master clock MCK to drive the SSC TK
|
||||
* signal which provides the codec BCLK. The TCMR.PERIOD and
|
||||
* RCMR.PERIOD fields further divide the BCLK signal to drive
|
||||
* the SSC TF and RF signals which provide the codec DACLRC and
|
||||
* ADCLRC clocks.
|
||||
*
|
||||
* The dividers were determined through trial and error, where a
|
||||
* CMR.DIV value is chosen such that the resulting BCLK value is
|
||||
* divisible, or almost divisible, by (2 * sample rate), and then
|
||||
* the TCMR.PERIOD or RCMR.PERIOD is BCLK / (2 * sample rate) - 1.
|
||||
*/
|
||||
rate = params_rate(params);
|
||||
|
||||
switch (rate) {
|
||||
case 8000:
|
||||
cmr_div = 55; /* BCLK = 133MHz/(2*55) = 1.209MHz */
|
||||
period = 74; /* LRC = BCLK/(2*(74+1)) ~= 8060,6Hz */
|
||||
break;
|
||||
case 11025:
|
||||
cmr_div = 67; /* BCLK = 133MHz/(2*60) = 1.108MHz */
|
||||
period = 45; /* LRC = BCLK/(2*(49+1)) = 11083,3Hz */
|
||||
break;
|
||||
case 16000:
|
||||
cmr_div = 63; /* BCLK = 133MHz/(2*63) = 1.055MHz */
|
||||
period = 32; /* LRC = BCLK/(2*(32+1)) = 15993,2Hz */
|
||||
break;
|
||||
case 22050:
|
||||
cmr_div = 52; /* BCLK = 133MHz/(2*52) = 1.278MHz */
|
||||
period = 28; /* LRC = BCLK/(2*(28+1)) = 22049Hz */
|
||||
break;
|
||||
case 32000:
|
||||
cmr_div = 66; /* BCLK = 133MHz/(2*66) = 1.007MHz */
|
||||
period = 15; /* LRC = BCLK/(2*(15+1)) = 31486,742Hz */
|
||||
break;
|
||||
case 44100:
|
||||
cmr_div = 29; /* BCLK = 133MHz/(2*29) = 2.293MHz */
|
||||
period = 25; /* LRC = BCLK/(2*(25+1)) = 44098Hz */
|
||||
break;
|
||||
case 48000:
|
||||
cmr_div = 33; /* BCLK = 133MHz/(2*33) = 2.015MHz */
|
||||
period = 20; /* LRC = BCLK/(2*(20+1)) = 47979,79Hz */
|
||||
break;
|
||||
case 88200:
|
||||
cmr_div = 29; /* BCLK = 133MHz/(2*29) = 2.293MHz */
|
||||
period = 12; /* LRC = BCLK/(2*(12+1)) = 88196Hz */
|
||||
break;
|
||||
case 96000:
|
||||
cmr_div = 23; /* BCLK = 133MHz/(2*23) = 2.891MHz */
|
||||
period = 14; /* LRC = BCLK/(2*(14+1)) = 96376Hz */
|
||||
break;
|
||||
default:
|
||||
printk(KERN_WARNING "unsupported rate %d"
|
||||
" on at91sam9g20ek board\n", rate);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* set the MCK divider for BCLK */
|
||||
ret = snd_soc_dai_set_clkdiv(cpu_dai, ATMEL_SSC_CMR_DIV, cmr_div);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
/* set the BCLK divider for DACLRC */
|
||||
ret = snd_soc_dai_set_clkdiv(cpu_dai,
|
||||
ATMEL_SSC_TCMR_PERIOD, period);
|
||||
} else {
|
||||
/* set the BCLK divider for ADCLRC */
|
||||
ret = snd_soc_dai_set_clkdiv(cpu_dai,
|
||||
ATMEL_SSC_RCMR_PERIOD, period);
|
||||
}
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_soc_ops at91sam9g20ek_ops = {
|
||||
.startup = at91sam9g20ek_startup,
|
||||
.hw_params = at91sam9g20ek_hw_params,
|
||||
.shutdown = at91sam9g20ek_shutdown,
|
||||
};
|
||||
|
||||
|
||||
static const struct snd_soc_dapm_widget at91sam9g20ek_dapm_widgets[] = {
|
||||
SND_SOC_DAPM_MIC("Int Mic", NULL),
|
||||
SND_SOC_DAPM_SPK("Ext Spk", NULL),
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route intercon[] = {
|
||||
|
||||
/* speaker connected to LHPOUT */
|
||||
{"Ext Spk", NULL, "LHPOUT"},
|
||||
|
||||
/* mic is connected to Mic Jack, with WM8731 Mic Bias */
|
||||
{"MICIN", NULL, "Mic Bias"},
|
||||
{"Mic Bias", NULL, "Int Mic"},
|
||||
};
|
||||
|
||||
/*
|
||||
* Logic for a wm8731 as connected on a at91sam9g20ek board.
|
||||
*/
|
||||
static int at91sam9g20ek_wm8731_init(struct snd_soc_codec *codec)
|
||||
{
|
||||
printk(KERN_DEBUG
|
||||
"at91sam9g20ek_wm8731 "
|
||||
": at91sam9g20ek_wm8731_init() called\n");
|
||||
|
||||
/* Add specific widgets */
|
||||
snd_soc_dapm_new_controls(codec, at91sam9g20ek_dapm_widgets,
|
||||
ARRAY_SIZE(at91sam9g20ek_dapm_widgets));
|
||||
/* Set up specific audio path interconnects */
|
||||
snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon));
|
||||
|
||||
/* not connected */
|
||||
snd_soc_dapm_disable_pin(codec, "RLINEIN");
|
||||
snd_soc_dapm_disable_pin(codec, "LLINEIN");
|
||||
|
||||
/* always connected */
|
||||
snd_soc_dapm_enable_pin(codec, "Int Mic");
|
||||
snd_soc_dapm_enable_pin(codec, "Ext Spk");
|
||||
|
||||
snd_soc_dapm_sync(codec);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_soc_dai_link at91sam9g20ek_dai = {
|
||||
.name = "WM8731",
|
||||
.stream_name = "WM8731 PCM",
|
||||
.cpu_dai = &atmel_ssc_dai[0],
|
||||
.codec_dai = &wm8731_dai,
|
||||
.init = at91sam9g20ek_wm8731_init,
|
||||
.ops = &at91sam9g20ek_ops,
|
||||
};
|
||||
|
||||
static struct snd_soc_card snd_soc_at91sam9g20ek = {
|
||||
.name = "WM8731",
|
||||
.platform = &atmel_soc_platform,
|
||||
.dai_link = &at91sam9g20ek_dai,
|
||||
.num_links = 1,
|
||||
};
|
||||
|
||||
static struct wm8731_setup_data at91sam9g20ek_wm8731_setup = {
|
||||
.i2c_bus = 0,
|
||||
.i2c_address = 0x1b,
|
||||
};
|
||||
|
||||
static struct snd_soc_device at91sam9g20ek_snd_devdata = {
|
||||
.card = &snd_soc_at91sam9g20ek,
|
||||
.codec_dev = &soc_codec_dev_wm8731,
|
||||
.codec_data = &at91sam9g20ek_wm8731_setup,
|
||||
};
|
||||
|
||||
static struct platform_device *at91sam9g20ek_snd_device;
|
||||
|
||||
static int __init at91sam9g20ek_init(void)
|
||||
{
|
||||
struct atmel_ssc_info *ssc_p = at91sam9g20ek_dai.cpu_dai->private_data;
|
||||
struct ssc_device *ssc = NULL;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Request SSC device
|
||||
*/
|
||||
ssc = ssc_request(0);
|
||||
if (IS_ERR(ssc)) {
|
||||
ret = PTR_ERR(ssc);
|
||||
ssc = NULL;
|
||||
goto err_ssc;
|
||||
}
|
||||
ssc_p->ssc = ssc;
|
||||
|
||||
at91sam9g20ek_snd_device = platform_device_alloc("soc-audio", -1);
|
||||
if (!at91sam9g20ek_snd_device) {
|
||||
printk(KERN_DEBUG
|
||||
"platform device allocation failed\n");
|
||||
ret = -ENOMEM;
|
||||
}
|
||||
|
||||
platform_set_drvdata(at91sam9g20ek_snd_device,
|
||||
&at91sam9g20ek_snd_devdata);
|
||||
at91sam9g20ek_snd_devdata.dev = &at91sam9g20ek_snd_device->dev;
|
||||
|
||||
ret = platform_device_add(at91sam9g20ek_snd_device);
|
||||
if (ret) {
|
||||
printk(KERN_DEBUG
|
||||
"platform device allocation failed\n");
|
||||
platform_device_put(at91sam9g20ek_snd_device);
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
err_ssc:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit at91sam9g20ek_exit(void)
|
||||
{
|
||||
struct atmel_ssc_info *ssc_p = at91sam9g20ek_dai.cpu_dai->private_data;
|
||||
struct ssc_device *ssc;
|
||||
|
||||
if (ssc_p != NULL) {
|
||||
ssc = ssc_p->ssc;
|
||||
if (ssc != NULL)
|
||||
ssc_free(ssc);
|
||||
ssc_p->ssc = NULL;
|
||||
}
|
||||
|
||||
platform_device_unregister(at91sam9g20ek_snd_device);
|
||||
at91sam9g20ek_snd_device = NULL;
|
||||
}
|
||||
|
||||
module_init(at91sam9g20ek_init);
|
||||
module_exit(at91sam9g20ek_exit);
|
||||
|
||||
/* Module information */
|
||||
MODULE_AUTHOR("Sedji Gaouaou <sedji.gaouaou@atmel.com>");
|
||||
MODULE_DESCRIPTION("ALSA SoC AT91SAM9G20EK_WM8731");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -406,11 +406,12 @@ static int __init au1xpsc_audio_dbdma_init(void)
|
|||
{
|
||||
au1xpsc_audio_pcmdma[PCM_TX] = NULL;
|
||||
au1xpsc_audio_pcmdma[PCM_RX] = NULL;
|
||||
return 0;
|
||||
return snd_soc_register_platform(&au1xpsc_soc_platform);
|
||||
}
|
||||
|
||||
static void __exit au1xpsc_audio_dbdma_exit(void)
|
||||
{
|
||||
snd_soc_unregister_platform(&au1xpsc_soc_platform);
|
||||
}
|
||||
|
||||
module_init(au1xpsc_audio_dbdma_init);
|
||||
|
|
|
@ -160,7 +160,8 @@ struct snd_ac97_bus_ops soc_ac97_ops = {
|
|||
EXPORT_SYMBOL_GPL(soc_ac97_ops);
|
||||
|
||||
static int au1xpsc_ac97_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
/* FIXME */
|
||||
struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata;
|
||||
|
@ -210,7 +211,7 @@ static int au1xpsc_ac97_hw_params(struct snd_pcm_substream *substream,
|
|||
}
|
||||
|
||||
static int au1xpsc_ac97_trigger(struct snd_pcm_substream *substream,
|
||||
int cmd)
|
||||
int cmd, struct snd_soc_dai *dai)
|
||||
{
|
||||
/* FIXME */
|
||||
struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata;
|
||||
|
@ -313,8 +314,7 @@ static void au1xpsc_ac97_remove(struct platform_device *pdev,
|
|||
au1xpsc_ac97_workdata = NULL;
|
||||
}
|
||||
|
||||
static int au1xpsc_ac97_suspend(struct platform_device *pdev,
|
||||
struct snd_soc_dai *dai)
|
||||
static int au1xpsc_ac97_suspend(struct snd_soc_dai *dai)
|
||||
{
|
||||
/* save interesting registers and disable PSC */
|
||||
au1xpsc_ac97_workdata->pm[0] =
|
||||
|
@ -328,8 +328,7 @@ static int au1xpsc_ac97_suspend(struct platform_device *pdev,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int au1xpsc_ac97_resume(struct platform_device *pdev,
|
||||
struct snd_soc_dai *dai)
|
||||
static int au1xpsc_ac97_resume(struct snd_soc_dai *dai)
|
||||
{
|
||||
/* restore PSC clock config */
|
||||
au_writel(au1xpsc_ac97_workdata->pm[0] | PSC_SEL_PS_AC97MODE,
|
||||
|
@ -345,7 +344,7 @@ static int au1xpsc_ac97_resume(struct platform_device *pdev,
|
|||
|
||||
struct snd_soc_dai au1xpsc_ac97_dai = {
|
||||
.name = "au1xpsc_ac97",
|
||||
.type = SND_SOC_DAI_AC97,
|
||||
.ac97_control = 1,
|
||||
.probe = au1xpsc_ac97_probe,
|
||||
.remove = au1xpsc_ac97_remove,
|
||||
.suspend = au1xpsc_ac97_suspend,
|
||||
|
@ -372,11 +371,12 @@ EXPORT_SYMBOL_GPL(au1xpsc_ac97_dai);
|
|||
static int __init au1xpsc_ac97_init(void)
|
||||
{
|
||||
au1xpsc_ac97_workdata = NULL;
|
||||
return 0;
|
||||
return snd_soc_register_dai(&au1xpsc_ac97_dai);
|
||||
}
|
||||
|
||||
static void __exit au1xpsc_ac97_exit(void)
|
||||
{
|
||||
snd_soc_unregister_dai(&au1xpsc_ac97_dai);
|
||||
}
|
||||
|
||||
module_init(au1xpsc_ac97_init);
|
||||
|
|
|
@ -116,7 +116,8 @@ static int au1xpsc_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
|
|||
}
|
||||
|
||||
static int au1xpsc_i2s_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct au1xpsc_audio_data *pscdata = au1xpsc_i2s_workdata;
|
||||
|
||||
|
@ -240,7 +241,8 @@ static int au1xpsc_i2s_stop(struct au1xpsc_audio_data *pscdata, int stype)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int au1xpsc_i2s_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
static int au1xpsc_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct au1xpsc_audio_data *pscdata = au1xpsc_i2s_workdata;
|
||||
int ret, stype = SUBSTREAM_TYPE(substream);
|
||||
|
@ -337,8 +339,7 @@ static void au1xpsc_i2s_remove(struct platform_device *pdev,
|
|||
au1xpsc_i2s_workdata = NULL;
|
||||
}
|
||||
|
||||
static int au1xpsc_i2s_suspend(struct platform_device *pdev,
|
||||
struct snd_soc_dai *cpu_dai)
|
||||
static int au1xpsc_i2s_suspend(struct snd_soc_dai *cpu_dai)
|
||||
{
|
||||
/* save interesting register and disable PSC */
|
||||
au1xpsc_i2s_workdata->pm[0] =
|
||||
|
@ -352,8 +353,7 @@ static int au1xpsc_i2s_suspend(struct platform_device *pdev,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int au1xpsc_i2s_resume(struct platform_device *pdev,
|
||||
struct snd_soc_dai *cpu_dai)
|
||||
static int au1xpsc_i2s_resume(struct snd_soc_dai *cpu_dai)
|
||||
{
|
||||
/* select I2S mode and PSC clock */
|
||||
au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_i2s_workdata));
|
||||
|
@ -369,7 +369,6 @@ static int au1xpsc_i2s_resume(struct platform_device *pdev,
|
|||
|
||||
struct snd_soc_dai au1xpsc_i2s_dai = {
|
||||
.name = "au1xpsc_i2s",
|
||||
.type = SND_SOC_DAI_I2S,
|
||||
.probe = au1xpsc_i2s_probe,
|
||||
.remove = au1xpsc_i2s_remove,
|
||||
.suspend = au1xpsc_i2s_suspend,
|
||||
|
@ -389,8 +388,6 @@ struct snd_soc_dai au1xpsc_i2s_dai = {
|
|||
.ops = {
|
||||
.trigger = au1xpsc_i2s_trigger,
|
||||
.hw_params = au1xpsc_i2s_hw_params,
|
||||
},
|
||||
.dai_ops = {
|
||||
.set_fmt = au1xpsc_i2s_set_fmt,
|
||||
},
|
||||
};
|
||||
|
@ -399,11 +396,12 @@ EXPORT_SYMBOL(au1xpsc_i2s_dai);
|
|||
static int __init au1xpsc_i2s_init(void)
|
||||
{
|
||||
au1xpsc_i2s_workdata = NULL;
|
||||
return 0;
|
||||
return snd_soc_register_dai(&au1xpsc_i2s_dai);
|
||||
}
|
||||
|
||||
static void __exit au1xpsc_i2s_exit(void)
|
||||
{
|
||||
snd_soc_unregister_dai(&au1xpsc_i2s_dai);
|
||||
}
|
||||
|
||||
module_init(au1xpsc_i2s_init);
|
||||
|
|
|
@ -42,14 +42,14 @@ static struct snd_soc_dai_link au1xpsc_sample_ac97_dai = {
|
|||
.ops = NULL,
|
||||
};
|
||||
|
||||
static struct snd_soc_machine au1xpsc_sample_ac97_machine = {
|
||||
static struct snd_soc_card au1xpsc_sample_ac97_machine = {
|
||||
.name = "Au1xxx PSC AC97 Audio",
|
||||
.dai_link = &au1xpsc_sample_ac97_dai,
|
||||
.num_links = 1,
|
||||
};
|
||||
|
||||
static struct snd_soc_device au1xpsc_sample_ac97_devdata = {
|
||||
.machine = &au1xpsc_sample_ac97_machine,
|
||||
.card = &au1xpsc_sample_ac97_machine,
|
||||
.platform = &au1xpsc_soc_platform, /* see dbdma2.c */
|
||||
.codec_dev = &soc_codec_dev_ac97,
|
||||
};
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
config SND_BF5XX_I2S
|
||||
tristate "SoC I2S Audio for the ADI BF5xx chip"
|
||||
depends on BLACKFIN && SND_SOC
|
||||
depends on BLACKFIN
|
||||
help
|
||||
Say Y or M if you want to add support for codecs attached to
|
||||
the Blackfin SPORT (synchronous serial ports) interface in I2S
|
||||
|
@ -13,7 +13,6 @@ config SND_BF5XX_SOC_SSM2602
|
|||
select SND_BF5XX_SOC_I2S
|
||||
select SND_SOC_SSM2602
|
||||
select I2C
|
||||
select I2C_BLACKFIN_TWI
|
||||
help
|
||||
Say Y if you want to add support for SoC audio on BF527-EZKIT.
|
||||
|
||||
|
@ -35,7 +34,7 @@ config SND_BFIN_AD73311_SE
|
|||
|
||||
config SND_BF5XX_AC97
|
||||
tristate "SoC AC97 Audio for the ADI BF5xx chip"
|
||||
depends on BLACKFIN && SND_SOC
|
||||
depends on BLACKFIN
|
||||
help
|
||||
Say Y or M if you want to add support for codecs attached to
|
||||
the Blackfin SPORT (synchronous serial ports) interface in slot 16
|
||||
|
@ -47,7 +46,7 @@ config SND_BF5XX_AC97
|
|||
properly with this driver. This driver is known to work with the
|
||||
Analog Devices line of AC97 codecs.
|
||||
|
||||
config SND_MMAP_SUPPORT
|
||||
config SND_BF5XX_MMAP_SUPPORT
|
||||
bool "Enable MMAP Support"
|
||||
depends on SND_BF5XX_AC97
|
||||
default y
|
||||
|
@ -55,9 +54,17 @@ config SND_MMAP_SUPPORT
|
|||
Say y if you want AC97 driver to support mmap mode.
|
||||
We introduce an intermediate buffer to simulate mmap.
|
||||
|
||||
config SND_BF5XX_MULTICHAN_SUPPORT
|
||||
bool "Enable Multichannel Support"
|
||||
depends on SND_BF5XX_AC97
|
||||
default n
|
||||
help
|
||||
Say y if you want AC97 driver to support up to 5.1 channel audio.
|
||||
this mode will consume much more memory for DMA.
|
||||
|
||||
config SND_BF5XX_SOC_SPORT
|
||||
tristate
|
||||
|
||||
|
||||
config SND_BF5XX_SOC_I2S
|
||||
tristate
|
||||
select SND_BF5XX_SOC_SPORT
|
||||
|
@ -80,7 +87,7 @@ config SND_BF5XX_SPORT_NUM
|
|||
int "Set a SPORT for Sound chip"
|
||||
depends on (SND_BF5XX_I2S || SND_BF5XX_AC97)
|
||||
range 0 3 if BF54x
|
||||
range 0 1 if (BF53x || BF561)
|
||||
range 0 1 if !BF54x
|
||||
default 0
|
||||
help
|
||||
Set the correct SPORT for sound chip.
|
||||
|
@ -90,12 +97,13 @@ config SND_BF5XX_HAVE_COLD_RESET
|
|||
depends on SND_BF5XX_AC97
|
||||
default y if BFIN548_EZKIT
|
||||
default n if !BFIN548_EZKIT
|
||||
|
||||
|
||||
config SND_BF5XX_RESET_GPIO_NUM
|
||||
int "Set a GPIO for cold reset"
|
||||
depends on SND_BF5XX_HAVE_COLD_RESET
|
||||
range 0 159
|
||||
default 19 if BFIN548_EZKIT
|
||||
default 5 if BFIN537_STAMP
|
||||
default 0
|
||||
help
|
||||
Set the correct GPIO for RESET the sound chip.
|
||||
|
|
|
@ -43,24 +43,34 @@
|
|||
#include "bf5xx-ac97.h"
|
||||
#include "bf5xx-sport.h"
|
||||
|
||||
#if defined(CONFIG_SND_MMAP_SUPPORT)
|
||||
static unsigned int ac97_chan_mask[] = {
|
||||
SP_FL, /* Mono */
|
||||
SP_STEREO, /* Stereo */
|
||||
SP_2DOT1, /* 2.1*/
|
||||
SP_QUAD,/*Quadraquic*/
|
||||
SP_FL | SP_FR | SP_FC | SP_SL | SP_SR,/*5 channels */
|
||||
SP_5DOT1, /* 5.1 */
|
||||
};
|
||||
|
||||
#if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
|
||||
static void bf5xx_mmap_copy(struct snd_pcm_substream *substream,
|
||||
snd_pcm_uframes_t count)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct sport_device *sport = runtime->private_data;
|
||||
unsigned int chan_mask = ac97_chan_mask[runtime->channels - 1];
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
bf5xx_pcm_to_ac97(
|
||||
(struct ac97_frame *)sport->tx_dma_buf + sport->tx_pos,
|
||||
(__u32 *)runtime->dma_area + sport->tx_pos, count);
|
||||
bf5xx_pcm_to_ac97((struct ac97_frame *)sport->tx_dma_buf +
|
||||
sport->tx_pos, (__u16 *)runtime->dma_area + sport->tx_pos *
|
||||
runtime->channels, count, chan_mask);
|
||||
sport->tx_pos += runtime->period_size;
|
||||
if (sport->tx_pos >= runtime->buffer_size)
|
||||
sport->tx_pos %= runtime->buffer_size;
|
||||
sport->tx_delay_pos = sport->tx_pos;
|
||||
} else {
|
||||
bf5xx_ac97_to_pcm(
|
||||
(struct ac97_frame *)sport->rx_dma_buf + sport->rx_pos,
|
||||
(__u32 *)runtime->dma_area + sport->rx_pos, count);
|
||||
bf5xx_ac97_to_pcm((struct ac97_frame *)sport->rx_dma_buf +
|
||||
sport->rx_pos, (__u16 *)runtime->dma_area + sport->rx_pos *
|
||||
runtime->channels, count);
|
||||
sport->rx_pos += runtime->period_size;
|
||||
if (sport->rx_pos >= runtime->buffer_size)
|
||||
sport->rx_pos %= runtime->buffer_size;
|
||||
|
@ -71,7 +81,7 @@ static void bf5xx_mmap_copy(struct snd_pcm_substream *substream,
|
|||
static void bf5xx_dma_irq(void *data)
|
||||
{
|
||||
struct snd_pcm_substream *pcm = data;
|
||||
#if defined(CONFIG_SND_MMAP_SUPPORT)
|
||||
#if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
|
||||
struct snd_pcm_runtime *runtime = pcm->runtime;
|
||||
struct sport_device *sport = runtime->private_data;
|
||||
bf5xx_mmap_copy(pcm, runtime->period_size);
|
||||
|
@ -90,17 +100,14 @@ static void bf5xx_dma_irq(void *data)
|
|||
* The total rx/tx buffer is for ac97 frame to hold all pcm data
|
||||
* is 0x20000 * sizeof(struct ac97_frame) / 4.
|
||||
*/
|
||||
#ifdef CONFIG_SND_MMAP_SUPPORT
|
||||
static const struct snd_pcm_hardware bf5xx_pcm_hardware = {
|
||||
.info = SNDRV_PCM_INFO_INTERLEAVED |
|
||||
#if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
|
||||
SNDRV_PCM_INFO_MMAP |
|
||||
SNDRV_PCM_INFO_MMAP_VALID |
|
||||
SNDRV_PCM_INFO_BLOCK_TRANSFER,
|
||||
#else
|
||||
static const struct snd_pcm_hardware bf5xx_pcm_hardware = {
|
||||
.info = SNDRV_PCM_INFO_INTERLEAVED |
|
||||
SNDRV_PCM_INFO_BLOCK_TRANSFER,
|
||||
#endif
|
||||
SNDRV_PCM_INFO_BLOCK_TRANSFER,
|
||||
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
.period_bytes_min = 32,
|
||||
.period_bytes_max = 0x10000,
|
||||
|
@ -123,10 +130,20 @@ static int bf5xx_pcm_hw_params(struct snd_pcm_substream *substream,
|
|||
|
||||
static int bf5xx_pcm_hw_free(struct snd_pcm_substream *substream)
|
||||
{
|
||||
#if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct sport_device *sport = runtime->private_data;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
memset(runtime->dma_area, 0, runtime->buffer_size);
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
sport->once = 0;
|
||||
if (runtime->dma_area)
|
||||
memset(runtime->dma_area, 0, runtime->buffer_size);
|
||||
memset(sport->tx_dma_buf, 0, runtime->buffer_size *
|
||||
sizeof(struct ac97_frame));
|
||||
} else
|
||||
memset(sport->rx_dma_buf, 0, runtime->buffer_size *
|
||||
sizeof(struct ac97_frame));
|
||||
#endif
|
||||
snd_pcm_lib_free_pages(substream);
|
||||
return 0;
|
||||
}
|
||||
|
@ -139,7 +156,7 @@ static int bf5xx_pcm_prepare(struct snd_pcm_substream *substream)
|
|||
/* An intermediate buffer is introduced for implementing mmap for
|
||||
* SPORT working in TMD mode(include AC97).
|
||||
*/
|
||||
#if defined(CONFIG_SND_MMAP_SUPPORT)
|
||||
#if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
sport_set_tx_callback(sport, bf5xx_dma_irq, substream);
|
||||
sport_config_tx_dma(sport, sport->tx_dma_buf, runtime->periods,
|
||||
|
@ -173,24 +190,24 @@ static int bf5xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
|
|||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
#if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
|
||||
bf5xx_mmap_copy(substream, runtime->period_size);
|
||||
snd_pcm_period_elapsed(substream);
|
||||
sport->tx_delay_pos = 0;
|
||||
#endif
|
||||
sport_tx_start(sport);
|
||||
}
|
||||
else
|
||||
} else
|
||||
sport_rx_start(sport);
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
#if defined(CONFIG_SND_MMAP_SUPPORT)
|
||||
#if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
|
||||
sport->tx_pos = 0;
|
||||
#endif
|
||||
sport_tx_stop(sport);
|
||||
} else {
|
||||
#if defined(CONFIG_SND_MMAP_SUPPORT)
|
||||
#if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
|
||||
sport->rx_pos = 0;
|
||||
#endif
|
||||
sport_rx_stop(sport);
|
||||
|
@ -208,7 +225,7 @@ static snd_pcm_uframes_t bf5xx_pcm_pointer(struct snd_pcm_substream *substream)
|
|||
struct sport_device *sport = runtime->private_data;
|
||||
unsigned int curr;
|
||||
|
||||
#if defined(CONFIG_SND_MMAP_SUPPORT)
|
||||
#if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
curr = sport->tx_delay_pos;
|
||||
else
|
||||
|
@ -249,22 +266,7 @@ static int bf5xx_pcm_open(struct snd_pcm_substream *substream)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int bf5xx_pcm_close(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct sport_device *sport = runtime->private_data;
|
||||
|
||||
pr_debug("%s enter\n", __func__);
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
sport->once = 0;
|
||||
memset(sport->tx_dma_buf, 0, runtime->buffer_size * sizeof(struct ac97_frame));
|
||||
} else
|
||||
memset(sport->rx_dma_buf, 0, runtime->buffer_size * sizeof(struct ac97_frame));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SND_MMAP_SUPPORT
|
||||
#if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
|
||||
static int bf5xx_pcm_mmap(struct snd_pcm_substream *substream,
|
||||
struct vm_area_struct *vma)
|
||||
{
|
||||
|
@ -281,32 +283,29 @@ static int bf5xx_pcm_copy(struct snd_pcm_substream *substream, int channel,
|
|||
void __user *buf, snd_pcm_uframes_t count)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
|
||||
unsigned int chan_mask = ac97_chan_mask[runtime->channels - 1];
|
||||
pr_debug("%s copy pos:0x%lx count:0x%lx\n",
|
||||
substream->stream ? "Capture" : "Playback", pos, count);
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
bf5xx_pcm_to_ac97(
|
||||
(struct ac97_frame *)runtime->dma_area + pos,
|
||||
buf, count);
|
||||
bf5xx_pcm_to_ac97((struct ac97_frame *)runtime->dma_area + pos,
|
||||
(__u16 *)buf, count, chan_mask);
|
||||
else
|
||||
bf5xx_ac97_to_pcm(
|
||||
(struct ac97_frame *)runtime->dma_area + pos,
|
||||
buf, count);
|
||||
bf5xx_ac97_to_pcm((struct ac97_frame *)runtime->dma_area + pos,
|
||||
(__u16 *)buf, count);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
struct snd_pcm_ops bf5xx_pcm_ac97_ops = {
|
||||
.open = bf5xx_pcm_open,
|
||||
.close = bf5xx_pcm_close,
|
||||
.ioctl = snd_pcm_lib_ioctl,
|
||||
.hw_params = bf5xx_pcm_hw_params,
|
||||
.hw_free = bf5xx_pcm_hw_free,
|
||||
.prepare = bf5xx_pcm_prepare,
|
||||
.trigger = bf5xx_pcm_trigger,
|
||||
.pointer = bf5xx_pcm_pointer,
|
||||
#ifdef CONFIG_SND_MMAP_SUPPORT
|
||||
#if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
|
||||
.mmap = bf5xx_pcm_mmap,
|
||||
#else
|
||||
.copy = bf5xx_pcm_copy,
|
||||
|
@ -344,7 +343,7 @@ static int bf5xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
|
|||
* Need to allocate local buffer when enable
|
||||
* MMAP for SPORT working in TMD mode (include AC97).
|
||||
*/
|
||||
#if defined(CONFIG_SND_MMAP_SUPPORT)
|
||||
#if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
if (!sport_handle->tx_dma_buf) {
|
||||
sport_handle->tx_dma_buf = dma_alloc_coherent(NULL, \
|
||||
|
@ -381,7 +380,7 @@ static void bf5xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
|
|||
struct snd_pcm_substream *substream;
|
||||
struct snd_dma_buffer *buf;
|
||||
int stream;
|
||||
#if defined(CONFIG_SND_MMAP_SUPPORT)
|
||||
#if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
|
||||
size_t size = bf5xx_pcm_hardware.buffer_bytes_max *
|
||||
sizeof(struct ac97_frame) / 4;
|
||||
#endif
|
||||
|
@ -395,7 +394,7 @@ static void bf5xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
|
|||
continue;
|
||||
dma_free_coherent(NULL, buf->bytes, buf->area, 0);
|
||||
buf->area = NULL;
|
||||
#if defined(CONFIG_SND_MMAP_SUPPORT)
|
||||
#if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
if (sport_handle->tx_dma_buf)
|
||||
dma_free_coherent(NULL, size, \
|
||||
|
@ -452,6 +451,18 @@ struct snd_soc_platform bf5xx_ac97_soc_platform = {
|
|||
};
|
||||
EXPORT_SYMBOL_GPL(bf5xx_ac97_soc_platform);
|
||||
|
||||
static int __init bfin_ac97_init(void)
|
||||
{
|
||||
return snd_soc_register_platform(&bf5xx_ac97_soc_platform);
|
||||
}
|
||||
module_init(bfin_ac97_init);
|
||||
|
||||
static void __exit bfin_ac97_exit(void)
|
||||
{
|
||||
snd_soc_unregister_platform(&bf5xx_ac97_soc_platform);
|
||||
}
|
||||
module_exit(bfin_ac97_exit);
|
||||
|
||||
MODULE_AUTHOR("Cliff Cai");
|
||||
MODULE_DESCRIPTION("ADI Blackfin AC97 PCM DMA module");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
|
@ -54,71 +54,103 @@
|
|||
static int *cmd_count;
|
||||
static int sport_num = CONFIG_SND_BF5XX_SPORT_NUM;
|
||||
|
||||
#if defined(CONFIG_BF54x)
|
||||
static u16 sport_req[][7] = {
|
||||
PIN_REQ_SPORT_0,
|
||||
#ifdef PIN_REQ_SPORT_1
|
||||
PIN_REQ_SPORT_1,
|
||||
#endif
|
||||
#ifdef PIN_REQ_SPORT_2
|
||||
PIN_REQ_SPORT_2,
|
||||
#endif
|
||||
#ifdef PIN_REQ_SPORT_3
|
||||
PIN_REQ_SPORT_3,
|
||||
#endif
|
||||
};
|
||||
|
||||
static struct sport_param sport_params[4] = {
|
||||
{
|
||||
.dma_rx_chan = CH_SPORT0_RX,
|
||||
.dma_tx_chan = CH_SPORT0_TX,
|
||||
.err_irq = IRQ_SPORT0_ERR,
|
||||
.regs = (struct sport_register *)SPORT0_TCR1,
|
||||
},
|
||||
{
|
||||
.dma_rx_chan = CH_SPORT1_RX,
|
||||
.dma_tx_chan = CH_SPORT1_TX,
|
||||
.err_irq = IRQ_SPORT1_ERR,
|
||||
.regs = (struct sport_register *)SPORT1_TCR1,
|
||||
},
|
||||
{
|
||||
.dma_rx_chan = CH_SPORT2_RX,
|
||||
.dma_tx_chan = CH_SPORT2_TX,
|
||||
.err_irq = IRQ_SPORT2_ERR,
|
||||
.regs = (struct sport_register *)SPORT2_TCR1,
|
||||
},
|
||||
{
|
||||
.dma_rx_chan = CH_SPORT3_RX,
|
||||
.dma_tx_chan = CH_SPORT3_TX,
|
||||
.err_irq = IRQ_SPORT3_ERR,
|
||||
.regs = (struct sport_register *)SPORT3_TCR1,
|
||||
}
|
||||
};
|
||||
#else
|
||||
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,
|
||||
},
|
||||
#ifdef PIN_REQ_SPORT_1
|
||||
{
|
||||
.dma_rx_chan = CH_SPORT1_RX,
|
||||
.dma_tx_chan = CH_SPORT1_TX,
|
||||
.err_irq = IRQ_SPORT1_ERROR,
|
||||
.regs = (struct sport_register *)SPORT1_TCR1,
|
||||
}
|
||||
};
|
||||
},
|
||||
#endif
|
||||
#ifdef PIN_REQ_SPORT_2
|
||||
{
|
||||
.dma_rx_chan = CH_SPORT2_RX,
|
||||
.dma_tx_chan = CH_SPORT2_TX,
|
||||
.err_irq = IRQ_SPORT2_ERROR,
|
||||
.regs = (struct sport_register *)SPORT2_TCR1,
|
||||
},
|
||||
#endif
|
||||
#ifdef PIN_REQ_SPORT_3
|
||||
{
|
||||
.dma_rx_chan = CH_SPORT3_RX,
|
||||
.dma_tx_chan = CH_SPORT3_TX,
|
||||
.err_irq = IRQ_SPORT3_ERROR,
|
||||
.regs = (struct sport_register *)SPORT3_TCR1,
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
void bf5xx_pcm_to_ac97(struct ac97_frame *dst, const __u32 *src, \
|
||||
size_t count)
|
||||
void bf5xx_pcm_to_ac97(struct ac97_frame *dst, const __u16 *src,
|
||||
size_t count, unsigned int chan_mask)
|
||||
{
|
||||
while (count--) {
|
||||
dst->ac97_tag = TAG_VALID | TAG_PCM;
|
||||
(dst++)->ac97_pcm = *src++;
|
||||
dst->ac97_tag = TAG_VALID;
|
||||
if (chan_mask & SP_FL) {
|
||||
dst->ac97_pcm_r = *src++;
|
||||
dst->ac97_tag |= TAG_PCM_RIGHT;
|
||||
}
|
||||
if (chan_mask & SP_FR) {
|
||||
dst->ac97_pcm_l = *src++;
|
||||
dst->ac97_tag |= TAG_PCM_LEFT;
|
||||
|
||||
}
|
||||
#if defined(CONFIG_SND_BF5XX_MULTICHAN_SUPPORT)
|
||||
if (chan_mask & SP_SR) {
|
||||
dst->ac97_sl = *src++;
|
||||
dst->ac97_tag |= TAG_PCM_SL;
|
||||
}
|
||||
if (chan_mask & SP_SL) {
|
||||
dst->ac97_sr = *src++;
|
||||
dst->ac97_tag |= TAG_PCM_SR;
|
||||
}
|
||||
if (chan_mask & SP_LFE) {
|
||||
dst->ac97_lfe = *src++;
|
||||
dst->ac97_tag |= TAG_PCM_LFE;
|
||||
}
|
||||
if (chan_mask & SP_FC) {
|
||||
dst->ac97_center = *src++;
|
||||
dst->ac97_tag |= TAG_PCM_CENTER;
|
||||
}
|
||||
#endif
|
||||
dst++;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(bf5xx_pcm_to_ac97);
|
||||
|
||||
void bf5xx_ac97_to_pcm(const struct ac97_frame *src, __u32 *dst, \
|
||||
void bf5xx_ac97_to_pcm(const struct ac97_frame *src, __u16 *dst,
|
||||
size_t count)
|
||||
{
|
||||
while (count--)
|
||||
*(dst++) = (src++)->ac97_pcm;
|
||||
while (count--) {
|
||||
*(dst++) = src->ac97_pcm_l;
|
||||
*(dst++) = src->ac97_pcm_r;
|
||||
src++;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(bf5xx_ac97_to_pcm);
|
||||
|
||||
static unsigned int sport_tx_curr_frag(struct sport_device *sport)
|
||||
{
|
||||
return sport->tx_curr_frag = sport_curr_offset_tx(sport) / \
|
||||
return sport->tx_curr_frag = sport_curr_offset_tx(sport) /
|
||||
sport->tx_fragsize;
|
||||
}
|
||||
|
||||
|
@ -130,7 +162,7 @@ static void enqueue_cmd(struct snd_ac97 *ac97, __u16 addr, __u16 data)
|
|||
|
||||
sport_incfrag(sport, &nextfrag, 1);
|
||||
|
||||
nextwrite = (struct ac97_frame *)(sport->tx_buf + \
|
||||
nextwrite = (struct ac97_frame *)(sport->tx_buf +
|
||||
nextfrag * sport->tx_fragsize);
|
||||
pr_debug("sport->tx_buf:%p, nextfrag:0x%x nextwrite:%p, cmd_count:%d\n",
|
||||
sport->tx_buf, nextfrag, nextwrite, cmd_count[nextfrag]);
|
||||
|
@ -237,8 +269,7 @@ struct snd_ac97_bus_ops soc_ac97_ops = {
|
|||
EXPORT_SYMBOL_GPL(soc_ac97_ops);
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int bf5xx_ac97_suspend(struct platform_device *pdev,
|
||||
struct snd_soc_dai *dai)
|
||||
static int bf5xx_ac97_suspend(struct snd_soc_dai *dai)
|
||||
{
|
||||
struct sport_device *sport =
|
||||
(struct sport_device *)dai->private_data;
|
||||
|
@ -253,8 +284,7 @@ static int bf5xx_ac97_suspend(struct platform_device *pdev,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int bf5xx_ac97_resume(struct platform_device *pdev,
|
||||
struct snd_soc_dai *dai)
|
||||
static int bf5xx_ac97_resume(struct snd_soc_dai *dai)
|
||||
{
|
||||
int ret;
|
||||
struct sport_device *sport =
|
||||
|
@ -297,20 +327,15 @@ static int bf5xx_ac97_resume(struct platform_device *pdev,
|
|||
static int bf5xx_ac97_probe(struct platform_device *pdev,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
int ret;
|
||||
#if defined(CONFIG_BF54x)
|
||||
u16 sport_req[][7] = {PIN_REQ_SPORT_0, PIN_REQ_SPORT_1,
|
||||
PIN_REQ_SPORT_2, PIN_REQ_SPORT_3};
|
||||
#else
|
||||
u16 sport_req[][7] = {PIN_REQ_SPORT_0, PIN_REQ_SPORT_1};
|
||||
#endif
|
||||
int ret = 0;
|
||||
cmd_count = (int *)get_zeroed_page(GFP_KERNEL);
|
||||
if (cmd_count == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
if (peripheral_request_list(&sport_req[sport_num][0], "soc-audio")) {
|
||||
pr_err("Requesting Peripherals failed\n");
|
||||
return -EFAULT;
|
||||
ret = -EFAULT;
|
||||
goto peripheral_err;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
|
||||
|
@ -318,54 +343,54 @@ static int bf5xx_ac97_probe(struct platform_device *pdev,
|
|||
if (gpio_request(CONFIG_SND_BF5XX_RESET_GPIO_NUM, "SND_AD198x RESET")) {
|
||||
pr_err("Failed to request GPIO_%d for reset\n",
|
||||
CONFIG_SND_BF5XX_RESET_GPIO_NUM);
|
||||
peripheral_free_list(&sport_req[sport_num][0]);
|
||||
return -1;
|
||||
ret = -1;
|
||||
goto gpio_err;
|
||||
}
|
||||
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);
|
||||
if (!sport_handle) {
|
||||
peripheral_free_list(&sport_req[sport_num][0]);
|
||||
#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
|
||||
gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM);
|
||||
#endif
|
||||
return -ENODEV;
|
||||
ret = -ENODEV;
|
||||
goto sport_err;
|
||||
}
|
||||
/*SPORT works in TDM mode to simulate AC97 transfers*/
|
||||
ret = sport_set_multichannel(sport_handle, 16, 0x1F, 1);
|
||||
if (ret) {
|
||||
pr_err("SPORT is busy!\n");
|
||||
kfree(sport_handle);
|
||||
peripheral_free_list(&sport_req[sport_num][0]);
|
||||
#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
|
||||
gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM);
|
||||
#endif
|
||||
return -EBUSY;
|
||||
ret = -EBUSY;
|
||||
goto sport_config_err;
|
||||
}
|
||||
|
||||
ret = sport_config_rx(sport_handle, IRFS, 0xF, 0, (16*16-1));
|
||||
if (ret) {
|
||||
pr_err("SPORT is busy!\n");
|
||||
kfree(sport_handle);
|
||||
peripheral_free_list(&sport_req[sport_num][0]);
|
||||
#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
|
||||
gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM);
|
||||
#endif
|
||||
return -EBUSY;
|
||||
ret = -EBUSY;
|
||||
goto sport_config_err;
|
||||
}
|
||||
|
||||
ret = sport_config_tx(sport_handle, ITFS, 0xF, 0, (16*16-1));
|
||||
if (ret) {
|
||||
pr_err("SPORT is busy!\n");
|
||||
kfree(sport_handle);
|
||||
peripheral_free_list(&sport_req[sport_num][0]);
|
||||
#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
|
||||
gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM);
|
||||
#endif
|
||||
return -EBUSY;
|
||||
ret = -EBUSY;
|
||||
goto sport_config_err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
sport_config_err:
|
||||
kfree(sport_handle);
|
||||
sport_err:
|
||||
#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
|
||||
gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM);
|
||||
#endif
|
||||
gpio_err:
|
||||
peripheral_free_list(&sport_req[sport_num][0]);
|
||||
peripheral_err:
|
||||
free_page((unsigned long)cmd_count);
|
||||
cmd_count = NULL;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void bf5xx_ac97_remove(struct platform_device *pdev,
|
||||
|
@ -373,6 +398,7 @@ static void bf5xx_ac97_remove(struct platform_device *pdev,
|
|||
{
|
||||
free_page((unsigned long)cmd_count);
|
||||
cmd_count = NULL;
|
||||
peripheral_free_list(&sport_req[sport_num][0]);
|
||||
#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
|
||||
gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM);
|
||||
#endif
|
||||
|
@ -381,7 +407,7 @@ static void bf5xx_ac97_remove(struct platform_device *pdev,
|
|||
struct snd_soc_dai bfin_ac97_dai = {
|
||||
.name = "bf5xx-ac97",
|
||||
.id = 0,
|
||||
.type = SND_SOC_DAI_AC97,
|
||||
.ac97_control = 1,
|
||||
.probe = bf5xx_ac97_probe,
|
||||
.remove = bf5xx_ac97_remove,
|
||||
.suspend = bf5xx_ac97_suspend,
|
||||
|
@ -389,7 +415,11 @@ struct snd_soc_dai bfin_ac97_dai = {
|
|||
.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 = {
|
||||
|
@ -401,6 +431,18 @@ struct snd_soc_dai bfin_ac97_dai = {
|
|||
};
|
||||
EXPORT_SYMBOL_GPL(bfin_ac97_dai);
|
||||
|
||||
static int __init bfin_ac97_init(void)
|
||||
{
|
||||
return snd_soc_register_dai(&bfin_ac97_dai);
|
||||
}
|
||||
module_init(bfin_ac97_init);
|
||||
|
||||
static void __exit bfin_ac97_exit(void)
|
||||
{
|
||||
snd_soc_unregister_dai(&bfin_ac97_dai);
|
||||
}
|
||||
module_exit(bfin_ac97_exit);
|
||||
|
||||
MODULE_AUTHOR("Roy Huang");
|
||||
MODULE_DESCRIPTION("AC97 driver for ADI Blackfin");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
|
@ -16,21 +16,46 @@ struct ac97_frame {
|
|||
u16 ac97_tag; /* slot 0 */
|
||||
u16 ac97_addr; /* slot 1 */
|
||||
u16 ac97_data; /* slot 2 */
|
||||
u32 ac97_pcm; /* slot 3 and 4: left and right pcm data */
|
||||
u16 ac97_pcm_l; /*slot 3:front left*/
|
||||
u16 ac97_pcm_r; /*slot 4:front left*/
|
||||
#if defined(CONFIG_SND_BF5XX_MULTICHAN_SUPPORT)
|
||||
u16 ac97_mdm_l1;
|
||||
u16 ac97_center; /*slot 6:center*/
|
||||
u16 ac97_sl; /*slot 7:surround left*/
|
||||
u16 ac97_sr; /*slot 8:surround right*/
|
||||
u16 ac97_lfe; /*slot 9:lfe*/
|
||||
#endif
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* Speaker location */
|
||||
#define SP_FL 0x0001
|
||||
#define SP_FR 0x0010
|
||||
#define SP_FC 0x0002
|
||||
#define SP_LFE 0x0020
|
||||
#define SP_SL 0x0004
|
||||
#define SP_SR 0x0040
|
||||
|
||||
#define SP_STEREO (SP_FL | SP_FR)
|
||||
#define SP_2DOT1 (SP_FL | SP_FR | SP_LFE)
|
||||
#define SP_QUAD (SP_FL | SP_FR | SP_SL | SP_SR)
|
||||
#define SP_5DOT1 (SP_FL | SP_FR | SP_FC | SP_LFE | SP_SL | SP_SR)
|
||||
|
||||
#define TAG_VALID 0x8000
|
||||
#define TAG_CMD 0x6000
|
||||
#define TAG_PCM_LEFT 0x1000
|
||||
#define TAG_PCM_RIGHT 0x0800
|
||||
#define TAG_PCM (TAG_PCM_LEFT | TAG_PCM_RIGHT)
|
||||
#define TAG_PCM_MDM_L1 0x0400
|
||||
#define TAG_PCM_CENTER 0x0200
|
||||
#define TAG_PCM_SL 0x0100
|
||||
#define TAG_PCM_SR 0x0080
|
||||
#define TAG_PCM_LFE 0x0040
|
||||
|
||||
extern struct snd_soc_dai bfin_ac97_dai;
|
||||
|
||||
void bf5xx_pcm_to_ac97(struct ac97_frame *dst, const __u32 *src, \
|
||||
size_t count);
|
||||
void bf5xx_pcm_to_ac97(struct ac97_frame *dst, const __u16 *src, \
|
||||
size_t count, unsigned int chan_mask);
|
||||
|
||||
void bf5xx_ac97_to_pcm(const struct ac97_frame *src, __u32 *dst, \
|
||||
void bf5xx_ac97_to_pcm(const struct ac97_frame *src, __u16 *dst, \
|
||||
size_t count);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -43,7 +43,7 @@
|
|||
#include "bf5xx-ac97-pcm.h"
|
||||
#include "bf5xx-ac97.h"
|
||||
|
||||
static struct snd_soc_machine bf5xx_board;
|
||||
static struct snd_soc_card bf5xx_board;
|
||||
|
||||
static int bf5xx_board_startup(struct snd_pcm_substream *substream)
|
||||
{
|
||||
|
@ -67,15 +67,15 @@ static struct snd_soc_dai_link bf5xx_board_dai = {
|
|||
.ops = &bf5xx_board_ops,
|
||||
};
|
||||
|
||||
static struct snd_soc_machine bf5xx_board = {
|
||||
static struct snd_soc_card bf5xx_board = {
|
||||
.name = "bf5xx-board",
|
||||
.platform = &bf5xx_ac97_soc_platform,
|
||||
.dai_link = &bf5xx_board_dai,
|
||||
.num_links = 1,
|
||||
};
|
||||
|
||||
static struct snd_soc_device bf5xx_board_snd_devdata = {
|
||||
.machine = &bf5xx_board,
|
||||
.platform = &bf5xx_ac97_soc_platform,
|
||||
.card = &bf5xx_board,
|
||||
.codec_dev = &soc_codec_dev_ad1980,
|
||||
};
|
||||
|
||||
|
|
|
@ -65,7 +65,7 @@
|
|||
|
||||
#define GPIO_SE CONFIG_SND_BFIN_AD73311_SE
|
||||
|
||||
static struct snd_soc_machine bf5xx_ad73311;
|
||||
static struct snd_soc_card bf5xx_ad73311;
|
||||
|
||||
static int snd_ad73311_startup(void)
|
||||
{
|
||||
|
@ -168,7 +168,7 @@ static int bf5xx_ad73311_hw_params(struct snd_pcm_substream *substream,
|
|||
params_format(params));
|
||||
|
||||
/* set cpu DAI configuration */
|
||||
ret = cpu_dai->dai_ops.set_fmt(cpu_dai, SND_SOC_DAIFMT_DSP_A |
|
||||
ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_DSP_A |
|
||||
SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
@ -190,16 +190,16 @@ static struct snd_soc_dai_link bf5xx_ad73311_dai = {
|
|||
.ops = &bf5xx_ad73311_ops,
|
||||
};
|
||||
|
||||
static struct snd_soc_machine bf5xx_ad73311 = {
|
||||
static struct snd_soc_card bf5xx_ad73311 = {
|
||||
.name = "bf5xx_ad73311",
|
||||
.platform = &bf5xx_i2s_soc_platform,
|
||||
.probe = bf5xx_probe,
|
||||
.dai_link = &bf5xx_ad73311_dai,
|
||||
.num_links = 1,
|
||||
};
|
||||
|
||||
static struct snd_soc_device bf5xx_ad73311_snd_devdata = {
|
||||
.machine = &bf5xx_ad73311,
|
||||
.platform = &bf5xx_i2s_soc_platform,
|
||||
.card = &bf5xx_ad73311,
|
||||
.codec_dev = &soc_codec_dev_ad73311,
|
||||
};
|
||||
|
||||
|
|
|
@ -283,6 +283,18 @@ struct snd_soc_platform bf5xx_i2s_soc_platform = {
|
|||
};
|
||||
EXPORT_SYMBOL_GPL(bf5xx_i2s_soc_platform);
|
||||
|
||||
static int __init bfin_i2s_init(void)
|
||||
{
|
||||
return snd_soc_register_platform(&bf5xx_i2s_soc_platform);
|
||||
}
|
||||
module_init(bfin_i2s_init);
|
||||
|
||||
static void __exit bfin_i2s_exit(void)
|
||||
{
|
||||
snd_soc_unregister_platform(&bf5xx_i2s_soc_platform);
|
||||
}
|
||||
module_exit(bfin_i2s_exit);
|
||||
|
||||
MODULE_AUTHOR("Cliff Cai");
|
||||
MODULE_DESCRIPTION("ADI Blackfin I2S PCM DMA module");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
|
@ -132,7 +132,8 @@ static int bf5xx_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int bf5xx_i2s_startup(struct snd_pcm_substream *substream)
|
||||
static int bf5xx_i2s_startup(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
pr_debug("%s enter\n", __func__);
|
||||
|
||||
|
@ -142,7 +143,8 @@ static int bf5xx_i2s_startup(struct snd_pcm_substream *substream)
|
|||
}
|
||||
|
||||
static int bf5xx_i2s_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
|
@ -193,7 +195,8 @@ static int bf5xx_i2s_hw_params(struct snd_pcm_substream *substream,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void bf5xx_i2s_shutdown(struct snd_pcm_substream *substream)
|
||||
static void bf5xx_i2s_shutdown(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
pr_debug("%s enter\n", __func__);
|
||||
bf5xx_i2s.counter--;
|
||||
|
@ -219,16 +222,14 @@ static int bf5xx_i2s_probe(struct platform_device *pdev,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void bf5xx_i2s_remove(struct platform_device *pdev,
|
||||
struct snd_soc_dai *dai)
|
||||
static void bf5xx_i2s_remove(struct snd_soc_dai *dai)
|
||||
{
|
||||
pr_debug("%s enter\n", __func__);
|
||||
peripheral_free_list(&sport_req[sport_num][0]);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int bf5xx_i2s_suspend(struct platform_device *dev,
|
||||
struct snd_soc_dai *dai)
|
||||
static int bf5xx_i2s_suspend(struct snd_soc_dai *dai)
|
||||
{
|
||||
struct sport_device *sport =
|
||||
(struct sport_device *)dai->private_data;
|
||||
|
@ -289,7 +290,6 @@ static int bf5xx_i2s_resume(struct platform_device *pdev,
|
|||
struct snd_soc_dai bf5xx_i2s_dai = {
|
||||
.name = "bf5xx-i2s",
|
||||
.id = 0,
|
||||
.type = SND_SOC_DAI_I2S,
|
||||
.probe = bf5xx_i2s_probe,
|
||||
.remove = bf5xx_i2s_remove,
|
||||
.suspend = bf5xx_i2s_suspend,
|
||||
|
@ -307,13 +307,24 @@ struct snd_soc_dai bf5xx_i2s_dai = {
|
|||
.ops = {
|
||||
.startup = bf5xx_i2s_startup,
|
||||
.shutdown = bf5xx_i2s_shutdown,
|
||||
.hw_params = bf5xx_i2s_hw_params,},
|
||||
.dai_ops = {
|
||||
.hw_params = bf5xx_i2s_hw_params,
|
||||
.set_fmt = bf5xx_i2s_set_dai_fmt,
|
||||
},
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(bf5xx_i2s_dai);
|
||||
|
||||
static int __init bfin_i2s_init(void)
|
||||
{
|
||||
return snd_soc_register_dai(&bf5xx_i2s_dai);
|
||||
}
|
||||
module_init(bfin_i2s_init);
|
||||
|
||||
static void __exit bfin_i2s_exit(void)
|
||||
{
|
||||
snd_soc_unregister_dai(&bf5xx_i2s_dai);
|
||||
}
|
||||
module_exit(bfin_i2s_exit);
|
||||
|
||||
/* Module information */
|
||||
MODULE_AUTHOR("Cliff Cai");
|
||||
MODULE_DESCRIPTION("I2S driver for ADI Blackfin");
|
||||
|
|
|
@ -116,7 +116,7 @@ struct sport_device {
|
|||
void *err_data;
|
||||
unsigned char *tx_dma_buf;
|
||||
unsigned char *rx_dma_buf;
|
||||
#ifdef CONFIG_SND_MMAP_SUPPORT
|
||||
#ifdef CONFIG_SND_BF5XX_MMAP_SUPPORT
|
||||
dma_addr_t tx_dma_phy;
|
||||
dma_addr_t rx_dma_phy;
|
||||
int tx_pos;/*pcm sample count*/
|
||||
|
|
|
@ -44,7 +44,7 @@
|
|||
#include "bf5xx-i2s-pcm.h"
|
||||
#include "bf5xx-i2s.h"
|
||||
|
||||
static struct snd_soc_machine bf5xx_ssm2602;
|
||||
static struct snd_soc_card bf5xx_ssm2602;
|
||||
|
||||
static int bf5xx_ssm2602_startup(struct snd_pcm_substream *substream)
|
||||
{
|
||||
|
@ -92,17 +92,17 @@ static int bf5xx_ssm2602_hw_params(struct snd_pcm_substream *substream,
|
|||
*/
|
||||
|
||||
/* set codec DAI configuration */
|
||||
ret = codec_dai->dai_ops.set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
|
||||
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;
|
||||
/* set cpu DAI configuration */
|
||||
ret = cpu_dai->dai_ops.set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
|
||||
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 = codec_dai->dai_ops.set_sysclk(codec_dai, SSM2602_SYSCLK, clk,
|
||||
ret = snd_soc_dai_set_sysclk(codec_dai, SSM2602_SYSCLK, clk,
|
||||
SND_SOC_CLOCK_IN);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
@ -135,15 +135,15 @@ static struct ssm2602_setup_data bf5xx_ssm2602_setup = {
|
|||
.i2c_address = 0x1b,
|
||||
};
|
||||
|
||||
static struct snd_soc_machine bf5xx_ssm2602 = {
|
||||
static struct snd_soc_card bf5xx_ssm2602 = {
|
||||
.name = "bf5xx_ssm2602",
|
||||
.platform = &bf5xx_i2s_soc_platform,
|
||||
.dai_link = &bf5xx_ssm2602_dai,
|
||||
.num_links = 1,
|
||||
};
|
||||
|
||||
static struct snd_soc_device bf5xx_ssm2602_snd_devdata = {
|
||||
.machine = &bf5xx_ssm2602,
|
||||
.platform = &bf5xx_i2s_soc_platform,
|
||||
.card = &bf5xx_ssm2602,
|
||||
.codec_dev = &soc_codec_dev_ssm2602,
|
||||
.codec_data = &bf5xx_ssm2602_setup,
|
||||
};
|
||||
|
|
|
@ -1,31 +1,40 @@
|
|||
config SND_SOC_ALL_CODECS
|
||||
tristate "Build all ASoC CODEC drivers"
|
||||
depends on I2C
|
||||
select SPI
|
||||
select SPI_MASTER
|
||||
select SND_SOC_AD73311
|
||||
select SND_SOC_AK4535
|
||||
select SND_SOC_CS4270
|
||||
select SND_SOC_SSM2602
|
||||
select SND_SOC_TLV320AIC23
|
||||
select SND_SOC_TLV320AIC26
|
||||
select SND_SOC_TLV320AIC3X
|
||||
select SND_SOC_UDA1380
|
||||
select SND_SOC_WM8510
|
||||
select SND_SOC_WM8580
|
||||
select SND_SOC_WM8731
|
||||
select SND_SOC_WM8750
|
||||
select SND_SOC_WM8753
|
||||
select SND_SOC_WM8900
|
||||
select SND_SOC_WM8903
|
||||
select SND_SOC_WM8971
|
||||
select SND_SOC_WM8990
|
||||
select SND_SOC_AC97_CODEC if SND_SOC_AC97_BUS
|
||||
select SND_SOC_AD1980 if SND_SOC_AC97_BUS
|
||||
select SND_SOC_AD73311 if I2C
|
||||
select SND_SOC_AK4535 if I2C
|
||||
select SND_SOC_CS4270 if I2C
|
||||
select SND_SOC_PCM3008
|
||||
select SND_SOC_SSM2602 if I2C
|
||||
select SND_SOC_TLV320AIC23 if I2C
|
||||
select SND_SOC_TLV320AIC26 if SPI_MASTER
|
||||
select SND_SOC_TLV320AIC3X if I2C
|
||||
select SND_SOC_TWL4030 if TWL4030_CORE
|
||||
select SND_SOC_UDA134X
|
||||
select SND_SOC_UDA1380 if I2C
|
||||
select SND_SOC_WM8350 if MFD_WM8350
|
||||
select SND_SOC_WM8510 if (I2C || SPI_MASTER)
|
||||
select SND_SOC_WM8580 if I2C
|
||||
select SND_SOC_WM8728 if (I2C || SPI_MASTER)
|
||||
select SND_SOC_WM8731 if (I2C || SPI_MASTER)
|
||||
select SND_SOC_WM8750 if (I2C || SPI_MASTER)
|
||||
select SND_SOC_WM8753 if (I2C || SPI_MASTER)
|
||||
select SND_SOC_WM8900 if I2C
|
||||
select SND_SOC_WM8903 if I2C
|
||||
select SND_SOC_WM8971 if I2C
|
||||
select SND_SOC_WM8990 if I2C
|
||||
select SND_SOC_WM9712 if SND_SOC_AC97_BUS
|
||||
select SND_SOC_WM9713 if SND_SOC_AC97_BUS
|
||||
help
|
||||
Normally ASoC codec drivers are only built if a machine driver which
|
||||
uses them is also built since they are only usable with a machine
|
||||
driver. Selecting this option will allow these drivers to be built
|
||||
without an explicit machine driver for test and development purposes.
|
||||
|
||||
Support for the bus types used to access the codecs to be built must
|
||||
be selected separately.
|
||||
|
||||
If unsure select "N".
|
||||
|
||||
|
||||
|
@ -60,6 +69,12 @@ config SND_SOC_CS4270_VD33_ERRATA
|
|||
bool
|
||||
depends on SND_SOC_CS4270
|
||||
|
||||
config SND_SOC_L3
|
||||
tristate
|
||||
|
||||
config SND_SOC_PCM3008
|
||||
tristate
|
||||
|
||||
config SND_SOC_SSM2602
|
||||
tristate
|
||||
|
||||
|
@ -75,15 +90,29 @@ config SND_SOC_TLV320AIC3X
|
|||
tristate
|
||||
depends on I2C
|
||||
|
||||
config SND_SOC_TWL4030
|
||||
tristate
|
||||
depends on TWL4030_CORE
|
||||
|
||||
config SND_SOC_UDA134X
|
||||
tristate
|
||||
select SND_SOC_L3
|
||||
|
||||
config SND_SOC_UDA1380
|
||||
tristate
|
||||
|
||||
config SND_SOC_WM8350
|
||||
tristate
|
||||
|
||||
config SND_SOC_WM8510
|
||||
tristate
|
||||
|
||||
config SND_SOC_WM8580
|
||||
tristate
|
||||
|
||||
config SND_SOC_WM8728
|
||||
tristate
|
||||
|
||||
config SND_SOC_WM8731
|
||||
tristate
|
||||
|
||||
|
|
|
@ -3,13 +3,19 @@ snd-soc-ad1980-objs := ad1980.o
|
|||
snd-soc-ad73311-objs := ad73311.o
|
||||
snd-soc-ak4535-objs := ak4535.o
|
||||
snd-soc-cs4270-objs := cs4270.o
|
||||
snd-soc-l3-objs := l3.o
|
||||
snd-soc-pcm3008-objs := pcm3008.o
|
||||
snd-soc-ssm2602-objs := ssm2602.o
|
||||
snd-soc-tlv320aic23-objs := tlv320aic23.o
|
||||
snd-soc-tlv320aic26-objs := tlv320aic26.o
|
||||
snd-soc-tlv320aic3x-objs := tlv320aic3x.o
|
||||
snd-soc-twl4030-objs := twl4030.o
|
||||
snd-soc-uda134x-objs := uda134x.o
|
||||
snd-soc-uda1380-objs := uda1380.o
|
||||
snd-soc-wm8350-objs := wm8350.o
|
||||
snd-soc-wm8510-objs := wm8510.o
|
||||
snd-soc-wm8580-objs := wm8580.o
|
||||
snd-soc-wm8728-objs := wm8728.o
|
||||
snd-soc-wm8731-objs := wm8731.o
|
||||
snd-soc-wm8750-objs := wm8750.o
|
||||
snd-soc-wm8753-objs := wm8753.o
|
||||
|
@ -25,13 +31,19 @@ obj-$(CONFIG_SND_SOC_AD1980) += snd-soc-ad1980.o
|
|||
obj-$(CONFIG_SND_SOC_AD73311) += snd-soc-ad73311.o
|
||||
obj-$(CONFIG_SND_SOC_AK4535) += snd-soc-ak4535.o
|
||||
obj-$(CONFIG_SND_SOC_CS4270) += snd-soc-cs4270.o
|
||||
obj-$(CONFIG_SND_SOC_L3) += snd-soc-l3.o
|
||||
obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o
|
||||
obj-$(CONFIG_SND_SOC_SSM2602) += snd-soc-ssm2602.o
|
||||
obj-$(CONFIG_SND_SOC_TLV320AIC23) += snd-soc-tlv320aic23.o
|
||||
obj-$(CONFIG_SND_SOC_TLV320AIC26) += snd-soc-tlv320aic26.o
|
||||
obj-$(CONFIG_SND_SOC_TLV320AIC3X) += snd-soc-tlv320aic3x.o
|
||||
obj-$(CONFIG_SND_SOC_TWL4030) += snd-soc-twl4030.o
|
||||
obj-$(CONFIG_SND_SOC_UDA134X) += snd-soc-uda134x.o
|
||||
obj-$(CONFIG_SND_SOC_UDA1380) += snd-soc-uda1380.o
|
||||
obj-$(CONFIG_SND_SOC_WM8350) += snd-soc-wm8350.o
|
||||
obj-$(CONFIG_SND_SOC_WM8510) += snd-soc-wm8510.o
|
||||
obj-$(CONFIG_SND_SOC_WM8580) += snd-soc-wm8580.o
|
||||
obj-$(CONFIG_SND_SOC_WM8728) += snd-soc-wm8728.o
|
||||
obj-$(CONFIG_SND_SOC_WM8731) += snd-soc-wm8731.o
|
||||
obj-$(CONFIG_SND_SOC_WM8750) += snd-soc-wm8750.o
|
||||
obj-$(CONFIG_SND_SOC_WM8753) += snd-soc-wm8753.o
|
||||
|
|
|
@ -24,7 +24,8 @@
|
|||
|
||||
#define AC97_VERSION "0.6"
|
||||
|
||||
static int ac97_prepare(struct snd_pcm_substream *substream)
|
||||
static int ac97_prepare(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
|
@ -42,7 +43,7 @@ static int ac97_prepare(struct snd_pcm_substream *substream)
|
|||
|
||||
struct snd_soc_dai ac97_dai = {
|
||||
.name = "AC97 HiFi",
|
||||
.type = SND_SOC_DAI_AC97,
|
||||
.ac97_control = 1,
|
||||
.playback = {
|
||||
.stream_name = "AC97 Playback",
|
||||
.channels_min = 1,
|
||||
|
@ -113,7 +114,7 @@ static int ac97_soc_probe(struct platform_device *pdev)
|
|||
if (ret < 0)
|
||||
goto bus_err;
|
||||
|
||||
ret = snd_soc_register_card(socdev);
|
||||
ret = snd_soc_init_card(socdev);
|
||||
if (ret < 0)
|
||||
goto bus_err;
|
||||
return 0;
|
||||
|
|
|
@ -85,6 +85,9 @@ SOC_DOUBLE("Line HP Swap Switch", AC97_AD_MISC, 10, 5, 1, 0),
|
|||
SOC_DOUBLE("Surround Playback Volume", AC97_SURROUND_MASTER, 8, 0, 31, 1),
|
||||
SOC_DOUBLE("Surround Playback Switch", AC97_SURROUND_MASTER, 15, 7, 1, 1),
|
||||
|
||||
SOC_DOUBLE("Center/LFE Playback Volume", AC97_CENTER_LFE_MASTER, 8, 0, 31, 1),
|
||||
SOC_DOUBLE("Center/LFE Playback Switch", AC97_CENTER_LFE_MASTER, 15, 7, 1, 1),
|
||||
|
||||
SOC_ENUM("Capture Source", ad1980_cap_src),
|
||||
|
||||
SOC_SINGLE("Mic Boost Switch", AC97_MIC, 6, 1, 0),
|
||||
|
@ -142,10 +145,11 @@ static int ac97_write(struct snd_soc_codec *codec, unsigned int reg,
|
|||
|
||||
struct snd_soc_dai ad1980_dai = {
|
||||
.name = "AC97",
|
||||
.ac97_control = 1,
|
||||
.playback = {
|
||||
.stream_name = "Playback",
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.channels_max = 6,
|
||||
.rates = SNDRV_PCM_RATE_48000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE, },
|
||||
.capture = {
|
||||
|
@ -192,6 +196,7 @@ static int ad1980_soc_probe(struct platform_device *pdev)
|
|||
struct snd_soc_codec *codec;
|
||||
int ret = 0;
|
||||
u16 vendor_id2;
|
||||
u16 ext_status;
|
||||
|
||||
printk(KERN_INFO "AD1980 SoC Audio Codec\n");
|
||||
|
||||
|
@ -234,7 +239,7 @@ static int ad1980_soc_probe(struct platform_device *pdev)
|
|||
|
||||
ret = ad1980_reset(codec, 0);
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR "AC97 link error\n");
|
||||
printk(KERN_ERR "Failed to reset AD1980: AC97 link error\n");
|
||||
goto reset_err;
|
||||
}
|
||||
|
||||
|
@ -253,12 +258,19 @@ static int ad1980_soc_probe(struct platform_device *pdev)
|
|||
"supported\n");
|
||||
}
|
||||
|
||||
ac97_write(codec, AC97_MASTER, 0x0000); /* unmute line out volume */
|
||||
ac97_write(codec, AC97_PCM, 0x0000); /* unmute PCM out volume */
|
||||
ac97_write(codec, AC97_REC_GAIN, 0x0000);/* unmute record volume */
|
||||
/* unmute captures and playbacks volume */
|
||||
ac97_write(codec, AC97_MASTER, 0x0000);
|
||||
ac97_write(codec, AC97_PCM, 0x0000);
|
||||
ac97_write(codec, AC97_REC_GAIN, 0x0000);
|
||||
ac97_write(codec, AC97_CENTER_LFE_MASTER, 0x0000);
|
||||
ac97_write(codec, AC97_SURROUND_MASTER, 0x0000);
|
||||
|
||||
/*power on LFE/CENTER/Surround DACs*/
|
||||
ext_status = ac97_read(codec, AC97_EXTENDED_STATUS);
|
||||
ac97_write(codec, AC97_EXTENDED_STATUS, ext_status&~0x3800);
|
||||
|
||||
ad1980_add_controls(codec);
|
||||
ret = snd_soc_register_card(socdev);
|
||||
ret = snd_soc_init_card(socdev);
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR "ad1980: failed to register card\n");
|
||||
goto reset_err;
|
||||
|
|
|
@ -8,14 +8,10 @@
|
|||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Revision history
|
||||
* 25th Sep 2008 Initial version.
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/device.h>
|
||||
#include <sound/core.h>
|
||||
|
@ -68,7 +64,7 @@ static int ad73311_soc_probe(struct platform_device *pdev)
|
|||
goto pcm_err;
|
||||
}
|
||||
|
||||
ret = snd_soc_register_card(socdev);
|
||||
ret = snd_soc_init_card(socdev);
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR "ad73311: failed to register card\n");
|
||||
goto register_err;
|
||||
|
@ -102,6 +98,18 @@ struct snd_soc_codec_device soc_codec_dev_ad73311 = {
|
|||
};
|
||||
EXPORT_SYMBOL_GPL(soc_codec_dev_ad73311);
|
||||
|
||||
static int __init ad73311_init(void)
|
||||
{
|
||||
return snd_soc_register_dai(&ad73311_dai);
|
||||
}
|
||||
module_init(ad73311_init);
|
||||
|
||||
static void __exit ad73311_exit(void)
|
||||
{
|
||||
snd_soc_unregister_dai(&ad73311_dai);
|
||||
}
|
||||
module_exit(ad73311_exit);
|
||||
|
||||
MODULE_DESCRIPTION("ASoC ad73311 driver");
|
||||
MODULE_AUTHOR("Cliff Cai ");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
|
@ -339,7 +339,8 @@ static int ak4535_set_dai_sysclk(struct snd_soc_dai *codec_dai,
|
|||
}
|
||||
|
||||
static int ak4535_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_device *socdev = rtd->socdev;
|
||||
|
@ -451,8 +452,6 @@ struct snd_soc_dai ak4535_dai = {
|
|||
.formats = SNDRV_PCM_FMTBIT_S16_LE,},
|
||||
.ops = {
|
||||
.hw_params = ak4535_hw_params,
|
||||
},
|
||||
.dai_ops = {
|
||||
.set_fmt = ak4535_set_dai_fmt,
|
||||
.digital_mute = ak4535_mute,
|
||||
.set_sysclk = ak4535_set_dai_sysclk,
|
||||
|
@ -513,7 +512,7 @@ static int ak4535_init(struct snd_soc_device *socdev)
|
|||
|
||||
ak4535_add_controls(codec);
|
||||
ak4535_add_widgets(codec);
|
||||
ret = snd_soc_register_card(socdev);
|
||||
ret = snd_soc_init_card(socdev);
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR "ak4535: failed to register card\n");
|
||||
goto card_err;
|
||||
|
@ -689,6 +688,18 @@ struct snd_soc_codec_device soc_codec_dev_ak4535 = {
|
|||
};
|
||||
EXPORT_SYMBOL_GPL(soc_codec_dev_ak4535);
|
||||
|
||||
static int __init ak4535_modinit(void)
|
||||
{
|
||||
return snd_soc_register_dai(&ak4535_dai);
|
||||
}
|
||||
module_init(ak4535_modinit);
|
||||
|
||||
static void __exit ak4535_exit(void)
|
||||
{
|
||||
snd_soc_unregister_dai(&ak4535_dai);
|
||||
}
|
||||
module_exit(ak4535_exit);
|
||||
|
||||
MODULE_DESCRIPTION("Soc AK4535 driver");
|
||||
MODULE_AUTHOR("Richard Purdie");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
|
@ -360,13 +360,14 @@ static int cs4270_i2c_write(struct snd_soc_codec *codec, unsigned int reg,
|
|||
/*
|
||||
* Program the CS4270 with the given hardware parameters.
|
||||
*
|
||||
* The .dai_ops functions are used to provide board-specific data, like
|
||||
* The .ops functions are used to provide board-specific data, like
|
||||
* input frequencies, to this driver. This function takes that information,
|
||||
* combines it with the hardware parameters provided, and programs the
|
||||
* hardware accordingly.
|
||||
*/
|
||||
static int cs4270_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_device *socdev = rtd->socdev;
|
||||
|
@ -450,6 +451,19 @@ static int cs4270_hw_params(struct snd_pcm_substream *substream,
|
|||
return ret;
|
||||
}
|
||||
|
||||
/* Disable automatic volume control. It's enabled by default, and
|
||||
* it causes volume change commands to be delayed, sometimes until
|
||||
* after playback has started.
|
||||
*/
|
||||
|
||||
reg = cs4270_read_reg_cache(codec, CS4270_TRANS);
|
||||
reg &= ~(CS4270_TRANS_SOFT | CS4270_TRANS_ZERO);
|
||||
ret = cs4270_i2c_write(codec, CS4270_TRANS, reg);
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR "I2C write failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Thaw and power-up the codec */
|
||||
|
||||
ret = snd_soc_write(codec, CS4270_PWRCTL, 0);
|
||||
|
@ -697,10 +711,10 @@ static int cs4270_probe(struct platform_device *pdev)
|
|||
if (codec->control_data) {
|
||||
/* Initialize codec ops */
|
||||
cs4270_dai.ops.hw_params = cs4270_hw_params;
|
||||
cs4270_dai.dai_ops.set_sysclk = cs4270_set_dai_sysclk;
|
||||
cs4270_dai.dai_ops.set_fmt = cs4270_set_dai_fmt;
|
||||
cs4270_dai.ops.set_sysclk = cs4270_set_dai_sysclk;
|
||||
cs4270_dai.ops.set_fmt = cs4270_set_dai_fmt;
|
||||
#ifdef CONFIG_SND_SOC_CS4270_HWMUTE
|
||||
cs4270_dai.dai_ops.digital_mute = cs4270_mute;
|
||||
cs4270_dai.ops.digital_mute = cs4270_mute;
|
||||
#endif
|
||||
} else
|
||||
printk(KERN_INFO "cs4270: no I2C device found, "
|
||||
|
@ -709,7 +723,7 @@ static int cs4270_probe(struct platform_device *pdev)
|
|||
printk(KERN_INFO "cs4270: I2C disabled, using stand-alone mode\n");
|
||||
#endif
|
||||
|
||||
ret = snd_soc_register_card(socdev);
|
||||
ret = snd_soc_init_card(socdev);
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR "cs4270: failed to register card\n");
|
||||
goto error_del_driver;
|
||||
|
@ -760,6 +774,18 @@ struct snd_soc_codec_device soc_codec_device_cs4270 = {
|
|||
};
|
||||
EXPORT_SYMBOL_GPL(soc_codec_device_cs4270);
|
||||
|
||||
static int __init cs4270_init(void)
|
||||
{
|
||||
return snd_soc_register_dai(&cs4270_dai);
|
||||
}
|
||||
module_init(cs4270_init);
|
||||
|
||||
static void __exit cs4270_exit(void)
|
||||
{
|
||||
snd_soc_unregister_dai(&cs4270_dai);
|
||||
}
|
||||
module_exit(cs4270_exit);
|
||||
|
||||
MODULE_AUTHOR("Timur Tabi <timur@freescale.com>");
|
||||
MODULE_DESCRIPTION("Cirrus Logic CS4270 ALSA SoC Codec Driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
91
sound/soc/codecs/l3.c
Normal file
91
sound/soc/codecs/l3.c
Normal file
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* L3 code
|
||||
*
|
||||
* Copyright (C) 2008, Christian Pellegrin <chripell@evolware.org>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*
|
||||
* based on:
|
||||
*
|
||||
* L3 bus algorithm module.
|
||||
*
|
||||
* Copyright (C) 2001 Russell King, All Rights Reserved.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include <sound/l3.h>
|
||||
|
||||
/*
|
||||
* Send one byte of data to the chip. Data is latched into the chip on
|
||||
* the rising edge of the clock.
|
||||
*/
|
||||
static void sendbyte(struct l3_pins *adap, unsigned int byte)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
adap->setclk(0);
|
||||
udelay(adap->data_hold);
|
||||
adap->setdat(byte & 1);
|
||||
udelay(adap->data_setup);
|
||||
adap->setclk(1);
|
||||
udelay(adap->clock_high);
|
||||
byte >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Send a set of bytes to the chip. We need to pulse the MODE line
|
||||
* between each byte, but never at the start nor at the end of the
|
||||
* transfer.
|
||||
*/
|
||||
static void sendbytes(struct l3_pins *adap, const u8 *buf,
|
||||
int len)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
if (i) {
|
||||
udelay(adap->mode_hold);
|
||||
adap->setmode(0);
|
||||
udelay(adap->mode);
|
||||
}
|
||||
adap->setmode(1);
|
||||
udelay(adap->mode_setup);
|
||||
sendbyte(adap, buf[i]);
|
||||
}
|
||||
}
|
||||
|
||||
int l3_write(struct l3_pins *adap, u8 addr, u8 *data, int len)
|
||||
{
|
||||
adap->setclk(1);
|
||||
adap->setdat(1);
|
||||
adap->setmode(1);
|
||||
udelay(adap->mode);
|
||||
|
||||
adap->setmode(0);
|
||||
udelay(adap->mode_setup);
|
||||
sendbyte(adap, addr);
|
||||
udelay(adap->mode_hold);
|
||||
|
||||
sendbytes(adap, data, len);
|
||||
|
||||
adap->setclk(1);
|
||||
adap->setdat(1);
|
||||
adap->setmode(0);
|
||||
|
||||
return len;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(l3_write);
|
||||
|
||||
MODULE_DESCRIPTION("L3 bit-banging driver");
|
||||
MODULE_AUTHOR("Christian Pellegrin <chripell@evolware.org>");
|
||||
MODULE_LICENSE("GPL");
|
212
sound/soc/codecs/pcm3008.c
Normal file
212
sound/soc/codecs/pcm3008.c
Normal file
|
@ -0,0 +1,212 @@
|
|||
/*
|
||||
* ALSA Soc PCM3008 codec support
|
||||
*
|
||||
* Author: Hugo Villeneuve
|
||||
* Copyright (C) 2008 Lyrtech inc
|
||||
*
|
||||
* Based on AC97 Soc codec, original copyright follow:
|
||||
* Copyright 2005 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.
|
||||
*
|
||||
* Generic PCM3008 support.
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/initval.h>
|
||||
#include <sound/soc.h>
|
||||
|
||||
#include "pcm3008.h"
|
||||
|
||||
#define PCM3008_VERSION "0.2"
|
||||
|
||||
#define PCM3008_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
|
||||
SNDRV_PCM_RATE_48000)
|
||||
|
||||
struct snd_soc_dai pcm3008_dai = {
|
||||
.name = "PCM3008 HiFi",
|
||||
.playback = {
|
||||
.stream_name = "PCM3008 Playback",
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.rates = PCM3008_RATES,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
},
|
||||
.capture = {
|
||||
.stream_name = "PCM3008 Capture",
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.rates = PCM3008_RATES,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
},
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(pcm3008_dai);
|
||||
|
||||
static void pcm3008_gpio_free(struct pcm3008_setup_data *setup)
|
||||
{
|
||||
gpio_free(setup->dem0_pin);
|
||||
gpio_free(setup->dem1_pin);
|
||||
gpio_free(setup->pdad_pin);
|
||||
gpio_free(setup->pdda_pin);
|
||||
}
|
||||
|
||||
static int pcm3008_soc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
|
||||
struct snd_soc_codec *codec;
|
||||
struct pcm3008_setup_data *setup = socdev->codec_data;
|
||||
int ret = 0;
|
||||
|
||||
printk(KERN_INFO "PCM3008 SoC Audio Codec %s\n", PCM3008_VERSION);
|
||||
|
||||
socdev->codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
|
||||
if (!socdev->codec)
|
||||
return -ENOMEM;
|
||||
|
||||
codec = socdev->codec;
|
||||
mutex_init(&codec->mutex);
|
||||
|
||||
codec->name = "PCM3008";
|
||||
codec->owner = THIS_MODULE;
|
||||
codec->dai = &pcm3008_dai;
|
||||
codec->num_dai = 1;
|
||||
codec->write = NULL;
|
||||
codec->read = NULL;
|
||||
INIT_LIST_HEAD(&codec->dapm_widgets);
|
||||
INIT_LIST_HEAD(&codec->dapm_paths);
|
||||
|
||||
/* Register PCMs. */
|
||||
ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR "pcm3008: failed to create pcms\n");
|
||||
goto pcm_err;
|
||||
}
|
||||
|
||||
/* Register Card. */
|
||||
ret = snd_soc_init_card(socdev);
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR "pcm3008: failed to register card\n");
|
||||
goto card_err;
|
||||
}
|
||||
|
||||
/* DEM1 DEM0 DE-EMPHASIS_MODE
|
||||
* Low Low De-emphasis 44.1 kHz ON
|
||||
* Low High De-emphasis OFF
|
||||
* High Low De-emphasis 48 kHz ON
|
||||
* High High De-emphasis 32 kHz ON
|
||||
*/
|
||||
|
||||
/* Configure DEM0 GPIO (turning OFF DAC De-emphasis). */
|
||||
ret = gpio_request(setup->dem0_pin, "codec_dem0");
|
||||
if (ret == 0)
|
||||
ret = gpio_direction_output(setup->dem0_pin, 1);
|
||||
if (ret != 0)
|
||||
goto gpio_err;
|
||||
|
||||
/* Configure DEM1 GPIO (turning OFF DAC De-emphasis). */
|
||||
ret = gpio_request(setup->dem1_pin, "codec_dem1");
|
||||
if (ret == 0)
|
||||
ret = gpio_direction_output(setup->dem1_pin, 0);
|
||||
if (ret != 0)
|
||||
goto gpio_err;
|
||||
|
||||
/* Configure PDAD GPIO. */
|
||||
ret = gpio_request(setup->pdad_pin, "codec_pdad");
|
||||
if (ret == 0)
|
||||
ret = gpio_direction_output(setup->pdad_pin, 1);
|
||||
if (ret != 0)
|
||||
goto gpio_err;
|
||||
|
||||
/* Configure PDDA GPIO. */
|
||||
ret = gpio_request(setup->pdda_pin, "codec_pdda");
|
||||
if (ret == 0)
|
||||
ret = gpio_direction_output(setup->pdda_pin, 1);
|
||||
if (ret != 0)
|
||||
goto gpio_err;
|
||||
|
||||
return ret;
|
||||
|
||||
gpio_err:
|
||||
pcm3008_gpio_free(setup);
|
||||
card_err:
|
||||
snd_soc_free_pcms(socdev);
|
||||
pcm_err:
|
||||
kfree(socdev->codec);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int pcm3008_soc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
|
||||
struct snd_soc_codec *codec = socdev->codec;
|
||||
struct pcm3008_setup_data *setup = socdev->codec_data;
|
||||
|
||||
if (!codec)
|
||||
return 0;
|
||||
|
||||
pcm3008_gpio_free(setup);
|
||||
snd_soc_free_pcms(socdev);
|
||||
kfree(socdev->codec);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int pcm3008_soc_suspend(struct platform_device *pdev, pm_message_t msg)
|
||||
{
|
||||
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
|
||||
struct pcm3008_setup_data *setup = socdev->codec_data;
|
||||
|
||||
gpio_set_value(setup->pdad_pin, 0);
|
||||
gpio_set_value(setup->pdda_pin, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pcm3008_soc_resume(struct platform_device *pdev)
|
||||
{
|
||||
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
|
||||
struct pcm3008_setup_data *setup = socdev->codec_data;
|
||||
|
||||
gpio_set_value(setup->pdad_pin, 1);
|
||||
gpio_set_value(setup->pdda_pin, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#define pcm3008_soc_suspend NULL
|
||||
#define pcm3008_soc_resume NULL
|
||||
#endif
|
||||
|
||||
struct snd_soc_codec_device soc_codec_dev_pcm3008 = {
|
||||
.probe = pcm3008_soc_probe,
|
||||
.remove = pcm3008_soc_remove,
|
||||
.suspend = pcm3008_soc_suspend,
|
||||
.resume = pcm3008_soc_resume,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(soc_codec_dev_pcm3008);
|
||||
|
||||
static int __init pcm3008_init(void)
|
||||
{
|
||||
return snd_soc_register_dai(&pcm3008_dai);
|
||||
}
|
||||
module_init(pcm3008_init);
|
||||
|
||||
static void __exit pcm3008_exit(void)
|
||||
{
|
||||
snd_soc_unregister_dai(&pcm3008_dai);
|
||||
}
|
||||
module_exit(pcm3008_exit);
|
||||
|
||||
MODULE_DESCRIPTION("Soc PCM3008 driver");
|
||||
MODULE_AUTHOR("Hugo Villeneuve");
|
||||
MODULE_LICENSE("GPL");
|
25
sound/soc/codecs/pcm3008.h
Normal file
25
sound/soc/codecs/pcm3008.h
Normal file
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* PCM3008 ALSA SoC Layer
|
||||
*
|
||||
* Author: Hugo Villeneuve
|
||||
* Copyright (C) 2008 Lyrtech inc
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_SND_SOC_PCM3008_H
|
||||
#define __LINUX_SND_SOC_PCM3008_H
|
||||
|
||||
struct pcm3008_setup_data {
|
||||
unsigned dem0_pin;
|
||||
unsigned dem1_pin;
|
||||
unsigned pdad_pin;
|
||||
unsigned pdda_pin;
|
||||
};
|
||||
|
||||
extern struct snd_soc_codec_device soc_codec_dev_pcm3008;
|
||||
extern struct snd_soc_dai pcm3008_dai;
|
||||
|
||||
#endif
|
|
@ -285,16 +285,23 @@ static inline int get_coeff(int mclk, int rate)
|
|||
}
|
||||
|
||||
static int ssm2602_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
u16 srate;
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_device *socdev = rtd->socdev;
|
||||
struct snd_soc_codec *codec = socdev->codec;
|
||||
struct ssm2602_priv *ssm2602 = codec->private_data;
|
||||
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));
|
||||
|
||||
if (substream == ssm2602->slave_substream) {
|
||||
dev_dbg(&i2c->dev, "Ignoring hw_params for slave substream\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*no match is found*/
|
||||
if (i == ARRAY_SIZE(coeff_div))
|
||||
return -EINVAL;
|
||||
|
@ -324,19 +331,26 @@ static int ssm2602_hw_params(struct snd_pcm_substream *substream,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int ssm2602_startup(struct snd_pcm_substream *substream)
|
||||
static int ssm2602_startup(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_device *socdev = rtd->socdev;
|
||||
struct snd_soc_codec *codec = socdev->codec;
|
||||
struct ssm2602_priv *ssm2602 = codec->private_data;
|
||||
struct i2c_client *i2c = codec->control_data;
|
||||
struct snd_pcm_runtime *master_runtime;
|
||||
|
||||
/* The DAI has shared clocks so if we already have a playback or
|
||||
* capture going then constrain this substream to match it.
|
||||
* TODO: the ssm2602 allows pairs of non-matching PB/REC rates
|
||||
*/
|
||||
if (ssm2602->master_substream) {
|
||||
master_runtime = ssm2602->master_substream->runtime;
|
||||
dev_dbg(&i2c->dev, "Constraining to %d bits at %dHz\n",
|
||||
master_runtime->sample_bits,
|
||||
master_runtime->rate);
|
||||
|
||||
snd_pcm_hw_constraint_minmax(substream->runtime,
|
||||
SNDRV_PCM_HW_PARAM_RATE,
|
||||
master_runtime->rate,
|
||||
|
@ -354,7 +368,8 @@ static int ssm2602_startup(struct snd_pcm_substream *substream)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int ssm2602_pcm_prepare(struct snd_pcm_substream *substream)
|
||||
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_device *socdev = rtd->socdev;
|
||||
|
@ -365,14 +380,21 @@ static int ssm2602_pcm_prepare(struct snd_pcm_substream *substream)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void ssm2602_shutdown(struct snd_pcm_substream *substream)
|
||||
static void ssm2602_shutdown(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_device *socdev = rtd->socdev;
|
||||
struct snd_soc_codec *codec = socdev->codec;
|
||||
struct ssm2602_priv *ssm2602 = codec->private_data;
|
||||
/* 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)
|
||||
|
@ -432,10 +454,10 @@ static int ssm2602_set_dai_fmt(struct snd_soc_dai *codec_dai,
|
|||
iface |= 0x0001;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_DSP_A:
|
||||
iface |= 0x0003;
|
||||
iface |= 0x0013;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_DSP_B:
|
||||
iface |= 0x0013;
|
||||
iface |= 0x0003;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
|
@ -496,6 +518,9 @@ static int ssm2602_set_bias_level(struct snd_soc_codec *codec,
|
|||
SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |\
|
||||
SNDRV_PCM_RATE_96000)
|
||||
|
||||
#define SSM2602_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
|
||||
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
|
||||
|
||||
struct snd_soc_dai ssm2602_dai = {
|
||||
.name = "SSM2602",
|
||||
.playback = {
|
||||
|
@ -503,20 +528,18 @@ struct snd_soc_dai ssm2602_dai = {
|
|||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.rates = SSM2602_RATES,
|
||||
.formats = SNDRV_PCM_FMTBIT_S32_LE,},
|
||||
.formats = SSM2602_FORMATS,},
|
||||
.capture = {
|
||||
.stream_name = "Capture",
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.rates = SSM2602_RATES,
|
||||
.formats = SNDRV_PCM_FMTBIT_S32_LE,},
|
||||
.formats = SSM2602_FORMATS,},
|
||||
.ops = {
|
||||
.startup = ssm2602_startup,
|
||||
.prepare = ssm2602_pcm_prepare,
|
||||
.hw_params = ssm2602_hw_params,
|
||||
.shutdown = ssm2602_shutdown,
|
||||
},
|
||||
.dai_ops = {
|
||||
.digital_mute = ssm2602_mute,
|
||||
.set_sysclk = ssm2602_set_dai_sysclk,
|
||||
.set_fmt = ssm2602_set_dai_fmt,
|
||||
|
@ -601,7 +624,7 @@ static int ssm2602_init(struct snd_soc_device *socdev)
|
|||
|
||||
ssm2602_add_controls(codec);
|
||||
ssm2602_add_widgets(codec);
|
||||
ret = snd_soc_register_card(socdev);
|
||||
ret = snd_soc_init_card(socdev);
|
||||
if (ret < 0) {
|
||||
pr_err("ssm2602: failed to register card\n");
|
||||
goto card_err;
|
||||
|
@ -770,6 +793,18 @@ struct snd_soc_codec_device soc_codec_dev_ssm2602 = {
|
|||
};
|
||||
EXPORT_SYMBOL_GPL(soc_codec_dev_ssm2602);
|
||||
|
||||
static int __init ssm2602_modinit(void)
|
||||
{
|
||||
return snd_soc_register_dai(&ssm2602_dai);
|
||||
}
|
||||
module_init(ssm2602_modinit);
|
||||
|
||||
static void __exit ssm2602_exit(void)
|
||||
{
|
||||
snd_soc_unregister_dai(&ssm2602_dai);
|
||||
}
|
||||
module_exit(ssm2602_exit);
|
||||
|
||||
MODULE_DESCRIPTION("ASoC ssm2602 driver");
|
||||
MODULE_AUTHOR("Cliff Cai");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
|
@ -37,12 +37,6 @@
|
|||
|
||||
#define AIC23_VERSION "0.1"
|
||||
|
||||
struct tlv320aic23_srate_reg_info {
|
||||
u32 sample_rate;
|
||||
u8 control; /* SR3, SR2, SR1, SR0 and BOSR */
|
||||
u8 divider; /* if 0 CLKIN = MCLK, if 1 CLKIN = MCLK/2 */
|
||||
};
|
||||
|
||||
/*
|
||||
* AIC23 register cache
|
||||
*/
|
||||
|
@ -261,20 +255,156 @@ static const struct snd_soc_dapm_route intercon[] = {
|
|||
|
||||
};
|
||||
|
||||
/* tlv320aic23 related */
|
||||
static const struct tlv320aic23_srate_reg_info srate_reg_info[] = {
|
||||
{4000, 0x06, 1}, /* 4000 */
|
||||
{8000, 0x06, 0}, /* 8000 */
|
||||
{16000, 0x0C, 1}, /* 16000 */
|
||||
{22050, 0x11, 1}, /* 22050 */
|
||||
{24000, 0x00, 1}, /* 24000 */
|
||||
{32000, 0x0C, 0}, /* 32000 */
|
||||
{44100, 0x11, 0}, /* 44100 */
|
||||
{48000, 0x00, 0}, /* 48000 */
|
||||
{88200, 0x1F, 0}, /* 88200 */
|
||||
{96000, 0x0E, 0}, /* 96000 */
|
||||
/* AIC23 driver data */
|
||||
struct aic23 {
|
||||
struct snd_soc_codec codec;
|
||||
int mclk;
|
||||
int requested_adc;
|
||||
int requested_dac;
|
||||
};
|
||||
|
||||
/*
|
||||
* Common Crystals used
|
||||
* 11.2896 Mhz /128 = *88.2k /192 = 58.8k
|
||||
* 12.0000 Mhz /125 = *96k /136 = 88.235K
|
||||
* 12.2880 Mhz /128 = *96k /192 = 64k
|
||||
* 16.9344 Mhz /128 = 132.3k /192 = *88.2k
|
||||
* 18.4320 Mhz /128 = 144k /192 = *96k
|
||||
*/
|
||||
|
||||
/*
|
||||
* Normal BOSR 0-256/2 = 128, 1-384/2 = 192
|
||||
* USB BOSR 0-250/2 = 125, 1-272/2 = 136
|
||||
*/
|
||||
static const int bosr_usb_divisor_table[] = {
|
||||
128, 125, 192, 136
|
||||
};
|
||||
#define LOWER_GROUP ((1<<0) | (1<<1) | (1<<2) | (1<<3) | (1<<6) | (1<<7))
|
||||
#define UPPER_GROUP ((1<<8) | (1<<9) | (1<<10) | (1<<11) | (1<<15))
|
||||
static const unsigned short sr_valid_mask[] = {
|
||||
LOWER_GROUP|UPPER_GROUP, /* Normal, bosr - 0*/
|
||||
LOWER_GROUP|UPPER_GROUP, /* Normal, bosr - 1*/
|
||||
LOWER_GROUP, /* Usb, bosr - 0*/
|
||||
UPPER_GROUP, /* Usb, bosr - 1*/
|
||||
};
|
||||
/*
|
||||
* Every divisor is a factor of 11*12
|
||||
*/
|
||||
#define SR_MULT (11*12)
|
||||
#define A(x) (x) ? (SR_MULT/x) : 0
|
||||
static const unsigned char sr_adc_mult_table[] = {
|
||||
A(2), A(2), A(12), A(12), A(0), A(0), A(3), A(1),
|
||||
A(2), A(2), A(11), A(11), A(0), A(0), A(0), A(1)
|
||||
};
|
||||
static const unsigned char sr_dac_mult_table[] = {
|
||||
A(2), A(12), A(2), A(12), A(0), A(0), A(3), A(1),
|
||||
A(2), A(11), A(2), A(11), A(0), A(0), A(0), A(1)
|
||||
};
|
||||
|
||||
static unsigned get_score(int adc, int adc_l, int adc_h, int need_adc,
|
||||
int dac, int dac_l, int dac_h, int need_dac)
|
||||
{
|
||||
if ((adc >= adc_l) && (adc <= adc_h) &&
|
||||
(dac >= dac_l) && (dac <= dac_h)) {
|
||||
int diff_adc = need_adc - adc;
|
||||
int diff_dac = need_dac - dac;
|
||||
return abs(diff_adc) + abs(diff_dac);
|
||||
}
|
||||
return UINT_MAX;
|
||||
}
|
||||
|
||||
static int find_rate(int mclk, u32 need_adc, u32 need_dac)
|
||||
{
|
||||
int i, j;
|
||||
int best_i = -1;
|
||||
int best_j = -1;
|
||||
int best_div = 0;
|
||||
unsigned best_score = UINT_MAX;
|
||||
int adc_l, adc_h, dac_l, dac_h;
|
||||
|
||||
need_adc *= SR_MULT;
|
||||
need_dac *= SR_MULT;
|
||||
/*
|
||||
* rates given are +/- 1/32
|
||||
*/
|
||||
adc_l = need_adc - (need_adc >> 5);
|
||||
adc_h = need_adc + (need_adc >> 5);
|
||||
dac_l = need_dac - (need_dac >> 5);
|
||||
dac_h = need_dac + (need_dac >> 5);
|
||||
for (i = 0; i < ARRAY_SIZE(bosr_usb_divisor_table); i++) {
|
||||
int base = mclk / bosr_usb_divisor_table[i];
|
||||
int mask = sr_valid_mask[i];
|
||||
for (j = 0; j < ARRAY_SIZE(sr_adc_mult_table);
|
||||
j++, mask >>= 1) {
|
||||
int adc;
|
||||
int dac;
|
||||
int score;
|
||||
if ((mask & 1) == 0)
|
||||
continue;
|
||||
adc = base * sr_adc_mult_table[j];
|
||||
dac = base * sr_dac_mult_table[j];
|
||||
score = get_score(adc, adc_l, adc_h, need_adc,
|
||||
dac, dac_l, dac_h, need_dac);
|
||||
if (best_score > score) {
|
||||
best_score = score;
|
||||
best_i = i;
|
||||
best_j = j;
|
||||
best_div = 0;
|
||||
}
|
||||
score = get_score((adc >> 1), adc_l, adc_h, need_adc,
|
||||
(dac >> 1), dac_l, dac_h, need_dac);
|
||||
/* prefer to have a /2 */
|
||||
if ((score != UINT_MAX) && (best_score >= score)) {
|
||||
best_score = score;
|
||||
best_i = i;
|
||||
best_j = j;
|
||||
best_div = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return (best_j << 2) | best_i | (best_div << TLV320AIC23_CLKIN_SHIFT);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
static void get_current_sample_rates(struct snd_soc_codec *codec, int mclk,
|
||||
u32 *sample_rate_adc, u32 *sample_rate_dac)
|
||||
{
|
||||
int src = tlv320aic23_read_reg_cache(codec, TLV320AIC23_SRATE);
|
||||
int sr = (src >> 2) & 0x0f;
|
||||
int val = (mclk / bosr_usb_divisor_table[src & 3]);
|
||||
int adc = (val * sr_adc_mult_table[sr]) / SR_MULT;
|
||||
int dac = (val * sr_dac_mult_table[sr]) / SR_MULT;
|
||||
if (src & TLV320AIC23_CLKIN_HALF) {
|
||||
adc >>= 1;
|
||||
dac >>= 1;
|
||||
}
|
||||
*sample_rate_adc = adc;
|
||||
*sample_rate_dac = dac;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int set_sample_rate_control(struct snd_soc_codec *codec, int mclk,
|
||||
u32 sample_rate_adc, u32 sample_rate_dac)
|
||||
{
|
||||
/* Search for the right sample rate */
|
||||
int data = find_rate(mclk, sample_rate_adc, sample_rate_dac);
|
||||
if (data < 0) {
|
||||
printk(KERN_ERR "%s:Invalid rate %u,%u requested\n",
|
||||
__func__, sample_rate_adc, sample_rate_dac);
|
||||
return -EINVAL;
|
||||
}
|
||||
tlv320aic23_write(codec, TLV320AIC23_SRATE, data);
|
||||
#ifdef DEBUG
|
||||
{
|
||||
u32 adc, dac;
|
||||
get_current_sample_rates(codec, mclk, &adc, &dac);
|
||||
printk(KERN_DEBUG "actual samplerate = %u,%u reg=%x\n",
|
||||
adc, dac, data);
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tlv320aic23_add_widgets(struct snd_soc_codec *codec)
|
||||
{
|
||||
snd_soc_dapm_new_controls(codec, tlv320aic23_dapm_widgets,
|
||||
|
@ -288,32 +418,36 @@ static int tlv320aic23_add_widgets(struct snd_soc_codec *codec)
|
|||
}
|
||||
|
||||
static int tlv320aic23_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_device *socdev = rtd->socdev;
|
||||
struct snd_soc_codec *codec = socdev->codec;
|
||||
u16 iface_reg, data;
|
||||
u8 count = 0;
|
||||
u16 iface_reg;
|
||||
int ret;
|
||||
struct aic23 *aic23 = container_of(codec, struct aic23, codec);
|
||||
u32 sample_rate_adc = aic23->requested_adc;
|
||||
u32 sample_rate_dac = aic23->requested_dac;
|
||||
u32 sample_rate = params_rate(params);
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
aic23->requested_dac = sample_rate_dac = sample_rate;
|
||||
if (!sample_rate_adc)
|
||||
sample_rate_adc = sample_rate;
|
||||
} else {
|
||||
aic23->requested_adc = sample_rate_adc = sample_rate;
|
||||
if (!sample_rate_dac)
|
||||
sample_rate_dac = sample_rate;
|
||||
}
|
||||
ret = set_sample_rate_control(codec, aic23->mclk, sample_rate_adc,
|
||||
sample_rate_dac);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
iface_reg =
|
||||
tlv320aic23_read_reg_cache(codec,
|
||||
TLV320AIC23_DIGT_FMT) & ~(0x03 << 2);
|
||||
|
||||
/* Search for the right sample rate */
|
||||
/* Verify what happens if the rate is not supported
|
||||
* now it goes to 96Khz */
|
||||
while ((srate_reg_info[count].sample_rate != params_rate(params)) &&
|
||||
(count < ARRAY_SIZE(srate_reg_info))) {
|
||||
count++;
|
||||
}
|
||||
|
||||
data = (srate_reg_info[count].divider << TLV320AIC23_CLKIN_SHIFT) |
|
||||
(srate_reg_info[count]. control << TLV320AIC23_BOSR_SHIFT) |
|
||||
TLV320AIC23_USB_CLK_ON;
|
||||
|
||||
tlv320aic23_write(codec, TLV320AIC23_SRATE, data);
|
||||
|
||||
switch (params_format(params)) {
|
||||
case SNDRV_PCM_FORMAT_S16_LE:
|
||||
break;
|
||||
|
@ -332,7 +466,8 @@ static int tlv320aic23_hw_params(struct snd_pcm_substream *substream,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int tlv320aic23_pcm_prepare(struct snd_pcm_substream *substream)
|
||||
static int tlv320aic23_pcm_prepare(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_device *socdev = rtd->socdev;
|
||||
|
@ -344,17 +479,23 @@ static int tlv320aic23_pcm_prepare(struct snd_pcm_substream *substream)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void tlv320aic23_shutdown(struct snd_pcm_substream *substream)
|
||||
static void tlv320aic23_shutdown(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_device *socdev = rtd->socdev;
|
||||
struct snd_soc_codec *codec = socdev->codec;
|
||||
struct aic23 *aic23 = container_of(codec, struct aic23, codec);
|
||||
|
||||
/* deactivate */
|
||||
if (!codec->active) {
|
||||
udelay(50);
|
||||
tlv320aic23_write(codec, TLV320AIC23_ACTIVE, 0x0);
|
||||
}
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
aic23->requested_dac = 0;
|
||||
else
|
||||
aic23->requested_adc = 0;
|
||||
}
|
||||
|
||||
static int tlv320aic23_mute(struct snd_soc_dai *dai, int mute)
|
||||
|
@ -400,7 +541,7 @@ static int tlv320aic23_set_dai_fmt(struct snd_soc_dai *codec_dai,
|
|||
case SND_SOC_DAIFMT_I2S:
|
||||
iface_reg |= TLV320AIC23_FOR_I2S;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_DSP_A:
|
||||
case SND_SOC_DAIFMT_DSP_B:
|
||||
iface_reg |= TLV320AIC23_FOR_DSP;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_RIGHT_J:
|
||||
|
@ -422,12 +563,9 @@ static int tlv320aic23_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;
|
||||
|
||||
switch (freq) {
|
||||
case 12000000:
|
||||
return 0;
|
||||
}
|
||||
return -EINVAL;
|
||||
struct aic23 *aic23 = container_of(codec, struct aic23, codec);
|
||||
aic23->mclk = freq;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tlv320aic23_set_bias_level(struct snd_soc_codec *codec,
|
||||
|
@ -478,12 +616,10 @@ struct snd_soc_dai tlv320aic23_dai = {
|
|||
.prepare = tlv320aic23_pcm_prepare,
|
||||
.hw_params = tlv320aic23_hw_params,
|
||||
.shutdown = tlv320aic23_shutdown,
|
||||
},
|
||||
.dai_ops = {
|
||||
.digital_mute = tlv320aic23_mute,
|
||||
.set_fmt = tlv320aic23_set_dai_fmt,
|
||||
.set_sysclk = tlv320aic23_set_dai_sysclk,
|
||||
}
|
||||
.digital_mute = tlv320aic23_mute,
|
||||
.set_fmt = tlv320aic23_set_dai_fmt,
|
||||
.set_sysclk = tlv320aic23_set_dai_sysclk,
|
||||
}
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(tlv320aic23_dai);
|
||||
|
||||
|
@ -584,7 +720,7 @@ static int tlv320aic23_init(struct snd_soc_device *socdev)
|
|||
|
||||
tlv320aic23_add_controls(codec);
|
||||
tlv320aic23_add_widgets(codec);
|
||||
ret = snd_soc_register_card(socdev);
|
||||
ret = snd_soc_init_card(socdev);
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR "tlv320aic23: failed to register card\n");
|
||||
goto card_err;
|
||||
|
@ -659,14 +795,15 @@ static int tlv320aic23_probe(struct platform_device *pdev)
|
|||
{
|
||||
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
|
||||
struct snd_soc_codec *codec;
|
||||
struct aic23 *aic23;
|
||||
int ret = 0;
|
||||
|
||||
printk(KERN_INFO "AIC23 Audio Codec %s\n", AIC23_VERSION);
|
||||
|
||||
codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
|
||||
if (codec == NULL)
|
||||
aic23 = kzalloc(sizeof(struct aic23), GFP_KERNEL);
|
||||
if (aic23 == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
codec = &aic23->codec;
|
||||
socdev->codec = codec;
|
||||
mutex_init(&codec->mutex);
|
||||
INIT_LIST_HEAD(&codec->dapm_widgets);
|
||||
|
@ -687,6 +824,7 @@ static int tlv320aic23_remove(struct platform_device *pdev)
|
|||
{
|
||||
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
|
||||
struct snd_soc_codec *codec = socdev->codec;
|
||||
struct aic23 *aic23 = container_of(codec, struct aic23, codec);
|
||||
|
||||
if (codec->control_data)
|
||||
tlv320aic23_set_bias_level(codec, SND_SOC_BIAS_OFF);
|
||||
|
@ -697,7 +835,7 @@ static int tlv320aic23_remove(struct platform_device *pdev)
|
|||
i2c_del_driver(&tlv320aic23_i2c_driver);
|
||||
#endif
|
||||
kfree(codec->reg_cache);
|
||||
kfree(codec);
|
||||
kfree(aic23);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -709,6 +847,18 @@ struct snd_soc_codec_device soc_codec_dev_tlv320aic23 = {
|
|||
};
|
||||
EXPORT_SYMBOL_GPL(soc_codec_dev_tlv320aic23);
|
||||
|
||||
static int __init tlv320aic23_modinit(void)
|
||||
{
|
||||
return snd_soc_register_dai(&tlv320aic23_dai);
|
||||
}
|
||||
module_init(tlv320aic23_modinit);
|
||||
|
||||
static void __exit tlv320aic23_exit(void)
|
||||
{
|
||||
snd_soc_unregister_dai(&tlv320aic23_dai);
|
||||
}
|
||||
module_exit(tlv320aic23_exit);
|
||||
|
||||
MODULE_DESCRIPTION("ASoC TLV320AIC23 codec driver");
|
||||
MODULE_AUTHOR("Arun KS <arunks@mistralsolutions.com>");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
|
@ -125,7 +125,8 @@ static int aic26_reg_write(struct snd_soc_codec *codec, unsigned int reg,
|
|||
* Digital Audio Interface Operations
|
||||
*/
|
||||
static int aic26_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_device *socdev = rtd->socdev;
|
||||
|
@ -287,8 +288,6 @@ struct snd_soc_dai aic26_dai = {
|
|||
},
|
||||
.ops = {
|
||||
.hw_params = aic26_hw_params,
|
||||
},
|
||||
.dai_ops = {
|
||||
.digital_mute = aic26_mute,
|
||||
.set_sysclk = aic26_set_sysclk,
|
||||
.set_fmt = aic26_set_fmt,
|
||||
|
@ -360,7 +359,7 @@ static int aic26_probe(struct platform_device *pdev)
|
|||
|
||||
/* CODEC is setup, we can register the card now */
|
||||
dev_dbg(&pdev->dev, "Registering card\n");
|
||||
ret = snd_soc_register_card(socdev);
|
||||
ret = snd_soc_init_card(socdev);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "aic26: failed to register card\n");
|
||||
goto card_err;
|
||||
|
@ -427,7 +426,7 @@ static DEVICE_ATTR(keyclick, 0644, aic26_keyclick_show, aic26_keyclick_set);
|
|||
static int aic26_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
struct aic26 *aic26;
|
||||
int rc, i, reg;
|
||||
int ret, i, reg;
|
||||
|
||||
dev_dbg(&spi->dev, "probing tlv320aic26 spi device\n");
|
||||
|
||||
|
@ -457,6 +456,14 @@ static int aic26_spi_probe(struct spi_device *spi)
|
|||
aic26->codec.reg_cache_size = AIC26_NUM_REGS;
|
||||
aic26->codec.reg_cache = aic26->reg_cache;
|
||||
|
||||
aic26_dai.dev = &spi->dev;
|
||||
ret = snd_soc_register_dai(&aic26_dai);
|
||||
if (ret != 0) {
|
||||
dev_err(&spi->dev, "Failed to register DAI: %d\n", ret);
|
||||
kfree(aic26);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Reset the codec to power on defaults */
|
||||
aic26_reg_write(&aic26->codec, AIC26_REG_RESET, 0xBB00);
|
||||
|
||||
|
@ -475,8 +482,8 @@ static int aic26_spi_probe(struct spi_device *spi)
|
|||
|
||||
/* Register the sysfs files for debugging */
|
||||
/* Create SysFS files */
|
||||
rc = device_create_file(&spi->dev, &dev_attr_keyclick);
|
||||
if (rc)
|
||||
ret = device_create_file(&spi->dev, &dev_attr_keyclick);
|
||||
if (ret)
|
||||
dev_info(&spi->dev, "error creating sysfs files\n");
|
||||
|
||||
#if defined(CONFIG_SND_SOC_OF_SIMPLE)
|
||||
|
@ -493,6 +500,7 @@ static int aic26_spi_remove(struct spi_device *spi)
|
|||
{
|
||||
struct aic26 *aic26 = dev_get_drvdata(&spi->dev);
|
||||
|
||||
snd_soc_unregister_dai(&aic26_dai);
|
||||
kfree(aic26);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -253,11 +253,17 @@ static const struct snd_kcontrol_new aic3x_snd_controls[] = {
|
|||
|
||||
SOC_DOUBLE_R("Line DAC Playback Volume", DACL1_2_LLOPM_VOL,
|
||||
DACR1_2_RLOPM_VOL, 0, 0x7f, 1),
|
||||
SOC_DOUBLE_R("Line DAC Playback Switch", LLOPM_CTRL, RLOPM_CTRL, 3,
|
||||
0x01, 0),
|
||||
SOC_DOUBLE_R("Line PGA Bypass Playback Volume", PGAL_2_LLOPM_VOL,
|
||||
PGAR_2_RLOPM_VOL, 0, 0x7f, 1),
|
||||
SOC_DOUBLE_R("Line Line2 Bypass Playback Volume", LINE2L_2_LLOPM_VOL,
|
||||
SOC_SINGLE("LineL Playback Switch", LLOPM_CTRL, 3, 0x01, 0),
|
||||
SOC_SINGLE("LineR Playback Switch", RLOPM_CTRL, 3, 0x01, 0),
|
||||
SOC_DOUBLE_R("LineL DAC Playback Volume", DACL1_2_LLOPM_VOL,
|
||||
DACR1_2_LLOPM_VOL, 0, 0x7f, 1),
|
||||
SOC_SINGLE("LineL Left PGA Bypass Playback Volume", PGAL_2_LLOPM_VOL,
|
||||
0, 0x7f, 1),
|
||||
SOC_SINGLE("LineR Right PGA Bypass Playback Volume", PGAR_2_RLOPM_VOL,
|
||||
0, 0x7f, 1),
|
||||
SOC_DOUBLE_R("LineL Line2 Bypass Playback Volume", LINE2L_2_LLOPM_VOL,
|
||||
LINE2R_2_LLOPM_VOL, 0, 0x7f, 1),
|
||||
SOC_DOUBLE_R("LineR Line2 Bypass Playback Volume", LINE2L_2_RLOPM_VOL,
|
||||
LINE2R_2_RLOPM_VOL, 0, 0x7f, 1),
|
||||
|
||||
SOC_DOUBLE_R("Mono DAC Playback Volume", DACL1_2_MONOLOPM_VOL,
|
||||
|
@ -272,8 +278,12 @@ static const struct snd_kcontrol_new aic3x_snd_controls[] = {
|
|||
DACR1_2_HPROUT_VOL, 0, 0x7f, 1),
|
||||
SOC_DOUBLE_R("HP DAC Playback Switch", HPLOUT_CTRL, HPROUT_CTRL, 3,
|
||||
0x01, 0),
|
||||
SOC_DOUBLE_R("HP PGA Bypass Playback Volume", PGAL_2_HPLOUT_VOL,
|
||||
SOC_DOUBLE_R("HP Right PGA Bypass Playback Volume", PGAR_2_HPLOUT_VOL,
|
||||
PGAR_2_HPROUT_VOL, 0, 0x7f, 1),
|
||||
SOC_SINGLE("HPL PGA Bypass Playback Volume", PGAL_2_HPLOUT_VOL,
|
||||
0, 0x7f, 1),
|
||||
SOC_SINGLE("HPR PGA Bypass Playback Volume", PGAL_2_HPROUT_VOL,
|
||||
0, 0x7f, 1),
|
||||
SOC_DOUBLE_R("HP Line2 Bypass Playback Volume", LINE2L_2_HPLOUT_VOL,
|
||||
LINE2R_2_HPROUT_VOL, 0, 0x7f, 1),
|
||||
|
||||
|
@ -281,8 +291,10 @@ static const struct snd_kcontrol_new aic3x_snd_controls[] = {
|
|||
DACR1_2_HPRCOM_VOL, 0, 0x7f, 1),
|
||||
SOC_DOUBLE_R("HPCOM DAC Playback Switch", HPLCOM_CTRL, HPRCOM_CTRL, 3,
|
||||
0x01, 0),
|
||||
SOC_DOUBLE_R("HPCOM PGA Bypass Playback Volume", PGAL_2_HPLCOM_VOL,
|
||||
PGAR_2_HPRCOM_VOL, 0, 0x7f, 1),
|
||||
SOC_SINGLE("HPLCOM PGA Bypass Playback Volume", PGAL_2_HPLCOM_VOL,
|
||||
0, 0x7f, 1),
|
||||
SOC_SINGLE("HPRCOM PGA Bypass Playback Volume", PGAL_2_HPRCOM_VOL,
|
||||
0, 0x7f, 1),
|
||||
SOC_DOUBLE_R("HPCOM Line2 Bypass Playback Volume", LINE2L_2_HPLCOM_VOL,
|
||||
LINE2R_2_HPRCOM_VOL, 0, 0x7f, 1),
|
||||
|
||||
|
@ -333,7 +345,8 @@ SOC_DAPM_ENUM("Route", aic3x_enum[RHPCOM_ENUM]);
|
|||
|
||||
/* Left DAC_L1 Mixer */
|
||||
static const struct snd_kcontrol_new aic3x_left_dac_mixer_controls[] = {
|
||||
SOC_DAPM_SINGLE("Line Switch", DACL1_2_LLOPM_VOL, 7, 1, 0),
|
||||
SOC_DAPM_SINGLE("LineL Switch", DACL1_2_LLOPM_VOL, 7, 1, 0),
|
||||
SOC_DAPM_SINGLE("LineR Switch", DACL1_2_RLOPM_VOL, 7, 1, 0),
|
||||
SOC_DAPM_SINGLE("Mono Switch", DACL1_2_MONOLOPM_VOL, 7, 1, 0),
|
||||
SOC_DAPM_SINGLE("HP Switch", DACL1_2_HPLOUT_VOL, 7, 1, 0),
|
||||
SOC_DAPM_SINGLE("HPCOM Switch", DACL1_2_HPLCOM_VOL, 7, 1, 0),
|
||||
|
@ -341,7 +354,8 @@ static const struct snd_kcontrol_new aic3x_left_dac_mixer_controls[] = {
|
|||
|
||||
/* Right DAC_R1 Mixer */
|
||||
static const struct snd_kcontrol_new aic3x_right_dac_mixer_controls[] = {
|
||||
SOC_DAPM_SINGLE("Line Switch", DACR1_2_RLOPM_VOL, 7, 1, 0),
|
||||
SOC_DAPM_SINGLE("LineL Switch", DACR1_2_LLOPM_VOL, 7, 1, 0),
|
||||
SOC_DAPM_SINGLE("LineR Switch", DACR1_2_RLOPM_VOL, 7, 1, 0),
|
||||
SOC_DAPM_SINGLE("Mono Switch", DACR1_2_MONOLOPM_VOL, 7, 1, 0),
|
||||
SOC_DAPM_SINGLE("HP Switch", DACR1_2_HPROUT_VOL, 7, 1, 0),
|
||||
SOC_DAPM_SINGLE("HPCOM Switch", DACR1_2_HPRCOM_VOL, 7, 1, 0),
|
||||
|
@ -350,14 +364,18 @@ static const struct snd_kcontrol_new aic3x_right_dac_mixer_controls[] = {
|
|||
/* Left PGA Mixer */
|
||||
static const struct snd_kcontrol_new aic3x_left_pga_mixer_controls[] = {
|
||||
SOC_DAPM_SINGLE_AIC3X("Line1L Switch", LINE1L_2_LADC_CTRL, 3, 1, 1),
|
||||
SOC_DAPM_SINGLE_AIC3X("Line1R Switch", LINE1R_2_LADC_CTRL, 3, 1, 1),
|
||||
SOC_DAPM_SINGLE_AIC3X("Line2L Switch", LINE2L_2_LADC_CTRL, 3, 1, 1),
|
||||
SOC_DAPM_SINGLE_AIC3X("Mic3L Switch", MIC3LR_2_LADC_CTRL, 4, 1, 1),
|
||||
SOC_DAPM_SINGLE_AIC3X("Mic3R Switch", MIC3LR_2_LADC_CTRL, 0, 1, 1),
|
||||
};
|
||||
|
||||
/* Right PGA Mixer */
|
||||
static const struct snd_kcontrol_new aic3x_right_pga_mixer_controls[] = {
|
||||
SOC_DAPM_SINGLE_AIC3X("Line1R Switch", LINE1R_2_RADC_CTRL, 3, 1, 1),
|
||||
SOC_DAPM_SINGLE_AIC3X("Line1L Switch", LINE1L_2_RADC_CTRL, 3, 1, 1),
|
||||
SOC_DAPM_SINGLE_AIC3X("Line2R Switch", LINE2R_2_RADC_CTRL, 3, 1, 1),
|
||||
SOC_DAPM_SINGLE_AIC3X("Mic3L Switch", MIC3LR_2_RADC_CTRL, 4, 1, 1),
|
||||
SOC_DAPM_SINGLE_AIC3X("Mic3R Switch", MIC3LR_2_RADC_CTRL, 0, 1, 1),
|
||||
};
|
||||
|
||||
|
@ -379,34 +397,42 @@ SOC_DAPM_ENUM("Route", aic3x_enum[LINE2R_ENUM]);
|
|||
|
||||
/* Left PGA Bypass Mixer */
|
||||
static const struct snd_kcontrol_new aic3x_left_pga_bp_mixer_controls[] = {
|
||||
SOC_DAPM_SINGLE("Line Switch", PGAL_2_LLOPM_VOL, 7, 1, 0),
|
||||
SOC_DAPM_SINGLE("LineL Switch", PGAL_2_LLOPM_VOL, 7, 1, 0),
|
||||
SOC_DAPM_SINGLE("LineR Switch", PGAL_2_RLOPM_VOL, 7, 1, 0),
|
||||
SOC_DAPM_SINGLE("Mono Switch", PGAL_2_MONOLOPM_VOL, 7, 1, 0),
|
||||
SOC_DAPM_SINGLE("HP Switch", PGAL_2_HPLOUT_VOL, 7, 1, 0),
|
||||
SOC_DAPM_SINGLE("HPCOM Switch", PGAL_2_HPLCOM_VOL, 7, 1, 0),
|
||||
SOC_DAPM_SINGLE("HPL Switch", PGAL_2_HPLOUT_VOL, 7, 1, 0),
|
||||
SOC_DAPM_SINGLE("HPR Switch", PGAL_2_HPROUT_VOL, 7, 1, 0),
|
||||
SOC_DAPM_SINGLE("HPLCOM Switch", PGAL_2_HPLCOM_VOL, 7, 1, 0),
|
||||
SOC_DAPM_SINGLE("HPRCOM Switch", PGAL_2_HPRCOM_VOL, 7, 1, 0),
|
||||
};
|
||||
|
||||
/* Right PGA Bypass Mixer */
|
||||
static const struct snd_kcontrol_new aic3x_right_pga_bp_mixer_controls[] = {
|
||||
SOC_DAPM_SINGLE("Line Switch", PGAR_2_RLOPM_VOL, 7, 1, 0),
|
||||
SOC_DAPM_SINGLE("LineL Switch", PGAR_2_LLOPM_VOL, 7, 1, 0),
|
||||
SOC_DAPM_SINGLE("LineR Switch", PGAR_2_RLOPM_VOL, 7, 1, 0),
|
||||
SOC_DAPM_SINGLE("Mono Switch", PGAR_2_MONOLOPM_VOL, 7, 1, 0),
|
||||
SOC_DAPM_SINGLE("HP Switch", PGAR_2_HPROUT_VOL, 7, 1, 0),
|
||||
SOC_DAPM_SINGLE("HPCOM Switch", PGAR_2_HPRCOM_VOL, 7, 1, 0),
|
||||
SOC_DAPM_SINGLE("HPL Switch", PGAR_2_HPLOUT_VOL, 7, 1, 0),
|
||||
SOC_DAPM_SINGLE("HPR Switch", PGAR_2_HPROUT_VOL, 7, 1, 0),
|
||||
SOC_DAPM_SINGLE("HPLCOM Switch", PGAR_2_HPLCOM_VOL, 7, 1, 0),
|
||||
SOC_DAPM_SINGLE("HPRCOM Switch", PGAR_2_HPRCOM_VOL, 7, 1, 0),
|
||||
};
|
||||
|
||||
/* Left Line2 Bypass Mixer */
|
||||
static const struct snd_kcontrol_new aic3x_left_line2_bp_mixer_controls[] = {
|
||||
SOC_DAPM_SINGLE("Line Switch", LINE2L_2_LLOPM_VOL, 7, 1, 0),
|
||||
SOC_DAPM_SINGLE("LineL Switch", LINE2L_2_LLOPM_VOL, 7, 1, 0),
|
||||
SOC_DAPM_SINGLE("LineR Switch", LINE2L_2_RLOPM_VOL, 7, 1, 0),
|
||||
SOC_DAPM_SINGLE("Mono Switch", LINE2L_2_MONOLOPM_VOL, 7, 1, 0),
|
||||
SOC_DAPM_SINGLE("HP Switch", LINE2L_2_HPLOUT_VOL, 7, 1, 0),
|
||||
SOC_DAPM_SINGLE("HPCOM Switch", LINE2L_2_HPLCOM_VOL, 7, 1, 0),
|
||||
SOC_DAPM_SINGLE("HPLCOM Switch", LINE2L_2_HPLCOM_VOL, 7, 1, 0),
|
||||
};
|
||||
|
||||
/* Right Line2 Bypass Mixer */
|
||||
static const struct snd_kcontrol_new aic3x_right_line2_bp_mixer_controls[] = {
|
||||
SOC_DAPM_SINGLE("Line Switch", LINE2R_2_RLOPM_VOL, 7, 1, 0),
|
||||
SOC_DAPM_SINGLE("LineL Switch", LINE2R_2_LLOPM_VOL, 7, 1, 0),
|
||||
SOC_DAPM_SINGLE("LineR Switch", LINE2R_2_RLOPM_VOL, 7, 1, 0),
|
||||
SOC_DAPM_SINGLE("Mono Switch", LINE2R_2_MONOLOPM_VOL, 7, 1, 0),
|
||||
SOC_DAPM_SINGLE("HP Switch", LINE2R_2_HPROUT_VOL, 7, 1, 0),
|
||||
SOC_DAPM_SINGLE("HPCOM Switch", LINE2R_2_HPRCOM_VOL, 7, 1, 0),
|
||||
SOC_DAPM_SINGLE("HPRCOM Switch", LINE2R_2_HPRCOM_VOL, 7, 1, 0),
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_widget aic3x_dapm_widgets[] = {
|
||||
|
@ -439,22 +465,26 @@ static const struct snd_soc_dapm_widget aic3x_dapm_widgets[] = {
|
|||
/* Mono Output */
|
||||
SND_SOC_DAPM_PGA("Mono Out", MONOLOPM_CTRL, 0, 0, NULL, 0),
|
||||
|
||||
/* Left Inputs to Left ADC */
|
||||
/* Inputs to Left ADC */
|
||||
SND_SOC_DAPM_ADC("Left ADC", "Left Capture", LINE1L_2_LADC_CTRL, 2, 0),
|
||||
SND_SOC_DAPM_MIXER("Left PGA Mixer", SND_SOC_NOPM, 0, 0,
|
||||
&aic3x_left_pga_mixer_controls[0],
|
||||
ARRAY_SIZE(aic3x_left_pga_mixer_controls)),
|
||||
SND_SOC_DAPM_MUX("Left Line1L Mux", SND_SOC_NOPM, 0, 0,
|
||||
&aic3x_left_line1_mux_controls),
|
||||
SND_SOC_DAPM_MUX("Left Line1R Mux", SND_SOC_NOPM, 0, 0,
|
||||
&aic3x_left_line1_mux_controls),
|
||||
SND_SOC_DAPM_MUX("Left Line2L Mux", SND_SOC_NOPM, 0, 0,
|
||||
&aic3x_left_line2_mux_controls),
|
||||
|
||||
/* Right Inputs to Right ADC */
|
||||
/* Inputs to Right ADC */
|
||||
SND_SOC_DAPM_ADC("Right ADC", "Right Capture",
|
||||
LINE1R_2_RADC_CTRL, 2, 0),
|
||||
SND_SOC_DAPM_MIXER("Right PGA Mixer", SND_SOC_NOPM, 0, 0,
|
||||
&aic3x_right_pga_mixer_controls[0],
|
||||
ARRAY_SIZE(aic3x_right_pga_mixer_controls)),
|
||||
SND_SOC_DAPM_MUX("Right Line1L Mux", SND_SOC_NOPM, 0, 0,
|
||||
&aic3x_right_line1_mux_controls),
|
||||
SND_SOC_DAPM_MUX("Right Line1R Mux", SND_SOC_NOPM, 0, 0,
|
||||
&aic3x_right_line1_mux_controls),
|
||||
SND_SOC_DAPM_MUX("Right Line2R Mux", SND_SOC_NOPM, 0, 0,
|
||||
|
@ -531,7 +561,8 @@ static const struct snd_soc_dapm_route intercon[] = {
|
|||
{"Left DAC Mux", "DAC_L2", "Left DAC"},
|
||||
{"Left DAC Mux", "DAC_L3", "Left DAC"},
|
||||
|
||||
{"Left DAC_L1 Mixer", "Line Switch", "Left DAC Mux"},
|
||||
{"Left DAC_L1 Mixer", "LineL Switch", "Left DAC Mux"},
|
||||
{"Left DAC_L1 Mixer", "LineR Switch", "Left DAC Mux"},
|
||||
{"Left DAC_L1 Mixer", "Mono Switch", "Left DAC Mux"},
|
||||
{"Left DAC_L1 Mixer", "HP Switch", "Left DAC Mux"},
|
||||
{"Left DAC_L1 Mixer", "HPCOM Switch", "Left DAC Mux"},
|
||||
|
@ -557,7 +588,8 @@ static const struct snd_soc_dapm_route intercon[] = {
|
|||
{"Right DAC Mux", "DAC_R2", "Right DAC"},
|
||||
{"Right DAC Mux", "DAC_R3", "Right DAC"},
|
||||
|
||||
{"Right DAC_R1 Mixer", "Line Switch", "Right DAC Mux"},
|
||||
{"Right DAC_R1 Mixer", "LineL Switch", "Right DAC Mux"},
|
||||
{"Right DAC_R1 Mixer", "LineR Switch", "Right DAC Mux"},
|
||||
{"Right DAC_R1 Mixer", "Mono Switch", "Right DAC Mux"},
|
||||
{"Right DAC_R1 Mixer", "HP Switch", "Right DAC Mux"},
|
||||
{"Right DAC_R1 Mixer", "HPCOM Switch", "Right DAC Mux"},
|
||||
|
@ -592,8 +624,10 @@ static const struct snd_soc_dapm_route intercon[] = {
|
|||
{"Left Line2L Mux", "differential", "LINE2L"},
|
||||
|
||||
{"Left PGA Mixer", "Line1L Switch", "Left Line1L Mux"},
|
||||
{"Left PGA Mixer", "Line1R Switch", "Left Line1R Mux"},
|
||||
{"Left PGA Mixer", "Line2L Switch", "Left Line2L Mux"},
|
||||
{"Left PGA Mixer", "Mic3L Switch", "MIC3L"},
|
||||
{"Left PGA Mixer", "Mic3R Switch", "MIC3R"},
|
||||
|
||||
{"Left ADC", NULL, "Left PGA Mixer"},
|
||||
{"Left ADC", NULL, "GPIO1 dmic modclk"},
|
||||
|
@ -605,18 +639,23 @@ static const struct snd_soc_dapm_route intercon[] = {
|
|||
{"Right Line2R Mux", "single-ended", "LINE2R"},
|
||||
{"Right Line2R Mux", "differential", "LINE2R"},
|
||||
|
||||
{"Right PGA Mixer", "Line1L Switch", "Right Line1L Mux"},
|
||||
{"Right PGA Mixer", "Line1R Switch", "Right Line1R Mux"},
|
||||
{"Right PGA Mixer", "Line2R Switch", "Right Line2R Mux"},
|
||||
{"Right PGA Mixer", "Mic3L Switch", "MIC3L"},
|
||||
{"Right PGA Mixer", "Mic3R Switch", "MIC3R"},
|
||||
|
||||
{"Right ADC", NULL, "Right PGA Mixer"},
|
||||
{"Right ADC", NULL, "GPIO1 dmic modclk"},
|
||||
|
||||
/* Left PGA Bypass */
|
||||
{"Left PGA Bypass Mixer", "Line Switch", "Left PGA Mixer"},
|
||||
{"Left PGA Bypass Mixer", "LineL Switch", "Left PGA Mixer"},
|
||||
{"Left PGA Bypass Mixer", "LineR Switch", "Left PGA Mixer"},
|
||||
{"Left PGA Bypass Mixer", "Mono Switch", "Left PGA Mixer"},
|
||||
{"Left PGA Bypass Mixer", "HP Switch", "Left PGA Mixer"},
|
||||
{"Left PGA Bypass Mixer", "HPCOM Switch", "Left PGA Mixer"},
|
||||
{"Left PGA Bypass Mixer", "HPL Switch", "Left PGA Mixer"},
|
||||
{"Left PGA Bypass Mixer", "HPR Switch", "Left PGA Mixer"},
|
||||
{"Left PGA Bypass Mixer", "HPLCOM Switch", "Left PGA Mixer"},
|
||||
{"Left PGA Bypass Mixer", "HPRCOM Switch", "Left PGA Mixer"},
|
||||
|
||||
{"Left HPCOM Mux", "differential of HPLOUT", "Left PGA Bypass Mixer"},
|
||||
{"Left HPCOM Mux", "constant VCM", "Left PGA Bypass Mixer"},
|
||||
|
@ -627,10 +666,13 @@ static const struct snd_soc_dapm_route intercon[] = {
|
|||
{"Left HP Out", NULL, "Left PGA Bypass Mixer"},
|
||||
|
||||
/* Right PGA Bypass */
|
||||
{"Right PGA Bypass Mixer", "Line Switch", "Right PGA Mixer"},
|
||||
{"Right PGA Bypass Mixer", "LineL Switch", "Right PGA Mixer"},
|
||||
{"Right PGA Bypass Mixer", "LineR Switch", "Right PGA Mixer"},
|
||||
{"Right PGA Bypass Mixer", "Mono Switch", "Right PGA Mixer"},
|
||||
{"Right PGA Bypass Mixer", "HP Switch", "Right PGA Mixer"},
|
||||
{"Right PGA Bypass Mixer", "HPCOM Switch", "Right PGA Mixer"},
|
||||
{"Right PGA Bypass Mixer", "HPL Switch", "Right PGA Mixer"},
|
||||
{"Right PGA Bypass Mixer", "HPR Switch", "Right PGA Mixer"},
|
||||
{"Right PGA Bypass Mixer", "HPLCOM Switch", "Right PGA Mixer"},
|
||||
{"Right PGA Bypass Mixer", "HPRCOM Switch", "Right PGA Mixer"},
|
||||
|
||||
{"Right HPCOM Mux", "differential of HPROUT", "Right PGA Bypass Mixer"},
|
||||
{"Right HPCOM Mux", "constant VCM", "Right PGA Bypass Mixer"},
|
||||
|
@ -643,10 +685,11 @@ static const struct snd_soc_dapm_route intercon[] = {
|
|||
{"Right HP Out", NULL, "Right PGA Bypass Mixer"},
|
||||
|
||||
/* Left Line2 Bypass */
|
||||
{"Left Line2 Bypass Mixer", "Line Switch", "Left Line2L Mux"},
|
||||
{"Left Line2 Bypass Mixer", "LineL Switch", "Left Line2L Mux"},
|
||||
{"Left Line2 Bypass Mixer", "LineR Switch", "Left Line2L Mux"},
|
||||
{"Left Line2 Bypass Mixer", "Mono Switch", "Left Line2L Mux"},
|
||||
{"Left Line2 Bypass Mixer", "HP Switch", "Left Line2L Mux"},
|
||||
{"Left Line2 Bypass Mixer", "HPCOM Switch", "Left Line2L Mux"},
|
||||
{"Left Line2 Bypass Mixer", "HPLCOM Switch", "Left Line2L Mux"},
|
||||
|
||||
{"Left HPCOM Mux", "differential of HPLOUT", "Left Line2 Bypass Mixer"},
|
||||
{"Left HPCOM Mux", "constant VCM", "Left Line2 Bypass Mixer"},
|
||||
|
@ -657,10 +700,11 @@ static const struct snd_soc_dapm_route intercon[] = {
|
|||
{"Left HP Out", NULL, "Left Line2 Bypass Mixer"},
|
||||
|
||||
/* Right Line2 Bypass */
|
||||
{"Right Line2 Bypass Mixer", "Line Switch", "Right Line2R Mux"},
|
||||
{"Right Line2 Bypass Mixer", "LineL Switch", "Right Line2R Mux"},
|
||||
{"Right Line2 Bypass Mixer", "LineR Switch", "Right Line2R Mux"},
|
||||
{"Right Line2 Bypass Mixer", "Mono Switch", "Right Line2R Mux"},
|
||||
{"Right Line2 Bypass Mixer", "HP Switch", "Right Line2R Mux"},
|
||||
{"Right Line2 Bypass Mixer", "HPCOM Switch", "Right Line2R Mux"},
|
||||
{"Right Line2 Bypass Mixer", "HPRCOM Switch", "Right Line2R Mux"},
|
||||
|
||||
{"Right HPCOM Mux", "differential of HPROUT", "Right Line2 Bypass Mixer"},
|
||||
{"Right HPCOM Mux", "constant VCM", "Right Line2 Bypass Mixer"},
|
||||
|
@ -694,7 +738,8 @@ static int aic3x_add_widgets(struct snd_soc_codec *codec)
|
|||
}
|
||||
|
||||
static int aic3x_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_device *socdev = rtd->socdev;
|
||||
|
@ -846,6 +891,7 @@ static int aic3x_set_dai_fmt(struct snd_soc_dai *codec_dai,
|
|||
struct snd_soc_codec *codec = codec_dai->codec;
|
||||
struct aic3x_priv *aic3x = codec->private_data;
|
||||
u8 iface_areg, iface_breg;
|
||||
int delay = 0;
|
||||
|
||||
iface_areg = aic3x_read_reg_cache(codec, AIC3X_ASD_INTF_CTRLA) & 0x3f;
|
||||
iface_breg = aic3x_read_reg_cache(codec, AIC3X_ASD_INTF_CTRLB) & 0x3f;
|
||||
|
@ -871,6 +917,8 @@ static int aic3x_set_dai_fmt(struct snd_soc_dai *codec_dai,
|
|||
SND_SOC_DAIFMT_INV_MASK)) {
|
||||
case (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF):
|
||||
break;
|
||||
case (SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_IB_NF):
|
||||
delay = 1;
|
||||
case (SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_IB_NF):
|
||||
iface_breg |= (0x01 << 6);
|
||||
break;
|
||||
|
@ -887,6 +935,7 @@ static int aic3x_set_dai_fmt(struct snd_soc_dai *codec_dai,
|
|||
/* set iface */
|
||||
aic3x_write(codec, AIC3X_ASD_INTF_CTRLA, iface_areg);
|
||||
aic3x_write(codec, AIC3X_ASD_INTF_CTRLB, iface_breg);
|
||||
aic3x_write(codec, AIC3X_ASD_INTF_CTRLC, delay);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -981,14 +1030,41 @@ int aic3x_get_gpio(struct snd_soc_codec *codec, int gpio)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(aic3x_get_gpio);
|
||||
|
||||
void aic3x_set_headset_detection(struct snd_soc_codec *codec, int detect,
|
||||
int headset_debounce, int button_debounce)
|
||||
{
|
||||
u8 val;
|
||||
|
||||
val = ((detect & AIC3X_HEADSET_DETECT_MASK)
|
||||
<< AIC3X_HEADSET_DETECT_SHIFT) |
|
||||
((headset_debounce & AIC3X_HEADSET_DEBOUNCE_MASK)
|
||||
<< AIC3X_HEADSET_DEBOUNCE_SHIFT) |
|
||||
((button_debounce & AIC3X_BUTTON_DEBOUNCE_MASK)
|
||||
<< AIC3X_BUTTON_DEBOUNCE_SHIFT);
|
||||
|
||||
if (detect & AIC3X_HEADSET_DETECT_MASK)
|
||||
val |= AIC3X_HEADSET_DETECT_ENABLED;
|
||||
|
||||
aic3x_write(codec, AIC3X_HEADSET_DETECT_CTRL_A, val);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(aic3x_set_headset_detection);
|
||||
|
||||
int aic3x_headset_detected(struct snd_soc_codec *codec)
|
||||
{
|
||||
u8 val;
|
||||
aic3x_read(codec, AIC3X_RT_IRQ_FLAGS_REG, &val);
|
||||
return (val >> 2) & 1;
|
||||
aic3x_read(codec, AIC3X_HEADSET_DETECT_CTRL_B, &val);
|
||||
return (val >> 4) & 1;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(aic3x_headset_detected);
|
||||
|
||||
int aic3x_button_pressed(struct snd_soc_codec *codec)
|
||||
{
|
||||
u8 val;
|
||||
aic3x_read(codec, AIC3X_HEADSET_DETECT_CTRL_B, &val);
|
||||
return (val >> 5) & 1;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(aic3x_button_pressed);
|
||||
|
||||
#define AIC3X_RATES SNDRV_PCM_RATE_8000_96000
|
||||
#define AIC3X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
|
||||
SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE)
|
||||
|
@ -1009,8 +1085,6 @@ struct snd_soc_dai aic3x_dai = {
|
|||
.formats = AIC3X_FORMATS,},
|
||||
.ops = {
|
||||
.hw_params = aic3x_hw_params,
|
||||
},
|
||||
.dai_ops = {
|
||||
.digital_mute = aic3x_mute,
|
||||
.set_sysclk = aic3x_set_dai_sysclk,
|
||||
.set_fmt = aic3x_set_dai_fmt,
|
||||
|
@ -1152,7 +1226,7 @@ static int aic3x_init(struct snd_soc_device *socdev)
|
|||
|
||||
aic3x_add_controls(codec);
|
||||
aic3x_add_widgets(codec);
|
||||
ret = snd_soc_register_card(socdev);
|
||||
ret = snd_soc_init_card(socdev);
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR "aic3x: failed to register card\n");
|
||||
goto card_err;
|
||||
|
@ -1341,6 +1415,18 @@ struct snd_soc_codec_device soc_codec_dev_aic3x = {
|
|||
};
|
||||
EXPORT_SYMBOL_GPL(soc_codec_dev_aic3x);
|
||||
|
||||
static int __init aic3x_modinit(void)
|
||||
{
|
||||
return snd_soc_register_dai(&aic3x_dai);
|
||||
}
|
||||
module_init(aic3x_modinit);
|
||||
|
||||
static void __exit aic3x_exit(void)
|
||||
{
|
||||
snd_soc_unregister_dai(&aic3x_dai);
|
||||
}
|
||||
module_exit(aic3x_exit);
|
||||
|
||||
MODULE_DESCRIPTION("ASoC TLV320AIC3X codec driver");
|
||||
MODULE_AUTHOR("Vladimir Barinov");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
|
@ -35,11 +35,15 @@
|
|||
#define AIC3X_ASD_INTF_CTRLA 8
|
||||
/* Audio serial data interface control register B */
|
||||
#define AIC3X_ASD_INTF_CTRLB 9
|
||||
/* Audio serial data interface control register C */
|
||||
#define AIC3X_ASD_INTF_CTRLC 10
|
||||
/* Audio overflow status and PLL R value programming register */
|
||||
#define AIC3X_OVRF_STATUS_AND_PLLR_REG 11
|
||||
/* Audio codec digital filter control register */
|
||||
#define AIC3X_CODEC_DFILT_CTRL 12
|
||||
|
||||
/* Headset/button press detection register */
|
||||
#define AIC3X_HEADSET_DETECT_CTRL_A 13
|
||||
#define AIC3X_HEADSET_DETECT_CTRL_B 14
|
||||
/* ADC PGA Gain control registers */
|
||||
#define LADC_VOL 15
|
||||
#define RADC_VOL 16
|
||||
|
@ -48,7 +52,9 @@
|
|||
#define MIC3LR_2_RADC_CTRL 18
|
||||
/* Line1 Input control registers */
|
||||
#define LINE1L_2_LADC_CTRL 19
|
||||
#define LINE1R_2_LADC_CTRL 21
|
||||
#define LINE1R_2_RADC_CTRL 22
|
||||
#define LINE1L_2_RADC_CTRL 24
|
||||
/* Line2 Input control registers */
|
||||
#define LINE2L_2_LADC_CTRL 20
|
||||
#define LINE2R_2_RADC_CTRL 23
|
||||
|
@ -79,6 +85,8 @@
|
|||
#define LINE2L_2_HPLOUT_VOL 45
|
||||
#define LINE2R_2_HPROUT_VOL 62
|
||||
#define PGAL_2_HPLOUT_VOL 46
|
||||
#define PGAL_2_HPROUT_VOL 60
|
||||
#define PGAR_2_HPLOUT_VOL 49
|
||||
#define PGAR_2_HPROUT_VOL 63
|
||||
#define DACL1_2_HPLOUT_VOL 47
|
||||
#define DACR1_2_HPROUT_VOL 64
|
||||
|
@ -88,6 +96,8 @@
|
|||
#define LINE2L_2_HPLCOM_VOL 52
|
||||
#define LINE2R_2_HPRCOM_VOL 69
|
||||
#define PGAL_2_HPLCOM_VOL 53
|
||||
#define PGAR_2_HPLCOM_VOL 56
|
||||
#define PGAL_2_HPRCOM_VOL 67
|
||||
#define PGAR_2_HPRCOM_VOL 70
|
||||
#define DACL1_2_HPLCOM_VOL 54
|
||||
#define DACR1_2_HPRCOM_VOL 71
|
||||
|
@ -103,11 +113,17 @@
|
|||
#define MONOLOPM_CTRL 79
|
||||
/* Line Output Plus/Minus control registers */
|
||||
#define LINE2L_2_LLOPM_VOL 80
|
||||
#define LINE2L_2_RLOPM_VOL 87
|
||||
#define LINE2R_2_LLOPM_VOL 83
|
||||
#define LINE2R_2_RLOPM_VOL 90
|
||||
#define PGAL_2_LLOPM_VOL 81
|
||||
#define PGAL_2_RLOPM_VOL 88
|
||||
#define PGAR_2_LLOPM_VOL 84
|
||||
#define PGAR_2_RLOPM_VOL 91
|
||||
#define DACL1_2_LLOPM_VOL 82
|
||||
#define DACL1_2_RLOPM_VOL 89
|
||||
#define DACR1_2_RLOPM_VOL 92
|
||||
#define DACR1_2_LLOPM_VOL 85
|
||||
#define LLOPM_CTRL 86
|
||||
#define RLOPM_CTRL 93
|
||||
/* GPIO/IRQ registers */
|
||||
|
@ -221,7 +237,49 @@ enum {
|
|||
|
||||
void aic3x_set_gpio(struct snd_soc_codec *codec, int gpio, int state);
|
||||
int aic3x_get_gpio(struct snd_soc_codec *codec, int gpio);
|
||||
|
||||
/* headset detection / button API */
|
||||
|
||||
/* The AIC3x supports detection of stereo headsets (GND + left + right signal)
|
||||
* and cellular headsets (GND + speaker output + microphone input).
|
||||
* It is recommended to enable MIC bias for this function to work properly.
|
||||
* For more information, please refer to the datasheet. */
|
||||
enum {
|
||||
AIC3X_HEADSET_DETECT_OFF = 0,
|
||||
AIC3X_HEADSET_DETECT_STEREO = 1,
|
||||
AIC3X_HEADSET_DETECT_CELLULAR = 2,
|
||||
AIC3X_HEADSET_DETECT_BOTH = 3
|
||||
};
|
||||
|
||||
enum {
|
||||
AIC3X_HEADSET_DEBOUNCE_16MS = 0,
|
||||
AIC3X_HEADSET_DEBOUNCE_32MS = 1,
|
||||
AIC3X_HEADSET_DEBOUNCE_64MS = 2,
|
||||
AIC3X_HEADSET_DEBOUNCE_128MS = 3,
|
||||
AIC3X_HEADSET_DEBOUNCE_256MS = 4,
|
||||
AIC3X_HEADSET_DEBOUNCE_512MS = 5
|
||||
};
|
||||
|
||||
enum {
|
||||
AIC3X_BUTTON_DEBOUNCE_0MS = 0,
|
||||
AIC3X_BUTTON_DEBOUNCE_8MS = 1,
|
||||
AIC3X_BUTTON_DEBOUNCE_16MS = 2,
|
||||
AIC3X_BUTTON_DEBOUNCE_32MS = 3
|
||||
};
|
||||
|
||||
#define AIC3X_HEADSET_DETECT_ENABLED 0x80
|
||||
#define AIC3X_HEADSET_DETECT_SHIFT 5
|
||||
#define AIC3X_HEADSET_DETECT_MASK 3
|
||||
#define AIC3X_HEADSET_DEBOUNCE_SHIFT 2
|
||||
#define AIC3X_HEADSET_DEBOUNCE_MASK 7
|
||||
#define AIC3X_BUTTON_DEBOUNCE_SHIFT 0
|
||||
#define AIC3X_BUTTON_DEBOUNCE_MASK 3
|
||||
|
||||
/* see the enums above for valid parameters to this function */
|
||||
void aic3x_set_headset_detection(struct snd_soc_codec *codec, int detect,
|
||||
int headset_debounce, int button_debounce);
|
||||
int aic3x_headset_detected(struct snd_soc_codec *codec);
|
||||
int aic3x_button_pressed(struct snd_soc_codec *codec);
|
||||
|
||||
struct aic3x_setup_data {
|
||||
int i2c_bus;
|
||||
|
|
1317
sound/soc/codecs/twl4030.c
Normal file
1317
sound/soc/codecs/twl4030.c
Normal file
File diff suppressed because it is too large
Load diff
219
sound/soc/codecs/twl4030.h
Normal file
219
sound/soc/codecs/twl4030.h
Normal file
|
@ -0,0 +1,219 @@
|
|||
/*
|
||||
* ALSA SoC TWL4030 codec driver
|
||||
*
|
||||
* Author: Steve Sakoman <steve@sakoman.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
|
||||
* 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __TWL4030_AUDIO_H__
|
||||
#define __TWL4030_AUDIO_H__
|
||||
|
||||
#define TWL4030_REG_CODEC_MODE 0x1
|
||||
#define TWL4030_REG_OPTION 0x2
|
||||
#define TWL4030_REG_UNKNOWN 0x3
|
||||
#define TWL4030_REG_MICBIAS_CTL 0x4
|
||||
#define TWL4030_REG_ANAMICL 0x5
|
||||
#define TWL4030_REG_ANAMICR 0x6
|
||||
#define TWL4030_REG_AVADC_CTL 0x7
|
||||
#define TWL4030_REG_ADCMICSEL 0x8
|
||||
#define TWL4030_REG_DIGMIXING 0x9
|
||||
#define TWL4030_REG_ATXL1PGA 0xA
|
||||
#define TWL4030_REG_ATXR1PGA 0xB
|
||||
#define TWL4030_REG_AVTXL2PGA 0xC
|
||||
#define TWL4030_REG_AVTXR2PGA 0xD
|
||||
#define TWL4030_REG_AUDIO_IF 0xE
|
||||
#define TWL4030_REG_VOICE_IF 0xF
|
||||
#define TWL4030_REG_ARXR1PGA 0x10
|
||||
#define TWL4030_REG_ARXL1PGA 0x11
|
||||
#define TWL4030_REG_ARXR2PGA 0x12
|
||||
#define TWL4030_REG_ARXL2PGA 0x13
|
||||
#define TWL4030_REG_VRXPGA 0x14
|
||||
#define TWL4030_REG_VSTPGA 0x15
|
||||
#define TWL4030_REG_VRX2ARXPGA 0x16
|
||||
#define TWL4030_REG_AVDAC_CTL 0x17
|
||||
#define TWL4030_REG_ARX2VTXPGA 0x18
|
||||
#define TWL4030_REG_ARXL1_APGA_CTL 0x19
|
||||
#define TWL4030_REG_ARXR1_APGA_CTL 0x1A
|
||||
#define TWL4030_REG_ARXL2_APGA_CTL 0x1B
|
||||
#define TWL4030_REG_ARXR2_APGA_CTL 0x1C
|
||||
#define TWL4030_REG_ATX2ARXPGA 0x1D
|
||||
#define TWL4030_REG_BT_IF 0x1E
|
||||
#define TWL4030_REG_BTPGA 0x1F
|
||||
#define TWL4030_REG_BTSTPGA 0x20
|
||||
#define TWL4030_REG_EAR_CTL 0x21
|
||||
#define TWL4030_REG_HS_SEL 0x22
|
||||
#define TWL4030_REG_HS_GAIN_SET 0x23
|
||||
#define TWL4030_REG_HS_POPN_SET 0x24
|
||||
#define TWL4030_REG_PREDL_CTL 0x25
|
||||
#define TWL4030_REG_PREDR_CTL 0x26
|
||||
#define TWL4030_REG_PRECKL_CTL 0x27
|
||||
#define TWL4030_REG_PRECKR_CTL 0x28
|
||||
#define TWL4030_REG_HFL_CTL 0x29
|
||||
#define TWL4030_REG_HFR_CTL 0x2A
|
||||
#define TWL4030_REG_ALC_CTL 0x2B
|
||||
#define TWL4030_REG_ALC_SET1 0x2C
|
||||
#define TWL4030_REG_ALC_SET2 0x2D
|
||||
#define TWL4030_REG_BOOST_CTL 0x2E
|
||||
#define TWL4030_REG_SOFTVOL_CTL 0x2F
|
||||
#define TWL4030_REG_DTMF_FREQSEL 0x30
|
||||
#define TWL4030_REG_DTMF_TONEXT1H 0x31
|
||||
#define TWL4030_REG_DTMF_TONEXT1L 0x32
|
||||
#define TWL4030_REG_DTMF_TONEXT2H 0x33
|
||||
#define TWL4030_REG_DTMF_TONEXT2L 0x34
|
||||
#define TWL4030_REG_DTMF_TONOFF 0x35
|
||||
#define TWL4030_REG_DTMF_WANONOFF 0x36
|
||||
#define TWL4030_REG_I2S_RX_SCRAMBLE_H 0x37
|
||||
#define TWL4030_REG_I2S_RX_SCRAMBLE_M 0x38
|
||||
#define TWL4030_REG_I2S_RX_SCRAMBLE_L 0x39
|
||||
#define TWL4030_REG_APLL_CTL 0x3A
|
||||
#define TWL4030_REG_DTMF_CTL 0x3B
|
||||
#define TWL4030_REG_DTMF_PGA_CTL2 0x3C
|
||||
#define TWL4030_REG_DTMF_PGA_CTL1 0x3D
|
||||
#define TWL4030_REG_MISC_SET_1 0x3E
|
||||
#define TWL4030_REG_PCMBTMUX 0x3F
|
||||
#define TWL4030_REG_RX_PATH_SEL 0x43
|
||||
#define TWL4030_REG_VDL_APGA_CTL 0x44
|
||||
#define TWL4030_REG_VIBRA_CTL 0x45
|
||||
#define TWL4030_REG_VIBRA_SET 0x46
|
||||
#define TWL4030_REG_VIBRA_PWM_SET 0x47
|
||||
#define TWL4030_REG_ANAMIC_GAIN 0x48
|
||||
#define TWL4030_REG_MISC_SET_2 0x49
|
||||
|
||||
#define TWL4030_CACHEREGNUM (TWL4030_REG_MISC_SET_2 + 1)
|
||||
|
||||
/* Bitfield Definitions */
|
||||
|
||||
/* TWL4030_CODEC_MODE (0x01) Fields */
|
||||
|
||||
#define TWL4030_APLL_RATE 0xF0
|
||||
#define TWL4030_APLL_RATE_8000 0x00
|
||||
#define TWL4030_APLL_RATE_11025 0x10
|
||||
#define TWL4030_APLL_RATE_12000 0x20
|
||||
#define TWL4030_APLL_RATE_16000 0x40
|
||||
#define TWL4030_APLL_RATE_22050 0x50
|
||||
#define TWL4030_APLL_RATE_24000 0x60
|
||||
#define TWL4030_APLL_RATE_32000 0x80
|
||||
#define TWL4030_APLL_RATE_44100 0x90
|
||||
#define TWL4030_APLL_RATE_48000 0xA0
|
||||
#define TWL4030_SEL_16K 0x04
|
||||
#define TWL4030_CODECPDZ 0x02
|
||||
#define TWL4030_OPT_MODE 0x01
|
||||
|
||||
/* TWL4030_REG_MICBIAS_CTL (0x04) Fields */
|
||||
|
||||
#define TWL4030_MICBIAS2_CTL 0x40
|
||||
#define TWL4030_MICBIAS1_CTL 0x20
|
||||
#define TWL4030_HSMICBIAS_EN 0x04
|
||||
#define TWL4030_MICBIAS2_EN 0x02
|
||||
#define TWL4030_MICBIAS1_EN 0x01
|
||||
|
||||
/* ANAMICL (0x05) Fields */
|
||||
|
||||
#define TWL4030_CNCL_OFFSET_START 0x80
|
||||
#define TWL4030_OFFSET_CNCL_SEL 0x60
|
||||
#define TWL4030_OFFSET_CNCL_SEL_ARX1 0x00
|
||||
#define TWL4030_OFFSET_CNCL_SEL_ARX2 0x20
|
||||
#define TWL4030_OFFSET_CNCL_SEL_VRX 0x40
|
||||
#define TWL4030_OFFSET_CNCL_SEL_ALL 0x60
|
||||
#define TWL4030_MICAMPL_EN 0x10
|
||||
#define TWL4030_CKMIC_EN 0x08
|
||||
#define TWL4030_AUXL_EN 0x04
|
||||
#define TWL4030_HSMIC_EN 0x02
|
||||
#define TWL4030_MAINMIC_EN 0x01
|
||||
|
||||
/* ANAMICR (0x06) Fields */
|
||||
|
||||
#define TWL4030_MICAMPR_EN 0x10
|
||||
#define TWL4030_AUXR_EN 0x04
|
||||
#define TWL4030_SUBMIC_EN 0x01
|
||||
|
||||
/* AVADC_CTL (0x07) Fields */
|
||||
|
||||
#define TWL4030_ADCL_EN 0x08
|
||||
#define TWL4030_AVADC_CLK_PRIORITY 0x04
|
||||
#define TWL4030_ADCR_EN 0x02
|
||||
|
||||
/* AUDIO_IF (0x0E) Fields */
|
||||
|
||||
#define TWL4030_AIF_SLAVE_EN 0x80
|
||||
#define TWL4030_DATA_WIDTH 0x60
|
||||
#define TWL4030_DATA_WIDTH_16S_16W 0x00
|
||||
#define TWL4030_DATA_WIDTH_32S_16W 0x40
|
||||
#define TWL4030_DATA_WIDTH_32S_24W 0x60
|
||||
#define TWL4030_AIF_FORMAT 0x18
|
||||
#define TWL4030_AIF_FORMAT_CODEC 0x00
|
||||
#define TWL4030_AIF_FORMAT_LEFT 0x08
|
||||
#define TWL4030_AIF_FORMAT_RIGHT 0x10
|
||||
#define TWL4030_AIF_FORMAT_TDM 0x18
|
||||
#define TWL4030_AIF_TRI_EN 0x04
|
||||
#define TWL4030_CLK256FS_EN 0x02
|
||||
#define TWL4030_AIF_EN 0x01
|
||||
|
||||
/* HS_GAIN_SET (0x23) Fields */
|
||||
|
||||
#define TWL4030_HSR_GAIN 0x0C
|
||||
#define TWL4030_HSR_GAIN_PWR_DOWN 0x00
|
||||
#define TWL4030_HSR_GAIN_PLUS_6DB 0x04
|
||||
#define TWL4030_HSR_GAIN_0DB 0x08
|
||||
#define TWL4030_HSR_GAIN_MINUS_6DB 0x0C
|
||||
#define TWL4030_HSL_GAIN 0x03
|
||||
#define TWL4030_HSL_GAIN_PWR_DOWN 0x00
|
||||
#define TWL4030_HSL_GAIN_PLUS_6DB 0x01
|
||||
#define TWL4030_HSL_GAIN_0DB 0x02
|
||||
#define TWL4030_HSL_GAIN_MINUS_6DB 0x03
|
||||
|
||||
/* HS_POPN_SET (0x24) Fields */
|
||||
|
||||
#define TWL4030_VMID_EN 0x40
|
||||
#define TWL4030_EXTMUTE 0x20
|
||||
#define TWL4030_RAMP_DELAY 0x1C
|
||||
#define TWL4030_RAMP_DELAY_20MS 0x00
|
||||
#define TWL4030_RAMP_DELAY_40MS 0x04
|
||||
#define TWL4030_RAMP_DELAY_81MS 0x08
|
||||
#define TWL4030_RAMP_DELAY_161MS 0x0C
|
||||
#define TWL4030_RAMP_DELAY_323MS 0x10
|
||||
#define TWL4030_RAMP_DELAY_645MS 0x14
|
||||
#define TWL4030_RAMP_DELAY_1291MS 0x18
|
||||
#define TWL4030_RAMP_DELAY_2581MS 0x1C
|
||||
#define TWL4030_RAMP_EN 0x02
|
||||
|
||||
/* HFL_CTL (0x29, 0x2A) Fields */
|
||||
#define TWL4030_HF_CTL_HB_EN 0x04
|
||||
#define TWL4030_HF_CTL_LOOP_EN 0x08
|
||||
#define TWL4030_HF_CTL_RAMP_EN 0x10
|
||||
#define TWL4030_HF_CTL_REF_EN 0x20
|
||||
|
||||
/* APLL_CTL (0x3A) Fields */
|
||||
|
||||
#define TWL4030_APLL_EN 0x10
|
||||
#define TWL4030_APLL_INFREQ 0x0F
|
||||
#define TWL4030_APLL_INFREQ_19200KHZ 0x05
|
||||
#define TWL4030_APLL_INFREQ_26000KHZ 0x06
|
||||
#define TWL4030_APLL_INFREQ_38400KHZ 0x0F
|
||||
|
||||
/* REG_MISC_SET_1 (0x3E) Fields */
|
||||
|
||||
#define TWL4030_CLK64_EN 0x80
|
||||
#define TWL4030_SCRAMBLE_EN 0x40
|
||||
#define TWL4030_FMLOOP_EN 0x20
|
||||
#define TWL4030_SMOOTH_ANAVOL_EN 0x02
|
||||
#define TWL4030_DIGMIC_LR_SWAP_EN 0x01
|
||||
|
||||
extern struct snd_soc_dai twl4030_dai;
|
||||
extern struct snd_soc_codec_device soc_codec_dev_twl4030;
|
||||
|
||||
#endif /* End of __TWL4030_AUDIO_H__ */
|
668
sound/soc/codecs/uda134x.c
Normal file
668
sound/soc/codecs/uda134x.c
Normal file
|
@ -0,0 +1,668 @@
|
|||
/*
|
||||
* uda134x.c -- UDA134X ALSA SoC Codec driver
|
||||
*
|
||||
* Modifications by Christian Pellegrin <chripell@evolware.org>
|
||||
*
|
||||
* Copyright 2007 Dension Audio Systems Ltd.
|
||||
* Author: Zoltan Devai
|
||||
*
|
||||
* Based on the WM87xx drivers by Liam Girdwood and 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/delay.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/soc-dapm.h>
|
||||
#include <sound/initval.h>
|
||||
|
||||
#include <sound/uda134x.h>
|
||||
#include <sound/l3.h>
|
||||
|
||||
#include "uda134x.h"
|
||||
|
||||
|
||||
#define POWER_OFF_ON_STANDBY 1
|
||||
/*
|
||||
ALSA SOC usually puts the device in standby mode when it's not used
|
||||
for sometime. If you define POWER_OFF_ON_STANDBY the driver will
|
||||
turn off the ADC/DAC when this callback is invoked and turn it back
|
||||
on when needed. Unfortunately this will result in a very light bump
|
||||
(it can be audible only with good earphones). If this bothers you
|
||||
just comment this line, you will have slightly higher power
|
||||
consumption . Please note that sending the L3 command for ADC is
|
||||
enough to make the bump, so it doesn't make difference if you
|
||||
completely take off power from the codec.
|
||||
*/
|
||||
|
||||
#define UDA134X_RATES SNDRV_PCM_RATE_8000_48000
|
||||
#define UDA134X_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | \
|
||||
SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S20_3LE)
|
||||
|
||||
struct uda134x_priv {
|
||||
int sysclk;
|
||||
int dai_fmt;
|
||||
|
||||
struct snd_pcm_substream *master_substream;
|
||||
struct snd_pcm_substream *slave_substream;
|
||||
};
|
||||
|
||||
/* In-data addresses are hard-coded into the reg-cache values */
|
||||
static const char uda134x_reg[UDA134X_REGS_NUM] = {
|
||||
/* Extended address registers */
|
||||
0x04, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
/* Status, data regs */
|
||||
0x00, 0x83, 0x00, 0x40, 0x80, 0x00,
|
||||
};
|
||||
|
||||
/*
|
||||
* The codec has no support for reading its registers except for peak level...
|
||||
*/
|
||||
static inline unsigned int uda134x_read_reg_cache(struct snd_soc_codec *codec,
|
||||
unsigned int reg)
|
||||
{
|
||||
u8 *cache = codec->reg_cache;
|
||||
|
||||
if (reg >= UDA134X_REGS_NUM)
|
||||
return -1;
|
||||
return cache[reg];
|
||||
}
|
||||
|
||||
/*
|
||||
* Write the register cache
|
||||
*/
|
||||
static inline void uda134x_write_reg_cache(struct snd_soc_codec *codec,
|
||||
u8 reg, unsigned int value)
|
||||
{
|
||||
u8 *cache = codec->reg_cache;
|
||||
|
||||
if (reg >= UDA134X_REGS_NUM)
|
||||
return;
|
||||
cache[reg] = value;
|
||||
}
|
||||
|
||||
/*
|
||||
* Write to the uda134x registers
|
||||
*
|
||||
*/
|
||||
static int uda134x_write(struct snd_soc_codec *codec, unsigned int reg,
|
||||
unsigned int value)
|
||||
{
|
||||
int ret;
|
||||
u8 addr;
|
||||
u8 data = value;
|
||||
struct uda134x_platform_data *pd = codec->control_data;
|
||||
|
||||
pr_debug("%s reg: %02X, value:%02X\n", __func__, reg, value);
|
||||
|
||||
if (reg >= UDA134X_REGS_NUM) {
|
||||
printk(KERN_ERR "%s unkown register: reg: %d",
|
||||
__func__, reg);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
uda134x_write_reg_cache(codec, reg, value);
|
||||
|
||||
switch (reg) {
|
||||
case UDA134X_STATUS0:
|
||||
case UDA134X_STATUS1:
|
||||
addr = UDA134X_STATUS_ADDR;
|
||||
break;
|
||||
case UDA134X_DATA000:
|
||||
case UDA134X_DATA001:
|
||||
case UDA134X_DATA010:
|
||||
addr = UDA134X_DATA0_ADDR;
|
||||
break;
|
||||
case UDA134X_DATA1:
|
||||
addr = UDA134X_DATA1_ADDR;
|
||||
break;
|
||||
default:
|
||||
/* It's an extended address register */
|
||||
addr = (reg | UDA134X_EXTADDR_PREFIX);
|
||||
|
||||
ret = l3_write(&pd->l3,
|
||||
UDA134X_DATA0_ADDR, &addr, 1);
|
||||
if (ret != 1)
|
||||
return -EIO;
|
||||
|
||||
addr = UDA134X_DATA0_ADDR;
|
||||
data = (value | UDA134X_EXTDATA_PREFIX);
|
||||
break;
|
||||
}
|
||||
|
||||
ret = l3_write(&pd->l3,
|
||||
addr, &data, 1);
|
||||
if (ret != 1)
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void uda134x_reset(struct snd_soc_codec *codec)
|
||||
{
|
||||
u8 reset_reg = uda134x_read_reg_cache(codec, UDA134X_STATUS0);
|
||||
uda134x_write(codec, UDA134X_STATUS0, reset_reg | (1<<6));
|
||||
msleep(1);
|
||||
uda134x_write(codec, UDA134X_STATUS0, reset_reg & ~(1<<6));
|
||||
}
|
||||
|
||||
static int uda134x_mute(struct snd_soc_dai *dai, int mute)
|
||||
{
|
||||
struct snd_soc_codec *codec = dai->codec;
|
||||
u8 mute_reg = uda134x_read_reg_cache(codec, UDA134X_DATA010);
|
||||
|
||||
pr_debug("%s mute: %d\n", __func__, mute);
|
||||
|
||||
if (mute)
|
||||
mute_reg |= (1<<2);
|
||||
else
|
||||
mute_reg &= ~(1<<2);
|
||||
|
||||
uda134x_write(codec, UDA134X_DATA010, mute_reg & ~(1<<2));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int uda134x_startup(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_device *socdev = rtd->socdev;
|
||||
struct snd_soc_codec *codec = socdev->codec;
|
||||
struct uda134x_priv *uda134x = codec->private_data;
|
||||
struct snd_pcm_runtime *master_runtime;
|
||||
|
||||
if (uda134x->master_substream) {
|
||||
master_runtime = uda134x->master_substream->runtime;
|
||||
|
||||
pr_debug("%s constraining to %d bits at %d\n", __func__,
|
||||
master_runtime->sample_bits,
|
||||
master_runtime->rate);
|
||||
|
||||
snd_pcm_hw_constraint_minmax(substream->runtime,
|
||||
SNDRV_PCM_HW_PARAM_RATE,
|
||||
master_runtime->rate,
|
||||
master_runtime->rate);
|
||||
|
||||
snd_pcm_hw_constraint_minmax(substream->runtime,
|
||||
SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
|
||||
master_runtime->sample_bits,
|
||||
master_runtime->sample_bits);
|
||||
|
||||
uda134x->slave_substream = substream;
|
||||
} else
|
||||
uda134x->master_substream = substream;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void uda134x_shutdown(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_device *socdev = rtd->socdev;
|
||||
struct snd_soc_codec *codec = socdev->codec;
|
||||
struct uda134x_priv *uda134x = codec->private_data;
|
||||
|
||||
if (uda134x->master_substream == substream)
|
||||
uda134x->master_substream = uda134x->slave_substream;
|
||||
|
||||
uda134x->slave_substream = NULL;
|
||||
}
|
||||
|
||||
static int uda134x_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_device *socdev = rtd->socdev;
|
||||
struct snd_soc_codec *codec = socdev->codec;
|
||||
struct uda134x_priv *uda134x = codec->private_data;
|
||||
u8 hw_params;
|
||||
|
||||
if (substream == uda134x->slave_substream) {
|
||||
pr_debug("%s ignoring hw_params for slave substream\n",
|
||||
__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
hw_params = uda134x_read_reg_cache(codec, UDA134X_STATUS0);
|
||||
hw_params &= STATUS0_SYSCLK_MASK;
|
||||
hw_params &= STATUS0_DAIFMT_MASK;
|
||||
|
||||
pr_debug("%s sysclk: %d, rate:%d\n", __func__,
|
||||
uda134x->sysclk, params_rate(params));
|
||||
|
||||
/* set SYSCLK / fs ratio */
|
||||
switch (uda134x->sysclk / params_rate(params)) {
|
||||
case 512:
|
||||
break;
|
||||
case 384:
|
||||
hw_params |= (1<<4);
|
||||
break;
|
||||
case 256:
|
||||
hw_params |= (1<<5);
|
||||
break;
|
||||
default:
|
||||
printk(KERN_ERR "%s unsupported fs\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
pr_debug("%s dai_fmt: %d, params_format:%d\n", __func__,
|
||||
uda134x->dai_fmt, params_format(params));
|
||||
|
||||
/* set DAI format and word length */
|
||||
switch (uda134x->dai_fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
|
||||
case SND_SOC_DAIFMT_I2S:
|
||||
break;
|
||||
case SND_SOC_DAIFMT_RIGHT_J:
|
||||
switch (params_format(params)) {
|
||||
case SNDRV_PCM_FORMAT_S16_LE:
|
||||
hw_params |= (1<<1);
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S18_3LE:
|
||||
hw_params |= (1<<2);
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S20_3LE:
|
||||
hw_params |= ((1<<2) | (1<<1));
|
||||
break;
|
||||
default:
|
||||
printk(KERN_ERR "%s unsupported format (right)\n",
|
||||
__func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
case SND_SOC_DAIFMT_LEFT_J:
|
||||
hw_params |= (1<<3);
|
||||
break;
|
||||
default:
|
||||
printk(KERN_ERR "%s unsupported format\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
uda134x_write(codec, UDA134X_STATUS0, hw_params);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int uda134x_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 uda134x_priv *uda134x = codec->private_data;
|
||||
|
||||
pr_debug("%s clk_id: %d, freq: %d, dir: %d\n", __func__,
|
||||
clk_id, freq, dir);
|
||||
|
||||
/* Anything between 256fs*8Khz and 512fs*48Khz should be acceptable
|
||||
because the codec is slave. Of course limitations of the clock
|
||||
master (the IIS controller) apply.
|
||||
We'll error out on set_hw_params if it's not OK */
|
||||
if ((freq >= (256 * 8000)) && (freq <= (512 * 48000))) {
|
||||
uda134x->sysclk = freq;
|
||||
return 0;
|
||||
}
|
||||
|
||||
printk(KERN_ERR "%s unsupported sysclk\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int uda134x_set_dai_fmt(struct snd_soc_dai *codec_dai,
|
||||
unsigned int fmt)
|
||||
{
|
||||
struct snd_soc_codec *codec = codec_dai->codec;
|
||||
struct uda134x_priv *uda134x = codec->private_data;
|
||||
|
||||
pr_debug("%s fmt: %08X\n", __func__, fmt);
|
||||
|
||||
/* codec supports only full slave mode */
|
||||
if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS) {
|
||||
printk(KERN_ERR "%s unsupported slave mode\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* no support for clock inversion */
|
||||
if ((fmt & SND_SOC_DAIFMT_INV_MASK) != SND_SOC_DAIFMT_NB_NF) {
|
||||
printk(KERN_ERR "%s unsupported clock inversion\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* We can't setup DAI format here as it depends on the word bit num */
|
||||
/* so let's just store the value for later */
|
||||
uda134x->dai_fmt = fmt;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int uda134x_set_bias_level(struct snd_soc_codec *codec,
|
||||
enum snd_soc_bias_level level)
|
||||
{
|
||||
u8 reg;
|
||||
struct uda134x_platform_data *pd = codec->control_data;
|
||||
int i;
|
||||
u8 *cache = codec->reg_cache;
|
||||
|
||||
pr_debug("%s bias level %d\n", __func__, level);
|
||||
|
||||
switch (level) {
|
||||
case SND_SOC_BIAS_ON:
|
||||
/* ADC, DAC on */
|
||||
reg = uda134x_read_reg_cache(codec, UDA134X_STATUS1);
|
||||
uda134x_write(codec, UDA134X_STATUS1, reg | 0x03);
|
||||
break;
|
||||
case SND_SOC_BIAS_PREPARE:
|
||||
/* power on */
|
||||
if (pd->power) {
|
||||
pd->power(1);
|
||||
/* Sync reg_cache with the hardware */
|
||||
for (i = 0; i < ARRAY_SIZE(uda134x_reg); i++)
|
||||
codec->write(codec, i, *cache++);
|
||||
}
|
||||
break;
|
||||
case SND_SOC_BIAS_STANDBY:
|
||||
/* ADC, DAC power off */
|
||||
reg = uda134x_read_reg_cache(codec, UDA134X_STATUS1);
|
||||
uda134x_write(codec, UDA134X_STATUS1, reg & ~(0x03));
|
||||
break;
|
||||
case SND_SOC_BIAS_OFF:
|
||||
/* power off */
|
||||
if (pd->power)
|
||||
pd->power(0);
|
||||
break;
|
||||
}
|
||||
codec->bias_level = level;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char *uda134x_dsp_setting[] = {"Flat", "Minimum1",
|
||||
"Minimum2", "Maximum"};
|
||||
static const char *uda134x_deemph[] = {"None", "32Khz", "44.1Khz", "48Khz"};
|
||||
static const char *uda134x_mixmode[] = {"Differential", "Analog1",
|
||||
"Analog2", "Both"};
|
||||
|
||||
static const struct soc_enum uda134x_mixer_enum[] = {
|
||||
SOC_ENUM_SINGLE(UDA134X_DATA010, 0, 0x04, uda134x_dsp_setting),
|
||||
SOC_ENUM_SINGLE(UDA134X_DATA010, 3, 0x04, uda134x_deemph),
|
||||
SOC_ENUM_SINGLE(UDA134X_EA010, 0, 0x04, uda134x_mixmode),
|
||||
};
|
||||
|
||||
static const struct snd_kcontrol_new uda1341_snd_controls[] = {
|
||||
SOC_SINGLE("Master Playback Volume", UDA134X_DATA000, 0, 0x3F, 1),
|
||||
SOC_SINGLE("Capture Volume", UDA134X_EA010, 2, 0x07, 0),
|
||||
SOC_SINGLE("Analog1 Volume", UDA134X_EA000, 0, 0x1F, 1),
|
||||
SOC_SINGLE("Analog2 Volume", UDA134X_EA001, 0, 0x1F, 1),
|
||||
|
||||
SOC_SINGLE("Mic Sensitivity", UDA134X_EA010, 2, 7, 0),
|
||||
SOC_SINGLE("Mic Volume", UDA134X_EA101, 0, 0x1F, 0),
|
||||
|
||||
SOC_SINGLE("Tone Control - Bass", UDA134X_DATA001, 2, 0xF, 0),
|
||||
SOC_SINGLE("Tone Control - Treble", UDA134X_DATA001, 0, 3, 0),
|
||||
|
||||
SOC_ENUM("Sound Processing Filter", uda134x_mixer_enum[0]),
|
||||
SOC_ENUM("PCM Playback De-emphasis", uda134x_mixer_enum[1]),
|
||||
SOC_ENUM("Input Mux", uda134x_mixer_enum[2]),
|
||||
|
||||
SOC_SINGLE("AGC Switch", UDA134X_EA100, 4, 1, 0),
|
||||
SOC_SINGLE("AGC Target Volume", UDA134X_EA110, 0, 0x03, 1),
|
||||
SOC_SINGLE("AGC Timing", UDA134X_EA110, 2, 0x07, 0),
|
||||
|
||||
SOC_SINGLE("DAC +6dB Switch", UDA134X_STATUS1, 6, 1, 0),
|
||||
SOC_SINGLE("ADC +6dB Switch", UDA134X_STATUS1, 5, 1, 0),
|
||||
SOC_SINGLE("ADC Polarity Switch", UDA134X_STATUS1, 4, 1, 0),
|
||||
SOC_SINGLE("DAC Polarity Switch", UDA134X_STATUS1, 3, 1, 0),
|
||||
SOC_SINGLE("Double Speed Playback Switch", UDA134X_STATUS1, 2, 1, 0),
|
||||
SOC_SINGLE("DC Filter Enable Switch", UDA134X_STATUS0, 0, 1, 0),
|
||||
};
|
||||
|
||||
static const struct snd_kcontrol_new uda1340_snd_controls[] = {
|
||||
SOC_SINGLE("Master Playback Volume", UDA134X_DATA000, 0, 0x3F, 1),
|
||||
|
||||
SOC_SINGLE("Tone Control - Bass", UDA134X_DATA001, 2, 0xF, 0),
|
||||
SOC_SINGLE("Tone Control - Treble", UDA134X_DATA001, 0, 3, 0),
|
||||
|
||||
SOC_ENUM("Sound Processing Filter", uda134x_mixer_enum[0]),
|
||||
SOC_ENUM("PCM Playback De-emphasis", uda134x_mixer_enum[1]),
|
||||
|
||||
SOC_SINGLE("DC Filter Enable Switch", UDA134X_STATUS0, 0, 1, 0),
|
||||
};
|
||||
|
||||
static int uda134x_add_controls(struct snd_soc_codec *codec)
|
||||
{
|
||||
int err, i, n;
|
||||
const struct snd_kcontrol_new *ctrls;
|
||||
struct uda134x_platform_data *pd = codec->control_data;
|
||||
|
||||
switch (pd->model) {
|
||||
case UDA134X_UDA1340:
|
||||
case UDA134X_UDA1344:
|
||||
n = ARRAY_SIZE(uda1340_snd_controls);
|
||||
ctrls = uda1340_snd_controls;
|
||||
break;
|
||||
case UDA134X_UDA1341:
|
||||
n = ARRAY_SIZE(uda1341_snd_controls);
|
||||
ctrls = uda1341_snd_controls;
|
||||
break;
|
||||
default:
|
||||
printk(KERN_ERR "%s unkown codec type: %d",
|
||||
__func__, pd->model);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
err = snd_ctl_add(codec->card,
|
||||
snd_soc_cnew(&ctrls[i],
|
||||
codec, NULL));
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct snd_soc_dai uda134x_dai = {
|
||||
.name = "UDA134X",
|
||||
/* playback capabilities */
|
||||
.playback = {
|
||||
.stream_name = "Playback",
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.rates = UDA134X_RATES,
|
||||
.formats = UDA134X_FORMATS,
|
||||
},
|
||||
/* capture capabilities */
|
||||
.capture = {
|
||||
.stream_name = "Capture",
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.rates = UDA134X_RATES,
|
||||
.formats = UDA134X_FORMATS,
|
||||
},
|
||||
/* pcm operations */
|
||||
.ops = {
|
||||
.startup = uda134x_startup,
|
||||
.shutdown = uda134x_shutdown,
|
||||
.hw_params = uda134x_hw_params,
|
||||
.digital_mute = uda134x_mute,
|
||||
.set_sysclk = uda134x_set_dai_sysclk,
|
||||
.set_fmt = uda134x_set_dai_fmt,
|
||||
}
|
||||
};
|
||||
EXPORT_SYMBOL(uda134x_dai);
|
||||
|
||||
|
||||
static int uda134x_soc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
|
||||
struct snd_soc_codec *codec;
|
||||
struct uda134x_priv *uda134x;
|
||||
void *codec_setup_data = socdev->codec_data;
|
||||
int ret = -ENOMEM;
|
||||
struct uda134x_platform_data *pd;
|
||||
|
||||
printk(KERN_INFO "UDA134X SoC Audio Codec\n");
|
||||
|
||||
if (!codec_setup_data) {
|
||||
printk(KERN_ERR "UDA134X SoC codec: "
|
||||
"missing L3 bitbang function\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
pd = codec_setup_data;
|
||||
switch (pd->model) {
|
||||
case UDA134X_UDA1340:
|
||||
case UDA134X_UDA1341:
|
||||
case UDA134X_UDA1344:
|
||||
break;
|
||||
default:
|
||||
printk(KERN_ERR "UDA134X SoC codec: "
|
||||
"unsupported model %d\n",
|
||||
pd->model);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
socdev->codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
|
||||
if (socdev->codec == NULL)
|
||||
return ret;
|
||||
|
||||
codec = socdev->codec;
|
||||
|
||||
uda134x = kzalloc(sizeof(struct uda134x_priv), GFP_KERNEL);
|
||||
if (uda134x == NULL)
|
||||
goto priv_err;
|
||||
codec->private_data = uda134x;
|
||||
|
||||
codec->reg_cache = kmemdup(uda134x_reg, sizeof(uda134x_reg),
|
||||
GFP_KERNEL);
|
||||
if (codec->reg_cache == NULL)
|
||||
goto reg_err;
|
||||
|
||||
mutex_init(&codec->mutex);
|
||||
|
||||
codec->reg_cache_size = sizeof(uda134x_reg);
|
||||
codec->reg_cache_step = 1;
|
||||
|
||||
codec->name = "UDA134X";
|
||||
codec->owner = THIS_MODULE;
|
||||
codec->dai = &uda134x_dai;
|
||||
codec->num_dai = 1;
|
||||
codec->read = uda134x_read_reg_cache;
|
||||
codec->write = uda134x_write;
|
||||
#ifdef POWER_OFF_ON_STANDBY
|
||||
codec->set_bias_level = uda134x_set_bias_level;
|
||||
#endif
|
||||
INIT_LIST_HEAD(&codec->dapm_widgets);
|
||||
INIT_LIST_HEAD(&codec->dapm_paths);
|
||||
|
||||
codec->control_data = codec_setup_data;
|
||||
|
||||
if (pd->power)
|
||||
pd->power(1);
|
||||
|
||||
uda134x_reset(codec);
|
||||
|
||||
/* register pcms */
|
||||
ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR "UDA134X: failed to register pcms\n");
|
||||
goto pcm_err;
|
||||
}
|
||||
|
||||
ret = uda134x_add_controls(codec);
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR "UDA134X: failed to register controls\n");
|
||||
goto pcm_err;
|
||||
}
|
||||
|
||||
ret = snd_soc_init_card(socdev);
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR "UDA134X: failed to register card\n");
|
||||
goto card_err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
card_err:
|
||||
snd_soc_free_pcms(socdev);
|
||||
snd_soc_dapm_free(socdev);
|
||||
pcm_err:
|
||||
kfree(codec->reg_cache);
|
||||
reg_err:
|
||||
kfree(codec->private_data);
|
||||
priv_err:
|
||||
kfree(codec);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* power down chip */
|
||||
static int uda134x_soc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
|
||||
struct snd_soc_codec *codec = socdev->codec;
|
||||
|
||||
uda134x_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
|
||||
uda134x_set_bias_level(codec, SND_SOC_BIAS_OFF);
|
||||
|
||||
snd_soc_free_pcms(socdev);
|
||||
snd_soc_dapm_free(socdev);
|
||||
|
||||
kfree(codec->private_data);
|
||||
kfree(codec->reg_cache);
|
||||
kfree(codec);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_PM)
|
||||
static int uda134x_soc_suspend(struct platform_device *pdev,
|
||||
pm_message_t state)
|
||||
{
|
||||
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
|
||||
struct snd_soc_codec *codec = socdev->codec;
|
||||
|
||||
uda134x_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
|
||||
uda134x_set_bias_level(codec, SND_SOC_BIAS_OFF);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int uda134x_soc_resume(struct platform_device *pdev)
|
||||
{
|
||||
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
|
||||
struct snd_soc_codec *codec = socdev->codec;
|
||||
|
||||
uda134x_set_bias_level(codec, SND_SOC_BIAS_PREPARE);
|
||||
uda134x_set_bias_level(codec, SND_SOC_BIAS_ON);
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#define uda134x_soc_suspend NULL
|
||||
#define uda134x_soc_resume NULL
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
struct snd_soc_codec_device soc_codec_dev_uda134x = {
|
||||
.probe = uda134x_soc_probe,
|
||||
.remove = uda134x_soc_remove,
|
||||
.suspend = uda134x_soc_suspend,
|
||||
.resume = uda134x_soc_resume,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(soc_codec_dev_uda134x);
|
||||
|
||||
static int __init uda134x_init(void)
|
||||
{
|
||||
return snd_soc_register_dai(&uda134x_dai);
|
||||
}
|
||||
module_init(uda134x_init);
|
||||
|
||||
static void __exit uda134x_exit(void)
|
||||
{
|
||||
snd_soc_unregister_dai(&uda134x_dai);
|
||||
}
|
||||
module_exit(uda134x_exit);
|
||||
|
||||
MODULE_DESCRIPTION("UDA134X ALSA soc codec driver");
|
||||
MODULE_AUTHOR("Zoltan Devai, Christian Pellegrin <chripell@evolware.org>");
|
||||
MODULE_LICENSE("GPL");
|
36
sound/soc/codecs/uda134x.h
Normal file
36
sound/soc/codecs/uda134x.h
Normal file
|
@ -0,0 +1,36 @@
|
|||
#ifndef _UDA134X_CODEC_H
|
||||
#define _UDA134X_CODEC_H
|
||||
|
||||
#define UDA134X_L3ADDR 5
|
||||
#define UDA134X_DATA0_ADDR ((UDA134X_L3ADDR << 2) | 0)
|
||||
#define UDA134X_DATA1_ADDR ((UDA134X_L3ADDR << 2) | 1)
|
||||
#define UDA134X_STATUS_ADDR ((UDA134X_L3ADDR << 2) | 2)
|
||||
|
||||
#define UDA134X_EXTADDR_PREFIX 0xC0
|
||||
#define UDA134X_EXTDATA_PREFIX 0xE0
|
||||
|
||||
/* UDA134X registers */
|
||||
#define UDA134X_EA000 0
|
||||
#define UDA134X_EA001 1
|
||||
#define UDA134X_EA010 2
|
||||
#define UDA134X_EA011 3
|
||||
#define UDA134X_EA100 4
|
||||
#define UDA134X_EA101 5
|
||||
#define UDA134X_EA110 6
|
||||
#define UDA134X_EA111 7
|
||||
#define UDA134X_STATUS0 8
|
||||
#define UDA134X_STATUS1 9
|
||||
#define UDA134X_DATA000 10
|
||||
#define UDA134X_DATA001 11
|
||||
#define UDA134X_DATA010 12
|
||||
#define UDA134X_DATA1 13
|
||||
|
||||
#define UDA134X_REGS_NUM 14
|
||||
|
||||
#define STATUS0_DAIFMT_MASK (~(7<<1))
|
||||
#define STATUS0_SYSCLK_MASK (~(3<<4))
|
||||
|
||||
extern struct snd_soc_dai uda134x_dai;
|
||||
extern struct snd_soc_codec_device soc_codec_dev_uda134x;
|
||||
|
||||
#endif
|
|
@ -407,7 +407,8 @@ static int uda1380_set_dai_fmt(struct snd_soc_dai *codec_dai,
|
|||
* when the DAI is being clocked by the CPU DAI. It's up to the
|
||||
* machine and cpu DAI driver to do this before we are called.
|
||||
*/
|
||||
static int uda1380_pcm_prepare(struct snd_pcm_substream *substream)
|
||||
static int uda1380_pcm_prepare(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_device *socdev = rtd->socdev;
|
||||
|
@ -439,7 +440,8 @@ static int uda1380_pcm_prepare(struct snd_pcm_substream *substream)
|
|||
}
|
||||
|
||||
static int uda1380_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_device *socdev = rtd->socdev;
|
||||
|
@ -477,7 +479,8 @@ static int uda1380_pcm_hw_params(struct snd_pcm_substream *substream,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void uda1380_pcm_shutdown(struct snd_pcm_substream *substream)
|
||||
static void uda1380_pcm_shutdown(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_device *socdev = rtd->socdev;
|
||||
|
@ -560,8 +563,6 @@ struct snd_soc_dai uda1380_dai[] = {
|
|||
.hw_params = uda1380_pcm_hw_params,
|
||||
.shutdown = uda1380_pcm_shutdown,
|
||||
.prepare = uda1380_pcm_prepare,
|
||||
},
|
||||
.dai_ops = {
|
||||
.digital_mute = uda1380_mute,
|
||||
.set_fmt = uda1380_set_dai_fmt,
|
||||
},
|
||||
|
@ -579,8 +580,6 @@ struct snd_soc_dai uda1380_dai[] = {
|
|||
.hw_params = uda1380_pcm_hw_params,
|
||||
.shutdown = uda1380_pcm_shutdown,
|
||||
.prepare = uda1380_pcm_prepare,
|
||||
},
|
||||
.dai_ops = {
|
||||
.digital_mute = uda1380_mute,
|
||||
.set_fmt = uda1380_set_dai_fmt,
|
||||
},
|
||||
|
@ -598,8 +597,6 @@ struct snd_soc_dai uda1380_dai[] = {
|
|||
.hw_params = uda1380_pcm_hw_params,
|
||||
.shutdown = uda1380_pcm_shutdown,
|
||||
.prepare = uda1380_pcm_prepare,
|
||||
},
|
||||
.dai_ops = {
|
||||
.set_fmt = uda1380_set_dai_fmt,
|
||||
},
|
||||
},
|
||||
|
@ -680,7 +677,7 @@ static int uda1380_init(struct snd_soc_device *socdev, int dac_clk)
|
|||
/* uda1380 init */
|
||||
uda1380_add_controls(codec);
|
||||
uda1380_add_widgets(codec);
|
||||
ret = snd_soc_register_card(socdev);
|
||||
ret = snd_soc_init_card(socdev);
|
||||
if (ret < 0) {
|
||||
pr_err("uda1380: failed to register card\n");
|
||||
goto card_err;
|
||||
|
@ -844,6 +841,18 @@ struct snd_soc_codec_device soc_codec_dev_uda1380 = {
|
|||
};
|
||||
EXPORT_SYMBOL_GPL(soc_codec_dev_uda1380);
|
||||
|
||||
static int __init uda1380_modinit(void)
|
||||
{
|
||||
return snd_soc_register_dais(uda1380_dai, ARRAY_SIZE(uda1380_dai));
|
||||
}
|
||||
module_init(uda1380_modinit);
|
||||
|
||||
static void __exit uda1380_exit(void)
|
||||
{
|
||||
snd_soc_unregister_dais(uda1380_dai, ARRAY_SIZE(uda1380_dai));
|
||||
}
|
||||
module_exit(uda1380_exit);
|
||||
|
||||
MODULE_AUTHOR("Giorgio Padrin");
|
||||
MODULE_DESCRIPTION("Audio support for codec Philips UDA1380");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
1583
sound/soc/codecs/wm8350.c
Normal file
1583
sound/soc/codecs/wm8350.c
Normal file
File diff suppressed because it is too large
Load diff
20
sound/soc/codecs/wm8350.h
Normal file
20
sound/soc/codecs/wm8350.h
Normal file
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* wm8350.h - WM8903 audio codec interface
|
||||
*
|
||||
* Copyright 2008 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.
|
||||
*/
|
||||
|
||||
#ifndef _WM8350_H
|
||||
#define _WM8350_H
|
||||
|
||||
#include <sound/soc.h>
|
||||
|
||||
extern struct snd_soc_dai wm8350_dai;
|
||||
extern struct snd_soc_codec_device soc_codec_dev_wm8350;
|
||||
|
||||
#endif
|
|
@ -463,7 +463,8 @@ static int wm8510_set_dai_fmt(struct snd_soc_dai *codec_dai,
|
|||
}
|
||||
|
||||
static int wm8510_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_device *socdev = rtd->socdev;
|
||||
|
@ -585,8 +586,6 @@ struct snd_soc_dai wm8510_dai = {
|
|||
.formats = WM8510_FORMATS,},
|
||||
.ops = {
|
||||
.hw_params = wm8510_pcm_hw_params,
|
||||
},
|
||||
.dai_ops = {
|
||||
.digital_mute = wm8510_mute,
|
||||
.set_fmt = wm8510_set_dai_fmt,
|
||||
.set_clkdiv = wm8510_set_dai_clkdiv,
|
||||
|
@ -659,7 +658,7 @@ static int wm8510_init(struct snd_soc_device *socdev)
|
|||
wm8510_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
|
||||
wm8510_add_controls(codec);
|
||||
wm8510_add_widgets(codec);
|
||||
ret = snd_soc_register_card(socdev);
|
||||
ret = snd_soc_init_card(socdev);
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR "wm8510: failed to register card\n");
|
||||
goto card_err;
|
||||
|
@ -890,6 +889,18 @@ struct snd_soc_codec_device soc_codec_dev_wm8510 = {
|
|||
};
|
||||
EXPORT_SYMBOL_GPL(soc_codec_dev_wm8510);
|
||||
|
||||
static int __init wm8510_modinit(void)
|
||||
{
|
||||
return snd_soc_register_dai(&wm8510_dai);
|
||||
}
|
||||
module_init(wm8510_modinit);
|
||||
|
||||
static void __exit wm8510_exit(void)
|
||||
{
|
||||
snd_soc_unregister_dai(&wm8510_dai);
|
||||
}
|
||||
module_exit(wm8510_exit);
|
||||
|
||||
MODULE_DESCRIPTION("ASoC WM8510 driver");
|
||||
MODULE_AUTHOR("Liam Girdwood");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
|
@ -548,13 +548,13 @@ static int wm8580_set_dai_pll(struct snd_soc_dai *codec_dai,
|
|||
* Set PCM DAI bit size and sample rate.
|
||||
*/
|
||||
static int wm8580_paif_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_dai_link *dai = rtd->dai;
|
||||
struct snd_soc_device *socdev = rtd->socdev;
|
||||
struct snd_soc_codec *codec = socdev->codec;
|
||||
u16 paifb = wm8580_read(codec, WM8580_PAIF3 + dai->codec_dai->id);
|
||||
u16 paifb = wm8580_read(codec, WM8580_PAIF3 + dai->id);
|
||||
|
||||
paifb &= ~WM8580_AIF_LENGTH_MASK;
|
||||
/* bit size */
|
||||
|
@ -574,7 +574,7 @@ static int wm8580_paif_hw_params(struct snd_pcm_substream *substream,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
wm8580_write(codec, WM8580_PAIF3 + dai->codec_dai->id, paifb);
|
||||
wm8580_write(codec, WM8580_PAIF3 + dai->id, paifb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -798,8 +798,6 @@ struct snd_soc_dai wm8580_dai[] = {
|
|||
},
|
||||
.ops = {
|
||||
.hw_params = wm8580_paif_hw_params,
|
||||
},
|
||||
.dai_ops = {
|
||||
.set_fmt = wm8580_set_paif_dai_fmt,
|
||||
.set_clkdiv = wm8580_set_dai_clkdiv,
|
||||
.set_pll = wm8580_set_dai_pll,
|
||||
|
@ -818,8 +816,6 @@ struct snd_soc_dai wm8580_dai[] = {
|
|||
},
|
||||
.ops = {
|
||||
.hw_params = wm8580_paif_hw_params,
|
||||
},
|
||||
.dai_ops = {
|
||||
.set_fmt = wm8580_set_paif_dai_fmt,
|
||||
.set_clkdiv = wm8580_set_dai_clkdiv,
|
||||
.set_pll = wm8580_set_dai_pll,
|
||||
|
@ -873,7 +869,7 @@ static int wm8580_init(struct snd_soc_device *socdev)
|
|||
wm8580_add_controls(codec);
|
||||
wm8580_add_widgets(codec);
|
||||
|
||||
ret = snd_soc_register_card(socdev);
|
||||
ret = snd_soc_init_card(socdev);
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR "wm8580: failed to register card\n");
|
||||
goto card_err;
|
||||
|
@ -900,85 +896,85 @@ static struct snd_soc_device *wm8580_socdev;
|
|||
* low = 0x1a
|
||||
* high = 0x1b
|
||||
*/
|
||||
static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END };
|
||||
|
||||
/* Magic definition of all other variables and things */
|
||||
I2C_CLIENT_INSMOD;
|
||||
|
||||
static struct i2c_driver wm8580_i2c_driver;
|
||||
static struct i2c_client client_template;
|
||||
|
||||
static int wm8580_codec_probe(struct i2c_adapter *adap, int addr, int kind)
|
||||
static int wm8580_i2c_probe(struct i2c_client *i2c,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct snd_soc_device *socdev = wm8580_socdev;
|
||||
struct wm8580_setup_data *setup = socdev->codec_data;
|
||||
struct snd_soc_codec *codec = socdev->codec;
|
||||
struct i2c_client *i2c;
|
||||
int ret;
|
||||
|
||||
if (addr != setup->i2c_address)
|
||||
return -ENODEV;
|
||||
|
||||
client_template.adapter = adap;
|
||||
client_template.addr = addr;
|
||||
|
||||
i2c = kmemdup(&client_template, sizeof(client_template), GFP_KERNEL);
|
||||
if (i2c == NULL) {
|
||||
kfree(codec);
|
||||
return -ENOMEM;
|
||||
}
|
||||
i2c_set_clientdata(i2c, codec);
|
||||
codec->control_data = i2c;
|
||||
|
||||
ret = i2c_attach_client(i2c);
|
||||
if (ret < 0) {
|
||||
dev_err(&i2c->dev, "failed to attach codec at addr %x\n", addr);
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = wm8580_init(socdev);
|
||||
if (ret < 0) {
|
||||
if (ret < 0)
|
||||
dev_err(&i2c->dev, "failed to initialise WM8580\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
err:
|
||||
kfree(codec);
|
||||
kfree(i2c);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int wm8580_i2c_detach(struct i2c_client *client)
|
||||
static int wm8580_i2c_remove(struct i2c_client *client)
|
||||
{
|
||||
struct snd_soc_codec *codec = i2c_get_clientdata(client);
|
||||
i2c_detach_client(client);
|
||||
kfree(codec->reg_cache);
|
||||
kfree(client);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wm8580_i2c_attach(struct i2c_adapter *adap)
|
||||
{
|
||||
return i2c_probe(adap, &addr_data, wm8580_codec_probe);
|
||||
}
|
||||
static const struct i2c_device_id wm8580_i2c_id[] = {
|
||||
{ "wm8580", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, wm8580_i2c_id);
|
||||
|
||||
/* corgi i2c codec control layer */
|
||||
static struct i2c_driver wm8580_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "WM8580 I2C Codec",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.attach_adapter = wm8580_i2c_attach,
|
||||
.detach_client = wm8580_i2c_detach,
|
||||
.command = NULL,
|
||||
.probe = wm8580_i2c_probe,
|
||||
.remove = wm8580_i2c_remove,
|
||||
.id_table = wm8580_i2c_id,
|
||||
};
|
||||
|
||||
static struct i2c_client client_template = {
|
||||
.name = "WM8580",
|
||||
.driver = &wm8580_i2c_driver,
|
||||
};
|
||||
static int wm8580_add_i2c_device(struct platform_device *pdev,
|
||||
const struct wm8580_setup_data *setup)
|
||||
{
|
||||
struct i2c_board_info info;
|
||||
struct i2c_adapter *adapter;
|
||||
struct i2c_client *client;
|
||||
int ret;
|
||||
|
||||
ret = i2c_add_driver(&wm8580_i2c_driver);
|
||||
if (ret != 0) {
|
||||
dev_err(&pdev->dev, "can't add i2c driver\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
memset(&info, 0, sizeof(struct i2c_board_info));
|
||||
info.addr = setup->i2c_address;
|
||||
strlcpy(info.type, "wm8580", I2C_NAME_SIZE);
|
||||
|
||||
adapter = i2c_get_adapter(setup->i2c_bus);
|
||||
if (!adapter) {
|
||||
dev_err(&pdev->dev, "can't get i2c adapter %d\n",
|
||||
setup->i2c_bus);
|
||||
goto err_driver;
|
||||
}
|
||||
|
||||
client = i2c_new_device(adapter, &info);
|
||||
i2c_put_adapter(adapter);
|
||||
if (!client) {
|
||||
dev_err(&pdev->dev, "can't add i2c device at 0x%x\n",
|
||||
(unsigned int)info.addr);
|
||||
goto err_driver;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_driver:
|
||||
i2c_del_driver(&wm8580_i2c_driver);
|
||||
return -ENODEV;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int wm8580_probe(struct platform_device *pdev)
|
||||
|
@ -1011,11 +1007,8 @@ static int wm8580_probe(struct platform_device *pdev)
|
|||
|
||||
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
|
||||
if (setup->i2c_address) {
|
||||
normal_i2c[0] = setup->i2c_address;
|
||||
codec->hw_write = (hw_write_t)i2c_master_send;
|
||||
ret = i2c_add_driver(&wm8580_i2c_driver);
|
||||
if (ret != 0)
|
||||
printk(KERN_ERR "can't add i2c driver");
|
||||
ret = wm8580_add_i2c_device(pdev, setup);
|
||||
}
|
||||
#else
|
||||
/* Add other interfaces here */
|
||||
|
@ -1034,6 +1027,7 @@ static int wm8580_remove(struct platform_device *pdev)
|
|||
snd_soc_free_pcms(socdev);
|
||||
snd_soc_dapm_free(socdev);
|
||||
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
|
||||
i2c_unregister_device(codec->control_data);
|
||||
i2c_del_driver(&wm8580_i2c_driver);
|
||||
#endif
|
||||
kfree(codec->private_data);
|
||||
|
@ -1048,6 +1042,18 @@ struct snd_soc_codec_device soc_codec_dev_wm8580 = {
|
|||
};
|
||||
EXPORT_SYMBOL_GPL(soc_codec_dev_wm8580);
|
||||
|
||||
static int __init wm8580_modinit(void)
|
||||
{
|
||||
return snd_soc_register_dais(wm8580_dai, ARRAY_SIZE(wm8580_dai));
|
||||
}
|
||||
module_init(wm8580_modinit);
|
||||
|
||||
static void __exit wm8580_exit(void)
|
||||
{
|
||||
snd_soc_unregister_dais(wm8580_dai, ARRAY_SIZE(wm8580_dai));
|
||||
}
|
||||
module_exit(wm8580_exit);
|
||||
|
||||
MODULE_DESCRIPTION("ASoC WM8580 driver");
|
||||
MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#define WM8580_CLKSRC_NONE 5
|
||||
|
||||
struct wm8580_setup_data {
|
||||
int i2c_bus;
|
||||
unsigned short i2c_address;
|
||||
};
|
||||
|
||||
|
|
585
sound/soc/codecs/wm8728.c
Normal file
585
sound/soc/codecs/wm8728.c
Normal file
|
@ -0,0 +1,585 @@
|
|||
/*
|
||||
* wm8728.c -- WM8728 ALSA SoC Audio driver
|
||||
*
|
||||
* Copyright 2008 Wolfson Microelectronics plc
|
||||
*
|
||||
* Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/soc-dapm.h>
|
||||
#include <sound/initval.h>
|
||||
#include <sound/tlv.h>
|
||||
|
||||
#include "wm8728.h"
|
||||
|
||||
struct snd_soc_codec_device soc_codec_dev_wm8728;
|
||||
|
||||
/*
|
||||
* We can't read the WM8728 register space so we cache them instead.
|
||||
* Note that the defaults here aren't the physical defaults, we latch
|
||||
* the volume update bits, mute the output and enable infinite zero
|
||||
* detect.
|
||||
*/
|
||||
static const u16 wm8728_reg_defaults[] = {
|
||||
0x1ff,
|
||||
0x1ff,
|
||||
0x001,
|
||||
0x100,
|
||||
};
|
||||
|
||||
static inline unsigned int wm8728_read_reg_cache(struct snd_soc_codec *codec,
|
||||
unsigned int reg)
|
||||
{
|
||||
u16 *cache = codec->reg_cache;
|
||||
BUG_ON(reg > ARRAY_SIZE(wm8728_reg_defaults));
|
||||
return cache[reg];
|
||||
}
|
||||
|
||||
static inline void wm8728_write_reg_cache(struct snd_soc_codec *codec,
|
||||
u16 reg, unsigned int value)
|
||||
{
|
||||
u16 *cache = codec->reg_cache;
|
||||
BUG_ON(reg > ARRAY_SIZE(wm8728_reg_defaults));
|
||||
cache[reg] = value;
|
||||
}
|
||||
|
||||
/*
|
||||
* write to the WM8728 register space
|
||||
*/
|
||||
static int wm8728_write(struct snd_soc_codec *codec, unsigned int reg,
|
||||
unsigned int value)
|
||||
{
|
||||
u8 data[2];
|
||||
|
||||
/* data is
|
||||
* D15..D9 WM8728 register offset
|
||||
* D8...D0 register data
|
||||
*/
|
||||
data[0] = (reg << 1) | ((value >> 8) & 0x0001);
|
||||
data[1] = value & 0x00ff;
|
||||
|
||||
wm8728_write_reg_cache(codec, reg, value);
|
||||
|
||||
if (codec->hw_write(codec->control_data, data, 2) == 2)
|
||||
return 0;
|
||||
else
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static const DECLARE_TLV_DB_SCALE(wm8728_tlv, -12750, 50, 1);
|
||||
|
||||
static const struct snd_kcontrol_new wm8728_snd_controls[] = {
|
||||
|
||||
SOC_DOUBLE_R_TLV("Digital Playback Volume", WM8728_DACLVOL, WM8728_DACRVOL,
|
||||
0, 255, 0, wm8728_tlv),
|
||||
|
||||
SOC_SINGLE("Deemphasis", WM8728_DACCTL, 1, 1, 0),
|
||||
};
|
||||
|
||||
static int wm8728_add_controls(struct snd_soc_codec *codec)
|
||||
{
|
||||
int err, i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(wm8728_snd_controls); i++) {
|
||||
err = snd_ctl_add(codec->card,
|
||||
snd_soc_cnew(&wm8728_snd_controls[i],
|
||||
codec, NULL));
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* DAPM controls.
|
||||
*/
|
||||
static const struct snd_soc_dapm_widget wm8728_dapm_widgets[] = {
|
||||
SND_SOC_DAPM_DAC("DAC", "HiFi Playback", SND_SOC_NOPM, 0, 0),
|
||||
SND_SOC_DAPM_OUTPUT("VOUTL"),
|
||||
SND_SOC_DAPM_OUTPUT("VOUTR"),
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route intercon[] = {
|
||||
{"VOUTL", NULL, "DAC"},
|
||||
{"VOUTR", NULL, "DAC"},
|
||||
};
|
||||
|
||||
static int wm8728_add_widgets(struct snd_soc_codec *codec)
|
||||
{
|
||||
snd_soc_dapm_new_controls(codec, wm8728_dapm_widgets,
|
||||
ARRAY_SIZE(wm8728_dapm_widgets));
|
||||
|
||||
snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon));
|
||||
|
||||
snd_soc_dapm_new_widgets(codec);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wm8728_mute(struct snd_soc_dai *dai, int mute)
|
||||
{
|
||||
struct snd_soc_codec *codec = dai->codec;
|
||||
u16 mute_reg = wm8728_read_reg_cache(codec, WM8728_DACCTL);
|
||||
|
||||
if (mute)
|
||||
wm8728_write(codec, WM8728_DACCTL, mute_reg | 1);
|
||||
else
|
||||
wm8728_write(codec, WM8728_DACCTL, mute_reg & ~1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wm8728_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_device *socdev = rtd->socdev;
|
||||
struct snd_soc_codec *codec = socdev->codec;
|
||||
u16 dac = wm8728_read_reg_cache(codec, WM8728_DACCTL);
|
||||
|
||||
dac &= ~0x18;
|
||||
|
||||
switch (params_format(params)) {
|
||||
case SNDRV_PCM_FORMAT_S16_LE:
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S20_3LE:
|
||||
dac |= 0x10;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S24_LE:
|
||||
dac |= 0x08;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
wm8728_write(codec, WM8728_DACCTL, dac);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wm8728_set_dai_fmt(struct snd_soc_dai *codec_dai,
|
||||
unsigned int fmt)
|
||||
{
|
||||
struct snd_soc_codec *codec = codec_dai->codec;
|
||||
u16 iface = wm8728_read_reg_cache(codec, WM8728_IFCTL);
|
||||
|
||||
/* Currently only I2S is supported by the driver, though the
|
||||
* hardware is more flexible.
|
||||
*/
|
||||
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
|
||||
case SND_SOC_DAIFMT_I2S:
|
||||
iface |= 1;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* The hardware only support full slave mode */
|
||||
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
|
||||
case SND_SOC_DAIFMT_CBS_CFS:
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
|
||||
case SND_SOC_DAIFMT_NB_NF:
|
||||
iface &= ~0x22;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_IB_NF:
|
||||
iface |= 0x20;
|
||||
iface &= ~0x02;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_NB_IF:
|
||||
iface |= 0x02;
|
||||
iface &= ~0x20;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_IB_IF:
|
||||
iface |= 0x22;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
wm8728_write(codec, WM8728_IFCTL, iface);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wm8728_set_bias_level(struct snd_soc_codec *codec,
|
||||
enum snd_soc_bias_level level)
|
||||
{
|
||||
u16 reg;
|
||||
int i;
|
||||
|
||||
switch (level) {
|
||||
case SND_SOC_BIAS_ON:
|
||||
case SND_SOC_BIAS_PREPARE:
|
||||
case SND_SOC_BIAS_STANDBY:
|
||||
if (codec->bias_level == SND_SOC_BIAS_OFF) {
|
||||
/* Power everything up... */
|
||||
reg = wm8728_read_reg_cache(codec, WM8728_DACCTL);
|
||||
wm8728_write(codec, WM8728_DACCTL, reg & ~0x4);
|
||||
|
||||
/* ..then sync in the register cache. */
|
||||
for (i = 0; i < ARRAY_SIZE(wm8728_reg_defaults); i++)
|
||||
wm8728_write(codec, i,
|
||||
wm8728_read_reg_cache(codec, i));
|
||||
}
|
||||
break;
|
||||
|
||||
case SND_SOC_BIAS_OFF:
|
||||
reg = wm8728_read_reg_cache(codec, WM8728_DACCTL);
|
||||
wm8728_write(codec, WM8728_DACCTL, reg | 0x4);
|
||||
break;
|
||||
}
|
||||
codec->bias_level = level;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define WM8728_RATES (SNDRV_PCM_RATE_8000_192000)
|
||||
|
||||
#define WM8728_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
|
||||
SNDRV_PCM_FMTBIT_S24_LE)
|
||||
|
||||
struct snd_soc_dai wm8728_dai = {
|
||||
.name = "WM8728",
|
||||
.playback = {
|
||||
.stream_name = "Playback",
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.rates = WM8728_RATES,
|
||||
.formats = WM8728_FORMATS,
|
||||
},
|
||||
.ops = {
|
||||
.hw_params = wm8728_hw_params,
|
||||
.digital_mute = wm8728_mute,
|
||||
.set_fmt = wm8728_set_dai_fmt,
|
||||
}
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(wm8728_dai);
|
||||
|
||||
static int wm8728_suspend(struct platform_device *pdev, pm_message_t state)
|
||||
{
|
||||
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
|
||||
struct snd_soc_codec *codec = socdev->codec;
|
||||
|
||||
wm8728_set_bias_level(codec, SND_SOC_BIAS_OFF);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wm8728_resume(struct platform_device *pdev)
|
||||
{
|
||||
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
|
||||
struct snd_soc_codec *codec = socdev->codec;
|
||||
|
||||
wm8728_set_bias_level(codec, codec->suspend_bias_level);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* initialise the WM8728 driver
|
||||
* register the mixer and dsp interfaces with the kernel
|
||||
*/
|
||||
static int wm8728_init(struct snd_soc_device *socdev)
|
||||
{
|
||||
struct snd_soc_codec *codec = socdev->codec;
|
||||
int ret = 0;
|
||||
|
||||
codec->name = "WM8728";
|
||||
codec->owner = THIS_MODULE;
|
||||
codec->read = wm8728_read_reg_cache;
|
||||
codec->write = wm8728_write;
|
||||
codec->set_bias_level = wm8728_set_bias_level;
|
||||
codec->dai = &wm8728_dai;
|
||||
codec->num_dai = 1;
|
||||
codec->bias_level = SND_SOC_BIAS_OFF;
|
||||
codec->reg_cache_size = ARRAY_SIZE(wm8728_reg_defaults);
|
||||
codec->reg_cache = kmemdup(wm8728_reg_defaults,
|
||||
sizeof(wm8728_reg_defaults),
|
||||
GFP_KERNEL);
|
||||
if (codec->reg_cache == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
/* register pcms */
|
||||
ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR "wm8728: failed to create pcms\n");
|
||||
goto pcm_err;
|
||||
}
|
||||
|
||||
/* power on device */
|
||||
wm8728_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
|
||||
|
||||
wm8728_add_controls(codec);
|
||||
wm8728_add_widgets(codec);
|
||||
ret = snd_soc_init_card(socdev);
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR "wm8728: failed to register card\n");
|
||||
goto card_err;
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
card_err:
|
||||
snd_soc_free_pcms(socdev);
|
||||
snd_soc_dapm_free(socdev);
|
||||
pcm_err:
|
||||
kfree(codec->reg_cache);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct snd_soc_device *wm8728_socdev;
|
||||
|
||||
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
|
||||
|
||||
/*
|
||||
* WM8728 2 wire address is determined by GPIO5
|
||||
* state during powerup.
|
||||
* low = 0x1a
|
||||
* high = 0x1b
|
||||
*/
|
||||
|
||||
static int wm8728_i2c_probe(struct i2c_client *i2c,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct snd_soc_device *socdev = wm8728_socdev;
|
||||
struct snd_soc_codec *codec = socdev->codec;
|
||||
int ret;
|
||||
|
||||
i2c_set_clientdata(i2c, codec);
|
||||
codec->control_data = i2c;
|
||||
|
||||
ret = wm8728_init(socdev);
|
||||
if (ret < 0)
|
||||
pr_err("failed to initialise WM8728\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int wm8728_i2c_remove(struct i2c_client *client)
|
||||
{
|
||||
struct snd_soc_codec *codec = i2c_get_clientdata(client);
|
||||
kfree(codec->reg_cache);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id wm8728_i2c_id[] = {
|
||||
{ "wm8728", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, wm8728_i2c_id);
|
||||
|
||||
static struct i2c_driver wm8728_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "WM8728 I2C Codec",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = wm8728_i2c_probe,
|
||||
.remove = wm8728_i2c_remove,
|
||||
.id_table = wm8728_i2c_id,
|
||||
};
|
||||
|
||||
static int wm8728_add_i2c_device(struct platform_device *pdev,
|
||||
const struct wm8728_setup_data *setup)
|
||||
{
|
||||
struct i2c_board_info info;
|
||||
struct i2c_adapter *adapter;
|
||||
struct i2c_client *client;
|
||||
int ret;
|
||||
|
||||
ret = i2c_add_driver(&wm8728_i2c_driver);
|
||||
if (ret != 0) {
|
||||
dev_err(&pdev->dev, "can't add i2c driver\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
memset(&info, 0, sizeof(struct i2c_board_info));
|
||||
info.addr = setup->i2c_address;
|
||||
strlcpy(info.type, "wm8728", I2C_NAME_SIZE);
|
||||
|
||||
adapter = i2c_get_adapter(setup->i2c_bus);
|
||||
if (!adapter) {
|
||||
dev_err(&pdev->dev, "can't get i2c adapter %d\n",
|
||||
setup->i2c_bus);
|
||||
goto err_driver;
|
||||
}
|
||||
|
||||
client = i2c_new_device(adapter, &info);
|
||||
i2c_put_adapter(adapter);
|
||||
if (!client) {
|
||||
dev_err(&pdev->dev, "can't add i2c device at 0x%x\n",
|
||||
(unsigned int)info.addr);
|
||||
goto err_driver;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_driver:
|
||||
i2c_del_driver(&wm8728_i2c_driver);
|
||||
return -ENODEV;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_SPI_MASTER)
|
||||
static int __devinit wm8728_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
struct snd_soc_device *socdev = wm8728_socdev;
|
||||
struct snd_soc_codec *codec = socdev->codec;
|
||||
int ret;
|
||||
|
||||
codec->control_data = spi;
|
||||
|
||||
ret = wm8728_init(socdev);
|
||||
if (ret < 0)
|
||||
dev_err(&spi->dev, "failed to initialise WM8728\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devexit wm8728_spi_remove(struct spi_device *spi)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct spi_driver wm8728_spi_driver = {
|
||||
.driver = {
|
||||
.name = "wm8728",
|
||||
.bus = &spi_bus_type,
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = wm8728_spi_probe,
|
||||
.remove = __devexit_p(wm8728_spi_remove),
|
||||
};
|
||||
|
||||
static int wm8728_spi_write(struct spi_device *spi, const char *data, int len)
|
||||
{
|
||||
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;
|
||||
}
|
||||
#endif /* CONFIG_SPI_MASTER */
|
||||
|
||||
static int wm8728_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
|
||||
struct wm8728_setup_data *setup;
|
||||
struct snd_soc_codec *codec;
|
||||
int ret = 0;
|
||||
|
||||
setup = socdev->codec_data;
|
||||
codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
|
||||
if (codec == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
socdev->codec = codec;
|
||||
mutex_init(&codec->mutex);
|
||||
INIT_LIST_HEAD(&codec->dapm_widgets);
|
||||
INIT_LIST_HEAD(&codec->dapm_paths);
|
||||
|
||||
wm8728_socdev = socdev;
|
||||
ret = -ENODEV;
|
||||
|
||||
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
|
||||
if (setup->i2c_address) {
|
||||
codec->hw_write = (hw_write_t)i2c_master_send;
|
||||
ret = wm8728_add_i2c_device(pdev, setup);
|
||||
}
|
||||
#endif
|
||||
#if defined(CONFIG_SPI_MASTER)
|
||||
if (setup->spi) {
|
||||
codec->hw_write = (hw_write_t)wm8728_spi_write;
|
||||
ret = spi_register_driver(&wm8728_spi_driver);
|
||||
if (ret != 0)
|
||||
printk(KERN_ERR "can't add spi driver");
|
||||
}
|
||||
#endif
|
||||
|
||||
if (ret != 0)
|
||||
kfree(codec);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* power down chip */
|
||||
static int wm8728_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
|
||||
struct snd_soc_codec *codec = socdev->codec;
|
||||
|
||||
if (codec->control_data)
|
||||
wm8728_set_bias_level(codec, SND_SOC_BIAS_OFF);
|
||||
|
||||
snd_soc_free_pcms(socdev);
|
||||
snd_soc_dapm_free(socdev);
|
||||
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
|
||||
i2c_unregister_device(codec->control_data);
|
||||
i2c_del_driver(&wm8728_i2c_driver);
|
||||
#endif
|
||||
#if defined(CONFIG_SPI_MASTER)
|
||||
spi_unregister_driver(&wm8728_spi_driver);
|
||||
#endif
|
||||
kfree(codec);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct snd_soc_codec_device soc_codec_dev_wm8728 = {
|
||||
.probe = wm8728_probe,
|
||||
.remove = wm8728_remove,
|
||||
.suspend = wm8728_suspend,
|
||||
.resume = wm8728_resume,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(soc_codec_dev_wm8728);
|
||||
|
||||
static int __init wm8728_modinit(void)
|
||||
{
|
||||
return snd_soc_register_dai(&wm8728_dai);
|
||||
}
|
||||
module_init(wm8728_modinit);
|
||||
|
||||
static void __exit wm8728_exit(void)
|
||||
{
|
||||
snd_soc_unregister_dai(&wm8728_dai);
|
||||
}
|
||||
module_exit(wm8728_exit);
|
||||
|
||||
MODULE_DESCRIPTION("ASoC WM8728 driver");
|
||||
MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
|
||||
MODULE_LICENSE("GPL");
|
30
sound/soc/codecs/wm8728.h
Normal file
30
sound/soc/codecs/wm8728.h
Normal file
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* wm8728.h -- WM8728 ASoC codec driver
|
||||
*
|
||||
* Copyright 2008 Wolfson Microelectronics plc
|
||||
*
|
||||
* Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef _WM8728_H
|
||||
#define _WM8728_H
|
||||
|
||||
#define WM8728_DACLVOL 0x00
|
||||
#define WM8728_DACRVOL 0x01
|
||||
#define WM8728_DACCTL 0x02
|
||||
#define WM8728_IFCTL 0x03
|
||||
|
||||
struct wm8728_setup_data {
|
||||
int spi;
|
||||
int i2c_bus;
|
||||
unsigned short i2c_address;
|
||||
};
|
||||
|
||||
extern struct snd_soc_dai wm8728_dai;
|
||||
extern struct snd_soc_codec_device soc_codec_dev_wm8728;
|
||||
|
||||
#endif
|
|
@ -264,7 +264,8 @@ static inline int get_coeff(int mclk, int rate)
|
|||
}
|
||||
|
||||
static int wm8731_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_device *socdev = rtd->socdev;
|
||||
|
@ -293,7 +294,8 @@ static int wm8731_hw_params(struct snd_pcm_substream *substream,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int wm8731_pcm_prepare(struct snd_pcm_substream *substream)
|
||||
static int wm8731_pcm_prepare(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_device *socdev = rtd->socdev;
|
||||
|
@ -305,7 +307,8 @@ static int wm8731_pcm_prepare(struct snd_pcm_substream *substream)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void wm8731_shutdown(struct snd_pcm_substream *substream)
|
||||
static void wm8731_shutdown(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_device *socdev = rtd->socdev;
|
||||
|
@ -461,8 +464,6 @@ struct snd_soc_dai wm8731_dai = {
|
|||
.prepare = wm8731_pcm_prepare,
|
||||
.hw_params = wm8731_hw_params,
|
||||
.shutdown = wm8731_shutdown,
|
||||
},
|
||||
.dai_ops = {
|
||||
.digital_mute = wm8731_mute,
|
||||
.set_sysclk = wm8731_set_dai_sysclk,
|
||||
.set_fmt = wm8731_set_dai_fmt,
|
||||
|
@ -544,7 +545,7 @@ static int wm8731_init(struct snd_soc_device *socdev)
|
|||
|
||||
wm8731_add_controls(codec);
|
||||
wm8731_add_widgets(codec);
|
||||
ret = snd_soc_register_card(socdev);
|
||||
ret = snd_soc_init_card(socdev);
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR "wm8731: failed to register card\n");
|
||||
goto card_err;
|
||||
|
@ -792,6 +793,18 @@ struct snd_soc_codec_device soc_codec_dev_wm8731 = {
|
|||
};
|
||||
EXPORT_SYMBOL_GPL(soc_codec_dev_wm8731);
|
||||
|
||||
static int __init wm8731_modinit(void)
|
||||
{
|
||||
return snd_soc_register_dai(&wm8731_dai);
|
||||
}
|
||||
module_init(wm8731_modinit);
|
||||
|
||||
static void __exit wm8731_exit(void)
|
||||
{
|
||||
snd_soc_unregister_dai(&wm8731_dai);
|
||||
}
|
||||
module_exit(wm8731_exit);
|
||||
|
||||
MODULE_DESCRIPTION("ASoC WM8731 driver");
|
||||
MODULE_AUTHOR("Richard Purdie");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
|
@ -614,7 +614,8 @@ static int wm8750_set_dai_fmt(struct snd_soc_dai *codec_dai,
|
|||
}
|
||||
|
||||
static int wm8750_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_device *socdev = rtd->socdev;
|
||||
|
@ -709,8 +710,6 @@ struct snd_soc_dai wm8750_dai = {
|
|||
.formats = WM8750_FORMATS,},
|
||||
.ops = {
|
||||
.hw_params = wm8750_pcm_hw_params,
|
||||
},
|
||||
.dai_ops = {
|
||||
.digital_mute = wm8750_mute,
|
||||
.set_fmt = wm8750_set_dai_fmt,
|
||||
.set_sysclk = wm8750_set_dai_sysclk,
|
||||
|
@ -819,7 +818,7 @@ static int wm8750_init(struct snd_soc_device *socdev)
|
|||
|
||||
wm8750_add_controls(codec);
|
||||
wm8750_add_widgets(codec);
|
||||
ret = snd_soc_register_card(socdev);
|
||||
ret = snd_soc_init_card(socdev);
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR "wm8750: failed to register card\n");
|
||||
goto card_err;
|
||||
|
@ -1086,6 +1085,18 @@ struct snd_soc_codec_device soc_codec_dev_wm8750 = {
|
|||
};
|
||||
EXPORT_SYMBOL_GPL(soc_codec_dev_wm8750);
|
||||
|
||||
static int __init wm8750_modinit(void)
|
||||
{
|
||||
return snd_soc_register_dai(&wm8750_dai);
|
||||
}
|
||||
module_init(wm8750_modinit);
|
||||
|
||||
static void __exit wm8750_exit(void)
|
||||
{
|
||||
snd_soc_unregister_dai(&wm8750_dai);
|
||||
}
|
||||
module_exit(wm8750_exit);
|
||||
|
||||
MODULE_DESCRIPTION("ASoC WM8750 driver");
|
||||
MODULE_AUTHOR("Liam Girdwood");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
|
@ -922,7 +922,8 @@ static int wm8753_vdac_adc_set_dai_fmt(struct snd_soc_dai *codec_dai,
|
|||
* Set PCM DAI bit size and sample rate.
|
||||
*/
|
||||
static int wm8753_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_device *socdev = rtd->socdev;
|
||||
|
@ -1155,7 +1156,8 @@ static int wm8753_i2s_set_dai_fmt(struct snd_soc_dai *codec_dai,
|
|||
* Set PCM DAI bit size and sample rate.
|
||||
*/
|
||||
static int wm8753_i2s_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_device *socdev = rtd->socdev;
|
||||
|
@ -1323,16 +1325,15 @@ static const struct snd_soc_dai wm8753_all_dai[] = {
|
|||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.rates = WM8753_RATES,
|
||||
.formats = WM8753_FORMATS,},
|
||||
.formats = WM8753_FORMATS},
|
||||
.capture = { /* dummy for fast DAI switching */
|
||||
.stream_name = "Capture",
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.rates = WM8753_RATES,
|
||||
.formats = WM8753_FORMATS,},
|
||||
.formats = WM8753_FORMATS},
|
||||
.ops = {
|
||||
.hw_params = wm8753_i2s_hw_params,},
|
||||
.dai_ops = {
|
||||
.hw_params = wm8753_i2s_hw_params,
|
||||
.digital_mute = wm8753_mute,
|
||||
.set_fmt = wm8753_mode1h_set_dai_fmt,
|
||||
.set_clkdiv = wm8753_set_dai_clkdiv,
|
||||
|
@ -1356,8 +1357,7 @@ static const struct snd_soc_dai wm8753_all_dai[] = {
|
|||
.rates = WM8753_RATES,
|
||||
.formats = WM8753_FORMATS,},
|
||||
.ops = {
|
||||
.hw_params = wm8753_pcm_hw_params,},
|
||||
.dai_ops = {
|
||||
.hw_params = wm8753_pcm_hw_params,
|
||||
.digital_mute = wm8753_mute,
|
||||
.set_fmt = wm8753_mode1v_set_dai_fmt,
|
||||
.set_clkdiv = wm8753_set_dai_clkdiv,
|
||||
|
@ -1385,8 +1385,7 @@ static const struct snd_soc_dai wm8753_all_dai[] = {
|
|||
.rates = WM8753_RATES,
|
||||
.formats = WM8753_FORMATS,},
|
||||
.ops = {
|
||||
.hw_params = wm8753_pcm_hw_params,},
|
||||
.dai_ops = {
|
||||
.hw_params = wm8753_pcm_hw_params,
|
||||
.digital_mute = wm8753_mute,
|
||||
.set_fmt = wm8753_mode2_set_dai_fmt,
|
||||
.set_clkdiv = wm8753_set_dai_clkdiv,
|
||||
|
@ -1410,8 +1409,7 @@ static const struct snd_soc_dai wm8753_all_dai[] = {
|
|||
.rates = WM8753_RATES,
|
||||
.formats = WM8753_FORMATS,},
|
||||
.ops = {
|
||||
.hw_params = wm8753_i2s_hw_params,},
|
||||
.dai_ops = {
|
||||
.hw_params = wm8753_i2s_hw_params,
|
||||
.digital_mute = wm8753_mute,
|
||||
.set_fmt = wm8753_mode3_4_set_dai_fmt,
|
||||
.set_clkdiv = wm8753_set_dai_clkdiv,
|
||||
|
@ -1439,8 +1437,7 @@ static const struct snd_soc_dai wm8753_all_dai[] = {
|
|||
.rates = WM8753_RATES,
|
||||
.formats = WM8753_FORMATS,},
|
||||
.ops = {
|
||||
.hw_params = wm8753_i2s_hw_params,},
|
||||
.dai_ops = {
|
||||
.hw_params = wm8753_i2s_hw_params,
|
||||
.digital_mute = wm8753_mute,
|
||||
.set_fmt = wm8753_mode3_4_set_dai_fmt,
|
||||
.set_clkdiv = wm8753_set_dai_clkdiv,
|
||||
|
@ -1608,7 +1605,7 @@ static int wm8753_init(struct snd_soc_device *socdev)
|
|||
|
||||
wm8753_add_controls(codec);
|
||||
wm8753_add_widgets(codec);
|
||||
ret = snd_soc_register_card(socdev);
|
||||
ret = snd_soc_init_card(socdev);
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR "wm8753: failed to register card\n");
|
||||
goto card_err;
|
||||
|
@ -1877,6 +1874,18 @@ struct snd_soc_codec_device soc_codec_dev_wm8753 = {
|
|||
};
|
||||
EXPORT_SYMBOL_GPL(soc_codec_dev_wm8753);
|
||||
|
||||
static int __init wm8753_modinit(void)
|
||||
{
|
||||
return snd_soc_register_dais(wm8753_dai, ARRAY_SIZE(wm8753_dai));
|
||||
}
|
||||
module_init(wm8753_modinit);
|
||||
|
||||
static void __exit wm8753_exit(void)
|
||||
{
|
||||
snd_soc_unregister_dais(wm8753_dai, ARRAY_SIZE(wm8753_dai));
|
||||
}
|
||||
module_exit(wm8753_exit);
|
||||
|
||||
MODULE_DESCRIPTION("ASoC WM8753 driver");
|
||||
MODULE_AUTHOR("Liam Girdwood");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
|
@ -138,6 +138,10 @@
|
|||
struct snd_soc_codec_device soc_codec_dev_wm8900;
|
||||
|
||||
struct wm8900_priv {
|
||||
struct snd_soc_codec codec;
|
||||
|
||||
u16 reg_cache[WM8900_MAXREG];
|
||||
|
||||
u32 fll_in; /* FLL input frequency */
|
||||
u32 fll_out; /* FLL output frequency */
|
||||
};
|
||||
|
@ -727,7 +731,8 @@ static int wm8900_add_widgets(struct snd_soc_codec *codec)
|
|||
}
|
||||
|
||||
static int wm8900_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_device *socdev = rtd->socdev;
|
||||
|
@ -1117,8 +1122,6 @@ struct snd_soc_dai wm8900_dai = {
|
|||
},
|
||||
.ops = {
|
||||
.hw_params = wm8900_hw_params,
|
||||
},
|
||||
.dai_ops = {
|
||||
.set_clkdiv = wm8900_set_dai_clkdiv,
|
||||
.set_pll = wm8900_set_dai_pll,
|
||||
.set_fmt = wm8900_set_dai_fmt,
|
||||
|
@ -1283,16 +1286,28 @@ static int wm8900_resume(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* initialise the WM8900 driver
|
||||
* register the mixer and dsp interfaces with the kernel
|
||||
*/
|
||||
static int wm8900_init(struct snd_soc_device *socdev)
|
||||
static struct snd_soc_codec *wm8900_codec;
|
||||
|
||||
static int wm8900_i2c_probe(struct i2c_client *i2c,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct snd_soc_codec *codec = socdev->codec;
|
||||
int ret = 0;
|
||||
struct wm8900_priv *wm8900;
|
||||
struct snd_soc_codec *codec;
|
||||
unsigned int reg;
|
||||
struct i2c_client *i2c_client = socdev->codec->control_data;
|
||||
int ret;
|
||||
|
||||
wm8900 = kzalloc(sizeof(struct wm8900_priv), GFP_KERNEL);
|
||||
if (wm8900 == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
codec = &wm8900->codec;
|
||||
codec->private_data = wm8900;
|
||||
codec->reg_cache = &wm8900->reg_cache[0];
|
||||
codec->reg_cache_size = WM8900_MAXREG;
|
||||
|
||||
mutex_init(&codec->mutex);
|
||||
INIT_LIST_HEAD(&codec->dapm_widgets);
|
||||
INIT_LIST_HEAD(&codec->dapm_paths);
|
||||
|
||||
codec->name = "WM8900";
|
||||
codec->owner = THIS_MODULE;
|
||||
|
@ -1300,33 +1315,28 @@ static int wm8900_init(struct snd_soc_device *socdev)
|
|||
codec->write = wm8900_write;
|
||||
codec->dai = &wm8900_dai;
|
||||
codec->num_dai = 1;
|
||||
codec->reg_cache_size = WM8900_MAXREG;
|
||||
codec->reg_cache = kmemdup(wm8900_reg_defaults,
|
||||
sizeof(wm8900_reg_defaults), GFP_KERNEL);
|
||||
|
||||
if (codec->reg_cache == NULL)
|
||||
return -ENOMEM;
|
||||
codec->hw_write = (hw_write_t)i2c_master_send;
|
||||
codec->control_data = i2c;
|
||||
codec->set_bias_level = wm8900_set_bias_level;
|
||||
codec->dev = &i2c->dev;
|
||||
|
||||
reg = wm8900_read(codec, WM8900_REG_ID);
|
||||
if (reg != 0x8900) {
|
||||
dev_err(&i2c_client->dev, "Device is not a WM8900 - ID %x\n",
|
||||
reg);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
codec->private_data = kzalloc(sizeof(struct wm8900_priv), GFP_KERNEL);
|
||||
if (codec->private_data == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto priv_err;
|
||||
dev_err(&i2c->dev, "Device is not a WM8900 - ID %x\n", reg);
|
||||
ret = -ENODEV;
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Read back from the chip */
|
||||
reg = wm8900_chip_read(codec, WM8900_REG_POWER1);
|
||||
reg = (reg >> 12) & 0xf;
|
||||
dev_info(&i2c_client->dev, "WM8900 revision %d\n", reg);
|
||||
dev_info(&i2c->dev, "WM8900 revision %d\n", reg);
|
||||
|
||||
wm8900_reset(codec);
|
||||
|
||||
/* Turn the chip on */
|
||||
wm8900_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
|
||||
|
||||
/* Latch the volume update bits */
|
||||
wm8900_write(codec, WM8900_REG_LINVOL,
|
||||
wm8900_read(codec, WM8900_REG_LINVOL) | 0x100);
|
||||
|
@ -1352,160 +1362,98 @@ static int wm8900_init(struct snd_soc_device *socdev)
|
|||
/* Set the DAC and mixer output bias */
|
||||
wm8900_write(codec, WM8900_REG_OUTBIASCTL, 0x81);
|
||||
|
||||
wm8900_dai.dev = &i2c->dev;
|
||||
|
||||
wm8900_codec = codec;
|
||||
|
||||
ret = snd_soc_register_codec(codec);
|
||||
if (ret != 0) {
|
||||
dev_err(&i2c->dev, "Failed to register codec: %d\n", ret);
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = snd_soc_register_dai(&wm8900_dai);
|
||||
if (ret != 0) {
|
||||
dev_err(&i2c->dev, "Failed to register DAI: %d\n", ret);
|
||||
goto err_codec;
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
err_codec:
|
||||
snd_soc_unregister_codec(codec);
|
||||
err:
|
||||
kfree(wm8900);
|
||||
wm8900_codec = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int wm8900_i2c_remove(struct i2c_client *client)
|
||||
{
|
||||
snd_soc_unregister_dai(&wm8900_dai);
|
||||
snd_soc_unregister_codec(wm8900_codec);
|
||||
|
||||
wm8900_set_bias_level(wm8900_codec, SND_SOC_BIAS_OFF);
|
||||
|
||||
wm8900_dai.dev = NULL;
|
||||
kfree(wm8900_codec->private_data);
|
||||
wm8900_codec = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id wm8900_i2c_id[] = {
|
||||
{ "wm8900", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, wm8900_i2c_id);
|
||||
|
||||
static struct i2c_driver wm8900_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "WM8900",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = wm8900_i2c_probe,
|
||||
.remove = wm8900_i2c_remove,
|
||||
.id_table = wm8900_i2c_id,
|
||||
};
|
||||
|
||||
static int wm8900_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
|
||||
struct snd_soc_codec *codec;
|
||||
int ret = 0;
|
||||
|
||||
if (!wm8900_codec) {
|
||||
dev_err(&pdev->dev, "I2C client not yet instantiated\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
codec = wm8900_codec;
|
||||
socdev->codec = codec;
|
||||
|
||||
/* Register pcms */
|
||||
ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
|
||||
if (ret < 0) {
|
||||
dev_err(&i2c_client->dev, "Failed to register new PCMs\n");
|
||||
dev_err(&pdev->dev, "Failed to register new PCMs\n");
|
||||
goto pcm_err;
|
||||
}
|
||||
|
||||
/* Turn the chip on */
|
||||
codec->bias_level = SND_SOC_BIAS_OFF;
|
||||
wm8900_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
|
||||
|
||||
wm8900_add_controls(codec);
|
||||
wm8900_add_widgets(codec);
|
||||
|
||||
ret = snd_soc_register_card(socdev);
|
||||
ret = snd_soc_init_card(socdev);
|
||||
if (ret < 0) {
|
||||
dev_err(&i2c_client->dev, "Failed to register card\n");
|
||||
dev_err(&pdev->dev, "Failed to register card\n");
|
||||
goto card_err;
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
card_err:
|
||||
snd_soc_free_pcms(socdev);
|
||||
snd_soc_dapm_free(socdev);
|
||||
pcm_err:
|
||||
kfree(codec->reg_cache);
|
||||
priv_err:
|
||||
kfree(codec->private_data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct snd_soc_device *wm8900_socdev;
|
||||
|
||||
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
|
||||
|
||||
static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END };
|
||||
|
||||
/* Magic definition of all other variables and things */
|
||||
I2C_CLIENT_INSMOD;
|
||||
|
||||
static struct i2c_driver wm8900_i2c_driver;
|
||||
static struct i2c_client client_template;
|
||||
|
||||
/* If the i2c layer weren't so broken, we could pass this kind of data
|
||||
around */
|
||||
static int wm8900_codec_probe(struct i2c_adapter *adap, int addr, int kind)
|
||||
{
|
||||
struct snd_soc_device *socdev = wm8900_socdev;
|
||||
struct wm8900_setup_data *setup = socdev->codec_data;
|
||||
struct snd_soc_codec *codec = socdev->codec;
|
||||
struct i2c_client *i2c;
|
||||
int ret;
|
||||
|
||||
if (addr != setup->i2c_address)
|
||||
return -ENODEV;
|
||||
|
||||
dev_err(&adap->dev, "Probe on %x\n", addr);
|
||||
|
||||
client_template.adapter = adap;
|
||||
client_template.addr = addr;
|
||||
|
||||
i2c = kmemdup(&client_template, sizeof(client_template), GFP_KERNEL);
|
||||
if (i2c == NULL) {
|
||||
kfree(codec);
|
||||
return -ENOMEM;
|
||||
}
|
||||
i2c_set_clientdata(i2c, codec);
|
||||
codec->control_data = i2c;
|
||||
|
||||
ret = i2c_attach_client(i2c);
|
||||
if (ret < 0) {
|
||||
dev_err(&adap->dev,
|
||||
"failed to attach codec at addr %x\n", addr);
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = wm8900_init(socdev);
|
||||
if (ret < 0) {
|
||||
dev_err(&adap->dev, "failed to initialise WM8900\n");
|
||||
goto err;
|
||||
}
|
||||
return ret;
|
||||
|
||||
err:
|
||||
kfree(codec);
|
||||
kfree(i2c);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int wm8900_i2c_detach(struct i2c_client *client)
|
||||
{
|
||||
struct snd_soc_codec *codec = i2c_get_clientdata(client);
|
||||
i2c_detach_client(client);
|
||||
kfree(codec->reg_cache);
|
||||
kfree(client);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wm8900_i2c_attach(struct i2c_adapter *adap)
|
||||
{
|
||||
return i2c_probe(adap, &addr_data, wm8900_codec_probe);
|
||||
}
|
||||
|
||||
/* corgi i2c codec control layer */
|
||||
static struct i2c_driver wm8900_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "WM8900 I2C codec",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.attach_adapter = wm8900_i2c_attach,
|
||||
.detach_client = wm8900_i2c_detach,
|
||||
.command = NULL,
|
||||
};
|
||||
|
||||
static struct i2c_client client_template = {
|
||||
.name = "WM8900",
|
||||
.driver = &wm8900_i2c_driver,
|
||||
};
|
||||
#endif
|
||||
|
||||
static int wm8900_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
|
||||
struct wm8900_setup_data *setup;
|
||||
struct snd_soc_codec *codec;
|
||||
int ret = 0;
|
||||
|
||||
dev_info(&pdev->dev, "WM8900 Audio Codec\n");
|
||||
|
||||
setup = socdev->codec_data;
|
||||
codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
|
||||
if (codec == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
mutex_init(&codec->mutex);
|
||||
INIT_LIST_HEAD(&codec->dapm_widgets);
|
||||
INIT_LIST_HEAD(&codec->dapm_paths);
|
||||
|
||||
socdev->codec = codec;
|
||||
|
||||
codec->set_bias_level = wm8900_set_bias_level;
|
||||
|
||||
wm8900_socdev = socdev;
|
||||
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
|
||||
if (setup->i2c_address) {
|
||||
normal_i2c[0] = setup->i2c_address;
|
||||
codec->hw_write = (hw_write_t)i2c_master_send;
|
||||
ret = i2c_add_driver(&wm8900_i2c_driver);
|
||||
if (ret != 0)
|
||||
printk(KERN_ERR "can't add i2c driver");
|
||||
}
|
||||
#else
|
||||
#error Non-I2C interfaces not yet supported
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -1513,17 +1461,9 @@ static int wm8900_probe(struct platform_device *pdev)
|
|||
static int wm8900_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
|
||||
struct snd_soc_codec *codec = socdev->codec;
|
||||
|
||||
if (codec->control_data)
|
||||
wm8900_set_bias_level(codec, SND_SOC_BIAS_OFF);
|
||||
|
||||
snd_soc_free_pcms(socdev);
|
||||
snd_soc_dapm_free(socdev);
|
||||
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
|
||||
i2c_del_driver(&wm8900_i2c_driver);
|
||||
#endif
|
||||
kfree(codec);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1536,6 +1476,18 @@ struct snd_soc_codec_device soc_codec_dev_wm8900 = {
|
|||
};
|
||||
EXPORT_SYMBOL_GPL(soc_codec_dev_wm8900);
|
||||
|
||||
static int __init wm8900_modinit(void)
|
||||
{
|
||||
return i2c_add_driver(&wm8900_i2c_driver);
|
||||
}
|
||||
module_init(wm8900_modinit);
|
||||
|
||||
static void __exit wm8900_exit(void)
|
||||
{
|
||||
i2c_del_driver(&wm8900_i2c_driver);
|
||||
}
|
||||
module_exit(wm8900_exit);
|
||||
|
||||
MODULE_DESCRIPTION("ASoC WM8900 driver");
|
||||
MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfonmicro.com>");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
|
@ -52,12 +52,6 @@
|
|||
#define WM8900_DAC_CLKDIV_5_5 0x14
|
||||
#define WM8900_DAC_CLKDIV_6 0x18
|
||||
|
||||
#define WM8900_
|
||||
|
||||
struct wm8900_setup_data {
|
||||
unsigned short i2c_address;
|
||||
};
|
||||
|
||||
extern struct snd_soc_dai wm8900_dai;
|
||||
extern struct snd_soc_codec_device soc_codec_dev_wm8900;
|
||||
|
||||
|
|
|
@ -33,19 +33,6 @@
|
|||
|
||||
#include "wm8903.h"
|
||||
|
||||
struct wm8903_priv {
|
||||
int sysclk;
|
||||
|
||||
/* Reference counts */
|
||||
int charge_pump_users;
|
||||
int class_w_users;
|
||||
int playback_active;
|
||||
int capture_active;
|
||||
|
||||
struct snd_pcm_substream *master_substream;
|
||||
struct snd_pcm_substream *slave_substream;
|
||||
};
|
||||
|
||||
/* Register defaults at reset */
|
||||
static u16 wm8903_reg_defaults[] = {
|
||||
0x8903, /* R0 - SW Reset and ID */
|
||||
|
@ -223,6 +210,23 @@ static u16 wm8903_reg_defaults[] = {
|
|||
0x0000, /* R172 - Analogue Output Bias 0 */
|
||||
};
|
||||
|
||||
struct wm8903_priv {
|
||||
struct snd_soc_codec codec;
|
||||
u16 reg_cache[ARRAY_SIZE(wm8903_reg_defaults)];
|
||||
|
||||
int sysclk;
|
||||
|
||||
/* Reference counts */
|
||||
int charge_pump_users;
|
||||
int class_w_users;
|
||||
int playback_active;
|
||||
int capture_active;
|
||||
|
||||
struct snd_pcm_substream *master_substream;
|
||||
struct snd_pcm_substream *slave_substream;
|
||||
};
|
||||
|
||||
|
||||
static unsigned int wm8903_read_reg_cache(struct snd_soc_codec *codec,
|
||||
unsigned int reg)
|
||||
{
|
||||
|
@ -360,6 +364,8 @@ static void wm8903_sync_reg_cache(struct snd_soc_codec *codec, u16 *cache)
|
|||
static void wm8903_reset(struct snd_soc_codec *codec)
|
||||
{
|
||||
wm8903_write(codec, WM8903_SW_RESET_AND_ID, 0);
|
||||
memcpy(codec->reg_cache, wm8903_reg_defaults,
|
||||
sizeof(wm8903_reg_defaults));
|
||||
}
|
||||
|
||||
#define WM8903_OUTPUT_SHORT 0x8
|
||||
|
@ -392,6 +398,7 @@ static int wm8903_output_event(struct snd_soc_dapm_widget *w,
|
|||
break;
|
||||
default:
|
||||
BUG();
|
||||
return -EINVAL; /* Spurious warning from some compilers */
|
||||
}
|
||||
|
||||
switch (w->shift) {
|
||||
|
@ -403,6 +410,7 @@ static int wm8903_output_event(struct snd_soc_dapm_widget *w,
|
|||
break;
|
||||
default:
|
||||
BUG();
|
||||
return -EINVAL; /* Spurious warning from some compilers */
|
||||
}
|
||||
|
||||
if (event & SND_SOC_DAPM_PRE_PMU) {
|
||||
|
@ -773,14 +781,14 @@ static const struct snd_kcontrol_new left_output_mixer[] = {
|
|||
SOC_DAPM_SINGLE("DACL Switch", WM8903_ANALOGUE_LEFT_MIX_0, 3, 1, 0),
|
||||
SOC_DAPM_SINGLE("DACR Switch", WM8903_ANALOGUE_LEFT_MIX_0, 2, 1, 0),
|
||||
SOC_DAPM_SINGLE_W("Left Bypass Switch", WM8903_ANALOGUE_LEFT_MIX_0, 1, 1, 0),
|
||||
SOC_DAPM_SINGLE_W("Right Bypass Switch", WM8903_ANALOGUE_LEFT_MIX_0, 1, 1, 0),
|
||||
SOC_DAPM_SINGLE_W("Right Bypass Switch", WM8903_ANALOGUE_LEFT_MIX_0, 0, 1, 0),
|
||||
};
|
||||
|
||||
static const struct snd_kcontrol_new right_output_mixer[] = {
|
||||
SOC_DAPM_SINGLE("DACL Switch", WM8903_ANALOGUE_RIGHT_MIX_0, 3, 1, 0),
|
||||
SOC_DAPM_SINGLE("DACR Switch", WM8903_ANALOGUE_RIGHT_MIX_0, 2, 1, 0),
|
||||
SOC_DAPM_SINGLE_W("Left Bypass Switch", WM8903_ANALOGUE_RIGHT_MIX_0, 1, 1, 0),
|
||||
SOC_DAPM_SINGLE_W("Right Bypass Switch", WM8903_ANALOGUE_RIGHT_MIX_0, 1, 1, 0),
|
||||
SOC_DAPM_SINGLE_W("Right Bypass Switch", WM8903_ANALOGUE_RIGHT_MIX_0, 0, 1, 0),
|
||||
};
|
||||
|
||||
static const struct snd_kcontrol_new left_speaker_mixer[] = {
|
||||
|
@ -788,7 +796,7 @@ SOC_DAPM_SINGLE("DACL Switch", WM8903_ANALOGUE_SPK_MIX_LEFT_0, 3, 1, 0),
|
|||
SOC_DAPM_SINGLE("DACR Switch", WM8903_ANALOGUE_SPK_MIX_LEFT_0, 2, 1, 0),
|
||||
SOC_DAPM_SINGLE("Left Bypass Switch", WM8903_ANALOGUE_SPK_MIX_LEFT_0, 1, 1, 0),
|
||||
SOC_DAPM_SINGLE("Right Bypass Switch", WM8903_ANALOGUE_SPK_MIX_LEFT_0,
|
||||
1, 1, 0),
|
||||
0, 1, 0),
|
||||
};
|
||||
|
||||
static const struct snd_kcontrol_new right_speaker_mixer[] = {
|
||||
|
@ -797,7 +805,7 @@ SOC_DAPM_SINGLE("DACR Switch", WM8903_ANALOGUE_SPK_MIX_RIGHT_0, 2, 1, 0),
|
|||
SOC_DAPM_SINGLE("Left Bypass Switch", WM8903_ANALOGUE_SPK_MIX_RIGHT_0,
|
||||
1, 1, 0),
|
||||
SOC_DAPM_SINGLE("Right Bypass Switch", WM8903_ANALOGUE_SPK_MIX_RIGHT_0,
|
||||
1, 1, 0),
|
||||
0, 1, 0),
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_widget wm8903_dapm_widgets[] = {
|
||||
|
@ -989,6 +997,9 @@ static int wm8903_set_bias_level(struct snd_soc_codec *codec,
|
|||
|
||||
case SND_SOC_BIAS_STANDBY:
|
||||
if (codec->bias_level == SND_SOC_BIAS_OFF) {
|
||||
wm8903_write(codec, WM8903_CLOCK_RATES_2,
|
||||
WM8903_CLK_SYS_ENA);
|
||||
|
||||
wm8903_run_sequence(codec, 0);
|
||||
wm8903_sync_reg_cache(codec, codec->reg_cache);
|
||||
|
||||
|
@ -1019,6 +1030,9 @@ static int wm8903_set_bias_level(struct snd_soc_codec *codec,
|
|||
|
||||
case SND_SOC_BIAS_OFF:
|
||||
wm8903_run_sequence(codec, 32);
|
||||
reg = wm8903_read(codec, WM8903_CLOCK_RATES_2);
|
||||
reg &= ~WM8903_CLK_SYS_ENA;
|
||||
wm8903_write(codec, WM8903_CLOCK_RATES_2, reg);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -1257,7 +1271,8 @@ static struct {
|
|||
{ 0, 0 },
|
||||
};
|
||||
|
||||
static int wm8903_startup(struct snd_pcm_substream *substream)
|
||||
static int wm8903_startup(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_device *socdev = rtd->socdev;
|
||||
|
@ -1298,7 +1313,8 @@ static int wm8903_startup(struct snd_pcm_substream *substream)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void wm8903_shutdown(struct snd_pcm_substream *substream)
|
||||
static void wm8903_shutdown(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_device *socdev = rtd->socdev;
|
||||
|
@ -1317,7 +1333,8 @@ static void wm8903_shutdown(struct snd_pcm_substream *substream)
|
|||
}
|
||||
|
||||
static int wm8903_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_device *socdev = rtd->socdev;
|
||||
|
@ -1515,8 +1532,6 @@ struct snd_soc_dai wm8903_dai = {
|
|||
.startup = wm8903_startup,
|
||||
.shutdown = wm8903_shutdown,
|
||||
.hw_params = wm8903_hw_params,
|
||||
},
|
||||
.dai_ops = {
|
||||
.digital_mute = wm8903_digital_mute,
|
||||
.set_fmt = wm8903_set_dai_fmt,
|
||||
.set_sysclk = wm8903_set_dai_sysclk
|
||||
|
@ -1560,17 +1575,43 @@ static int wm8903_resume(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* initialise the WM8903 driver
|
||||
* register the mixer and dsp interfaces with the kernel
|
||||
*/
|
||||
static int wm8903_init(struct snd_soc_device *socdev)
|
||||
static struct snd_soc_codec *wm8903_codec;
|
||||
|
||||
static int wm8903_i2c_probe(struct i2c_client *i2c,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct snd_soc_codec *codec = socdev->codec;
|
||||
struct i2c_client *i2c = codec->control_data;
|
||||
int ret = 0;
|
||||
struct wm8903_priv *wm8903;
|
||||
struct snd_soc_codec *codec;
|
||||
int ret;
|
||||
u16 val;
|
||||
|
||||
wm8903 = kzalloc(sizeof(struct wm8903_priv), GFP_KERNEL);
|
||||
if (wm8903 == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
codec = &wm8903->codec;
|
||||
|
||||
mutex_init(&codec->mutex);
|
||||
INIT_LIST_HEAD(&codec->dapm_widgets);
|
||||
INIT_LIST_HEAD(&codec->dapm_paths);
|
||||
|
||||
codec->dev = &i2c->dev;
|
||||
codec->name = "WM8903";
|
||||
codec->owner = THIS_MODULE;
|
||||
codec->read = wm8903_read;
|
||||
codec->write = wm8903_write;
|
||||
codec->hw_write = (hw_write_t)i2c_master_send;
|
||||
codec->bias_level = SND_SOC_BIAS_OFF;
|
||||
codec->set_bias_level = wm8903_set_bias_level;
|
||||
codec->dai = &wm8903_dai;
|
||||
codec->num_dai = 1;
|
||||
codec->reg_cache_size = ARRAY_SIZE(wm8903->reg_cache);
|
||||
codec->reg_cache = &wm8903->reg_cache[0];
|
||||
codec->private_data = wm8903;
|
||||
|
||||
i2c_set_clientdata(i2c, codec);
|
||||
codec->control_data = i2c;
|
||||
|
||||
val = wm8903_hw_read(codec, WM8903_SW_RESET_AND_ID);
|
||||
if (val != wm8903_reg_defaults[WM8903_SW_RESET_AND_ID]) {
|
||||
dev_err(&i2c->dev,
|
||||
|
@ -1578,39 +1619,12 @@ static int wm8903_init(struct snd_soc_device *socdev)
|
|||
return -ENODEV;
|
||||
}
|
||||
|
||||
codec->name = "WM8903";
|
||||
codec->owner = THIS_MODULE;
|
||||
codec->read = wm8903_read;
|
||||
codec->write = wm8903_write;
|
||||
codec->bias_level = SND_SOC_BIAS_OFF;
|
||||
codec->set_bias_level = wm8903_set_bias_level;
|
||||
codec->dai = &wm8903_dai;
|
||||
codec->num_dai = 1;
|
||||
codec->reg_cache_size = ARRAY_SIZE(wm8903_reg_defaults);
|
||||
codec->reg_cache = kmemdup(wm8903_reg_defaults,
|
||||
sizeof(wm8903_reg_defaults),
|
||||
GFP_KERNEL);
|
||||
if (codec->reg_cache == NULL) {
|
||||
dev_err(&i2c->dev, "Failed to allocate register cache\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
val = wm8903_read(codec, WM8903_REVISION_NUMBER);
|
||||
dev_info(&i2c->dev, "WM8903 revision %d\n",
|
||||
val & WM8903_CHIP_REV_MASK);
|
||||
|
||||
wm8903_reset(codec);
|
||||
|
||||
/* register pcms */
|
||||
ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
|
||||
if (ret < 0) {
|
||||
dev_err(&i2c->dev, "failed to create pcms\n");
|
||||
goto pcm_err;
|
||||
}
|
||||
|
||||
/* SYSCLK is required for pretty much anything */
|
||||
wm8903_write(codec, WM8903_CLOCK_RATES_2, WM8903_CLK_SYS_ENA);
|
||||
|
||||
/* power on device */
|
||||
wm8903_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
|
||||
|
||||
|
@ -1645,47 +1659,45 @@ static int wm8903_init(struct snd_soc_device *socdev)
|
|||
val |= WM8903_DAC_MUTEMODE;
|
||||
wm8903_write(codec, WM8903_DAC_DIGITAL_1, val);
|
||||
|
||||
wm8903_add_controls(codec);
|
||||
wm8903_add_widgets(codec);
|
||||
ret = snd_soc_register_card(socdev);
|
||||
if (ret < 0) {
|
||||
dev_err(&i2c->dev, "wm8903: failed to register card\n");
|
||||
goto card_err;
|
||||
wm8903_dai.dev = &i2c->dev;
|
||||
wm8903_codec = codec;
|
||||
|
||||
ret = snd_soc_register_codec(codec);
|
||||
if (ret != 0) {
|
||||
dev_err(&i2c->dev, "Failed to register codec: %d\n", ret);
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = snd_soc_register_dai(&wm8903_dai);
|
||||
if (ret != 0) {
|
||||
dev_err(&i2c->dev, "Failed to register DAI: %d\n", ret);
|
||||
goto err_codec;
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
card_err:
|
||||
snd_soc_free_pcms(socdev);
|
||||
snd_soc_dapm_free(socdev);
|
||||
pcm_err:
|
||||
kfree(codec->reg_cache);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct snd_soc_device *wm8903_socdev;
|
||||
|
||||
static int wm8903_i2c_probe(struct i2c_client *i2c,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct snd_soc_device *socdev = wm8903_socdev;
|
||||
struct snd_soc_codec *codec = socdev->codec;
|
||||
int ret;
|
||||
|
||||
i2c_set_clientdata(i2c, codec);
|
||||
codec->control_data = i2c;
|
||||
|
||||
ret = wm8903_init(socdev);
|
||||
if (ret < 0)
|
||||
dev_err(&i2c->dev, "Device initialisation failed\n");
|
||||
|
||||
err_codec:
|
||||
snd_soc_unregister_codec(codec);
|
||||
err:
|
||||
wm8903_codec = NULL;
|
||||
kfree(wm8903);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int wm8903_i2c_remove(struct i2c_client *client)
|
||||
{
|
||||
struct snd_soc_codec *codec = i2c_get_clientdata(client);
|
||||
kfree(codec->reg_cache);
|
||||
|
||||
snd_soc_unregister_dai(&wm8903_dai);
|
||||
snd_soc_unregister_codec(codec);
|
||||
|
||||
wm8903_set_bias_level(codec, SND_SOC_BIAS_OFF);
|
||||
|
||||
kfree(codec->private_data);
|
||||
|
||||
wm8903_codec = NULL;
|
||||
wm8903_dai.dev = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1709,75 +1721,37 @@ static struct i2c_driver wm8903_i2c_driver = {
|
|||
static int wm8903_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
|
||||
struct wm8903_setup_data *setup;
|
||||
struct snd_soc_codec *codec;
|
||||
struct wm8903_priv *wm8903;
|
||||
struct i2c_board_info board_info;
|
||||
struct i2c_adapter *adapter;
|
||||
struct i2c_client *i2c_client;
|
||||
int ret = 0;
|
||||
|
||||
setup = socdev->codec_data;
|
||||
|
||||
if (!setup->i2c_address) {
|
||||
dev_err(&pdev->dev, "No codec address provided\n");
|
||||
return -ENODEV;
|
||||
if (!wm8903_codec) {
|
||||
dev_err(&pdev->dev, "I2C device not yet probed\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
|
||||
if (codec == NULL)
|
||||
return -ENOMEM;
|
||||
socdev->codec = wm8903_codec;
|
||||
|
||||
wm8903 = kzalloc(sizeof(struct wm8903_priv), GFP_KERNEL);
|
||||
if (wm8903 == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto err_codec;
|
||||
/* register pcms */
|
||||
ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "failed to create pcms\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
codec->private_data = wm8903;
|
||||
socdev->codec = codec;
|
||||
mutex_init(&codec->mutex);
|
||||
INIT_LIST_HEAD(&codec->dapm_widgets);
|
||||
INIT_LIST_HEAD(&codec->dapm_paths);
|
||||
wm8903_add_controls(socdev->codec);
|
||||
wm8903_add_widgets(socdev->codec);
|
||||
|
||||
wm8903_socdev = socdev;
|
||||
|
||||
codec->hw_write = (hw_write_t)i2c_master_send;
|
||||
ret = i2c_add_driver(&wm8903_i2c_driver);
|
||||
if (ret != 0) {
|
||||
dev_err(&pdev->dev, "can't add i2c driver\n");
|
||||
goto err_priv;
|
||||
} else {
|
||||
memset(&board_info, 0, sizeof(board_info));
|
||||
strlcpy(board_info.type, "wm8903", I2C_NAME_SIZE);
|
||||
board_info.addr = setup->i2c_address;
|
||||
|
||||
adapter = i2c_get_adapter(setup->i2c_bus);
|
||||
if (!adapter) {
|
||||
dev_err(&pdev->dev, "Can't get I2C bus %d\n",
|
||||
setup->i2c_bus);
|
||||
ret = -ENODEV;
|
||||
goto err_adapter;
|
||||
}
|
||||
|
||||
i2c_client = i2c_new_device(adapter, &board_info);
|
||||
i2c_put_adapter(adapter);
|
||||
if (i2c_client == NULL) {
|
||||
dev_err(&pdev->dev,
|
||||
"I2C driver registration failed\n");
|
||||
ret = -ENODEV;
|
||||
goto err_adapter;
|
||||
}
|
||||
ret = snd_soc_init_card(socdev);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "wm8903: failed to register card\n");
|
||||
goto card_err;
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
err_adapter:
|
||||
i2c_del_driver(&wm8903_i2c_driver);
|
||||
err_priv:
|
||||
kfree(codec->private_data);
|
||||
err_codec:
|
||||
kfree(codec);
|
||||
card_err:
|
||||
snd_soc_free_pcms(socdev);
|
||||
snd_soc_dapm_free(socdev);
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -1792,10 +1766,6 @@ static int wm8903_remove(struct platform_device *pdev)
|
|||
|
||||
snd_soc_free_pcms(socdev);
|
||||
snd_soc_dapm_free(socdev);
|
||||
i2c_unregister_device(socdev->codec->control_data);
|
||||
i2c_del_driver(&wm8903_i2c_driver);
|
||||
kfree(codec->private_data);
|
||||
kfree(codec);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1808,6 +1778,18 @@ struct snd_soc_codec_device soc_codec_dev_wm8903 = {
|
|||
};
|
||||
EXPORT_SYMBOL_GPL(soc_codec_dev_wm8903);
|
||||
|
||||
static int __init wm8903_modinit(void)
|
||||
{
|
||||
return i2c_add_driver(&wm8903_i2c_driver);
|
||||
}
|
||||
module_init(wm8903_modinit);
|
||||
|
||||
static void __exit wm8903_exit(void)
|
||||
{
|
||||
i2c_del_driver(&wm8903_i2c_driver);
|
||||
}
|
||||
module_exit(wm8903_exit);
|
||||
|
||||
MODULE_DESCRIPTION("ASoC WM8903 driver");
|
||||
MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.cm>");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
|
@ -18,11 +18,6 @@
|
|||
extern struct snd_soc_dai wm8903_dai;
|
||||
extern struct snd_soc_codec_device soc_codec_dev_wm8903;
|
||||
|
||||
struct wm8903_setup_data {
|
||||
int i2c_bus;
|
||||
int i2c_address;
|
||||
};
|
||||
|
||||
#define WM8903_MCLK_DIV_2 1
|
||||
#define WM8903_CLK_SYS 2
|
||||
#define WM8903_BCLK 3
|
||||
|
|
|
@ -541,7 +541,8 @@ static int wm8971_set_dai_fmt(struct snd_soc_dai *codec_dai,
|
|||
}
|
||||
|
||||
static int wm8971_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_device *socdev = rtd->socdev;
|
||||
|
@ -634,8 +635,6 @@ struct snd_soc_dai wm8971_dai = {
|
|||
.formats = WM8971_FORMATS,},
|
||||
.ops = {
|
||||
.hw_params = wm8971_pcm_hw_params,
|
||||
},
|
||||
.dai_ops = {
|
||||
.digital_mute = wm8971_mute,
|
||||
.set_fmt = wm8971_set_dai_fmt,
|
||||
.set_sysclk = wm8971_set_dai_sysclk,
|
||||
|
@ -748,7 +747,7 @@ static int wm8971_init(struct snd_soc_device *socdev)
|
|||
|
||||
wm8971_add_controls(codec);
|
||||
wm8971_add_widgets(codec);
|
||||
ret = snd_soc_register_card(socdev);
|
||||
ret = snd_soc_init_card(socdev);
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR "wm8971: failed to register card\n");
|
||||
goto card_err;
|
||||
|
@ -936,6 +935,18 @@ struct snd_soc_codec_device soc_codec_dev_wm8971 = {
|
|||
|
||||
EXPORT_SYMBOL_GPL(soc_codec_dev_wm8971);
|
||||
|
||||
static int __init wm8971_modinit(void)
|
||||
{
|
||||
return snd_soc_register_dai(&wm8971_dai);
|
||||
}
|
||||
module_init(wm8971_modinit);
|
||||
|
||||
static void __exit wm8971_exit(void)
|
||||
{
|
||||
snd_soc_unregister_dai(&wm8971_dai);
|
||||
}
|
||||
module_exit(wm8971_exit);
|
||||
|
||||
MODULE_DESCRIPTION("ASoC WM8971 driver");
|
||||
MODULE_AUTHOR("Lab126");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
|
@ -106,6 +106,7 @@ static const u16 wm8990_reg[] = {
|
|||
0x0008, /* R60 - PLL1 */
|
||||
0x0031, /* R61 - PLL2 */
|
||||
0x0026, /* R62 - PLL3 */
|
||||
0x0000, /* R63 - Driver internal */
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -126,10 +127,9 @@ static inline void wm8990_write_reg_cache(struct snd_soc_codec *codec,
|
|||
unsigned int reg, unsigned int value)
|
||||
{
|
||||
u16 *cache = codec->reg_cache;
|
||||
BUG_ON(reg > (ARRAY_SIZE(wm8990_reg)) - 1);
|
||||
|
||||
/* Reset register is uncached */
|
||||
if (reg == 0)
|
||||
/* Reset register and reserved registers are uncached */
|
||||
if (reg == 0 || reg > ARRAY_SIZE(wm8990_reg) - 1)
|
||||
return;
|
||||
|
||||
cache[reg] = value;
|
||||
|
@ -1172,7 +1172,8 @@ static int wm8990_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
|
|||
* Set PCM DAI bit size and sample rate.
|
||||
*/
|
||||
static int wm8990_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_device *socdev = rtd->socdev;
|
||||
|
@ -1222,8 +1223,14 @@ static int wm8990_set_bias_level(struct snd_soc_codec *codec,
|
|||
switch (level) {
|
||||
case SND_SOC_BIAS_ON:
|
||||
break;
|
||||
|
||||
case SND_SOC_BIAS_PREPARE:
|
||||
/* VMID=2*50k */
|
||||
val = wm8990_read_reg_cache(codec, WM8990_POWER_MANAGEMENT_1) &
|
||||
~WM8990_VMID_MODE_MASK;
|
||||
wm8990_write(codec, WM8990_POWER_MANAGEMENT_1, val | 0x2);
|
||||
break;
|
||||
|
||||
case SND_SOC_BIAS_STANDBY:
|
||||
if (codec->bias_level == SND_SOC_BIAS_OFF) {
|
||||
/* Enable all output discharge bits */
|
||||
|
@ -1272,10 +1279,17 @@ static int wm8990_set_bias_level(struct snd_soc_codec *codec,
|
|||
|
||||
/* disable POBCTRL, SOFT_ST and BUFDCOPEN */
|
||||
wm8990_write(codec, WM8990_ANTIPOP2, WM8990_BUFIOEN);
|
||||
} else {
|
||||
/* ON -> standby */
|
||||
|
||||
/* Enable workaround for ADC clocking issue. */
|
||||
wm8990_write(codec, WM8990_EXT_ACCESS_ENA, 0x2);
|
||||
wm8990_write(codec, WM8990_EXT_CTL1, 0xa003);
|
||||
wm8990_write(codec, WM8990_EXT_ACCESS_ENA, 0);
|
||||
}
|
||||
|
||||
/* VMID=2*250k */
|
||||
val = wm8990_read_reg_cache(codec, WM8990_POWER_MANAGEMENT_1) &
|
||||
~WM8990_VMID_MODE_MASK;
|
||||
wm8990_write(codec, WM8990_POWER_MANAGEMENT_1, val | 0x4);
|
||||
break;
|
||||
|
||||
case SND_SOC_BIAS_OFF:
|
||||
|
@ -1349,8 +1363,7 @@ struct snd_soc_dai wm8990_dai = {
|
|||
.rates = WM8990_RATES,
|
||||
.formats = WM8990_FORMATS,},
|
||||
.ops = {
|
||||
.hw_params = wm8990_hw_params,},
|
||||
.dai_ops = {
|
||||
.hw_params = wm8990_hw_params,
|
||||
.digital_mute = wm8990_mute,
|
||||
.set_fmt = wm8990_set_dai_fmt,
|
||||
.set_clkdiv = wm8990_set_dai_clkdiv,
|
||||
|
@ -1449,7 +1462,7 @@ static int wm8990_init(struct snd_soc_device *socdev)
|
|||
|
||||
wm8990_add_controls(codec);
|
||||
wm8990_add_widgets(codec);
|
||||
ret = snd_soc_register_card(socdev);
|
||||
ret = snd_soc_init_card(socdev);
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR "wm8990: failed to register card\n");
|
||||
goto card_err;
|
||||
|
@ -1630,6 +1643,18 @@ struct snd_soc_codec_device soc_codec_dev_wm8990 = {
|
|||
};
|
||||
EXPORT_SYMBOL_GPL(soc_codec_dev_wm8990);
|
||||
|
||||
static int __init wm8990_modinit(void)
|
||||
{
|
||||
return snd_soc_register_dai(&wm8990_dai);
|
||||
}
|
||||
module_init(wm8990_modinit);
|
||||
|
||||
static void __exit wm8990_exit(void)
|
||||
{
|
||||
snd_soc_unregister_dai(&wm8990_dai);
|
||||
}
|
||||
module_exit(wm8990_exit);
|
||||
|
||||
MODULE_DESCRIPTION("ASoC WM8990 driver");
|
||||
MODULE_AUTHOR("Liam Girdwood");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
|
@ -80,8 +80,8 @@
|
|||
#define WM8990_PLL3 0x3E
|
||||
#define WM8990_INTDRIVBITS 0x3F
|
||||
|
||||
#define WM8990_REGISTER_COUNT 60
|
||||
#define WM8990_MAX_REGISTER 0x3F
|
||||
#define WM8990_EXT_ACCESS_ENA 0x75
|
||||
#define WM8990_EXT_CTL1 0x7a
|
||||
|
||||
/*
|
||||
* Field Definitions.
|
||||
|
|
|
@ -487,7 +487,8 @@ static int ac97_write(struct snd_soc_codec *codec, unsigned int reg,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int ac97_prepare(struct snd_pcm_substream *substream)
|
||||
static int ac97_prepare(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
|
@ -507,7 +508,8 @@ static int ac97_prepare(struct snd_pcm_substream *substream)
|
|||
return ac97_write(codec, reg, runtime->rate);
|
||||
}
|
||||
|
||||
static int ac97_aux_prepare(struct snd_pcm_substream *substream)
|
||||
static int ac97_aux_prepare(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
|
@ -533,7 +535,7 @@ static int ac97_aux_prepare(struct snd_pcm_substream *substream)
|
|||
struct snd_soc_dai wm9712_dai[] = {
|
||||
{
|
||||
.name = "AC97 HiFi",
|
||||
.type = SND_SOC_DAI_AC97_BUS,
|
||||
.ac97_control = 1,
|
||||
.playback = {
|
||||
.stream_name = "HiFi Playback",
|
||||
.channels_min = 1,
|
||||
|
@ -688,7 +690,7 @@ static int wm9712_soc_probe(struct platform_device *pdev)
|
|||
|
||||
ret = wm9712_reset(codec, 0);
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR "AC97 link error\n");
|
||||
printk(KERN_ERR "Failed to reset WM9712: AC97 link error\n");
|
||||
goto reset_err;
|
||||
}
|
||||
|
||||
|
@ -698,7 +700,7 @@ static int wm9712_soc_probe(struct platform_device *pdev)
|
|||
wm9712_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
|
||||
wm9712_add_controls(codec);
|
||||
wm9712_add_widgets(codec);
|
||||
ret = snd_soc_register_card(socdev);
|
||||
ret = snd_soc_init_card(socdev);
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR "wm9712: failed to register card\n");
|
||||
goto reset_err;
|
||||
|
|
|
@ -928,11 +928,10 @@ static int wm9713_set_dai_fmt(struct snd_soc_dai *codec_dai,
|
|||
}
|
||||
|
||||
static int wm9713_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_device *socdev = rtd->socdev;
|
||||
struct snd_soc_codec *codec = socdev->codec;
|
||||
struct snd_soc_codec *codec = dai->codec;
|
||||
u16 reg = ac97_read(codec, AC97_CENTER_LFE_MASTER) & 0xfff3;
|
||||
|
||||
switch (params_format(params)) {
|
||||
|
@ -954,11 +953,10 @@ static int wm9713_pcm_hw_params(struct snd_pcm_substream *substream,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void wm9713_voiceshutdown(struct snd_pcm_substream *substream)
|
||||
static void wm9713_voiceshutdown(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_device *socdev = rtd->socdev;
|
||||
struct snd_soc_codec *codec = socdev->codec;
|
||||
struct snd_soc_codec *codec = dai->codec;
|
||||
u16 status;
|
||||
|
||||
/* Gracefully shut down the voice interface. */
|
||||
|
@ -969,12 +967,11 @@ static void wm9713_voiceshutdown(struct snd_pcm_substream *substream)
|
|||
ac97_write(codec, AC97_EXTENDED_MID, status);
|
||||
}
|
||||
|
||||
static int ac97_hifi_prepare(struct snd_pcm_substream *substream)
|
||||
static int ac97_hifi_prepare(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_codec *codec = dai->codec;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_device *socdev = rtd->socdev;
|
||||
struct snd_soc_codec *codec = socdev->codec;
|
||||
int reg;
|
||||
u16 vra;
|
||||
|
||||
|
@ -989,12 +986,11 @@ static int ac97_hifi_prepare(struct snd_pcm_substream *substream)
|
|||
return ac97_write(codec, reg, runtime->rate);
|
||||
}
|
||||
|
||||
static int ac97_aux_prepare(struct snd_pcm_substream *substream)
|
||||
static int ac97_aux_prepare(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_codec *codec = dai->codec;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_device *socdev = rtd->socdev;
|
||||
struct snd_soc_codec *codec = socdev->codec;
|
||||
u16 vra, xsle;
|
||||
|
||||
vra = ac97_read(codec, AC97_EXTENDED_STATUS);
|
||||
|
@ -1028,7 +1024,7 @@ static int ac97_aux_prepare(struct snd_pcm_substream *substream)
|
|||
struct snd_soc_dai wm9713_dai[] = {
|
||||
{
|
||||
.name = "AC97 HiFi",
|
||||
.type = SND_SOC_DAI_AC97_BUS,
|
||||
.ac97_control = 1,
|
||||
.playback = {
|
||||
.stream_name = "HiFi Playback",
|
||||
.channels_min = 1,
|
||||
|
@ -1042,8 +1038,7 @@ struct snd_soc_dai wm9713_dai[] = {
|
|||
.rates = WM9713_RATES,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,},
|
||||
.ops = {
|
||||
.prepare = ac97_hifi_prepare,},
|
||||
.dai_ops = {
|
||||
.prepare = ac97_hifi_prepare,
|
||||
.set_clkdiv = wm9713_set_dai_clkdiv,
|
||||
.set_pll = wm9713_set_dai_pll,},
|
||||
},
|
||||
|
@ -1056,8 +1051,7 @@ struct snd_soc_dai wm9713_dai[] = {
|
|||
.rates = WM9713_RATES,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,},
|
||||
.ops = {
|
||||
.prepare = ac97_aux_prepare,},
|
||||
.dai_ops = {
|
||||
.prepare = ac97_aux_prepare,
|
||||
.set_clkdiv = wm9713_set_dai_clkdiv,
|
||||
.set_pll = wm9713_set_dai_pll,},
|
||||
},
|
||||
|
@ -1077,8 +1071,7 @@ struct snd_soc_dai wm9713_dai[] = {
|
|||
.formats = WM9713_PCM_FORMATS,},
|
||||
.ops = {
|
||||
.hw_params = wm9713_pcm_hw_params,
|
||||
.shutdown = wm9713_voiceshutdown,},
|
||||
.dai_ops = {
|
||||
.shutdown = wm9713_voiceshutdown,
|
||||
.set_clkdiv = wm9713_set_dai_clkdiv,
|
||||
.set_pll = wm9713_set_dai_pll,
|
||||
.set_fmt = wm9713_set_dai_fmt,
|
||||
|
@ -1097,6 +1090,8 @@ int wm9713_reset(struct snd_soc_codec *codec, int try_warm)
|
|||
}
|
||||
|
||||
soc_ac97_ops.reset(codec->ac97);
|
||||
if (soc_ac97_ops.warm_reset)
|
||||
soc_ac97_ops.warm_reset(codec->ac97);
|
||||
if (ac97_read(codec, 0) != wm9713_reg[0])
|
||||
return -EIO;
|
||||
return 0;
|
||||
|
@ -1240,7 +1235,7 @@ static int wm9713_soc_probe(struct platform_device *pdev)
|
|||
wm9713_reset(codec, 0);
|
||||
ret = wm9713_reset(codec, 1);
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR "AC97 link error\n");
|
||||
printk(KERN_ERR "Failed to reset WM9713: AC97 link error\n");
|
||||
goto reset_err;
|
||||
}
|
||||
|
||||
|
@ -1252,7 +1247,7 @@ static int wm9713_soc_probe(struct platform_device *pdev)
|
|||
|
||||
wm9713_add_controls(codec);
|
||||
wm9713_add_widgets(codec);
|
||||
ret = snd_soc_register_card(socdev);
|
||||
ret = snd_soc_init_card(socdev);
|
||||
if (ret < 0)
|
||||
goto reset_err;
|
||||
return 0;
|
||||
|
@ -1288,7 +1283,6 @@ static int wm9713_soc_remove(struct platform_device *pdev)
|
|||
snd_soc_free_ac97_codec(codec);
|
||||
kfree(codec->private_data);
|
||||
kfree(codec->reg_cache);
|
||||
kfree(codec->dai);
|
||||
kfree(codec);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -17,3 +17,13 @@ config SND_DAVINCI_SOC_EVM
|
|||
help
|
||||
Say Y if you want to add support for SoC audio on TI
|
||||
DaVinci EVM platform.
|
||||
|
||||
config SND_DAVINCI_SOC_SFFSDR
|
||||
tristate "SoC Audio support for SFFSDR"
|
||||
depends on SND_DAVINCI_SOC && MACH_DAVINCI_SFFSDR
|
||||
select SND_DAVINCI_SOC_I2S
|
||||
select SND_SOC_PCM3008
|
||||
select SFFSDR_FPGA
|
||||
help
|
||||
Say Y if you want to add support for SoC audio on
|
||||
Lyrtech SFFSDR board.
|
||||
|
|
|
@ -7,5 +7,7 @@ obj-$(CONFIG_SND_DAVINCI_SOC_I2S) += snd-soc-davinci-i2s.o
|
|||
|
||||
# DAVINCI Machine Support
|
||||
snd-soc-evm-objs := davinci-evm.o
|
||||
snd-soc-sffsdr-objs := davinci-sffsdr.o
|
||||
|
||||
obj-$(CONFIG_SND_DAVINCI_SOC_EVM) += snd-soc-evm.o
|
||||
obj-$(CONFIG_SND_DAVINCI_SOC_SFFSDR) += snd-soc-sffsdr.o
|
||||
|
|
|
@ -28,6 +28,8 @@
|
|||
|
||||
#define EVM_CODEC_CLOCK 22579200
|
||||
|
||||
#define AUDIO_FORMAT (SND_SOC_DAIFMT_DSP_B | \
|
||||
SND_SOC_DAIFMT_CBM_CFM | SND_SOC_DAIFMT_IB_NF)
|
||||
static int evm_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
|
@ -37,14 +39,12 @@ static int evm_hw_params(struct snd_pcm_substream *substream,
|
|||
int ret = 0;
|
||||
|
||||
/* set codec DAI configuration */
|
||||
ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
|
||||
SND_SOC_DAIFMT_CBM_CFM);
|
||||
ret = snd_soc_dai_set_fmt(codec_dai, AUDIO_FORMAT);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* set cpu DAI configuration */
|
||||
ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_CBM_CFM |
|
||||
SND_SOC_DAIFMT_IB_NF);
|
||||
ret = snd_soc_dai_set_fmt(cpu_dai, AUDIO_FORMAT);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
@ -128,8 +128,9 @@ static struct snd_soc_dai_link evm_dai = {
|
|||
};
|
||||
|
||||
/* davinci-evm audio machine driver */
|
||||
static struct snd_soc_machine snd_soc_machine_evm = {
|
||||
static struct snd_soc_card snd_soc_card_evm = {
|
||||
.name = "DaVinci EVM",
|
||||
.platform = &davinci_soc_platform,
|
||||
.dai_link = &evm_dai,
|
||||
.num_links = 1,
|
||||
};
|
||||
|
@ -142,8 +143,7 @@ static struct aic3x_setup_data evm_aic3x_setup = {
|
|||
|
||||
/* evm audio subsystem */
|
||||
static struct snd_soc_device evm_snd_devdata = {
|
||||
.machine = &snd_soc_machine_evm,
|
||||
.platform = &davinci_soc_platform,
|
||||
.card = &snd_soc_card_evm,
|
||||
.codec_dev = &soc_codec_dev_aic3x,
|
||||
.codec_data = &evm_aic3x_setup,
|
||||
};
|
||||
|
|
|
@ -59,6 +59,7 @@
|
|||
#define DAVINCI_MCBSP_PCR_CLKXP (1 << 1)
|
||||
#define DAVINCI_MCBSP_PCR_FSRP (1 << 2)
|
||||
#define DAVINCI_MCBSP_PCR_FSXP (1 << 3)
|
||||
#define DAVINCI_MCBSP_PCR_SCLKME (1 << 7)
|
||||
#define DAVINCI_MCBSP_PCR_CLKRM (1 << 8)
|
||||
#define DAVINCI_MCBSP_PCR_CLKXM (1 << 9)
|
||||
#define DAVINCI_MCBSP_PCR_FSRM (1 << 10)
|
||||
|
@ -110,17 +111,60 @@ static void davinci_mcbsp_start(struct snd_pcm_substream *substream)
|
|||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct davinci_mcbsp_dev *dev = rtd->dai->cpu_dai->private_data;
|
||||
struct snd_soc_device *socdev = rtd->socdev;
|
||||
struct snd_soc_platform *platform = socdev->card->platform;
|
||||
u32 w;
|
||||
int ret;
|
||||
|
||||
/* Start the sample generator and enable transmitter/receiver */
|
||||
w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
|
||||
MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_GRST, 1);
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_XRST, 1);
|
||||
else
|
||||
MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_RRST, 1);
|
||||
davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
/* Stop the DMA to avoid data loss */
|
||||
/* while the transmitter is out of reset to handle XSYNCERR */
|
||||
if (platform->pcm_ops->trigger) {
|
||||
ret = platform->pcm_ops->trigger(substream,
|
||||
SNDRV_PCM_TRIGGER_STOP);
|
||||
if (ret < 0)
|
||||
printk(KERN_DEBUG "Playback DMA stop failed\n");
|
||||
}
|
||||
|
||||
/* Enable the transmitter */
|
||||
w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
|
||||
MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_XRST, 1);
|
||||
davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
|
||||
|
||||
/* wait for any unexpected frame sync error to occur */
|
||||
udelay(100);
|
||||
|
||||
/* Disable the transmitter to clear any outstanding XSYNCERR */
|
||||
w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
|
||||
MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_XRST, 0);
|
||||
davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
|
||||
|
||||
/* Restart the DMA */
|
||||
if (platform->pcm_ops->trigger) {
|
||||
ret = platform->pcm_ops->trigger(substream,
|
||||
SNDRV_PCM_TRIGGER_START);
|
||||
if (ret < 0)
|
||||
printk(KERN_DEBUG "Playback DMA start failed\n");
|
||||
}
|
||||
/* Enable the transmitter */
|
||||
w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
|
||||
MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_XRST, 1);
|
||||
davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
|
||||
|
||||
} else {
|
||||
|
||||
/* Enable the reciever */
|
||||
w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
|
||||
MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_RRST, 1);
|
||||
davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
|
||||
}
|
||||
|
||||
|
||||
/* Start frame sync */
|
||||
w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
|
||||
MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_FRST, 1);
|
||||
|
@ -144,7 +188,8 @@ static void davinci_mcbsp_stop(struct snd_pcm_substream *substream)
|
|||
davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
|
||||
}
|
||||
|
||||
static int davinci_i2s_startup(struct snd_pcm_substream *substream)
|
||||
static int davinci_i2s_startup(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
|
||||
|
@ -155,61 +200,138 @@ static int davinci_i2s_startup(struct snd_pcm_substream *substream)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#define DEFAULT_BITPERSAMPLE 16
|
||||
|
||||
static int davinci_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
|
||||
unsigned int fmt)
|
||||
{
|
||||
struct davinci_mcbsp_dev *dev = cpu_dai->private_data;
|
||||
u32 w;
|
||||
unsigned int pcr;
|
||||
unsigned int srgr;
|
||||
unsigned int rcr;
|
||||
unsigned int xcr;
|
||||
srgr = DAVINCI_MCBSP_SRGR_FSGM |
|
||||
DAVINCI_MCBSP_SRGR_FPER(DEFAULT_BITPERSAMPLE * 2 - 1) |
|
||||
DAVINCI_MCBSP_SRGR_FWID(DEFAULT_BITPERSAMPLE - 1);
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
|
||||
case SND_SOC_DAIFMT_CBS_CFS:
|
||||
davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_PCR_REG,
|
||||
DAVINCI_MCBSP_PCR_FSXM |
|
||||
DAVINCI_MCBSP_PCR_FSRM |
|
||||
DAVINCI_MCBSP_PCR_CLKXM |
|
||||
DAVINCI_MCBSP_PCR_CLKRM);
|
||||
davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SRGR_REG,
|
||||
DAVINCI_MCBSP_SRGR_FSGM);
|
||||
/* cpu is master */
|
||||
pcr = DAVINCI_MCBSP_PCR_FSXM |
|
||||
DAVINCI_MCBSP_PCR_FSRM |
|
||||
DAVINCI_MCBSP_PCR_CLKXM |
|
||||
DAVINCI_MCBSP_PCR_CLKRM;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_CBM_CFS:
|
||||
/* McBSP CLKR pin is the input for the Sample Rate Generator.
|
||||
* McBSP FSR and FSX are driven by the Sample Rate Generator. */
|
||||
pcr = DAVINCI_MCBSP_PCR_SCLKME |
|
||||
DAVINCI_MCBSP_PCR_FSXM |
|
||||
DAVINCI_MCBSP_PCR_FSRM;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_CBM_CFM:
|
||||
davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_PCR_REG, 0);
|
||||
/* codec is master */
|
||||
pcr = 0;
|
||||
break;
|
||||
default:
|
||||
printk(KERN_ERR "%s:bad master\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rcr = DAVINCI_MCBSP_RCR_RFRLEN1(1);
|
||||
xcr = DAVINCI_MCBSP_XCR_XFIG | DAVINCI_MCBSP_XCR_XFRLEN1(1);
|
||||
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
|
||||
case SND_SOC_DAIFMT_DSP_B:
|
||||
break;
|
||||
case SND_SOC_DAIFMT_I2S:
|
||||
/* Davinci doesn't support TRUE I2S, but some codecs will have
|
||||
* the left and right channels contiguous. This allows
|
||||
* dsp_a mode to be used with an inverted normal frame clk.
|
||||
* If your codec is master and does not have contiguous
|
||||
* channels, then you will have sound on only one channel.
|
||||
* Try using a different mode, or codec as slave.
|
||||
*
|
||||
* The TLV320AIC33 is an example of a codec where this works.
|
||||
* It has a variable bit clock frequency allowing it to have
|
||||
* valid data on every bit clock.
|
||||
*
|
||||
* The TLV320AIC23 is an example of a codec where this does not
|
||||
* work. It has a fixed bit clock frequency with progressively
|
||||
* more empty bit clock slots between channels as the sample
|
||||
* rate is lowered.
|
||||
*/
|
||||
fmt ^= SND_SOC_DAIFMT_NB_IF;
|
||||
case SND_SOC_DAIFMT_DSP_A:
|
||||
rcr |= DAVINCI_MCBSP_RCR_RDATDLY(1);
|
||||
xcr |= DAVINCI_MCBSP_XCR_XDATDLY(1);
|
||||
break;
|
||||
default:
|
||||
printk(KERN_ERR "%s:bad format\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
|
||||
case SND_SOC_DAIFMT_IB_NF:
|
||||
w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_PCR_REG);
|
||||
MOD_REG_BIT(w, DAVINCI_MCBSP_PCR_CLKXP |
|
||||
DAVINCI_MCBSP_PCR_CLKRP, 1);
|
||||
davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_PCR_REG, w);
|
||||
break;
|
||||
case SND_SOC_DAIFMT_NB_IF:
|
||||
w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_PCR_REG);
|
||||
MOD_REG_BIT(w, DAVINCI_MCBSP_PCR_FSXP |
|
||||
DAVINCI_MCBSP_PCR_FSRP, 1);
|
||||
davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_PCR_REG, w);
|
||||
case SND_SOC_DAIFMT_NB_NF:
|
||||
/* CLKRP Receive clock polarity,
|
||||
* 1 - sampled on rising edge of CLKR
|
||||
* valid on rising edge
|
||||
* CLKXP Transmit clock polarity,
|
||||
* 1 - clocked on falling edge of CLKX
|
||||
* valid on rising edge
|
||||
* FSRP Receive frame sync pol, 0 - active high
|
||||
* FSXP Transmit frame sync pol, 0 - active high
|
||||
*/
|
||||
pcr |= (DAVINCI_MCBSP_PCR_CLKXP | DAVINCI_MCBSP_PCR_CLKRP);
|
||||
break;
|
||||
case SND_SOC_DAIFMT_IB_IF:
|
||||
w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_PCR_REG);
|
||||
MOD_REG_BIT(w, DAVINCI_MCBSP_PCR_CLKXP |
|
||||
DAVINCI_MCBSP_PCR_CLKRP |
|
||||
DAVINCI_MCBSP_PCR_FSXP |
|
||||
DAVINCI_MCBSP_PCR_FSRP, 1);
|
||||
davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_PCR_REG, w);
|
||||
/* CLKRP Receive clock polarity,
|
||||
* 0 - sampled on falling edge of CLKR
|
||||
* valid on falling edge
|
||||
* CLKXP Transmit clock polarity,
|
||||
* 0 - clocked on rising edge of CLKX
|
||||
* valid on falling edge
|
||||
* FSRP Receive frame sync pol, 1 - active low
|
||||
* FSXP Transmit frame sync pol, 1 - active low
|
||||
*/
|
||||
pcr |= (DAVINCI_MCBSP_PCR_FSXP | DAVINCI_MCBSP_PCR_FSRP);
|
||||
break;
|
||||
case SND_SOC_DAIFMT_NB_NF:
|
||||
case SND_SOC_DAIFMT_NB_IF:
|
||||
/* CLKRP Receive clock polarity,
|
||||
* 1 - sampled on rising edge of CLKR
|
||||
* valid on rising edge
|
||||
* CLKXP Transmit clock polarity,
|
||||
* 1 - clocked on falling edge of CLKX
|
||||
* valid on rising edge
|
||||
* FSRP Receive frame sync pol, 1 - active low
|
||||
* FSXP Transmit frame sync pol, 1 - active low
|
||||
*/
|
||||
pcr |= (DAVINCI_MCBSP_PCR_CLKXP | DAVINCI_MCBSP_PCR_CLKRP |
|
||||
DAVINCI_MCBSP_PCR_FSXP | DAVINCI_MCBSP_PCR_FSRP);
|
||||
break;
|
||||
case SND_SOC_DAIFMT_IB_NF:
|
||||
/* CLKRP Receive clock polarity,
|
||||
* 0 - sampled on falling edge of CLKR
|
||||
* valid on falling edge
|
||||
* CLKXP Transmit clock polarity,
|
||||
* 0 - clocked on rising edge of CLKX
|
||||
* valid on falling edge
|
||||
* FSRP Receive frame sync pol, 0 - active high
|
||||
* FSXP Transmit frame sync pol, 0 - active high
|
||||
*/
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SRGR_REG, srgr);
|
||||
davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_PCR_REG, pcr);
|
||||
davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_RCR_REG, rcr);
|
||||
davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_XCR_REG, xcr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int davinci_i2s_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct davinci_pcm_dma_params *dma_params = rtd->dai->cpu_dai->dma_data;
|
||||
|
@ -219,25 +341,20 @@ static int davinci_i2s_hw_params(struct snd_pcm_substream *substream,
|
|||
u32 w;
|
||||
|
||||
/* general line settings */
|
||||
davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG,
|
||||
DAVINCI_MCBSP_SPCR_RINTM(3) |
|
||||
DAVINCI_MCBSP_SPCR_XINTM(3) |
|
||||
DAVINCI_MCBSP_SPCR_FREE);
|
||||
davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_RCR_REG,
|
||||
DAVINCI_MCBSP_RCR_RFRLEN1(1) |
|
||||
DAVINCI_MCBSP_RCR_RDATDLY(1));
|
||||
davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_XCR_REG,
|
||||
DAVINCI_MCBSP_XCR_XFRLEN1(1) |
|
||||
DAVINCI_MCBSP_XCR_XDATDLY(1) |
|
||||
DAVINCI_MCBSP_XCR_XFIG);
|
||||
w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
|
||||
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
|
||||
w |= DAVINCI_MCBSP_SPCR_RINTM(3) | DAVINCI_MCBSP_SPCR_FREE;
|
||||
davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
|
||||
} else {
|
||||
w |= DAVINCI_MCBSP_SPCR_XINTM(3) | DAVINCI_MCBSP_SPCR_FREE;
|
||||
davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
|
||||
}
|
||||
|
||||
i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS);
|
||||
w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SRGR_REG);
|
||||
w = DAVINCI_MCBSP_SRGR_FSGM;
|
||||
MOD_REG_BIT(w, DAVINCI_MCBSP_SRGR_FWID(snd_interval_value(i) - 1), 1);
|
||||
davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SRGR_REG, w);
|
||||
|
||||
i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_FRAME_BITS);
|
||||
w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SRGR_REG);
|
||||
MOD_REG_BIT(w, DAVINCI_MCBSP_SRGR_FPER(snd_interval_value(i) - 1), 1);
|
||||
davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SRGR_REG, w);
|
||||
|
||||
|
@ -260,20 +377,24 @@ static int davinci_i2s_hw_params(struct snd_pcm_substream *substream,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_RCR_REG);
|
||||
MOD_REG_BIT(w, DAVINCI_MCBSP_RCR_RWDLEN1(mcbsp_word_length) |
|
||||
DAVINCI_MCBSP_RCR_RWDLEN2(mcbsp_word_length), 1);
|
||||
davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_RCR_REG, w);
|
||||
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
|
||||
w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_RCR_REG);
|
||||
MOD_REG_BIT(w, DAVINCI_MCBSP_RCR_RWDLEN1(mcbsp_word_length) |
|
||||
DAVINCI_MCBSP_RCR_RWDLEN2(mcbsp_word_length), 1);
|
||||
davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_RCR_REG, w);
|
||||
|
||||
w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_XCR_REG);
|
||||
MOD_REG_BIT(w, DAVINCI_MCBSP_XCR_XWDLEN1(mcbsp_word_length) |
|
||||
DAVINCI_MCBSP_XCR_XWDLEN2(mcbsp_word_length), 1);
|
||||
davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_XCR_REG, w);
|
||||
} else {
|
||||
w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_XCR_REG);
|
||||
MOD_REG_BIT(w, DAVINCI_MCBSP_XCR_XWDLEN1(mcbsp_word_length) |
|
||||
DAVINCI_MCBSP_XCR_XWDLEN2(mcbsp_word_length), 1);
|
||||
davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_XCR_REG, w);
|
||||
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int davinci_i2s_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
static int davinci_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
|
@ -299,8 +420,8 @@ static int davinci_i2s_probe(struct platform_device *pdev,
|
|||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
|
||||
struct snd_soc_machine *machine = socdev->machine;
|
||||
struct snd_soc_dai *cpu_dai = machine->dai_link[pdev->id].cpu_dai;
|
||||
struct snd_soc_card *card = socdev->card;
|
||||
struct snd_soc_dai *cpu_dai = card->dai_link[pdev->id].cpu_dai;
|
||||
struct davinci_mcbsp_dev *dev;
|
||||
struct resource *mem, *ioarea;
|
||||
struct evm_snd_platform_data *pdata;
|
||||
|
@ -361,8 +482,8 @@ static void davinci_i2s_remove(struct platform_device *pdev,
|
|||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
|
||||
struct snd_soc_machine *machine = socdev->machine;
|
||||
struct snd_soc_dai *cpu_dai = machine->dai_link[pdev->id].cpu_dai;
|
||||
struct snd_soc_card *card = socdev->card;
|
||||
struct snd_soc_dai *cpu_dai = card->dai_link[pdev->id].cpu_dai;
|
||||
struct davinci_mcbsp_dev *dev = cpu_dai->private_data;
|
||||
struct resource *mem;
|
||||
|
||||
|
@ -381,7 +502,6 @@ static void davinci_i2s_remove(struct platform_device *pdev,
|
|||
struct snd_soc_dai davinci_i2s_dai = {
|
||||
.name = "davinci-i2s",
|
||||
.id = 0,
|
||||
.type = SND_SOC_DAI_I2S,
|
||||
.probe = davinci_i2s_probe,
|
||||
.remove = davinci_i2s_remove,
|
||||
.playback = {
|
||||
|
@ -397,13 +517,24 @@ struct snd_soc_dai davinci_i2s_dai = {
|
|||
.ops = {
|
||||
.startup = davinci_i2s_startup,
|
||||
.trigger = davinci_i2s_trigger,
|
||||
.hw_params = davinci_i2s_hw_params,},
|
||||
.dai_ops = {
|
||||
.hw_params = davinci_i2s_hw_params,
|
||||
.set_fmt = davinci_i2s_set_dai_fmt,
|
||||
},
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(davinci_i2s_dai);
|
||||
|
||||
static int __init davinci_i2s_init(void)
|
||||
{
|
||||
return snd_soc_register_dai(&davinci_i2s_dai);
|
||||
}
|
||||
module_init(davinci_i2s_init);
|
||||
|
||||
static void __exit davinci_i2s_exit(void)
|
||||
{
|
||||
snd_soc_unregister_dai(&davinci_i2s_dai);
|
||||
}
|
||||
module_exit(davinci_i2s_exit);
|
||||
|
||||
MODULE_AUTHOR("Vladimir Barinov");
|
||||
MODULE_DESCRIPTION("TI DAVINCI I2S (McBSP) SoC Interface");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/kernel.h>
|
||||
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
|
@ -24,13 +25,6 @@
|
|||
|
||||
#include "davinci-pcm.h"
|
||||
|
||||
#define DAVINCI_PCM_DEBUG 0
|
||||
#if DAVINCI_PCM_DEBUG
|
||||
#define DPRINTK(x...) printk(KERN_DEBUG x)
|
||||
#else
|
||||
#define DPRINTK(x...)
|
||||
#endif
|
||||
|
||||
static struct snd_pcm_hardware davinci_pcm_hardware = {
|
||||
.info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
|
||||
SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
|
||||
|
@ -78,8 +72,8 @@ static void davinci_pcm_enqueue_dma(struct snd_pcm_substream *substream)
|
|||
dma_offset = prtd->period * period_size;
|
||||
dma_pos = runtime->dma_addr + dma_offset;
|
||||
|
||||
DPRINTK("audio_set_dma_params_play channel = %d dma_ptr = %x "
|
||||
"period_size=%x\n", lch, dma_pos, period_size);
|
||||
pr_debug("davinci_pcm: audio_set_dma_params_play channel = %d "
|
||||
"dma_ptr = %x period_size=%x\n", lch, dma_pos, period_size);
|
||||
|
||||
data_type = prtd->params->data_type;
|
||||
count = period_size / data_type;
|
||||
|
@ -112,7 +106,7 @@ static void davinci_pcm_dma_irq(int lch, u16 ch_status, void *data)
|
|||
struct snd_pcm_substream *substream = data;
|
||||
struct davinci_runtime_data *prtd = substream->runtime->private_data;
|
||||
|
||||
DPRINTK("lch=%d, status=0x%x\n", lch, ch_status);
|
||||
pr_debug("davinci_pcm: lch=%d, status=0x%x\n", lch, ch_status);
|
||||
|
||||
if (unlikely(ch_status != DMA_COMPLETE))
|
||||
return;
|
||||
|
@ -316,8 +310,8 @@ static int davinci_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
|
|||
buf->area = dma_alloc_writecombine(pcm->card->dev, size,
|
||||
&buf->addr, GFP_KERNEL);
|
||||
|
||||
DPRINTK("preallocate_dma_buffer: area=%p, addr=%p, size=%d\n",
|
||||
(void *) buf->area, (void *) buf->addr, size);
|
||||
pr_debug("davinci_pcm: preallocate_dma_buffer: area=%p, addr=%p, "
|
||||
"size=%d\n", (void *) buf->area, (void *) buf->addr, size);
|
||||
|
||||
if (!buf->area)
|
||||
return -ENOMEM;
|
||||
|
@ -384,6 +378,18 @@ struct snd_soc_platform davinci_soc_platform = {
|
|||
};
|
||||
EXPORT_SYMBOL_GPL(davinci_soc_platform);
|
||||
|
||||
static int __init davinci_soc_platform_init(void)
|
||||
{
|
||||
return snd_soc_register_platform(&davinci_soc_platform);
|
||||
}
|
||||
module_init(davinci_soc_platform_init);
|
||||
|
||||
static void __exit davinci_soc_platform_exit(void)
|
||||
{
|
||||
snd_soc_unregister_platform(&davinci_soc_platform);
|
||||
}
|
||||
module_exit(davinci_soc_platform_exit);
|
||||
|
||||
MODULE_AUTHOR("Vladimir Barinov");
|
||||
MODULE_DESCRIPTION("TI DAVINCI PCM DMA module");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
157
sound/soc/davinci/davinci-sffsdr.c
Normal file
157
sound/soc/davinci/davinci-sffsdr.c
Normal file
|
@ -0,0 +1,157 @@
|
|||
/*
|
||||
* ASoC driver for Lyrtech SFFSDR board.
|
||||
*
|
||||
* Author: Hugo Villeneuve
|
||||
* Copyright (C) 2008 Lyrtech inc
|
||||
*
|
||||
* Based on ASoC driver for TI DAVINCI EVM platform, original copyright follow:
|
||||
* Copyright: (C) 2007 MontaVista Software, Inc., <source@mvista.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/soc-dapm.h>
|
||||
|
||||
#include <asm/dma.h>
|
||||
#include <asm/plat-sffsdr/sffsdr-fpga.h>
|
||||
|
||||
#include <mach/mcbsp.h>
|
||||
#include <mach/edma.h>
|
||||
|
||||
#include "../codecs/pcm3008.h"
|
||||
#include "davinci-pcm.h"
|
||||
#include "davinci-i2s.h"
|
||||
|
||||
static int sffsdr_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_dai *cpu_dai = rtd->dai->cpu_dai;
|
||||
int fs;
|
||||
int ret = 0;
|
||||
|
||||
/* Set cpu DAI configuration:
|
||||
* CLKX and CLKR are the inputs for the Sample Rate Generator.
|
||||
* FSX and FSR are outputs, driven by the sample Rate Generator. */
|
||||
ret = snd_soc_dai_set_fmt(cpu_dai,
|
||||
SND_SOC_DAIFMT_RIGHT_J |
|
||||
SND_SOC_DAIFMT_CBM_CFS |
|
||||
SND_SOC_DAIFMT_IB_NF);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Fsref can be 32000, 44100 or 48000. */
|
||||
fs = params_rate(params);
|
||||
|
||||
pr_debug("sffsdr_hw_params: rate = %d Hz\n", fs);
|
||||
|
||||
return sffsdr_fpga_set_codec_fs(fs);
|
||||
}
|
||||
|
||||
static struct snd_soc_ops sffsdr_ops = {
|
||||
.hw_params = sffsdr_hw_params,
|
||||
};
|
||||
|
||||
/* davinci-sffsdr digital audio interface glue - connects codec <--> CPU */
|
||||
static struct snd_soc_dai_link sffsdr_dai = {
|
||||
.name = "PCM3008", /* Codec name */
|
||||
.stream_name = "PCM3008 HiFi",
|
||||
.cpu_dai = &davinci_i2s_dai,
|
||||
.codec_dai = &pcm3008_dai,
|
||||
.ops = &sffsdr_ops,
|
||||
};
|
||||
|
||||
/* davinci-sffsdr audio machine driver */
|
||||
static struct snd_soc_card snd_soc_sffsdr = {
|
||||
.name = "DaVinci SFFSDR",
|
||||
.platform = &davinci_soc_platform,
|
||||
.dai_link = &sffsdr_dai,
|
||||
.num_links = 1,
|
||||
};
|
||||
|
||||
/* sffsdr audio private data */
|
||||
static struct pcm3008_setup_data sffsdr_pcm3008_setup = {
|
||||
.dem0_pin = GPIO(45),
|
||||
.dem1_pin = GPIO(46),
|
||||
.pdad_pin = GPIO(47),
|
||||
.pdda_pin = GPIO(38),
|
||||
};
|
||||
|
||||
/* sffsdr audio subsystem */
|
||||
static struct snd_soc_device sffsdr_snd_devdata = {
|
||||
.card = &snd_soc_sffsdr,
|
||||
.codec_dev = &soc_codec_dev_pcm3008,
|
||||
.codec_data = &sffsdr_pcm3008_setup,
|
||||
};
|
||||
|
||||
static struct resource sffsdr_snd_resources[] = {
|
||||
{
|
||||
.start = DAVINCI_MCBSP_BASE,
|
||||
.end = DAVINCI_MCBSP_BASE + SZ_8K - 1,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
};
|
||||
|
||||
static struct evm_snd_platform_data sffsdr_snd_data = {
|
||||
.tx_dma_ch = DAVINCI_DMA_MCBSP_TX,
|
||||
.rx_dma_ch = DAVINCI_DMA_MCBSP_RX,
|
||||
};
|
||||
|
||||
static struct platform_device *sffsdr_snd_device;
|
||||
|
||||
static int __init sffsdr_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
sffsdr_snd_device = platform_device_alloc("soc-audio", 0);
|
||||
if (!sffsdr_snd_device) {
|
||||
printk(KERN_ERR "platform device allocation failed\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
platform_set_drvdata(sffsdr_snd_device, &sffsdr_snd_devdata);
|
||||
sffsdr_snd_devdata.dev = &sffsdr_snd_device->dev;
|
||||
sffsdr_snd_device->dev.platform_data = &sffsdr_snd_data;
|
||||
|
||||
ret = platform_device_add_resources(sffsdr_snd_device,
|
||||
sffsdr_snd_resources,
|
||||
ARRAY_SIZE(sffsdr_snd_resources));
|
||||
if (ret) {
|
||||
printk(KERN_ERR "platform device add ressources failed\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
ret = platform_device_add(sffsdr_snd_device);
|
||||
if (ret)
|
||||
goto error;
|
||||
|
||||
return ret;
|
||||
|
||||
error:
|
||||
platform_device_put(sffsdr_snd_device);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit sffsdr_exit(void)
|
||||
{
|
||||
platform_device_unregister(sffsdr_snd_device);
|
||||
}
|
||||
|
||||
module_init(sffsdr_init);
|
||||
module_exit(sffsdr_exit);
|
||||
|
||||
MODULE_AUTHOR("Hugo Villeneuve");
|
||||
MODULE_DESCRIPTION("Lyrtech SFFSDR ASoC driver");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -20,7 +20,7 @@ config SND_SOC_MPC8610_HPCD
|
|||
|
||||
config SND_SOC_MPC5200_I2S
|
||||
tristate "Freescale MPC5200 PSC in I2S mode driver"
|
||||
depends on SND_SOC && PPC_MPC52xx && PPC_BESTCOMM
|
||||
depends on PPC_MPC52xx && PPC_BESTCOMM
|
||||
select SND_SOC_OF_SIMPLE
|
||||
select PPC_BESTCOMM_GEN_BD
|
||||
help
|
||||
|
|
|
@ -284,7 +284,7 @@ static irqreturn_t fsl_dma_isr(int irq, void *dev_id)
|
|||
* fsl_dma_new: initialize this PCM driver.
|
||||
*
|
||||
* This function is called when the codec driver calls snd_soc_new_pcms(),
|
||||
* once for each .dai_link in the machine driver's snd_soc_machine
|
||||
* once for each .dai_link in the machine driver's snd_soc_card
|
||||
* structure.
|
||||
*/
|
||||
static int fsl_dma_new(struct snd_card *card, struct snd_soc_dai *dai,
|
||||
|
@ -853,6 +853,18 @@ int fsl_dma_configure(struct fsl_dma_info *dma_info)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(fsl_dma_configure);
|
||||
|
||||
static int __init fsl_soc_platform_init(void)
|
||||
{
|
||||
return snd_soc_register_platform(&fsl_soc_platform);
|
||||
}
|
||||
module_init(fsl_soc_platform_init);
|
||||
|
||||
static void __exit fsl_soc_platform_exit(void)
|
||||
{
|
||||
snd_soc_unregister_platform(&fsl_soc_platform);
|
||||
}
|
||||
module_exit(fsl_soc_platform_exit);
|
||||
|
||||
MODULE_AUTHOR("Timur Tabi <timur@freescale.com>");
|
||||
MODULE_DESCRIPTION("Freescale Elo DMA ASoC PCM module");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
|
@ -266,7 +266,8 @@ static irqreturn_t fsl_ssi_isr(int irq, void *dev_id)
|
|||
* If this is the first stream open, then grab the IRQ and program most of
|
||||
* the SSI registers.
|
||||
*/
|
||||
static int fsl_ssi_startup(struct snd_pcm_substream *substream)
|
||||
static int fsl_ssi_startup(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct fsl_ssi_private *ssi_private = rtd->dai->cpu_dai->private_data;
|
||||
|
@ -411,7 +412,8 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream)
|
|||
* Note: The SxCCR.DC and SxCCR.PM bits are only used if the SSI is the
|
||||
* clock master.
|
||||
*/
|
||||
static int fsl_ssi_prepare(struct snd_pcm_substream *substream)
|
||||
static int fsl_ssi_prepare(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
|
@ -441,7 +443,8 @@ static int fsl_ssi_prepare(struct snd_pcm_substream *substream)
|
|||
* The DMA channel is in external master start and pause mode, which
|
||||
* means the SSI completely controls the flow of data.
|
||||
*/
|
||||
static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct fsl_ssi_private *ssi_private = rtd->dai->cpu_dai->private_data;
|
||||
|
@ -490,7 +493,8 @@ static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd)
|
|||
*
|
||||
* Shutdown the SSI if there are no other substreams open.
|
||||
*/
|
||||
static void fsl_ssi_shutdown(struct snd_pcm_substream *substream)
|
||||
static void fsl_ssi_shutdown(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct fsl_ssi_private *ssi_private = rtd->dai->cpu_dai->private_data;
|
||||
|
@ -578,8 +582,6 @@ static struct snd_soc_dai fsl_ssi_dai_template = {
|
|||
.prepare = fsl_ssi_prepare,
|
||||
.shutdown = fsl_ssi_shutdown,
|
||||
.trigger = fsl_ssi_trigger,
|
||||
},
|
||||
.dai_ops = {
|
||||
.set_sysclk = fsl_ssi_set_sysclk,
|
||||
.set_fmt = fsl_ssi_set_fmt,
|
||||
},
|
||||
|
@ -671,6 +673,14 @@ struct snd_soc_dai *fsl_ssi_create_dai(struct fsl_ssi_info *ssi_info)
|
|||
fsl_ssi_dai->private_data = ssi_private;
|
||||
fsl_ssi_dai->name = ssi_private->name;
|
||||
fsl_ssi_dai->id = ssi_info->id;
|
||||
fsl_ssi_dai->dev = ssi_info->dev;
|
||||
|
||||
ret = snd_soc_register_dai(fsl_ssi_dai);
|
||||
if (ret != 0) {
|
||||
dev_err(ssi_info->dev, "failed to register DAI: %d\n", ret);
|
||||
kfree(fsl_ssi_dai);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return fsl_ssi_dai;
|
||||
}
|
||||
|
@ -688,6 +698,8 @@ void fsl_ssi_destroy_dai(struct snd_soc_dai *fsl_ssi_dai)
|
|||
|
||||
device_remove_file(ssi_private->dev, &ssi_private->dev_attr);
|
||||
|
||||
snd_soc_unregister_dai(&ssi_private->cpu_dai);
|
||||
|
||||
kfree(ssi_private);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fsl_ssi_destroy_dai);
|
||||
|
|
|
@ -187,7 +187,8 @@ static irqreturn_t psc_i2s_bcom_irq(int irq, void *_psc_i2s_stream)
|
|||
* If this is the first stream open, then grab the IRQ and program most of
|
||||
* the PSC registers.
|
||||
*/
|
||||
static int psc_i2s_startup(struct snd_pcm_substream *substream)
|
||||
static int psc_i2s_startup(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data;
|
||||
|
@ -220,7 +221,8 @@ static int psc_i2s_startup(struct snd_pcm_substream *substream)
|
|||
}
|
||||
|
||||
static int psc_i2s_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data;
|
||||
|
@ -256,7 +258,8 @@ static int psc_i2s_hw_params(struct snd_pcm_substream *substream,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int psc_i2s_hw_free(struct snd_pcm_substream *substream)
|
||||
static int psc_i2s_hw_free(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
snd_pcm_set_runtime_buffer(substream, NULL);
|
||||
return 0;
|
||||
|
@ -268,7 +271,8 @@ static int psc_i2s_hw_free(struct snd_pcm_substream *substream)
|
|||
* This function is called by ALSA to start, stop, pause, and resume the DMA
|
||||
* transfer of data.
|
||||
*/
|
||||
static int psc_i2s_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
static int psc_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data;
|
||||
|
@ -383,7 +387,8 @@ static int psc_i2s_trigger(struct snd_pcm_substream *substream, int cmd)
|
|||
*
|
||||
* Shutdown the PSC if there are no other substreams open.
|
||||
*/
|
||||
static void psc_i2s_shutdown(struct snd_pcm_substream *substream)
|
||||
static void psc_i2s_shutdown(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data;
|
||||
|
@ -464,7 +469,6 @@ static int psc_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int format)
|
|||
* psc_i2s_dai_template: template CPU Digital Audio Interface
|
||||
*/
|
||||
static struct snd_soc_dai psc_i2s_dai_template = {
|
||||
.type = SND_SOC_DAI_I2S,
|
||||
.playback = {
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
|
@ -483,8 +487,6 @@ static struct snd_soc_dai psc_i2s_dai_template = {
|
|||
.hw_free = psc_i2s_hw_free,
|
||||
.shutdown = psc_i2s_shutdown,
|
||||
.trigger = psc_i2s_trigger,
|
||||
},
|
||||
.dai_ops = {
|
||||
.set_sysclk = psc_i2s_set_sysclk,
|
||||
.set_fmt = psc_i2s_set_fmt,
|
||||
},
|
||||
|
@ -826,6 +828,8 @@ static int __devinit psc_i2s_of_probe(struct of_device *op,
|
|||
if (rc)
|
||||
dev_info(psc_i2s->dev, "error creating sysfs files\n");
|
||||
|
||||
snd_soc_register_platform(&psc_i2s_pcm_soc_platform);
|
||||
|
||||
/* Tell the ASoC OF helpers about it */
|
||||
of_snd_soc_register_platform(&psc_i2s_pcm_soc_platform, op->node,
|
||||
&psc_i2s->dai);
|
||||
|
@ -839,6 +843,8 @@ static int __devexit psc_i2s_of_remove(struct of_device *op)
|
|||
|
||||
dev_dbg(&op->dev, "psc_i2s_remove()\n");
|
||||
|
||||
snd_soc_unregister_platform(&psc_i2s_pcm_soc_platform);
|
||||
|
||||
bcom_gen_bd_rx_release(psc_i2s->capture.bcom_task);
|
||||
bcom_gen_bd_tx_release(psc_i2s->playback.bcom_task);
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
struct mpc8610_hpcd_data {
|
||||
struct snd_soc_device sound_devdata;
|
||||
struct snd_soc_dai_link dai;
|
||||
struct snd_soc_machine machine;
|
||||
struct snd_soc_card machine;
|
||||
unsigned int dai_format;
|
||||
unsigned int codec_clk_direction;
|
||||
unsigned int cpu_clk_direction;
|
||||
|
@ -185,7 +185,7 @@ static struct snd_soc_ops mpc8610_hpcd_ops = {
|
|||
/**
|
||||
* mpc8610_hpcd_machine: ASoC machine data
|
||||
*/
|
||||
static struct snd_soc_machine mpc8610_hpcd_machine = {
|
||||
static struct snd_soc_card mpc8610_hpcd_machine = {
|
||||
.probe = mpc8610_hpcd_machine_probe,
|
||||
.remove = mpc8610_hpcd_machine_remove,
|
||||
.name = "MPC8610 HPCD",
|
||||
|
@ -465,9 +465,9 @@ static int mpc8610_hpcd_probe(struct of_device *ofdev,
|
|||
goto error;
|
||||
}
|
||||
|
||||
machine_data->sound_devdata.machine = &mpc8610_hpcd_machine;
|
||||
machine_data->sound_devdata.card = &mpc8610_hpcd_machine;
|
||||
machine_data->sound_devdata.codec_dev = &soc_codec_device_cs4270;
|
||||
machine_data->sound_devdata.platform = &fsl_soc_platform;
|
||||
machine_data->machine.platform = &fsl_soc_platform;
|
||||
|
||||
sound_device->dev.platform_data = machine_data;
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@ struct of_snd_soc_device {
|
|||
int id;
|
||||
struct list_head list;
|
||||
struct snd_soc_device device;
|
||||
struct snd_soc_machine machine;
|
||||
struct snd_soc_card card;
|
||||
struct snd_soc_dai_link dai_link;
|
||||
struct platform_device *pdev;
|
||||
struct device_node *platform_node;
|
||||
|
@ -58,9 +58,9 @@ of_snd_soc_get_device(struct device_node *codec_node)
|
|||
/* Initialize the structure and add it to the global list */
|
||||
of_soc->codec_node = codec_node;
|
||||
of_soc->id = of_snd_soc_next_index++;
|
||||
of_soc->machine.dai_link = &of_soc->dai_link;
|
||||
of_soc->machine.num_links = 1;
|
||||
of_soc->device.machine = &of_soc->machine;
|
||||
of_soc->card.dai_link = &of_soc->dai_link;
|
||||
of_soc->card.num_links = 1;
|
||||
of_soc->device.card = &of_soc->card;
|
||||
of_soc->dai_link.ops = &of_snd_soc_ops;
|
||||
list_add(&of_soc->list, &of_snd_soc_device_list);
|
||||
|
||||
|
@ -158,8 +158,8 @@ int of_snd_soc_register_platform(struct snd_soc_platform *platform,
|
|||
|
||||
of_soc->platform_node = node;
|
||||
of_soc->dai_link.cpu_dai = cpu_dai;
|
||||
of_soc->device.platform = platform;
|
||||
of_soc->machine.name = of_soc->dai_link.cpu_dai->name;
|
||||
of_soc->card.platform = platform;
|
||||
of_soc->card.name = of_soc->dai_link.cpu_dai->name;
|
||||
|
||||
/* Now try to register the SoC device */
|
||||
of_snd_soc_register_device(of_soc);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
config SND_OMAP_SOC
|
||||
tristate "SoC Audio for the Texas Instruments OMAP chips"
|
||||
depends on ARCH_OMAP && SND_SOC
|
||||
depends on ARCH_OMAP
|
||||
|
||||
config SND_OMAP_SOC_MCBSP
|
||||
tristate
|
||||
|
@ -21,3 +21,36 @@ config SND_OMAP_SOC_OSK5912
|
|||
select SND_SOC_TLV320AIC23
|
||||
help
|
||||
Say Y if you want to add support for SoC audio on osk5912.
|
||||
|
||||
config SND_OMAP_SOC_OVERO
|
||||
tristate "SoC Audio support for Gumstix Overo"
|
||||
depends on TWL4030_CORE && SND_OMAP_SOC && MACH_OVERO
|
||||
select SND_OMAP_SOC_MCBSP
|
||||
select SND_SOC_TWL4030
|
||||
help
|
||||
Say Y if you want to add support for SoC audio on the Gumstix Overo.
|
||||
|
||||
config SND_OMAP_SOC_OMAP2EVM
|
||||
tristate "SoC Audio support for OMAP2EVM board"
|
||||
depends on TWL4030_CORE && SND_OMAP_SOC && MACH_OMAP2EVM
|
||||
select SND_OMAP_SOC_MCBSP
|
||||
select SND_SOC_TWL4030
|
||||
help
|
||||
Say Y if you want to add support for SoC audio on the omap2evm board.
|
||||
|
||||
config SND_OMAP_SOC_SDP3430
|
||||
tristate "SoC Audio support for Texas Instruments SDP3430"
|
||||
depends on TWL4030_CORE && SND_OMAP_SOC && MACH_OMAP_3430SDP
|
||||
select SND_OMAP_SOC_MCBSP
|
||||
select SND_SOC_TWL4030
|
||||
help
|
||||
Say Y if you want to add support for SoC audio on Texas Instruments
|
||||
SDP3430.
|
||||
|
||||
config SND_OMAP_SOC_OMAP3_PANDORA
|
||||
tristate "SoC Audio support for OMAP3 Pandora"
|
||||
depends on TWL4030_CORE && SND_OMAP_SOC && MACH_OMAP3_PANDORA
|
||||
select SND_OMAP_SOC_MCBSP
|
||||
select SND_SOC_TWL4030
|
||||
help
|
||||
Say Y if you want to add support for SoC audio on the OMAP3 Pandora.
|
||||
|
|
|
@ -8,6 +8,14 @@ obj-$(CONFIG_SND_OMAP_SOC_MCBSP) += snd-soc-omap-mcbsp.o
|
|||
# OMAP Machine Support
|
||||
snd-soc-n810-objs := n810.o
|
||||
snd-soc-osk5912-objs := osk5912.o
|
||||
snd-soc-overo-objs := overo.o
|
||||
snd-soc-omap2evm-objs := omap2evm.o
|
||||
snd-soc-sdp3430-objs := sdp3430.o
|
||||
snd-soc-omap3pandora-objs := omap3pandora.o
|
||||
|
||||
obj-$(CONFIG_SND_OMAP_SOC_N810) += snd-soc-n810.o
|
||||
obj-$(CONFIG_SND_OMAP_SOC_OSK5912) += snd-soc-osk5912.o
|
||||
obj-$(CONFIG_SND_OMAP_SOC_OVERO) += snd-soc-overo.o
|
||||
obj-$(CONFIG_MACH_OMAP2EVM) += snd-soc-omap2evm.o
|
||||
obj-$(CONFIG_SND_OMAP_SOC_SDP3430) += snd-soc-sdp3430.o
|
||||
obj-$(CONFIG_SND_OMAP_SOC_OMAP3_PANDORA) += snd-soc-omap3pandora.o
|
||||
|
|
|
@ -70,9 +70,13 @@ static void n810_ext_control(struct snd_soc_codec *codec)
|
|||
|
||||
static int n810_startup(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_codec *codec = rtd->socdev->codec;
|
||||
|
||||
snd_pcm_hw_constraint_minmax(runtime,
|
||||
SNDRV_PCM_HW_PARAM_CHANNELS, 2, 2);
|
||||
|
||||
n810_ext_control(codec);
|
||||
return clk_enable(sys_clkout2);
|
||||
}
|
||||
|
@ -282,8 +286,9 @@ static struct snd_soc_dai_link n810_dai = {
|
|||
};
|
||||
|
||||
/* Audio machine driver */
|
||||
static struct snd_soc_machine snd_soc_machine_n810 = {
|
||||
static struct snd_soc_card snd_soc_n810 = {
|
||||
.name = "N810",
|
||||
.platform = &omap_soc_platform,
|
||||
.dai_link = &n810_dai,
|
||||
.num_links = 1,
|
||||
};
|
||||
|
@ -298,8 +303,7 @@ static struct aic3x_setup_data n810_aic33_setup = {
|
|||
|
||||
/* Audio subsystem */
|
||||
static struct snd_soc_device n810_snd_devdata = {
|
||||
.machine = &snd_soc_machine_n810,
|
||||
.platform = &omap_soc_platform,
|
||||
.card = &snd_soc_n810,
|
||||
.codec_dev = &soc_codec_dev_aic3x,
|
||||
.codec_data = &n810_aic33_setup,
|
||||
};
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue