1
0
Fork 0

ASoC: Final updates for v3.16

A few more updates from the last week of development, nothing too
 exciting.  Highlights include:
 
 - GPIO descriptor support for jacks
 - More updates and fixes to the Freescale SSI, Intel and rsnd drivers.
 - New drivers for Analog Devices ADAU1361, ADAU1381, ADAU1761 and
   ADAU1781, and Realtek RT5677.
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQIcBAABAgAGBQJTjZkHAAoJELSic+t+oim9pl0P/RTeNipwuUbFUWSYMcARc2wE
 Zpw+imtqjgE91ix5QR4OcQtL6+JtoOF7wfXA8dUmXRLrBqgSGDT/OJh771Jq4J7X
 LNTd7And4INeELeV7yajsDnHKD8P0V5Dmn+94HrjBgDuseELFn1izdGjM+0GpfKo
 i/C+595PtLSx3ekdYmsrXWyxgkUOrArhimiYKQzOtjncsGfXJCyhzvusXZQb3tCl
 Fa/HMTgMH8DlRInruN87s5CFr3O4seDB7LYOmjUIsPj4lorEFbqaGOUtPOCDJN1h
 tqMVuSIfbAEHX/ny9chsEYtMzOMPWk8s9TWn3LNfHkG8embkzaCaJqg8b8WIk/0U
 scqcKptarASYAGpSMu8IoSruC4+7GKtpo8zpGGJBclAMgmpXBSa381QnsiUGGocq
 BoBIjNSXzft3eRB0wjhGPLmJ5qMdHSEKuaDaACf2cot63ePF764cdg9EBItJLWEt
 G8nHQErUm4UfJapLvrDC+ItSNMLeaSJvCXrM1DPdfObqryKzJDbST/4B7m+w1ZGe
 2Cnc536hkYdZ6kFdDgtIyK5yTeAgoxHzYNMndejt4bjkK7bnCIxa49tZV9d8LsTG
 g03cCEkkE4nCHWsSL891YBKfLLujTZoIY+f5M396FhLHBXL5krCtPRWE42I/xo58
 wXH46Sd7HqDXxom5jpP+
 =iPCP
 -----END PGP SIGNATURE-----

Merge tag 'asoc-v3.16-2' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound into for-next

ASoC: Final updates for v3.16

A few more updates from the last week of development, nothing too
exciting.  Highlights include:

- GPIO descriptor support for jacks
- More updates and fixes to the Freescale SSI, Intel and rsnd drivers.
- New drivers for Analog Devices ADAU1361, ADAU1381, ADAU1761 and
  ADAU1781, and Realtek RT5677.
wifi-calibration
Takashi Iwai 2014-06-03 11:51:14 +02:00
commit 8743dcd663
94 changed files with 9613 additions and 907 deletions

View File

@ -10,6 +10,12 @@ Required properties:
- interrupts : The CODEC's interrupt output.
Optional properties:
- clocks: The phandle of the master clock to the CODEC
- clock-names: Should be "mclk"
Pins on the device (for linking into audio routes):
* MIC1

View File

@ -8,6 +8,12 @@ Required properties:
- reg : The I2C address of the device.
Optional properties:
- clocks: The phandle of the master clock to the CODEC
- clock-names: Should be "mclk"
Example:
max98095: codec@11 {

View File

@ -20,6 +20,7 @@ Required properties:
SSI subnode properties:
- interrupts : Should contain SSI interrupt for PIO transfer
- shared-pin : if shared clock pin
- pio-transfer : use PIO transfer mode
SRC subnode properties:
no properties at this point

View File

@ -15,6 +15,9 @@ Optional properties:
Each entry is a pair of strings, the first being the
connection's sink, the second being the connection's
source.
- simple-audio-card,mclk-fs : Multiplication factor between stream rate and codec
mclk.
Optional subnodes:
- simple-audio-card,dai-link : Container for dai-link level

View File

@ -0,0 +1,109 @@
/*
* Driver for ADAU1761/ADAU1461/ADAU1761/ADAU1961/ADAU1781/ADAU1781 codecs
*
* Copyright 2011-2014 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
*
* Licensed under the GPL-2 or later.
*/
#ifndef __LINUX_PLATFORM_DATA_ADAU17X1_H__
#define __LINUX_PLATFORM_DATA_ADAU17X1_H__
/**
* enum adau17x1_micbias_voltage - Microphone bias voltage
* @ADAU17X1_MICBIAS_0_90_AVDD: 0.9 * AVDD
* @ADAU17X1_MICBIAS_0_65_AVDD: 0.65 * AVDD
*/
enum adau17x1_micbias_voltage {
ADAU17X1_MICBIAS_0_90_AVDD = 0,
ADAU17X1_MICBIAS_0_65_AVDD = 1,
};
/**
* enum adau1761_digmic_jackdet_pin_mode - Configuration of the JACKDET/MICIN pin
* @ADAU1761_DIGMIC_JACKDET_PIN_MODE_NONE: Disable the pin
* @ADAU1761_DIGMIC_JACKDET_PIN_MODE_DIGMIC: Configure the pin for usage as
* digital microphone input.
* @ADAU1761_DIGMIC_JACKDET_PIN_MODE_JACKDETECT: Configure the pin for jack
* insertion detection.
*/
enum adau1761_digmic_jackdet_pin_mode {
ADAU1761_DIGMIC_JACKDET_PIN_MODE_NONE,
ADAU1761_DIGMIC_JACKDET_PIN_MODE_DIGMIC,
ADAU1761_DIGMIC_JACKDET_PIN_MODE_JACKDETECT,
};
/**
* adau1761_jackdetect_debounce_time - Jack insertion detection debounce time
* @ADAU1761_JACKDETECT_DEBOUNCE_5MS: 5 milliseconds
* @ADAU1761_JACKDETECT_DEBOUNCE_10MS: 10 milliseconds
* @ADAU1761_JACKDETECT_DEBOUNCE_20MS: 20 milliseconds
* @ADAU1761_JACKDETECT_DEBOUNCE_40MS: 40 milliseconds
*/
enum adau1761_jackdetect_debounce_time {
ADAU1761_JACKDETECT_DEBOUNCE_5MS = 0,
ADAU1761_JACKDETECT_DEBOUNCE_10MS = 1,
ADAU1761_JACKDETECT_DEBOUNCE_20MS = 2,
ADAU1761_JACKDETECT_DEBOUNCE_40MS = 3,
};
/**
* enum adau1761_output_mode - Output mode configuration
* @ADAU1761_OUTPUT_MODE_HEADPHONE: Headphone output
* @ADAU1761_OUTPUT_MODE_HEADPHONE_CAPLESS: Capless headphone output
* @ADAU1761_OUTPUT_MODE_LINE: Line output
*/
enum adau1761_output_mode {
ADAU1761_OUTPUT_MODE_HEADPHONE,
ADAU1761_OUTPUT_MODE_HEADPHONE_CAPLESS,
ADAU1761_OUTPUT_MODE_LINE,
};
/**
* struct adau1761_platform_data - ADAU1761 Codec driver platform data
* @input_differential: If true the input pins will be configured in
* differential mode.
* @lineout_mode: Output mode for the LOUT/ROUT pins
* @headphone_mode: Output mode for the LHP/RHP pins
* @digmic_jackdetect_pin_mode: JACKDET/MICIN pin configuration
* @jackdetect_debounce_time: Jack insertion detection debounce time.
* Note: This value will only be used, if the JACKDET/MICIN pin is configured
* for jack insertion detection.
* @jackdetect_active_low: If true the jack insertion detection is active low.
* Othwise it will be active high.
* @micbias_voltage: Microphone voltage bias
*/
struct adau1761_platform_data {
bool input_differential;
enum adau1761_output_mode lineout_mode;
enum adau1761_output_mode headphone_mode;
enum adau1761_digmic_jackdet_pin_mode digmic_jackdetect_pin_mode;
enum adau1761_jackdetect_debounce_time jackdetect_debounce_time;
bool jackdetect_active_low;
enum adau17x1_micbias_voltage micbias_voltage;
};
/**
* struct adau1781_platform_data - ADAU1781 Codec driver platform data
* @left_input_differential: If true configure the left input as
* differential input.
* @right_input_differential: If true configure the right input as differntial
* input.
* @use_dmic: If true configure the MIC pins as digital microphone pins instead
* of analog microphone pins.
* @micbias_voltage: Microphone voltage bias
*/
struct adau1781_platform_data {
bool left_input_differential;
bool right_input_differential;
bool use_dmic;
enum adau17x1_micbias_voltage micbias_voltage;
};
#endif

View File

@ -0,0 +1,21 @@
/*
* linux/sound/rt5677.h -- Platform data for RT5677
*
* Copyright 2013 Realtek Semiconductor Corp.
* Author: Oder Chiou <oder_chiou@realtek.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 __LINUX_SND_RT5677_H
#define __LINUX_SND_RT5677_H
struct rt5677_platform_data {
/* IN1 IN2 can optionally be differential */
bool in1_diff;
bool in2_diff;
};
#endif

View File

@ -452,6 +452,9 @@ int snd_soc_jack_get_type(struct snd_soc_jack *jack, int micbias_voltage);
#ifdef CONFIG_GPIOLIB
int snd_soc_jack_add_gpios(struct snd_soc_jack *jack, int count,
struct snd_soc_jack_gpio *gpios);
int snd_soc_jack_add_gpiods(struct device *gpiod_dev,
struct snd_soc_jack *jack,
int count, struct snd_soc_jack_gpio *gpios);
void snd_soc_jack_free_gpios(struct snd_soc_jack *jack, int count,
struct snd_soc_jack_gpio *gpios);
#else
@ -461,6 +464,14 @@ static inline int snd_soc_jack_add_gpios(struct snd_soc_jack *jack, int count,
return 0;
}
static inline int snd_soc_jack_add_gpiods(struct device *gpiod_dev,
struct snd_soc_jack *jack,
int count,
struct snd_soc_jack_gpio *gpios)
{
return 0;
}
static inline void snd_soc_jack_free_gpios(struct snd_soc_jack *jack, int count,
struct snd_soc_jack_gpio *gpios)
{
@ -587,8 +598,12 @@ struct snd_soc_jack_zone {
/**
* struct snd_soc_jack_gpio - Describes a gpio pin for jack detection
*
* @gpio: gpio number
* @name: gpio name
* @gpio: legacy gpio number
* @idx: gpio descriptor index within the function of the GPIO
* consumer device
* @gpiod_dev GPIO consumer device
* @name: gpio name. Also as connection ID for the GPIO consumer
* device function name lookup
* @report: value to report when jack detected
* @invert: report presence in low state
* @debouce_time: debouce time in ms
@ -599,6 +614,8 @@ struct snd_soc_jack_zone {
*/
struct snd_soc_jack_gpio {
unsigned int gpio;
unsigned int idx;
struct device *gpiod_dev;
const char *name;
int report;
int invert;
@ -607,6 +624,7 @@ struct snd_soc_jack_gpio {
struct snd_soc_jack *jack;
struct delayed_work work;
struct gpio_desc *desc;
void *data;
int (*jack_status_check)(void *data);
@ -1146,6 +1164,33 @@ static inline struct snd_soc_platform *snd_soc_component_to_platform(
return container_of(component, struct snd_soc_platform, component);
}
/**
* snd_soc_dapm_to_codec() - Casts a DAPM context to the CODEC it is embedded in
* @dapm: The DAPM context to cast to the CODEC
*
* This function must only be used on DAPM contexts that are known to be part of
* a CODEC (e.g. in a CODEC driver). Otherwise the behavior is undefined.
*/
static inline struct snd_soc_codec *snd_soc_dapm_to_codec(
struct snd_soc_dapm_context *dapm)
{
return container_of(dapm, struct snd_soc_codec, dapm);
}
/**
* snd_soc_dapm_to_platform() - Casts a DAPM context to the platform it is
* embedded in
* @dapm: The DAPM context to cast to the platform.
*
* This function must only be used on DAPM contexts that are known to be part of
* a platform (e.g. in a platform driver). Otherwise the behavior is undefined.
*/
static inline struct snd_soc_platform *snd_soc_dapm_to_platform(
struct snd_soc_dapm_context *dapm)
{
return container_of(dapm, struct snd_soc_platform, dapm);
}
/* codec IO */
unsigned int snd_soc_read(struct snd_soc_codec *codec, unsigned int reg);
int snd_soc_write(struct snd_soc_codec *codec, unsigned int reg,

View File

@ -43,6 +43,32 @@ config SND_SOC_BFIN_EVAL_ADAU1373
Note: This driver assumes that first ADAU1373 DAI is connected to the
first SPORT port on the BF5XX board.
config SND_SOC_BFIN_EVAL_ADAU1X61
tristate "Support for the EVAL-ADAU1X61 board on Blackfin eval boards"
depends on SND_BF5XX_I2S && I2C
select SND_BF5XX_SOC_I2S
select SND_SOC_ADAU1761_I2C
help
Say Y if you want to add support for the Analog Devices EVAL-ADAU1X61
board connected to one of the Blackfin evaluation boards like the
BF5XX-STAMP or BF5XX-EZKIT.
Note: This driver assumes that the ADAU1X61 is connected to the
first SPORT port on the BF5XX board.
config SND_SOC_BFIN_EVAL_ADAU1X81
tristate "Support for the EVAL-ADAU1X81 boards on Blackfin eval boards"
depends on SND_BF5XX_I2S && I2C
select SND_BF5XX_SOC_I2S
select SND_SOC_ADAU1781_I2C
help
Say Y if you want to add support for the Analog Devices EVAL-ADAU1X81
board connected to one of the Blackfin evaluation boards like the
BF5XX-STAMP or BF5XX-EZKIT.
Note: This driver assumes that the ADAU1X81 is connected to the
first SPORT port on the BF5XX board.
config SND_SOC_BFIN_EVAL_ADAV80X
tristate "Support for the EVAL-ADAV80X boards on Blackfin eval boards"
depends on SND_BF5XX_I2S && SND_SOC_I2C_AND_SPI

View File

@ -22,6 +22,8 @@ snd-ssm2602-objs := bf5xx-ssm2602.o
snd-ad73311-objs := bf5xx-ad73311.o
snd-ad193x-objs := bf5xx-ad193x.o
snd-soc-bfin-eval-adau1373-objs := bfin-eval-adau1373.o
snd-soc-bfin-eval-adau1x61-objs := bfin-eval-adau1x61.o
snd-soc-bfin-eval-adau1x81-objs := bfin-eval-adau1x81.o
snd-soc-bfin-eval-adau1701-objs := bfin-eval-adau1701.o
snd-soc-bfin-eval-adav80x-objs := bfin-eval-adav80x.o
@ -31,5 +33,7 @@ obj-$(CONFIG_SND_BF5XX_SOC_SSM2602) += snd-ssm2602.o
obj-$(CONFIG_SND_BF5XX_SOC_AD73311) += snd-ad73311.o
obj-$(CONFIG_SND_BF5XX_SOC_AD193X) += snd-ad193x.o
obj-$(CONFIG_SND_SOC_BFIN_EVAL_ADAU1373) += snd-soc-bfin-eval-adau1373.o
obj-$(CONFIG_SND_SOC_BFIN_EVAL_ADAU1X61) += snd-soc-bfin-eval-adau1x61.o
obj-$(CONFIG_SND_SOC_BFIN_EVAL_ADAU1X81) += snd-soc-bfin-eval-adau1x81.o
obj-$(CONFIG_SND_SOC_BFIN_EVAL_ADAU1701) += snd-soc-bfin-eval-adau1701.o
obj-$(CONFIG_SND_SOC_BFIN_EVAL_ADAV80X) += snd-soc-bfin-eval-adav80x.o

View File

@ -0,0 +1,142 @@
/*
* Machine driver for EVAL-ADAU1x61MINIZ on Analog Devices bfin
* evaluation boards.
*
* Copyright 2011-2014 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
*
* Licensed under the GPL-2 or later.
*/
#include <linux/module.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/soc.h>
#include <sound/pcm_params.h>
#include "../codecs/adau17x1.h"
static const struct snd_soc_dapm_widget bfin_eval_adau1x61_dapm_widgets[] = {
SND_SOC_DAPM_LINE("In 1", NULL),
SND_SOC_DAPM_LINE("In 2", NULL),
SND_SOC_DAPM_LINE("In 3-4", NULL),
SND_SOC_DAPM_LINE("Diff Out L", NULL),
SND_SOC_DAPM_LINE("Diff Out R", NULL),
SND_SOC_DAPM_LINE("Stereo Out", NULL),
SND_SOC_DAPM_HP("Capless HP Out", NULL),
};
static const struct snd_soc_dapm_route bfin_eval_adau1x61_dapm_routes[] = {
{ "LAUX", NULL, "In 3-4" },
{ "RAUX", NULL, "In 3-4" },
{ "LINP", NULL, "In 1" },
{ "LINN", NULL, "In 1"},
{ "RINP", NULL, "In 2" },
{ "RINN", NULL, "In 2" },
{ "In 1", NULL, "MICBIAS" },
{ "In 2", NULL, "MICBIAS" },
{ "Capless HP Out", NULL, "LHP" },
{ "Capless HP Out", NULL, "RHP" },
{ "Diff Out L", NULL, "LOUT" },
{ "Diff Out R", NULL, "ROUT" },
{ "Stereo Out", NULL, "LOUT" },
{ "Stereo Out", NULL, "ROUT" },
};
static int bfin_eval_adau1x61_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *codec_dai = rtd->codec_dai;
int pll_rate;
int ret;
switch (params_rate(params)) {
case 48000:
case 8000:
case 12000:
case 16000:
case 24000:
case 32000:
case 96000:
pll_rate = 48000 * 1024;
break;
case 44100:
case 7350:
case 11025:
case 14700:
case 22050:
case 29400:
case 88200:
pll_rate = 44100 * 1024;
break;
default:
return -EINVAL;
}
ret = snd_soc_dai_set_pll(codec_dai, ADAU17X1_PLL,
ADAU17X1_PLL_SRC_MCLK, 12288000, pll_rate);
if (ret)
return ret;
ret = snd_soc_dai_set_sysclk(codec_dai, ADAU17X1_CLK_SRC_PLL, pll_rate,
SND_SOC_CLOCK_IN);
return ret;
}
static const struct snd_soc_ops bfin_eval_adau1x61_ops = {
.hw_params = bfin_eval_adau1x61_hw_params,
};
static struct snd_soc_dai_link bfin_eval_adau1x61_dai = {
.name = "adau1x61",
.stream_name = "adau1x61",
.cpu_dai_name = "bfin-i2s.0",
.codec_dai_name = "adau-hifi",
.platform_name = "bfin-i2s-pcm-audio",
.codec_name = "adau1761.0-0038",
.ops = &bfin_eval_adau1x61_ops,
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBM_CFM,
};
static struct snd_soc_card bfin_eval_adau1x61 = {
.name = "bfin-eval-adau1x61",
.driver_name = "eval-adau1x61",
.dai_link = &bfin_eval_adau1x61_dai,
.num_links = 1,
.dapm_widgets = bfin_eval_adau1x61_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(bfin_eval_adau1x61_dapm_widgets),
.dapm_routes = bfin_eval_adau1x61_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(bfin_eval_adau1x61_dapm_routes),
.fully_routed = true,
};
static int bfin_eval_adau1x61_probe(struct platform_device *pdev)
{
bfin_eval_adau1x61.dev = &pdev->dev;
return devm_snd_soc_register_card(&pdev->dev, &bfin_eval_adau1x61);
}
static struct platform_driver bfin_eval_adau1x61_driver = {
.driver = {
.name = "bfin-eval-adau1x61",
.owner = THIS_MODULE,
.pm = &snd_soc_pm_ops,
},
.probe = bfin_eval_adau1x61_probe,
};
module_platform_driver(bfin_eval_adau1x61_driver);
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
MODULE_DESCRIPTION("ALSA SoC bfin adau1x61 driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:bfin-eval-adau1x61");

View File

@ -0,0 +1,130 @@
/*
* Machine driver for EVAL-ADAU1x81 on Analog Devices bfin
* evaluation boards.
*
* Copyright 2011-2014 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
*
* Licensed under the GPL-2 or later.
*/
#include <linux/module.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/soc.h>
#include <sound/pcm_params.h>
#include "../codecs/adau17x1.h"
static const struct snd_soc_dapm_widget bfin_eval_adau1x81_dapm_widgets[] = {
SND_SOC_DAPM_LINE("Stereo In", NULL),
SND_SOC_DAPM_LINE("Beep", NULL),
SND_SOC_DAPM_SPK("Speaker", NULL),
SND_SOC_DAPM_HP("Headphone", NULL),
};
static const struct snd_soc_dapm_route bfin_eval_adau1x81_dapm_routes[] = {
{ "BEEP", NULL, "Beep" },
{ "LMIC", NULL, "Stereo In" },
{ "LMIC", NULL, "Stereo In" },
{ "Headphone", NULL, "AOUTL" },
{ "Headphone", NULL, "AOUTR" },
{ "Speaker", NULL, "SP" },
};
static int bfin_eval_adau1x81_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *codec_dai = rtd->codec_dai;
int pll_rate;
int ret;
switch (params_rate(params)) {
case 48000:
case 8000:
case 12000:
case 16000:
case 24000:
case 32000:
case 96000:
pll_rate = 48000 * 1024;
break;
case 44100:
case 7350:
case 11025:
case 14700:
case 22050:
case 29400:
case 88200:
pll_rate = 44100 * 1024;
break;
default:
return -EINVAL;
}
ret = snd_soc_dai_set_pll(codec_dai, ADAU17X1_PLL,
ADAU17X1_PLL_SRC_MCLK, 12288000, pll_rate);
if (ret)
return ret;
ret = snd_soc_dai_set_sysclk(codec_dai, ADAU17X1_CLK_SRC_PLL, pll_rate,
SND_SOC_CLOCK_IN);
return ret;
}
static const struct snd_soc_ops bfin_eval_adau1x81_ops = {
.hw_params = bfin_eval_adau1x81_hw_params,
};
static struct snd_soc_dai_link bfin_eval_adau1x81_dai = {
.name = "adau1x81",
.stream_name = "adau1x81",
.cpu_dai_name = "bfin-i2s.0",
.codec_dai_name = "adau-hifi",
.platform_name = "bfin-i2s-pcm-audio",
.codec_name = "adau1781.0-0038",
.ops = &bfin_eval_adau1x81_ops,
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBM_CFM,
};
static struct snd_soc_card bfin_eval_adau1x81 = {
.name = "bfin-eval-adau1x81",
.driver_name = "eval-adau1x81",
.dai_link = &bfin_eval_adau1x81_dai,
.num_links = 1,
.dapm_widgets = bfin_eval_adau1x81_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(bfin_eval_adau1x81_dapm_widgets),
.dapm_routes = bfin_eval_adau1x81_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(bfin_eval_adau1x81_dapm_routes),
.fully_routed = true,
};
static int bfin_eval_adau1x81_probe(struct platform_device *pdev)
{
bfin_eval_adau1x81.dev = &pdev->dev;
return devm_snd_soc_register_card(&pdev->dev, &bfin_eval_adau1x81);
}
static struct platform_driver bfin_eval_adau1x81_driver = {
.driver = {
.name = "bfin-eval-adau1x81",
.owner = THIS_MODULE,
.pm = &snd_soc_pm_ops,
},
.probe = bfin_eval_adau1x81_probe,
};
module_platform_driver(bfin_eval_adau1x81_driver);
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
MODULE_DESCRIPTION("ALSA SoC bfin adau1x81 driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:bfin-eval-adau1x81");

View File

@ -23,6 +23,10 @@ config SND_SOC_ALL_CODECS
select SND_SOC_AD1980 if SND_SOC_AC97_BUS
select SND_SOC_AD73311
select SND_SOC_ADAU1373 if I2C
select SND_SOC_ADAU1761_I2C if I2C
select SND_SOC_ADAU1761_SPI if SPI
select SND_SOC_ADAU1781_I2C if I2C
select SND_SOC_ADAU1781_SPI if SPI
select SND_SOC_ADAV801 if SPI_MASTER
select SND_SOC_ADAV803 if I2C
select SND_SOC_ADAU1977_SPI if SPI_MASTER
@ -74,6 +78,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_RT5640 if I2C
select SND_SOC_RT5645 if I2C
select SND_SOC_RT5651 if I2C
select SND_SOC_RT5677 if I2C
select SND_SOC_SGTL5000 if I2C
select SND_SOC_SI476X if MFD_SI476X_CORE
select SND_SOC_SIRF_AUDIO_CODEC
@ -214,13 +219,45 @@ config SND_SOC_AD1980
config SND_SOC_AD73311
tristate
config SND_SOC_ADAU1373
tristate
config SND_SOC_ADAU1701
tristate "Analog Devices ADAU1701 CODEC"
depends on I2C
select SND_SOC_SIGMADSP
config SND_SOC_ADAU1373
config SND_SOC_ADAU17X1
tristate
select SND_SOC_SIGMADSP
config SND_SOC_ADAU1761
tristate
select SND_SOC_ADAU17X1
config SND_SOC_ADAU1761_I2C
tristate
select SND_SOC_ADAU1761
select REGMAP_I2C
config SND_SOC_ADAU1761_SPI
tristate
select SND_SOC_ADAU1761
select REGMAP_SPI
config SND_SOC_ADAU1781
select SND_SOC_ADAU17X1
tristate
config SND_SOC_ADAU1781_I2C
tristate
select SND_SOC_ADAU1781
select REGMAP_I2C
config SND_SOC_ADAU1781_SPI
tristate
select SND_SOC_ADAU1781
select REGMAP_SPI
config SND_SOC_ADAU1977
tristate
@ -274,6 +311,7 @@ config SND_SOC_AK5386
config SND_SOC_ALC5623
tristate "Realtek ALC5623 CODEC"
depends on I2C
config SND_SOC_ALC5632
tristate
@ -402,6 +440,15 @@ config SND_SOC_PCM512x_SPI
select SND_SOC_PCM512x
select REGMAP_SPI
config SND_SOC_RL6231
tristate
default y if SND_SOC_RT5640=y
default y if SND_SOC_RT5645=y
default y if SND_SOC_RT5651=y
default m if SND_SOC_RT5640=m
default m if SND_SOC_RT5645=m
default m if SND_SOC_RT5651=m
config SND_SOC_RT5631
tristate
@ -414,6 +461,9 @@ config SND_SOC_RT5645
config SND_SOC_RT5651
tristate
config SND_SOC_RT5677
tristate
#Freescale sgtl5000 codec
config SND_SOC_SGTL5000
tristate "Freescale SGTL5000 CODEC"

View File

@ -7,8 +7,15 @@ snd-soc-ad193x-spi-objs := ad193x-spi.o
snd-soc-ad193x-i2c-objs := ad193x-i2c.o
snd-soc-ad1980-objs := ad1980.o
snd-soc-ad73311-objs := ad73311.o
snd-soc-adau1701-objs := adau1701.o
snd-soc-adau1373-objs := adau1373.o
snd-soc-adau1701-objs := adau1701.o
snd-soc-adau17x1-objs := adau17x1.o
snd-soc-adau1761-objs := adau1761.o
snd-soc-adau1761-i2c-objs := adau1761-i2c.o
snd-soc-adau1761-spi-objs := adau1761-spi.o
snd-soc-adau1781-objs := adau1781.o
snd-soc-adau1781-i2c-objs := adau1781-i2c.o
snd-soc-adau1781-spi-objs := adau1781-spi.o
snd-soc-adau1977-objs := adau1977.o
snd-soc-adau1977-spi-objs := adau1977-spi.o
snd-soc-adau1977-i2c-objs := adau1977-i2c.o
@ -60,10 +67,12 @@ snd-soc-pcm3008-objs := pcm3008.o
snd-soc-pcm512x-objs := pcm512x.o
snd-soc-pcm512x-i2c-objs := pcm512x-i2c.o
snd-soc-pcm512x-spi-objs := pcm512x-spi.o
snd-soc-rl6231-objs := rl6231.o
snd-soc-rt5631-objs := rt5631.o
snd-soc-rt5640-objs := rt5640.o
snd-soc-rt5645-objs := rt5645.o
snd-soc-rt5651-objs := rt5651.o
snd-soc-rt5677-objs := rt5677.o
snd-soc-sgtl5000-objs := sgtl5000.o
snd-soc-alc5623-objs := alc5623.o
snd-soc-alc5632-objs := alc5632.o
@ -162,10 +171,17 @@ obj-$(CONFIG_SND_SOC_AD193X_I2C) += snd-soc-ad193x-i2c.o
obj-$(CONFIG_SND_SOC_AD1980) += snd-soc-ad1980.o
obj-$(CONFIG_SND_SOC_AD73311) += snd-soc-ad73311.o
obj-$(CONFIG_SND_SOC_ADAU1373) += snd-soc-adau1373.o
obj-$(CONFIG_SND_SOC_ADAU1701) += snd-soc-adau1701.o
obj-$(CONFIG_SND_SOC_ADAU17X1) += snd-soc-adau17x1.o
obj-$(CONFIG_SND_SOC_ADAU1761) += snd-soc-adau1761.o
obj-$(CONFIG_SND_SOC_ADAU1761_I2C) += snd-soc-adau1761-i2c.o
obj-$(CONFIG_SND_SOC_ADAU1761_SPI) += snd-soc-adau1761-spi.o
obj-$(CONFIG_SND_SOC_ADAU1781) += snd-soc-adau1781.o
obj-$(CONFIG_SND_SOC_ADAU1781_I2C) += snd-soc-adau1781-i2c.o
obj-$(CONFIG_SND_SOC_ADAU1781_SPI) += snd-soc-adau1781-spi.o
obj-$(CONFIG_SND_SOC_ADAU1977) += snd-soc-adau1977.o
obj-$(CONFIG_SND_SOC_ADAU1977_SPI) += snd-soc-adau1977-spi.o
obj-$(CONFIG_SND_SOC_ADAU1977_I2C) += snd-soc-adau1977-i2c.o
obj-$(CONFIG_SND_SOC_ADAU1701) += snd-soc-adau1701.o
obj-$(CONFIG_SND_SOC_ADAV80X) += snd-soc-adav80x.o
obj-$(CONFIG_SND_SOC_ADAV801) += snd-soc-adav801.o
obj-$(CONFIG_SND_SOC_ADAV803) += snd-soc-adav803.o
@ -216,10 +232,12 @@ obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o
obj-$(CONFIG_SND_SOC_PCM512x) += snd-soc-pcm512x.o
obj-$(CONFIG_SND_SOC_PCM512x_I2C) += snd-soc-pcm512x-i2c.o
obj-$(CONFIG_SND_SOC_PCM512x_SPI) += snd-soc-pcm512x-spi.o
obj-$(CONFIG_SND_SOC_RL6231) += snd-soc-rl6231.o
obj-$(CONFIG_SND_SOC_RT5631) += snd-soc-rt5631.o
obj-$(CONFIG_SND_SOC_RT5640) += snd-soc-rt5640.o
obj-$(CONFIG_SND_SOC_RT5645) += snd-soc-rt5645.o
obj-$(CONFIG_SND_SOC_RT5651) += snd-soc-rt5651.o
obj-$(CONFIG_SND_SOC_RT5677) += snd-soc-rt5677.o
obj-$(CONFIG_SND_SOC_SGTL5000) += snd-soc-sgtl5000.o
obj-$(CONFIG_SND_SOC_SIGMADSP) += snd-soc-sigmadsp.o
obj-$(CONFIG_SND_SOC_SI476X) += snd-soc-si476x.o

View File

@ -0,0 +1,60 @@
/*
* Driver for ADAU1761/ADAU1461/ADAU1761/ADAU1961 codec
*
* Copyright 2014 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
*
* Licensed under the GPL-2.
*/
#include <linux/i2c.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <sound/soc.h>
#include "adau1761.h"
static int adau1761_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct regmap_config config;
config = adau1761_regmap_config;
config.val_bits = 8;
config.reg_bits = 16;
return adau1761_probe(&client->dev,
devm_regmap_init_i2c(client, &config),
id->driver_data, NULL);
}
static int adau1761_i2c_remove(struct i2c_client *client)
{
snd_soc_unregister_codec(&client->dev);
return 0;
}
static const struct i2c_device_id adau1761_i2c_ids[] = {
{ "adau1361", ADAU1361 },
{ "adau1461", ADAU1761 },
{ "adau1761", ADAU1761 },
{ "adau1961", ADAU1361 },
{ }
};
MODULE_DEVICE_TABLE(i2c, adau1761_i2c_ids);
static struct i2c_driver adau1761_i2c_driver = {
.driver = {
.name = "adau1761",
.owner = THIS_MODULE,
},
.probe = adau1761_i2c_probe,
.remove = adau1761_i2c_remove,
.id_table = adau1761_i2c_ids,
};
module_i2c_driver(adau1761_i2c_driver);
MODULE_DESCRIPTION("ASoC ADAU1361/ADAU1461/ADAU1761/ADAU1961 CODEC I2C driver");
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,77 @@
/*
* Driver for ADAU1761/ADAU1461/ADAU1761/ADAU1961 codec
*
* Copyright 2014 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
*
* Licensed under the GPL-2.
*/
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/spi/spi.h>
#include <sound/soc.h>
#include "adau1761.h"
static void adau1761_spi_switch_mode(struct device *dev)
{
struct spi_device *spi = to_spi_device(dev);
/*
* To get the device into SPI mode CLATCH has to be pulled low three
* times. Do this by issuing three dummy reads.
*/
spi_w8r8(spi, 0x00);
spi_w8r8(spi, 0x00);
spi_w8r8(spi, 0x00);
}
static int adau1761_spi_probe(struct spi_device *spi)
{
const struct spi_device_id *id = spi_get_device_id(spi);
struct regmap_config config;
if (!id)
return -EINVAL;
config = adau1761_regmap_config;
config.val_bits = 8;
config.reg_bits = 24;
config.read_flag_mask = 0x1;
return adau1761_probe(&spi->dev,
devm_regmap_init_spi(spi, &config),
id->driver_data, adau1761_spi_switch_mode);
}
static int adau1761_spi_remove(struct spi_device *spi)
{
snd_soc_unregister_codec(&spi->dev);
return 0;
}
static const struct spi_device_id adau1761_spi_id[] = {
{ "adau1361", ADAU1361 },
{ "adau1461", ADAU1761 },
{ "adau1761", ADAU1761 },
{ "adau1961", ADAU1361 },
{ }
};
MODULE_DEVICE_TABLE(spi, adau1761_spi_id);
static struct spi_driver adau1761_spi_driver = {
.driver = {
.name = "adau1761",
.owner = THIS_MODULE,
},
.probe = adau1761_spi_probe,
.remove = adau1761_spi_remove,
.id_table = adau1761_spi_id,
};
module_spi_driver(adau1761_spi_driver);
MODULE_DESCRIPTION("ASoC ADAU1361/ADAU1461/ADAU1761/ADAU1961 CODEC SPI driver");
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,803 @@
/*
* Driver for ADAU1761/ADAU1461/ADAU1761/ADAU1961 codec
*
* Copyright 2011-2013 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
*
* Licensed under the GPL-2 or later.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/spi/spi.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/tlv.h>
#include <linux/platform_data/adau17x1.h>
#include "adau17x1.h"
#include "adau1761.h"
#define ADAU1761_DIGMIC_JACKDETECT 0x4008
#define ADAU1761_REC_MIXER_LEFT0 0x400a
#define ADAU1761_REC_MIXER_LEFT1 0x400b
#define ADAU1761_REC_MIXER_RIGHT0 0x400c
#define ADAU1761_REC_MIXER_RIGHT1 0x400d
#define ADAU1761_LEFT_DIFF_INPUT_VOL 0x400e
#define ADAU1761_RIGHT_DIFF_INPUT_VOL 0x400f
#define ADAU1761_PLAY_LR_MIXER_LEFT 0x4020
#define ADAU1761_PLAY_MIXER_LEFT0 0x401c
#define ADAU1761_PLAY_MIXER_LEFT1 0x401d
#define ADAU1761_PLAY_MIXER_RIGHT0 0x401e
#define ADAU1761_PLAY_MIXER_RIGHT1 0x401f
#define ADAU1761_PLAY_LR_MIXER_RIGHT 0x4021
#define ADAU1761_PLAY_MIXER_MONO 0x4022
#define ADAU1761_PLAY_HP_LEFT_VOL 0x4023
#define ADAU1761_PLAY_HP_RIGHT_VOL 0x4024
#define ADAU1761_PLAY_LINE_LEFT_VOL 0x4025
#define ADAU1761_PLAY_LINE_RIGHT_VOL 0x4026
#define ADAU1761_PLAY_MONO_OUTPUT_VOL 0x4027
#define ADAU1761_POP_CLICK_SUPPRESS 0x4028
#define ADAU1761_JACK_DETECT_PIN 0x4031
#define ADAU1761_DEJITTER 0x4036
#define ADAU1761_CLK_ENABLE0 0x40f9
#define ADAU1761_CLK_ENABLE1 0x40fa
#define ADAU1761_DIGMIC_JACKDETECT_ACTIVE_LOW BIT(0)
#define ADAU1761_DIGMIC_JACKDETECT_DIGMIC BIT(5)
#define ADAU1761_DIFF_INPUT_VOL_LDEN BIT(0)
#define ADAU1761_PLAY_MONO_OUTPUT_VOL_MODE_HP BIT(0)
#define ADAU1761_PLAY_MONO_OUTPUT_VOL_UNMUTE BIT(1)
#define ADAU1761_PLAY_HP_RIGHT_VOL_MODE_HP BIT(0)
#define ADAU1761_PLAY_LINE_LEFT_VOL_MODE_HP BIT(0)
#define ADAU1761_PLAY_LINE_RIGHT_VOL_MODE_HP BIT(0)
#define ADAU1761_FIRMWARE "adau1761.bin"
static const struct reg_default adau1761_reg_defaults[] = {
{ ADAU1761_DEJITTER, 0x03 },
{ ADAU1761_DIGMIC_JACKDETECT, 0x00 },
{ ADAU1761_REC_MIXER_LEFT0, 0x00 },
{ ADAU1761_REC_MIXER_LEFT1, 0x00 },
{ ADAU1761_REC_MIXER_RIGHT0, 0x00 },
{ ADAU1761_REC_MIXER_RIGHT1, 0x00 },
{ ADAU1761_LEFT_DIFF_INPUT_VOL, 0x00 },
{ ADAU1761_RIGHT_DIFF_INPUT_VOL, 0x00 },
{ ADAU1761_PLAY_LR_MIXER_LEFT, 0x00 },
{ ADAU1761_PLAY_MIXER_LEFT0, 0x00 },
{ ADAU1761_PLAY_MIXER_LEFT1, 0x00 },
{ ADAU1761_PLAY_MIXER_RIGHT0, 0x00 },
{ ADAU1761_PLAY_MIXER_RIGHT1, 0x00 },
{ ADAU1761_PLAY_LR_MIXER_RIGHT, 0x00 },
{ ADAU1761_PLAY_MIXER_MONO, 0x00 },
{ ADAU1761_PLAY_HP_LEFT_VOL, 0x00 },
{ ADAU1761_PLAY_HP_RIGHT_VOL, 0x00 },
{ ADAU1761_PLAY_LINE_LEFT_VOL, 0x00 },
{ ADAU1761_PLAY_LINE_RIGHT_VOL, 0x00 },
{ ADAU1761_PLAY_MONO_OUTPUT_VOL, 0x00 },
{ ADAU1761_POP_CLICK_SUPPRESS, 0x00 },
{ ADAU1761_JACK_DETECT_PIN, 0x00 },
{ ADAU1761_CLK_ENABLE0, 0x00 },
{ ADAU1761_CLK_ENABLE1, 0x00 },
{ ADAU17X1_CLOCK_CONTROL, 0x00 },
{ ADAU17X1_PLL_CONTROL, 0x00 },
{ ADAU17X1_REC_POWER_MGMT, 0x00 },
{ ADAU17X1_MICBIAS, 0x00 },
{ ADAU17X1_SERIAL_PORT0, 0x00 },
{ ADAU17X1_SERIAL_PORT1, 0x00 },
{ ADAU17X1_CONVERTER0, 0x00 },
{ ADAU17X1_CONVERTER1, 0x00 },
{ ADAU17X1_LEFT_INPUT_DIGITAL_VOL, 0x00 },
{ ADAU17X1_RIGHT_INPUT_DIGITAL_VOL, 0x00 },
{ ADAU17X1_ADC_CONTROL, 0x00 },
{ ADAU17X1_PLAY_POWER_MGMT, 0x00 },
{ ADAU17X1_DAC_CONTROL0, 0x00 },
{ ADAU17X1_DAC_CONTROL1, 0x00 },
{ ADAU17X1_DAC_CONTROL2, 0x00 },
{ ADAU17X1_SERIAL_PORT_PAD, 0xaa },
{ ADAU17X1_CONTROL_PORT_PAD0, 0xaa },
{ ADAU17X1_CONTROL_PORT_PAD1, 0x00 },
{ ADAU17X1_DSP_SAMPLING_RATE, 0x01 },
{ ADAU17X1_SERIAL_INPUT_ROUTE, 0x00 },
{ ADAU17X1_SERIAL_OUTPUT_ROUTE, 0x00 },
{ ADAU17X1_DSP_ENABLE, 0x00 },
{ ADAU17X1_DSP_RUN, 0x00 },
{ ADAU17X1_SERIAL_SAMPLING_RATE, 0x00 },
};
static const DECLARE_TLV_DB_SCALE(adau1761_sing_in_tlv, -1500, 300, 1);
static const DECLARE_TLV_DB_SCALE(adau1761_diff_in_tlv, -1200, 75, 0);
static const DECLARE_TLV_DB_SCALE(adau1761_out_tlv, -5700, 100, 0);
static const DECLARE_TLV_DB_SCALE(adau1761_sidetone_tlv, -1800, 300, 1);
static const DECLARE_TLV_DB_SCALE(adau1761_boost_tlv, -600, 600, 1);
static const DECLARE_TLV_DB_SCALE(adau1761_pga_boost_tlv, -2000, 2000, 1);
static const unsigned int adau1761_bias_select_values[] = {
0, 2, 3,
};
static const char * const adau1761_bias_select_text[] = {
"Normal operation", "Enhanced performance", "Power saving",
};
static const char * const adau1761_bias_select_extreme_text[] = {
"Normal operation", "Extreme power saving", "Enhanced performance",
"Power saving",
};
static SOC_ENUM_SINGLE_DECL(adau1761_adc_bias_enum,
ADAU17X1_REC_POWER_MGMT, 3, adau1761_bias_select_extreme_text);
static SOC_ENUM_SINGLE_DECL(adau1761_hp_bias_enum,
ADAU17X1_PLAY_POWER_MGMT, 6, adau1761_bias_select_extreme_text);
static SOC_ENUM_SINGLE_DECL(adau1761_dac_bias_enum,
ADAU17X1_PLAY_POWER_MGMT, 4, adau1761_bias_select_extreme_text);
static SOC_VALUE_ENUM_SINGLE_DECL(adau1761_playback_bias_enum,
ADAU17X1_PLAY_POWER_MGMT, 2, 0x3, adau1761_bias_select_text,
adau1761_bias_select_values);
static SOC_VALUE_ENUM_SINGLE_DECL(adau1761_capture_bias_enum,
ADAU17X1_REC_POWER_MGMT, 1, 0x3, adau1761_bias_select_text,
adau1761_bias_select_values);
static const struct snd_kcontrol_new adau1761_jack_detect_controls[] = {
SOC_SINGLE("Speaker Auto-mute Switch", ADAU1761_DIGMIC_JACKDETECT,
4, 1, 0),
};
static const struct snd_kcontrol_new adau1761_differential_mode_controls[] = {
SOC_DOUBLE_R_TLV("Capture Volume", ADAU1761_LEFT_DIFF_INPUT_VOL,
ADAU1761_RIGHT_DIFF_INPUT_VOL, 2, 0x3f, 0,
adau1761_diff_in_tlv),
SOC_DOUBLE_R("Capture Switch", ADAU1761_LEFT_DIFF_INPUT_VOL,
ADAU1761_RIGHT_DIFF_INPUT_VOL, 1, 1, 0),
SOC_DOUBLE_R_TLV("PGA Boost Capture Volume", ADAU1761_REC_MIXER_LEFT1,
ADAU1761_REC_MIXER_RIGHT1, 3, 2, 0, adau1761_pga_boost_tlv),
};
static const struct snd_kcontrol_new adau1761_single_mode_controls[] = {
SOC_SINGLE_TLV("Input 1 Capture Volume", ADAU1761_REC_MIXER_LEFT0,
4, 7, 0, adau1761_sing_in_tlv),
SOC_SINGLE_TLV("Input 2 Capture Volume", ADAU1761_REC_MIXER_LEFT0,
1, 7, 0, adau1761_sing_in_tlv),
SOC_SINGLE_TLV("Input 3 Capture Volume", ADAU1761_REC_MIXER_RIGHT0,
4, 7, 0, adau1761_sing_in_tlv),
SOC_SINGLE_TLV("Input 4 Capture Volume", ADAU1761_REC_MIXER_RIGHT0,
1, 7, 0, adau1761_sing_in_tlv),
};
static const struct snd_kcontrol_new adau1761_controls[] = {
SOC_DOUBLE_R_TLV("Aux Capture Volume", ADAU1761_REC_MIXER_LEFT1,
ADAU1761_REC_MIXER_RIGHT1, 0, 7, 0, adau1761_sing_in_tlv),
SOC_DOUBLE_R_TLV("Headphone Playback Volume", ADAU1761_PLAY_HP_LEFT_VOL,
ADAU1761_PLAY_HP_RIGHT_VOL, 2, 0x3f, 0, adau1761_out_tlv),
SOC_DOUBLE_R("Headphone Playback Switch", ADAU1761_PLAY_HP_LEFT_VOL,
ADAU1761_PLAY_HP_RIGHT_VOL, 1, 1, 0),
SOC_DOUBLE_R_TLV("Lineout Playback Volume", ADAU1761_PLAY_LINE_LEFT_VOL,
ADAU1761_PLAY_LINE_RIGHT_VOL, 2, 0x3f, 0, adau1761_out_tlv),
SOC_DOUBLE_R("Lineout Playback Switch", ADAU1761_PLAY_LINE_LEFT_VOL,
ADAU1761_PLAY_LINE_RIGHT_VOL, 1, 1, 0),
SOC_ENUM("ADC Bias", adau1761_adc_bias_enum),
SOC_ENUM("DAC Bias", adau1761_dac_bias_enum),
SOC_ENUM("Capture Bias", adau1761_capture_bias_enum),
SOC_ENUM("Playback Bias", adau1761_playback_bias_enum),
SOC_ENUM("Headphone Bias", adau1761_hp_bias_enum),
};
static const struct snd_kcontrol_new adau1761_mono_controls[] = {
SOC_SINGLE_TLV("Mono Playback Volume", ADAU1761_PLAY_MONO_OUTPUT_VOL,
2, 0x3f, 0, adau1761_out_tlv),
SOC_SINGLE("Mono Playback Switch", ADAU1761_PLAY_MONO_OUTPUT_VOL,
1, 1, 0),
};
static const struct snd_kcontrol_new adau1761_left_mixer_controls[] = {
SOC_DAPM_SINGLE_AUTODISABLE("Left DAC Switch",
ADAU1761_PLAY_MIXER_LEFT0, 5, 1, 0),
SOC_DAPM_SINGLE_AUTODISABLE("Right DAC Switch",
ADAU1761_PLAY_MIXER_LEFT0, 6, 1, 0),
SOC_DAPM_SINGLE_TLV("Aux Bypass Volume",
ADAU1761_PLAY_MIXER_LEFT0, 1, 8, 0, adau1761_sidetone_tlv),
SOC_DAPM_SINGLE_TLV("Right Bypass Volume",
ADAU1761_PLAY_MIXER_LEFT1, 4, 8, 0, adau1761_sidetone_tlv),
SOC_DAPM_SINGLE_TLV("Left Bypass Volume",
ADAU1761_PLAY_MIXER_LEFT1, 0, 8, 0, adau1761_sidetone_tlv),
};
static const struct snd_kcontrol_new adau1761_right_mixer_controls[] = {
SOC_DAPM_SINGLE_AUTODISABLE("Left DAC Switch",
ADAU1761_PLAY_MIXER_RIGHT0, 5, 1, 0),
SOC_DAPM_SINGLE_AUTODISABLE("Right DAC Switch",
ADAU1761_PLAY_MIXER_RIGHT0, 6, 1, 0),
SOC_DAPM_SINGLE_TLV("Aux Bypass Volume",
ADAU1761_PLAY_MIXER_RIGHT0, 1, 8, 0, adau1761_sidetone_tlv),
SOC_DAPM_SINGLE_TLV("Right Bypass Volume",
ADAU1761_PLAY_MIXER_RIGHT1, 4, 8, 0, adau1761_sidetone_tlv),
SOC_DAPM_SINGLE_TLV("Left Bypass Volume",
ADAU1761_PLAY_MIXER_RIGHT1, 0, 8, 0, adau1761_sidetone_tlv),
};
static const struct snd_kcontrol_new adau1761_left_lr_mixer_controls[] = {
SOC_DAPM_SINGLE_TLV("Left Volume",
ADAU1761_PLAY_LR_MIXER_LEFT, 1, 2, 0, adau1761_boost_tlv),
SOC_DAPM_SINGLE_TLV("Right Volume",
ADAU1761_PLAY_LR_MIXER_LEFT, 3, 2, 0, adau1761_boost_tlv),
};
static const struct snd_kcontrol_new adau1761_right_lr_mixer_controls[] = {
SOC_DAPM_SINGLE_TLV("Left Volume",
ADAU1761_PLAY_LR_MIXER_RIGHT, 1, 2, 0, adau1761_boost_tlv),
SOC_DAPM_SINGLE_TLV("Right Volume",
ADAU1761_PLAY_LR_MIXER_RIGHT, 3, 2, 0, adau1761_boost_tlv),
};
static const char * const adau1761_input_mux_text[] = {
"ADC", "DMIC",
};
static SOC_ENUM_SINGLE_DECL(adau1761_input_mux_enum,
ADAU17X1_ADC_CONTROL, 2, adau1761_input_mux_text);
static const struct snd_kcontrol_new adau1761_input_mux_control =
SOC_DAPM_ENUM("Input Select", adau1761_input_mux_enum);
static int adau1761_dejitter_fixup(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct adau *adau = snd_soc_codec_get_drvdata(w->codec);
/* After any power changes have been made the dejitter circuit
* has to be reinitialized. */
regmap_write(adau->regmap, ADAU1761_DEJITTER, 0);
if (!adau->master)
regmap_write(adau->regmap, ADAU1761_DEJITTER, 3);
return 0;
}
static const struct snd_soc_dapm_widget adau1x61_dapm_widgets[] = {
SND_SOC_DAPM_MIXER("Left Input Mixer", ADAU1761_REC_MIXER_LEFT0, 0, 0,
NULL, 0),
SND_SOC_DAPM_MIXER("Right Input Mixer", ADAU1761_REC_MIXER_RIGHT0, 0, 0,
NULL, 0),
SOC_MIXER_ARRAY("Left Playback Mixer", ADAU1761_PLAY_MIXER_LEFT0,
0, 0, adau1761_left_mixer_controls),
SOC_MIXER_ARRAY("Right Playback Mixer", ADAU1761_PLAY_MIXER_RIGHT0,
0, 0, adau1761_right_mixer_controls),
SOC_MIXER_ARRAY("Left LR Playback Mixer", ADAU1761_PLAY_LR_MIXER_LEFT,
0, 0, adau1761_left_lr_mixer_controls),
SOC_MIXER_ARRAY("Right LR Playback Mixer", ADAU1761_PLAY_LR_MIXER_RIGHT,
0, 0, adau1761_right_lr_mixer_controls),
SND_SOC_DAPM_SUPPLY("Headphone", ADAU1761_PLAY_HP_LEFT_VOL,
0, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY_S("SYSCLK", 2, SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_POST("Dejitter fixup", adau1761_dejitter_fixup),
SND_SOC_DAPM_INPUT("LAUX"),
SND_SOC_DAPM_INPUT("RAUX"),
SND_SOC_DAPM_INPUT("LINP"),
SND_SOC_DAPM_INPUT("LINN"),
SND_SOC_DAPM_INPUT("RINP"),
SND_SOC_DAPM_INPUT("RINN"),
SND_SOC_DAPM_OUTPUT("LOUT"),
SND_SOC_DAPM_OUTPUT("ROUT"),
SND_SOC_DAPM_OUTPUT("LHP"),
SND_SOC_DAPM_OUTPUT("RHP"),
};
static const struct snd_soc_dapm_widget adau1761_mono_dapm_widgets[] = {
SND_SOC_DAPM_MIXER("Mono Playback Mixer", ADAU1761_PLAY_MIXER_MONO,
0, 0, NULL, 0),
SND_SOC_DAPM_OUTPUT("MONOOUT"),
};
static const struct snd_soc_dapm_widget adau1761_capless_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY_S("Headphone VGND", 1, ADAU1761_PLAY_MIXER_MONO,
0, 0, NULL, 0),
};
static const struct snd_soc_dapm_route adau1x61_dapm_routes[] = {
{ "Left Input Mixer", NULL, "LINP" },
{ "Left Input Mixer", NULL, "LINN" },
{ "Left Input Mixer", NULL, "LAUX" },
{ "Right Input Mixer", NULL, "RINP" },
{ "Right Input Mixer", NULL, "RINN" },
{ "Right Input Mixer", NULL, "RAUX" },
{ "Left Playback Mixer", NULL, "Left Playback Enable"},
{ "Right Playback Mixer", NULL, "Right Playback Enable"},
{ "Left LR Playback Mixer", NULL, "Left Playback Enable"},
{ "Right LR Playback Mixer", NULL, "Right Playback Enable"},
{ "Left Playback Mixer", "Left DAC Switch", "Left DAC" },
{ "Left Playback Mixer", "Right DAC Switch", "Right DAC" },
{ "Right Playback Mixer", "Left DAC Switch", "Left DAC" },
{ "Right Playback Mixer", "Right DAC Switch", "Right DAC" },
{ "Left LR Playback Mixer", "Left Volume", "Left Playback Mixer" },
{ "Left LR Playback Mixer", "Right Volume", "Right Playback Mixer" },
{ "Right LR Playback Mixer", "Left Volume", "Left Playback Mixer" },
{ "Right LR Playback Mixer", "Right Volume", "Right Playback Mixer" },
{ "LHP", NULL, "Left Playback Mixer" },
{ "RHP", NULL, "Right Playback Mixer" },
{ "LHP", NULL, "Headphone" },
{ "RHP", NULL, "Headphone" },
{ "LOUT", NULL, "Left LR Playback Mixer" },
{ "ROUT", NULL, "Right LR Playback Mixer" },
{ "Left Playback Mixer", "Aux Bypass Volume", "LAUX" },
{ "Left Playback Mixer", "Left Bypass Volume", "Left Input Mixer" },
{ "Left Playback Mixer", "Right Bypass Volume", "Right Input Mixer" },
{ "Right Playback Mixer", "Aux Bypass Volume", "RAUX" },
{ "Right Playback Mixer", "Left Bypass Volume", "Left Input Mixer" },
{ "Right Playback Mixer", "Right Bypass Volume", "Right Input Mixer" },
};
static const struct snd_soc_dapm_route adau1761_mono_dapm_routes[] = {
{ "Mono Playback Mixer", NULL, "Left Playback Mixer" },
{ "Mono Playback Mixer", NULL, "Right Playback Mixer" },
{ "MONOOUT", NULL, "Mono Playback Mixer" },
};
static const struct snd_soc_dapm_route adau1761_capless_dapm_routes[] = {
{ "Headphone", NULL, "Headphone VGND" },
};
static const struct snd_soc_dapm_widget adau1761_dmic_widgets[] = {
SND_SOC_DAPM_MUX("Left Decimator Mux", SND_SOC_NOPM, 0, 0,
&adau1761_input_mux_control),
SND_SOC_DAPM_MUX("Right Decimator Mux", SND_SOC_NOPM, 0, 0,
&adau1761_input_mux_control),
SND_SOC_DAPM_INPUT("DMIC"),
};
static const struct snd_soc_dapm_route adau1761_dmic_routes[] = {
{ "Left Decimator Mux", "ADC", "Left Input Mixer" },
{ "Left Decimator Mux", "DMIC", "DMIC" },
{ "Right Decimator Mux", "ADC", "Right Input Mixer" },
{ "Right Decimator Mux", "DMIC", "DMIC" },
{ "Left Decimator", NULL, "Left Decimator Mux" },
{ "Right Decimator", NULL, "Right Decimator Mux" },
};
static const struct snd_soc_dapm_route adau1761_no_dmic_routes[] = {
{ "Left Decimator", NULL, "Left Input Mixer" },
{ "Right Decimator", NULL, "Right Input Mixer" },
};
static const struct snd_soc_dapm_widget adau1761_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY("Serial Port Clock", ADAU1761_CLK_ENABLE0,
0, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("Serial Input Routing Clock", ADAU1761_CLK_ENABLE0,
1, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("Serial Output Routing Clock", ADAU1761_CLK_ENABLE0,
3, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("Decimator Resync Clock", ADAU1761_CLK_ENABLE0,
4, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("Interpolator Resync Clock", ADAU1761_CLK_ENABLE0,
2, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("Slew Clock", ADAU1761_CLK_ENABLE0, 6, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY_S("Digital Clock 0", 1, ADAU1761_CLK_ENABLE1,
0, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY_S("Digital Clock 1", 1, ADAU1761_CLK_ENABLE1,
1, 0, NULL, 0),
};
static const struct snd_soc_dapm_route adau1761_dapm_routes[] = {
{ "Left Decimator", NULL, "Digital Clock 0", },
{ "Right Decimator", NULL, "Digital Clock 0", },
{ "Left DAC", NULL, "Digital Clock 0", },
{ "Right DAC", NULL, "Digital Clock 0", },
{ "AIFCLK", NULL, "Digital Clock 1" },
{ "Playback", NULL, "Serial Port Clock" },
{ "Capture", NULL, "Serial Port Clock" },
{ "Playback", NULL, "Serial Input Routing Clock" },
{ "Capture", NULL, "Serial Output Routing Clock" },
{ "Left Decimator", NULL, "Decimator Resync Clock" },
{ "Right Decimator", NULL, "Decimator Resync Clock" },
{ "Left DAC", NULL, "Interpolator Resync Clock" },
{ "Right DAC", NULL, "Interpolator Resync Clock" },
{ "DSP", NULL, "Digital Clock 0" },
{ "Slew Clock", NULL, "Digital Clock 0" },
{ "Right Playback Mixer", NULL, "Slew Clock" },
{ "Left Playback Mixer", NULL, "Slew Clock" },
{ "Digital Clock 0", NULL, "SYSCLK" },
{ "Digital Clock 1", NULL, "SYSCLK" },
};
static int adau1761_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
struct adau *adau = snd_soc_codec_get_drvdata(codec);
switch (level) {
case SND_SOC_BIAS_ON:
break;
case SND_SOC_BIAS_PREPARE:
break;
case SND_SOC_BIAS_STANDBY:
regmap_update_bits(adau->regmap, ADAU17X1_CLOCK_CONTROL,
ADAU17X1_CLOCK_CONTROL_SYSCLK_EN,
ADAU17X1_CLOCK_CONTROL_SYSCLK_EN);
break;
case SND_SOC_BIAS_OFF:
regmap_update_bits(adau->regmap, ADAU17X1_CLOCK_CONTROL,
ADAU17X1_CLOCK_CONTROL_SYSCLK_EN, 0);
break;
}
codec->dapm.bias_level = level;
return 0;
}
static enum adau1761_output_mode adau1761_get_lineout_mode(
struct snd_soc_codec *codec)
{
struct adau1761_platform_data *pdata = codec->dev->platform_data;
if (pdata)
return pdata->lineout_mode;
return ADAU1761_OUTPUT_MODE_LINE;
}
static int adau1761_setup_digmic_jackdetect(struct snd_soc_codec *codec)
{
struct adau1761_platform_data *pdata = codec->dev->platform_data;
struct adau *adau = snd_soc_codec_get_drvdata(codec);
enum adau1761_digmic_jackdet_pin_mode mode;
unsigned int val = 0;
int ret;
if (pdata)
mode = pdata->digmic_jackdetect_pin_mode;
else
mode = ADAU1761_DIGMIC_JACKDET_PIN_MODE_NONE;
switch (mode) {
case ADAU1761_DIGMIC_JACKDET_PIN_MODE_JACKDETECT:
switch (pdata->jackdetect_debounce_time) {
case ADAU1761_JACKDETECT_DEBOUNCE_5MS:
case ADAU1761_JACKDETECT_DEBOUNCE_10MS:
case ADAU1761_JACKDETECT_DEBOUNCE_20MS:
case ADAU1761_JACKDETECT_DEBOUNCE_40MS:
val |= pdata->jackdetect_debounce_time << 6;
break;
default:
return -EINVAL;
}
if (pdata->jackdetect_active_low)
val |= ADAU1761_DIGMIC_JACKDETECT_ACTIVE_LOW;
ret = snd_soc_add_codec_controls(codec,
adau1761_jack_detect_controls,
ARRAY_SIZE(adau1761_jack_detect_controls));
if (ret)
return ret;
case ADAU1761_DIGMIC_JACKDET_PIN_MODE_NONE: /* fallthrough */
ret = snd_soc_dapm_add_routes(&codec->dapm,
adau1761_no_dmic_routes,
ARRAY_SIZE(adau1761_no_dmic_routes));
if (ret)
return ret;
break;
case ADAU1761_DIGMIC_JACKDET_PIN_MODE_DIGMIC:
ret = snd_soc_dapm_new_controls(&codec->dapm,
adau1761_dmic_widgets,
ARRAY_SIZE(adau1761_dmic_widgets));
if (ret)
return ret;
ret = snd_soc_dapm_add_routes(&codec->dapm,
adau1761_dmic_routes,
ARRAY_SIZE(adau1761_dmic_routes));
if (ret)
return ret;
val |= ADAU1761_DIGMIC_JACKDETECT_DIGMIC;
break;
default:
return -EINVAL;
}
regmap_write(adau->regmap, ADAU1761_DIGMIC_JACKDETECT, val);
return 0;
}
static int adau1761_setup_headphone_mode(struct snd_soc_codec *codec)
{
struct adau *adau = snd_soc_codec_get_drvdata(codec);
struct adau1761_platform_data *pdata = codec->dev->platform_data;
enum adau1761_output_mode mode;
int ret;
if (pdata)
mode = pdata->headphone_mode;
else
mode = ADAU1761_OUTPUT_MODE_HEADPHONE;
switch (mode) {
case ADAU1761_OUTPUT_MODE_LINE:
break;
case ADAU1761_OUTPUT_MODE_HEADPHONE_CAPLESS:
regmap_update_bits(adau->regmap, ADAU1761_PLAY_MONO_OUTPUT_VOL,
ADAU1761_PLAY_MONO_OUTPUT_VOL_MODE_HP |
ADAU1761_PLAY_MONO_OUTPUT_VOL_UNMUTE,
ADAU1761_PLAY_MONO_OUTPUT_VOL_MODE_HP |
ADAU1761_PLAY_MONO_OUTPUT_VOL_UNMUTE);
/* fallthrough */
case ADAU1761_OUTPUT_MODE_HEADPHONE:
regmap_update_bits(adau->regmap, ADAU1761_PLAY_HP_RIGHT_VOL,
ADAU1761_PLAY_HP_RIGHT_VOL_MODE_HP,
ADAU1761_PLAY_HP_RIGHT_VOL_MODE_HP);
break;
default:
return -EINVAL;
}
if (mode == ADAU1761_OUTPUT_MODE_HEADPHONE_CAPLESS) {
ret = snd_soc_dapm_new_controls(&codec->dapm,
adau1761_capless_dapm_widgets,
ARRAY_SIZE(adau1761_capless_dapm_widgets));
if (ret)
return ret;
ret = snd_soc_dapm_add_routes(&codec->dapm,
adau1761_capless_dapm_routes,
ARRAY_SIZE(adau1761_capless_dapm_routes));
} else {
ret = snd_soc_add_codec_controls(codec, adau1761_mono_controls,
ARRAY_SIZE(adau1761_mono_controls));
if (ret)
return ret;
ret = snd_soc_dapm_new_controls(&codec->dapm,
adau1761_mono_dapm_widgets,
ARRAY_SIZE(adau1761_mono_dapm_widgets));
if (ret)
return ret;
ret = snd_soc_dapm_add_routes(&codec->dapm,
adau1761_mono_dapm_routes,
ARRAY_SIZE(adau1761_mono_dapm_routes));
}
return ret;
}
static bool adau1761_readable_register(struct device *dev, unsigned int reg)
{
switch (reg) {
case ADAU1761_DIGMIC_JACKDETECT:
case ADAU1761_REC_MIXER_LEFT0:
case ADAU1761_REC_MIXER_LEFT1:
case ADAU1761_REC_MIXER_RIGHT0:
case ADAU1761_REC_MIXER_RIGHT1:
case ADAU1761_LEFT_DIFF_INPUT_VOL:
case ADAU1761_RIGHT_DIFF_INPUT_VOL:
case ADAU1761_PLAY_LR_MIXER_LEFT:
case ADAU1761_PLAY_MIXER_LEFT0:
case ADAU1761_PLAY_MIXER_LEFT1:
case ADAU1761_PLAY_MIXER_RIGHT0:
case ADAU1761_PLAY_MIXER_RIGHT1:
case ADAU1761_PLAY_LR_MIXER_RIGHT:
case ADAU1761_PLAY_MIXER_MONO:
case ADAU1761_PLAY_HP_LEFT_VOL:
case ADAU1761_PLAY_HP_RIGHT_VOL:
case ADAU1761_PLAY_LINE_LEFT_VOL:
case ADAU1761_PLAY_LINE_RIGHT_VOL:
case ADAU1761_PLAY_MONO_OUTPUT_VOL:
case ADAU1761_POP_CLICK_SUPPRESS:
case ADAU1761_JACK_DETECT_PIN:
case ADAU1761_DEJITTER:
case ADAU1761_CLK_ENABLE0:
case ADAU1761_CLK_ENABLE1:
return true;
default:
break;
}
return adau17x1_readable_register(dev, reg);
}
static int adau1761_codec_probe(struct snd_soc_codec *codec)
{
struct adau1761_platform_data *pdata = codec->dev->platform_data;
struct adau *adau = snd_soc_codec_get_drvdata(codec);
int ret;
ret = adau17x1_add_widgets(codec);
if (ret < 0)
return ret;
if (pdata && pdata->input_differential) {
regmap_update_bits(adau->regmap, ADAU1761_LEFT_DIFF_INPUT_VOL,
ADAU1761_DIFF_INPUT_VOL_LDEN,
ADAU1761_DIFF_INPUT_VOL_LDEN);
regmap_update_bits(adau->regmap, ADAU1761_RIGHT_DIFF_INPUT_VOL,
ADAU1761_DIFF_INPUT_VOL_LDEN,
ADAU1761_DIFF_INPUT_VOL_LDEN);
ret = snd_soc_add_codec_controls(codec,
adau1761_differential_mode_controls,
ARRAY_SIZE(adau1761_differential_mode_controls));
if (ret)
return ret;
} else {
ret = snd_soc_add_codec_controls(codec,
adau1761_single_mode_controls,
ARRAY_SIZE(adau1761_single_mode_controls));
if (ret)
return ret;
}
switch (adau1761_get_lineout_mode(codec)) {
case ADAU1761_OUTPUT_MODE_LINE:
break;
case ADAU1761_OUTPUT_MODE_HEADPHONE:
regmap_update_bits(adau->regmap, ADAU1761_PLAY_LINE_LEFT_VOL,
ADAU1761_PLAY_LINE_LEFT_VOL_MODE_HP,
ADAU1761_PLAY_LINE_LEFT_VOL_MODE_HP);
regmap_update_bits(adau->regmap, ADAU1761_PLAY_LINE_RIGHT_VOL,
ADAU1761_PLAY_LINE_RIGHT_VOL_MODE_HP,
ADAU1761_PLAY_LINE_RIGHT_VOL_MODE_HP);
break;
default:
return -EINVAL;
}
ret = adau1761_setup_headphone_mode(codec);
if (ret)
return ret;
ret = adau1761_setup_digmic_jackdetect(codec);
if (ret)
return ret;
if (adau->type == ADAU1761) {
ret = snd_soc_dapm_new_controls(&codec->dapm,
adau1761_dapm_widgets,
ARRAY_SIZE(adau1761_dapm_widgets));
if (ret)
return ret;
ret = snd_soc_dapm_add_routes(&codec->dapm,
adau1761_dapm_routes,
ARRAY_SIZE(adau1761_dapm_routes));
if (ret)
return ret;
ret = adau17x1_load_firmware(adau, codec->dev,
ADAU1761_FIRMWARE);
if (ret)
dev_warn(codec->dev, "Failed to firmware\n");
}
ret = adau17x1_add_routes(codec);
if (ret < 0)
return ret;
return 0;
}
static const struct snd_soc_codec_driver adau1761_codec_driver = {
.probe = adau1761_codec_probe,
.suspend = adau17x1_suspend,
.resume = adau17x1_resume,
.set_bias_level = adau1761_set_bias_level,
.controls = adau1761_controls,
.num_controls = ARRAY_SIZE(adau1761_controls),
.dapm_widgets = adau1x61_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(adau1x61_dapm_widgets),
.dapm_routes = adau1x61_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(adau1x61_dapm_routes),
};
#define ADAU1761_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \
SNDRV_PCM_FMTBIT_S32_LE)
static struct snd_soc_dai_driver adau1361_dai_driver = {
.name = "adau-hifi",
.playback = {
.stream_name = "Playback",
.channels_min = 2,
.channels_max = 4,
.rates = SNDRV_PCM_RATE_8000_96000,
.formats = ADAU1761_FORMATS,
},
.capture = {
.stream_name = "Capture",
.channels_min = 2,
.channels_max = 4,
.rates = SNDRV_PCM_RATE_8000_96000,
.formats = ADAU1761_FORMATS,
},
.ops = &adau17x1_dai_ops,
};
static struct snd_soc_dai_driver adau1761_dai_driver = {
.name = "adau-hifi",
.playback = {
.stream_name = "Playback",
.channels_min = 2,
.channels_max = 8,
.rates = SNDRV_PCM_RATE_8000_96000,
.formats = ADAU1761_FORMATS,
},
.capture = {
.stream_name = "Capture",
.channels_min = 2,
.channels_max = 8,
.rates = SNDRV_PCM_RATE_8000_96000,
.formats = ADAU1761_FORMATS,
},
.ops = &adau17x1_dai_ops,
};
int adau1761_probe(struct device *dev, struct regmap *regmap,
enum adau17x1_type type, void (*switch_mode)(struct device *dev))
{
struct snd_soc_dai_driver *dai_drv;
int ret;
ret = adau17x1_probe(dev, regmap, type, switch_mode);
if (ret)
return ret;
if (type == ADAU1361)
dai_drv = &adau1361_dai_driver;
else
dai_drv = &adau1761_dai_driver;
return snd_soc_register_codec(dev, &adau1761_codec_driver, dai_drv, 1);
}
EXPORT_SYMBOL_GPL(adau1761_probe);
const struct regmap_config adau1761_regmap_config = {
.val_bits = 8,
.reg_bits = 16,
.max_register = 0x40fa,
.reg_defaults = adau1761_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(adau1761_reg_defaults),
.readable_reg = adau1761_readable_register,
.volatile_reg = adau17x1_volatile_register,
.cache_type = REGCACHE_RBTREE,
};
EXPORT_SYMBOL_GPL(adau1761_regmap_config);
MODULE_DESCRIPTION("ASoC ADAU1361/ADAU1461/ADAU1761/ADAU1961 CODEC driver");
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,23 @@
/*
* ADAU1361/ADAU1461/ADAU1761/ADAU1961 driver
*
* Copyright 2014 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
*
* Licensed under the GPL-2.
*/
#ifndef __SOUND_SOC_CODECS_ADAU1761_H__
#define __SOUND_SOC_CODECS_ADAU1761_H__
#include <linux/regmap.h>
#include "adau17x1.h"
struct device;
int adau1761_probe(struct device *dev, struct regmap *regmap,
enum adau17x1_type type, void (*switch_mode)(struct device *dev));
extern const struct regmap_config adau1761_regmap_config;
#endif

View File

@ -0,0 +1,58 @@
/*
* Driver for ADAU1381/ADAU1781 CODEC
*
* Copyright 2014 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
*
* Licensed under the GPL-2.
*/
#include <linux/i2c.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <sound/soc.h>
#include "adau1781.h"
static int adau1781_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct regmap_config config;
config = adau1781_regmap_config;
config.val_bits = 8;
config.reg_bits = 16;
return adau1781_probe(&client->dev,
devm_regmap_init_i2c(client, &config),
id->driver_data, NULL);
}
static int adau1781_i2c_remove(struct i2c_client *client)
{
snd_soc_unregister_codec(&client->dev);
return 0;
}
static const struct i2c_device_id adau1781_i2c_ids[] = {
{ "adau1381", ADAU1381 },
{ "adau1781", ADAU1781 },
{ }
};
MODULE_DEVICE_TABLE(i2c, adau1781_i2c_ids);
static struct i2c_driver adau1781_i2c_driver = {
.driver = {
.name = "adau1781",
.owner = THIS_MODULE,
},
.probe = adau1781_i2c_probe,
.remove = adau1781_i2c_remove,
.id_table = adau1781_i2c_ids,
};
module_i2c_driver(adau1781_i2c_driver);
MODULE_DESCRIPTION("ASoC ADAU1381/ADAU1781 CODEC I2C driver");
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,75 @@
/*
* Driver for ADAU1381/ADAU1781 CODEC
*
* Copyright 2014 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
*
* Licensed under the GPL-2.
*/
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/spi/spi.h>
#include <sound/soc.h>
#include "adau1781.h"
static void adau1781_spi_switch_mode(struct device *dev)
{
struct spi_device *spi = to_spi_device(dev);
/*
* To get the device into SPI mode CLATCH has to be pulled low three
* times. Do this by issuing three dummy reads.
*/
spi_w8r8(spi, 0x00);
spi_w8r8(spi, 0x00);
spi_w8r8(spi, 0x00);
}
static int adau1781_spi_probe(struct spi_device *spi)
{
const struct spi_device_id *id = spi_get_device_id(spi);
struct regmap_config config;
if (!id)
return -EINVAL;
config = adau1781_regmap_config;
config.val_bits = 8;
config.reg_bits = 24;
config.read_flag_mask = 0x1;
return adau1781_probe(&spi->dev,
devm_regmap_init_spi(spi, &config),
id->driver_data, adau1781_spi_switch_mode);
}
static int adau1781_spi_remove(struct spi_device *spi)
{
snd_soc_unregister_codec(&spi->dev);
return 0;
}
static const struct spi_device_id adau1781_spi_id[] = {
{ "adau1381", ADAU1381 },
{ "adau1781", ADAU1781 },
{ }
};
MODULE_DEVICE_TABLE(spi, adau1781_spi_id);
static struct spi_driver adau1781_spi_driver = {
.driver = {
.name = "adau1781",
.owner = THIS_MODULE,
},
.probe = adau1781_spi_probe,
.remove = adau1781_spi_remove,
.id_table = adau1781_spi_id,
};
module_spi_driver(adau1781_spi_driver);
MODULE_DESCRIPTION("ASoC ADAU1381/ADAU1781 CODEC SPI driver");
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,511 @@
/*
* Driver for ADAU1781/ADAU1781 codec
*
* Copyright 2011-2013 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
*
* Licensed under the GPL-2 or later.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/spi/spi.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/tlv.h>
#include <linux/platform_data/adau17x1.h>
#include "adau17x1.h"
#include "adau1781.h"
#define ADAU1781_DMIC_BEEP_CTRL 0x4008
#define ADAU1781_LEFT_PGA 0x400e
#define ADAU1781_RIGHT_PGA 0x400f
#define ADAU1781_LEFT_PLAYBACK_MIXER 0x401c
#define ADAU1781_RIGHT_PLAYBACK_MIXER 0x401e
#define ADAU1781_MONO_PLAYBACK_MIXER 0x401f
#define ADAU1781_LEFT_LINEOUT 0x4025
#define ADAU1781_RIGHT_LINEOUT 0x4026
#define ADAU1781_SPEAKER 0x4027
#define ADAU1781_BEEP_ZC 0x4028
#define ADAU1781_DEJITTER 0x4032
#define ADAU1781_DIG_PWDN0 0x4080
#define ADAU1781_DIG_PWDN1 0x4081
#define ADAU1781_INPUT_DIFFERNTIAL BIT(3)
#define ADAU1381_FIRMWARE "adau1381.bin"
#define ADAU1781_FIRMWARE "adau1781.bin"
static const struct reg_default adau1781_reg_defaults[] = {
{ ADAU1781_DMIC_BEEP_CTRL, 0x00 },
{ ADAU1781_LEFT_PGA, 0xc7 },
{ ADAU1781_RIGHT_PGA, 0xc7 },
{ ADAU1781_LEFT_PLAYBACK_MIXER, 0x00 },
{ ADAU1781_RIGHT_PLAYBACK_MIXER, 0x00 },
{ ADAU1781_MONO_PLAYBACK_MIXER, 0x00 },
{ ADAU1781_LEFT_LINEOUT, 0x00 },
{ ADAU1781_RIGHT_LINEOUT, 0x00 },
{ ADAU1781_SPEAKER, 0x00 },
{ ADAU1781_BEEP_ZC, 0x19 },
{ ADAU1781_DEJITTER, 0x60 },
{ ADAU1781_DIG_PWDN1, 0x0c },
{ ADAU1781_DIG_PWDN1, 0x00 },
{ ADAU17X1_CLOCK_CONTROL, 0x00 },
{ ADAU17X1_PLL_CONTROL, 0x00 },
{ ADAU17X1_REC_POWER_MGMT, 0x00 },
{ ADAU17X1_MICBIAS, 0x04 },
{ ADAU17X1_SERIAL_PORT0, 0x00 },
{ ADAU17X1_SERIAL_PORT1, 0x00 },
{ ADAU17X1_CONVERTER0, 0x00 },
{ ADAU17X1_CONVERTER1, 0x00 },
{ ADAU17X1_LEFT_INPUT_DIGITAL_VOL, 0x00 },
{ ADAU17X1_RIGHT_INPUT_DIGITAL_VOL, 0x00 },
{ ADAU17X1_ADC_CONTROL, 0x00 },
{ ADAU17X1_PLAY_POWER_MGMT, 0x00 },
{ ADAU17X1_DAC_CONTROL0, 0x00 },
{ ADAU17X1_DAC_CONTROL1, 0x00 },
{ ADAU17X1_DAC_CONTROL2, 0x00 },
{ ADAU17X1_SERIAL_PORT_PAD, 0x00 },
{ ADAU17X1_CONTROL_PORT_PAD0, 0x00 },
{ ADAU17X1_CONTROL_PORT_PAD1, 0x00 },
{ ADAU17X1_DSP_SAMPLING_RATE, 0x01 },
{ ADAU17X1_SERIAL_INPUT_ROUTE, 0x00 },
{ ADAU17X1_SERIAL_OUTPUT_ROUTE, 0x00 },
{ ADAU17X1_DSP_ENABLE, 0x00 },
{ ADAU17X1_DSP_RUN, 0x00 },
{ ADAU17X1_SERIAL_SAMPLING_RATE, 0x00 },
};
static const DECLARE_TLV_DB_SCALE(adau1781_speaker_tlv, 0, 200, 0);
static const DECLARE_TLV_DB_RANGE(adau1781_pga_tlv,
0, 1, TLV_DB_SCALE_ITEM(0, 600, 0),
2, 3, TLV_DB_SCALE_ITEM(1000, 400, 0),
4, 4, TLV_DB_SCALE_ITEM(1700, 0, 0),
5, 7, TLV_DB_SCALE_ITEM(2000, 600, 0)
);
static const DECLARE_TLV_DB_RANGE(adau1781_beep_tlv,
0, 1, TLV_DB_SCALE_ITEM(0, 600, 0),
2, 3, TLV_DB_SCALE_ITEM(1000, 400, 0),
4, 4, TLV_DB_SCALE_ITEM(-2300, 0, 0),
5, 7, TLV_DB_SCALE_ITEM(2000, 600, 0)
);
static const DECLARE_TLV_DB_SCALE(adau1781_sidetone_tlv, -1800, 300, 1);
static const char * const adau1781_speaker_bias_select_text[] = {
"Normal operation", "Power saving", "Enhanced performance",
};
static const char * const adau1781_bias_select_text[] = {
"Normal operation", "Extreme power saving", "Power saving",
"Enhanced performance",
};
static SOC_ENUM_SINGLE_DECL(adau1781_adc_bias_enum,
ADAU17X1_REC_POWER_MGMT, 3, adau1781_bias_select_text);
static SOC_ENUM_SINGLE_DECL(adau1781_speaker_bias_enum,
ADAU17X1_PLAY_POWER_MGMT, 6, adau1781_speaker_bias_select_text);
static SOC_ENUM_SINGLE_DECL(adau1781_dac_bias_enum,
ADAU17X1_PLAY_POWER_MGMT, 4, adau1781_bias_select_text);
static SOC_ENUM_SINGLE_DECL(adau1781_playback_bias_enum,
ADAU17X1_PLAY_POWER_MGMT, 2, adau1781_bias_select_text);
static SOC_ENUM_SINGLE_DECL(adau1781_capture_bias_enum,
ADAU17X1_REC_POWER_MGMT, 1, adau1781_bias_select_text);
static const struct snd_kcontrol_new adau1781_controls[] = {
SOC_SINGLE_TLV("Beep Capture Volume", ADAU1781_DMIC_BEEP_CTRL, 0, 7, 0,
adau1781_beep_tlv),
SOC_DOUBLE_R_TLV("PGA Capture Volume", ADAU1781_LEFT_PGA,
ADAU1781_RIGHT_PGA, 5, 7, 0, adau1781_pga_tlv),
SOC_DOUBLE_R("PGA Capture Switch", ADAU1781_LEFT_PGA,
ADAU1781_RIGHT_PGA, 1, 1, 0),
SOC_DOUBLE_R("Lineout Playback Switch", ADAU1781_LEFT_LINEOUT,
ADAU1781_RIGHT_LINEOUT, 1, 1, 0),
SOC_SINGLE("Beep ZC Switch", ADAU1781_BEEP_ZC, 0, 1, 0),
SOC_SINGLE("Mono Playback Switch", ADAU1781_MONO_PLAYBACK_MIXER,
0, 1, 0),
SOC_SINGLE_TLV("Mono Playback Volume", ADAU1781_SPEAKER, 6, 3, 0,
adau1781_speaker_tlv),
SOC_ENUM("ADC Bias", adau1781_adc_bias_enum),
SOC_ENUM("DAC Bias", adau1781_dac_bias_enum),
SOC_ENUM("Capture Bias", adau1781_capture_bias_enum),
SOC_ENUM("Playback Bias", adau1781_playback_bias_enum),
SOC_ENUM("Speaker Bias", adau1781_speaker_bias_enum),
};
static const struct snd_kcontrol_new adau1781_beep_mixer_controls[] = {
SOC_DAPM_SINGLE("Beep Capture Switch", ADAU1781_DMIC_BEEP_CTRL,
3, 1, 0),
};
static const struct snd_kcontrol_new adau1781_left_mixer_controls[] = {
SOC_DAPM_SINGLE_AUTODISABLE("Switch",
ADAU1781_LEFT_PLAYBACK_MIXER, 5, 1, 0),
SOC_DAPM_SINGLE_TLV("Beep Playback Volume",
ADAU1781_LEFT_PLAYBACK_MIXER, 1, 8, 0, adau1781_sidetone_tlv),
};
static const struct snd_kcontrol_new adau1781_right_mixer_controls[] = {
SOC_DAPM_SINGLE_AUTODISABLE("Switch",
ADAU1781_RIGHT_PLAYBACK_MIXER, 6, 1, 0),
SOC_DAPM_SINGLE_TLV("Beep Playback Volume",
ADAU1781_LEFT_PLAYBACK_MIXER, 1, 8, 0, adau1781_sidetone_tlv),
};
static const struct snd_kcontrol_new adau1781_mono_mixer_controls[] = {
SOC_DAPM_SINGLE_AUTODISABLE("Left Switch",
ADAU1781_MONO_PLAYBACK_MIXER, 7, 1, 0),
SOC_DAPM_SINGLE_AUTODISABLE("Right Switch",
ADAU1781_MONO_PLAYBACK_MIXER, 6, 1, 0),
SOC_DAPM_SINGLE_TLV("Beep Playback Volume",
ADAU1781_MONO_PLAYBACK_MIXER, 2, 8, 0, adau1781_sidetone_tlv),
};
static int adau1781_dejitter_fixup(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = w->codec;
struct adau *adau = snd_soc_codec_get_drvdata(codec);
/* After any power changes have been made the dejitter circuit
* has to be reinitialized. */
regmap_write(adau->regmap, ADAU1781_DEJITTER, 0);
if (!adau->master)
regmap_write(adau->regmap, ADAU1781_DEJITTER, 5);
return 0;
}
static const struct snd_soc_dapm_widget adau1781_dapm_widgets[] = {
SND_SOC_DAPM_PGA("Left PGA", ADAU1781_LEFT_PGA, 0, 0, NULL, 0),
SND_SOC_DAPM_PGA("Right PGA", ADAU1781_RIGHT_PGA, 0, 0, NULL, 0),
SND_SOC_DAPM_OUT_DRV("Speaker", ADAU1781_SPEAKER, 0, 0, NULL, 0),
SOC_MIXER_NAMED_CTL_ARRAY("Beep Mixer", ADAU17X1_MICBIAS, 4, 0,
adau1781_beep_mixer_controls),
SOC_MIXER_ARRAY("Left Lineout Mixer", SND_SOC_NOPM, 0, 0,
adau1781_left_mixer_controls),
SOC_MIXER_ARRAY("Right Lineout Mixer", SND_SOC_NOPM, 0, 0,
adau1781_right_mixer_controls),
SOC_MIXER_ARRAY("Mono Mixer", SND_SOC_NOPM, 0, 0,
adau1781_mono_mixer_controls),
SND_SOC_DAPM_SUPPLY("Serial Input Routing", ADAU1781_DIG_PWDN0,
2, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("Serial Output Routing", ADAU1781_DIG_PWDN0,
3, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("Clock Domain Transfer", ADAU1781_DIG_PWDN0,
5, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("Serial Ports", ADAU1781_DIG_PWDN0, 4, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("ADC Engine", ADAU1781_DIG_PWDN0, 7, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("DAC Engine", ADAU1781_DIG_PWDN1, 0, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("Digital Mic", ADAU1781_DIG_PWDN1, 1, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("Sound Engine", ADAU1781_DIG_PWDN0, 0, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY_S("SYSCLK", 1, ADAU1781_DIG_PWDN0, 1, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("Zero Crossing Detector", ADAU1781_DIG_PWDN1, 2, 0,
NULL, 0),
SND_SOC_DAPM_POST("Dejitter fixup", adau1781_dejitter_fixup),
SND_SOC_DAPM_INPUT("BEEP"),
SND_SOC_DAPM_OUTPUT("AOUTL"),
SND_SOC_DAPM_OUTPUT("AOUTR"),
SND_SOC_DAPM_OUTPUT("SP"),
SND_SOC_DAPM_INPUT("LMIC"),
SND_SOC_DAPM_INPUT("RMIC"),
};
static const struct snd_soc_dapm_route adau1781_dapm_routes[] = {
{ "Left Lineout Mixer", NULL, "Left Playback Enable" },
{ "Right Lineout Mixer", NULL, "Right Playback Enable" },
{ "Left Lineout Mixer", "Beep Playback Volume", "Beep Mixer" },
{ "Left Lineout Mixer", "Switch", "Left DAC" },
{ "Right Lineout Mixer", "Beep Playback Volume", "Beep Mixer" },
{ "Right Lineout Mixer", "Switch", "Right DAC" },
{ "Mono Mixer", "Beep Playback Volume", "Beep Mixer" },
{ "Mono Mixer", "Right Switch", "Right DAC" },
{ "Mono Mixer", "Left Switch", "Left DAC" },
{ "Speaker", NULL, "Mono Mixer" },
{ "Mono Mixer", NULL, "SYSCLK" },
{ "Left Lineout Mixer", NULL, "SYSCLK" },
{ "Left Lineout Mixer", NULL, "SYSCLK" },
{ "Beep Mixer", "Beep Capture Switch", "BEEP" },
{ "Beep Mixer", NULL, "Zero Crossing Detector" },
{ "Left DAC", NULL, "DAC Engine" },
{ "Right DAC", NULL, "DAC Engine" },
{ "Sound Engine", NULL, "SYSCLK" },
{ "DSP", NULL, "Sound Engine" },
{ "Left Decimator", NULL, "ADC Engine" },
{ "Right Decimator", NULL, "ADC Engine" },
{ "AIFCLK", NULL, "SYSCLK" },
{ "Playback", NULL, "Serial Input Routing" },
{ "Playback", NULL, "Serial Ports" },
{ "Playback", NULL, "Clock Domain Transfer" },
{ "Capture", NULL, "Serial Output Routing" },
{ "Capture", NULL, "Serial Ports" },
{ "Capture", NULL, "Clock Domain Transfer" },
{ "AOUTL", NULL, "Left Lineout Mixer" },
{ "AOUTR", NULL, "Right Lineout Mixer" },
{ "SP", NULL, "Speaker" },
};
static const struct snd_soc_dapm_route adau1781_adc_dapm_routes[] = {
{ "Left PGA", NULL, "LMIC" },
{ "Right PGA", NULL, "RMIC" },
{ "Left Decimator", NULL, "Left PGA" },
{ "Right Decimator", NULL, "Right PGA" },
};
static const char * const adau1781_dmic_select_text[] = {
"DMIC1", "DMIC2",
};
static SOC_ENUM_SINGLE_VIRT_DECL(adau1781_dmic_select_enum,
adau1781_dmic_select_text);
static const struct snd_kcontrol_new adau1781_dmic_mux =
SOC_DAPM_ENUM("DMIC Select", adau1781_dmic_select_enum);
static const struct snd_soc_dapm_widget adau1781_dmic_dapm_widgets[] = {
SND_SOC_DAPM_MUX("DMIC Select", SND_SOC_NOPM, 0, 0, &adau1781_dmic_mux),
SND_SOC_DAPM_ADC("DMIC1", NULL, ADAU1781_DMIC_BEEP_CTRL, 4, 0),
SND_SOC_DAPM_ADC("DMIC2", NULL, ADAU1781_DMIC_BEEP_CTRL, 5, 0),
};
static const struct snd_soc_dapm_route adau1781_dmic_dapm_routes[] = {
{ "DMIC1", NULL, "LMIC" },
{ "DMIC2", NULL, "RMIC" },
{ "DMIC1", NULL, "Digital Mic" },
{ "DMIC2", NULL, "Digital Mic" },
{ "DMIC Select", "DMIC1", "DMIC1" },
{ "DMIC Select", "DMIC2", "DMIC2" },
{ "Left Decimator", NULL, "DMIC Select" },
{ "Right Decimator", NULL, "DMIC Select" },
};
static int adau1781_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
struct adau *adau = snd_soc_codec_get_drvdata(codec);
switch (level) {
case SND_SOC_BIAS_ON:
break;
case SND_SOC_BIAS_PREPARE:
break;
case SND_SOC_BIAS_STANDBY:
regmap_update_bits(adau->regmap, ADAU17X1_CLOCK_CONTROL,
ADAU17X1_CLOCK_CONTROL_SYSCLK_EN,
ADAU17X1_CLOCK_CONTROL_SYSCLK_EN);
/* Precharge */
regmap_update_bits(adau->regmap, ADAU1781_DIG_PWDN1, 0x8, 0x8);
break;
case SND_SOC_BIAS_OFF:
regmap_update_bits(adau->regmap, ADAU1781_DIG_PWDN1, 0xc, 0x0);
regmap_update_bits(adau->regmap, ADAU17X1_CLOCK_CONTROL,
ADAU17X1_CLOCK_CONTROL_SYSCLK_EN, 0);
break;
}
codec->dapm.bias_level = level;
return 0;
}
static bool adau1781_readable_register(struct device *dev, unsigned int reg)
{
switch (reg) {
case ADAU1781_DMIC_BEEP_CTRL:
case ADAU1781_LEFT_PGA:
case ADAU1781_RIGHT_PGA:
case ADAU1781_LEFT_PLAYBACK_MIXER:
case ADAU1781_RIGHT_PLAYBACK_MIXER:
case ADAU1781_MONO_PLAYBACK_MIXER:
case ADAU1781_LEFT_LINEOUT:
case ADAU1781_RIGHT_LINEOUT:
case ADAU1781_SPEAKER:
case ADAU1781_BEEP_ZC:
case ADAU1781_DEJITTER:
case ADAU1781_DIG_PWDN0:
case ADAU1781_DIG_PWDN1:
return true;
default:
break;
}
return adau17x1_readable_register(dev, reg);
}
static int adau1781_set_input_mode(struct adau *adau, unsigned int reg,
bool differential)
{
unsigned int val;
if (differential)
val = ADAU1781_INPUT_DIFFERNTIAL;
else
val = 0;
return regmap_update_bits(adau->regmap, reg,
ADAU1781_INPUT_DIFFERNTIAL, val);
}
static int adau1781_codec_probe(struct snd_soc_codec *codec)
{
struct adau1781_platform_data *pdata = dev_get_platdata(codec->dev);
struct adau *adau = snd_soc_codec_get_drvdata(codec);
const char *firmware;
int ret;
ret = adau17x1_add_widgets(codec);
if (ret)
return ret;
if (pdata) {
ret = adau1781_set_input_mode(adau, ADAU1781_LEFT_PGA,
pdata->left_input_differential);
if (ret)
return ret;
ret = adau1781_set_input_mode(adau, ADAU1781_RIGHT_PGA,
pdata->right_input_differential);
if (ret)
return ret;
}
if (pdata && pdata->use_dmic) {
ret = snd_soc_dapm_new_controls(&codec->dapm,
adau1781_dmic_dapm_widgets,
ARRAY_SIZE(adau1781_dmic_dapm_widgets));
if (ret)
return ret;
ret = snd_soc_dapm_add_routes(&codec->dapm,
adau1781_dmic_dapm_routes,
ARRAY_SIZE(adau1781_dmic_dapm_routes));
if (ret)
return ret;
} else {
ret = snd_soc_dapm_add_routes(&codec->dapm,
adau1781_adc_dapm_routes,
ARRAY_SIZE(adau1781_adc_dapm_routes));
if (ret)
return ret;
}
switch (adau->type) {
case ADAU1381:
firmware = ADAU1381_FIRMWARE;
break;
case ADAU1781:
firmware = ADAU1781_FIRMWARE;
break;
default:
return -EINVAL;
}
ret = adau17x1_add_routes(codec);
if (ret < 0)
return ret;
ret = adau17x1_load_firmware(adau, codec->dev, firmware);
if (ret)
dev_warn(codec->dev, "Failed to load firmware\n");
return 0;
}
static const struct snd_soc_codec_driver adau1781_codec_driver = {
.probe = adau1781_codec_probe,
.suspend = adau17x1_suspend,
.resume = adau17x1_resume,
.set_bias_level = adau1781_set_bias_level,
.controls = adau1781_controls,
.num_controls = ARRAY_SIZE(adau1781_controls),
.dapm_widgets = adau1781_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(adau1781_dapm_widgets),
.dapm_routes = adau1781_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(adau1781_dapm_routes),
};
#define ADAU1781_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \
SNDRV_PCM_FMTBIT_S32_LE)
static struct snd_soc_dai_driver adau1781_dai_driver = {
.name = "adau-hifi",
.playback = {
.stream_name = "Playback",
.channels_min = 2,
.channels_max = 8,
.rates = SNDRV_PCM_RATE_8000_96000,
.formats = ADAU1781_FORMATS,
},
.capture = {
.stream_name = "Capture",
.channels_min = 2,
.channels_max = 8,
.rates = SNDRV_PCM_RATE_8000_96000,
.formats = ADAU1781_FORMATS,
},
.ops = &adau17x1_dai_ops,
};
const struct regmap_config adau1781_regmap_config = {
.val_bits = 8,
.reg_bits = 16,
.max_register = 0x40f8,
.reg_defaults = adau1781_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(adau1781_reg_defaults),
.readable_reg = adau1781_readable_register,
.volatile_reg = adau17x1_volatile_register,
.cache_type = REGCACHE_RBTREE,
};
EXPORT_SYMBOL_GPL(adau1781_regmap_config);
int adau1781_probe(struct device *dev, struct regmap *regmap,
enum adau17x1_type type, void (*switch_mode)(struct device *dev))
{
int ret;
ret = adau17x1_probe(dev, regmap, type, switch_mode);
if (ret)
return ret;
return snd_soc_register_codec(dev, &adau1781_codec_driver,
&adau1781_dai_driver, 1);
}
EXPORT_SYMBOL_GPL(adau1781_probe);
MODULE_DESCRIPTION("ASoC ADAU1381/ADAU1781 driver");
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,23 @@
/*
* ADAU1381/ADAU1781 driver
*
* Copyright 2014 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
*
* Licensed under the GPL-2.
*/
#ifndef __SOUND_SOC_CODECS_ADAU1781_H__
#define __SOUND_SOC_CODECS_ADAU1781_H__
#include <linux/regmap.h>
#include "adau17x1.h"
struct device;
int adau1781_probe(struct device *dev, struct regmap *regmap,
enum adau17x1_type type, void (*switch_mode)(struct device *dev));
extern const struct regmap_config adau1781_regmap_config;
#endif

View File

@ -0,0 +1,866 @@
/*
* Common code for ADAU1X61 and ADAU1X81 codecs
*
* Copyright 2011-2014 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
*
* Licensed under the GPL-2 or later.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/tlv.h>
#include <linux/gcd.h>
#include <linux/i2c.h>
#include <linux/spi/spi.h>
#include <linux/regmap.h>
#include "sigmadsp.h"
#include "adau17x1.h"
static const char * const adau17x1_capture_mixer_boost_text[] = {
"Normal operation", "Boost Level 1", "Boost Level 2", "Boost Level 3",
};
static SOC_ENUM_SINGLE_DECL(adau17x1_capture_boost_enum,
ADAU17X1_REC_POWER_MGMT, 5, adau17x1_capture_mixer_boost_text);
static const char * const adau17x1_mic_bias_mode_text[] = {
"Normal operation", "High performance",
};
static SOC_ENUM_SINGLE_DECL(adau17x1_mic_bias_mode_enum,
ADAU17X1_MICBIAS, 3, adau17x1_mic_bias_mode_text);
static const DECLARE_TLV_DB_MINMAX(adau17x1_digital_tlv, -9563, 0);
static const struct snd_kcontrol_new adau17x1_controls[] = {
SOC_DOUBLE_R_TLV("Digital Capture Volume",
ADAU17X1_LEFT_INPUT_DIGITAL_VOL,
ADAU17X1_RIGHT_INPUT_DIGITAL_VOL,
0, 0xff, 1, adau17x1_digital_tlv),
SOC_DOUBLE_R_TLV("Digital Playback Volume", ADAU17X1_DAC_CONTROL1,
ADAU17X1_DAC_CONTROL2, 0, 0xff, 1, adau17x1_digital_tlv),
SOC_SINGLE("ADC High Pass Filter Switch", ADAU17X1_ADC_CONTROL,
5, 1, 0),
SOC_SINGLE("Playback De-emphasis Switch", ADAU17X1_DAC_CONTROL0,
2, 1, 0),
SOC_ENUM("Capture Boost", adau17x1_capture_boost_enum),
SOC_ENUM("Mic Bias Mode", adau17x1_mic_bias_mode_enum),
};
static int adau17x1_pll_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct adau *adau = snd_soc_codec_get_drvdata(w->codec);
int ret;
if (SND_SOC_DAPM_EVENT_ON(event)) {
adau->pll_regs[5] = 1;
} else {
adau->pll_regs[5] = 0;
/* Bypass the PLL when disabled, otherwise registers will become
* inaccessible. */
regmap_update_bits(adau->regmap, ADAU17X1_CLOCK_CONTROL,
ADAU17X1_CLOCK_CONTROL_CORECLK_SRC_PLL, 0);
}
/* The PLL register is 6 bytes long and can only be written at once. */
ret = regmap_raw_write(adau->regmap, ADAU17X1_PLL_CONTROL,
adau->pll_regs, ARRAY_SIZE(adau->pll_regs));
if (SND_SOC_DAPM_EVENT_ON(event)) {
mdelay(5);
regmap_update_bits(adau->regmap, ADAU17X1_CLOCK_CONTROL,
ADAU17X1_CLOCK_CONTROL_CORECLK_SRC_PLL,
ADAU17X1_CLOCK_CONTROL_CORECLK_SRC_PLL);
}
return 0;
}
static const char * const adau17x1_mono_stereo_text[] = {
"Stereo",
"Mono Left Channel (L+R)",
"Mono Right Channel (L+R)",
"Mono (L+R)",
};
static SOC_ENUM_SINGLE_DECL(adau17x1_dac_mode_enum,
ADAU17X1_DAC_CONTROL0, 6, adau17x1_mono_stereo_text);
static const struct snd_kcontrol_new adau17x1_dac_mode_mux =
SOC_DAPM_ENUM("DAC Mono-Stereo-Mode", adau17x1_dac_mode_enum);
static const struct snd_soc_dapm_widget adau17x1_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY_S("PLL", 3, SND_SOC_NOPM, 0, 0, adau17x1_pll_event,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_SUPPLY("AIFCLK", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("MICBIAS", ADAU17X1_MICBIAS, 0, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("Left Playback Enable", ADAU17X1_PLAY_POWER_MGMT,
0, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("Right Playback Enable", ADAU17X1_PLAY_POWER_MGMT,
1, 0, NULL, 0),
SND_SOC_DAPM_MUX("Left DAC Mode Mux", SND_SOC_NOPM, 0, 0,
&adau17x1_dac_mode_mux),
SND_SOC_DAPM_MUX("Right DAC Mode Mux", SND_SOC_NOPM, 0, 0,
&adau17x1_dac_mode_mux),
SND_SOC_DAPM_ADC("Left Decimator", NULL, ADAU17X1_ADC_CONTROL, 0, 0),
SND_SOC_DAPM_ADC("Right Decimator", NULL, ADAU17X1_ADC_CONTROL, 1, 0),
SND_SOC_DAPM_DAC("Left DAC", NULL, ADAU17X1_DAC_CONTROL0, 0, 0),
SND_SOC_DAPM_DAC("Right DAC", NULL, ADAU17X1_DAC_CONTROL0, 1, 0),
};
static const struct snd_soc_dapm_route adau17x1_dapm_routes[] = {
{ "Left Decimator", NULL, "SYSCLK" },
{ "Right Decimator", NULL, "SYSCLK" },
{ "Left DAC", NULL, "SYSCLK" },
{ "Right DAC", NULL, "SYSCLK" },
{ "Capture", NULL, "SYSCLK" },
{ "Playback", NULL, "SYSCLK" },
{ "Left DAC", NULL, "Left DAC Mode Mux" },
{ "Right DAC", NULL, "Right DAC Mode Mux" },
{ "Capture", NULL, "AIFCLK" },
{ "Playback", NULL, "AIFCLK" },
};
static const struct snd_soc_dapm_route adau17x1_dapm_pll_route = {
"SYSCLK", NULL, "PLL",
};
/*
* The MUX register for the Capture and Playback MUXs selects either DSP as
* source/destination or one of the TDM slots. The TDM slot is selected via
* snd_soc_dai_set_tdm_slot(), so we only expose whether to go to the DSP or
* directly to the DAI interface with this control.
*/
static int adau17x1_dsp_mux_enum_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
struct adau *adau = snd_soc_codec_get_drvdata(codec);
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
struct snd_soc_dapm_update update;
unsigned int stream = e->shift_l;
unsigned int val, change;
int reg;
if (ucontrol->value.enumerated.item[0] >= e->items)
return -EINVAL;
switch (ucontrol->value.enumerated.item[0]) {
case 0:
val = 0;
adau->dsp_bypass[stream] = false;
break;
default:
val = (adau->tdm_slot[stream] * 2) + 1;
adau->dsp_bypass[stream] = true;
break;
}
if (stream == SNDRV_PCM_STREAM_PLAYBACK)
reg = ADAU17X1_SERIAL_INPUT_ROUTE;
else
reg = ADAU17X1_SERIAL_OUTPUT_ROUTE;
change = snd_soc_test_bits(codec, reg, 0xff, val);
if (change) {
update.kcontrol = kcontrol;
update.mask = 0xff;
update.reg = reg;
update.val = val;
snd_soc_dapm_mux_update_power(&codec->dapm, kcontrol,
ucontrol->value.enumerated.item[0], e, &update);
}
return change;
}
static int adau17x1_dsp_mux_enum_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
struct adau *adau = snd_soc_codec_get_drvdata(codec);
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
unsigned int stream = e->shift_l;
unsigned int reg, val;
int ret;
if (stream == SNDRV_PCM_STREAM_PLAYBACK)
reg = ADAU17X1_SERIAL_INPUT_ROUTE;
else
reg = ADAU17X1_SERIAL_OUTPUT_ROUTE;
ret = regmap_read(adau->regmap, reg, &val);
if (ret)
return ret;
if (val != 0)
val = 1;
ucontrol->value.enumerated.item[0] = val;
return 0;
}
#define DECLARE_ADAU17X1_DSP_MUX_CTRL(_name, _label, _stream, _text) \
const struct snd_kcontrol_new _name = \
SOC_DAPM_ENUM_EXT(_label, (const struct soc_enum)\
SOC_ENUM_SINGLE(SND_SOC_NOPM, _stream, \
ARRAY_SIZE(_text), _text), \
adau17x1_dsp_mux_enum_get, adau17x1_dsp_mux_enum_put)
static const char * const adau17x1_dac_mux_text[] = {
"DSP",
"AIFIN",
};
static const char * const adau17x1_capture_mux_text[] = {
"DSP",
"Decimator",
};
static DECLARE_ADAU17X1_DSP_MUX_CTRL(adau17x1_dac_mux, "DAC Playback Mux",
SNDRV_PCM_STREAM_PLAYBACK, adau17x1_dac_mux_text);
static DECLARE_ADAU17X1_DSP_MUX_CTRL(adau17x1_capture_mux, "Capture Mux",
SNDRV_PCM_STREAM_CAPTURE, adau17x1_capture_mux_text);
static const struct snd_soc_dapm_widget adau17x1_dsp_dapm_widgets[] = {
SND_SOC_DAPM_PGA("DSP", ADAU17X1_DSP_RUN, 0, 0, NULL, 0),
SND_SOC_DAPM_SIGGEN("DSP Siggen"),
SND_SOC_DAPM_MUX("DAC Playback Mux", SND_SOC_NOPM, 0, 0,
&adau17x1_dac_mux),
SND_SOC_DAPM_MUX("Capture Mux", SND_SOC_NOPM, 0, 0,
&adau17x1_capture_mux),
};
static const struct snd_soc_dapm_route adau17x1_dsp_dapm_routes[] = {
{ "DAC Playback Mux", "DSP", "DSP" },
{ "DAC Playback Mux", "AIFIN", "Playback" },
{ "Left DAC Mode Mux", "Stereo", "DAC Playback Mux" },
{ "Left DAC Mode Mux", "Mono (L+R)", "DAC Playback Mux" },
{ "Left DAC Mode Mux", "Mono Left Channel (L+R)", "DAC Playback Mux" },
{ "Right DAC Mode Mux", "Stereo", "DAC Playback Mux" },
{ "Right DAC Mode Mux", "Mono (L+R)", "DAC Playback Mux" },
{ "Right DAC Mode Mux", "Mono Right Channel (L+R)", "DAC Playback Mux" },
{ "Capture Mux", "DSP", "DSP" },
{ "Capture Mux", "Decimator", "Left Decimator" },
{ "Capture Mux", "Decimator", "Right Decimator" },
{ "Capture", NULL, "Capture Mux" },
{ "DSP", NULL, "DSP Siggen" },
{ "DSP", NULL, "Left Decimator" },
{ "DSP", NULL, "Right Decimator" },
};
static const struct snd_soc_dapm_route adau17x1_no_dsp_dapm_routes[] = {
{ "Left DAC Mode Mux", "Stereo", "Playback" },
{ "Left DAC Mode Mux", "Mono (L+R)", "Playback" },
{ "Left DAC Mode Mux", "Mono Left Channel (L+R)", "Playback" },
{ "Right DAC Mode Mux", "Stereo", "Playback" },
{ "Right DAC Mode Mux", "Mono (L+R)", "Playback" },
{ "Right DAC Mode Mux", "Mono Right Channel (L+R)", "Playback" },
{ "Capture", NULL, "Left Decimator" },
{ "Capture", NULL, "Right Decimator" },
};
bool adau17x1_has_dsp(struct adau *adau)
{
switch (adau->type) {
case ADAU1761:
case ADAU1381:
case ADAU1781:
return true;
default:
return false;
}
}
EXPORT_SYMBOL_GPL(adau17x1_has_dsp);
static int adau17x1_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
struct adau *adau = snd_soc_codec_get_drvdata(codec);
unsigned int val, div, dsp_div;
unsigned int freq;
if (adau->clk_src == ADAU17X1_CLK_SRC_PLL)
freq = adau->pll_freq;
else
freq = adau->sysclk;
if (freq % params_rate(params) != 0)
return -EINVAL;
switch (freq / params_rate(params)) {
case 1024: /* fs */
div = 0;
dsp_div = 1;
break;
case 6144: /* fs / 6 */
div = 1;
dsp_div = 6;
break;
case 4096: /* fs / 4 */
div = 2;
dsp_div = 5;
break;
case 3072: /* fs / 3 */
div = 3;
dsp_div = 4;
break;
case 2048: /* fs / 2 */
div = 4;
dsp_div = 3;
break;
case 1536: /* fs / 1.5 */
div = 5;
dsp_div = 2;
break;
case 512: /* fs / 0.5 */
div = 6;
dsp_div = 0;
break;
default:
return -EINVAL;
}
regmap_update_bits(adau->regmap, ADAU17X1_CONVERTER0,
ADAU17X1_CONVERTER0_CONVSR_MASK, div);
if (adau17x1_has_dsp(adau)) {
regmap_write(adau->regmap, ADAU17X1_SERIAL_SAMPLING_RATE, div);
regmap_write(adau->regmap, ADAU17X1_DSP_SAMPLING_RATE, dsp_div);
}
if (adau->dai_fmt != SND_SOC_DAIFMT_RIGHT_J)
return 0;
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE:
val = ADAU17X1_SERIAL_PORT1_DELAY16;
break;
case SNDRV_PCM_FORMAT_S24_LE:
val = ADAU17X1_SERIAL_PORT1_DELAY8;
break;
case SNDRV_PCM_FORMAT_S32_LE:
val = ADAU17X1_SERIAL_PORT1_DELAY0;
break;
default:
return -EINVAL;
}
return regmap_update_bits(adau->regmap, ADAU17X1_SERIAL_PORT1,
ADAU17X1_SERIAL_PORT1_DELAY_MASK, val);
}
static int adau17x1_set_dai_pll(struct snd_soc_dai *dai, int pll_id,
int source, unsigned int freq_in, unsigned int freq_out)
{
struct snd_soc_codec *codec = dai->codec;
struct adau *adau = snd_soc_codec_get_drvdata(codec);
unsigned int r, n, m, i, j;
unsigned int div;
int ret;
if (freq_in < 8000000 || freq_in > 27000000)
return -EINVAL;
if (!freq_out) {
r = 0;
n = 0;
m = 0;
div = 0;
} else {
if (freq_out % freq_in != 0) {
div = DIV_ROUND_UP(freq_in, 13500000);
freq_in /= div;
r = freq_out / freq_in;
i = freq_out % freq_in;
j = gcd(i, freq_in);
n = i / j;
m = freq_in / j;
div--;
} else {
r = freq_out / freq_in;
n = 0;
m = 0;
div = 0;
}
if (n > 0xffff || m > 0xffff || div > 3 || r > 8 || r < 2)
return -EINVAL;
}
adau->pll_regs[0] = m >> 8;
adau->pll_regs[1] = m & 0xff;
adau->pll_regs[2] = n >> 8;
adau->pll_regs[3] = n & 0xff;
adau->pll_regs[4] = (r << 3) | (div << 1);
if (m != 0)
adau->pll_regs[4] |= 1; /* Fractional mode */
/* The PLL register is 6 bytes long and can only be written at once. */
ret = regmap_raw_write(adau->regmap, ADAU17X1_PLL_CONTROL,
adau->pll_regs, ARRAY_SIZE(adau->pll_regs));
if (ret)
return ret;
adau->pll_freq = freq_out;
return 0;
}
static int adau17x1_set_dai_sysclk(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir)
{
struct adau *adau = snd_soc_codec_get_drvdata(dai->codec);
struct snd_soc_dapm_context *dapm = &dai->codec->dapm;
switch (clk_id) {
case ADAU17X1_CLK_SRC_MCLK:
case ADAU17X1_CLK_SRC_PLL:
break;
default:
return -EINVAL;
}
adau->sysclk = freq;
if (adau->clk_src != clk_id) {
if (clk_id == ADAU17X1_CLK_SRC_PLL) {
snd_soc_dapm_add_routes(dapm,
&adau17x1_dapm_pll_route, 1);
} else {
snd_soc_dapm_del_routes(dapm,
&adau17x1_dapm_pll_route, 1);
}
}
adau->clk_src = clk_id;
return 0;
}
static int adau17x1_set_dai_fmt(struct snd_soc_dai *dai,
unsigned int fmt)
{
struct adau *adau = snd_soc_codec_get_drvdata(dai->codec);
unsigned int ctrl0, ctrl1;
int lrclk_pol;
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBM_CFM:
ctrl0 = ADAU17X1_SERIAL_PORT0_MASTER;
adau->master = true;
break;
case SND_SOC_DAIFMT_CBS_CFS:
ctrl0 = 0;
adau->master = false;
break;
default:
return -EINVAL;
}
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
lrclk_pol = 0;
ctrl1 = ADAU17X1_SERIAL_PORT1_DELAY1;
break;
case SND_SOC_DAIFMT_LEFT_J:
case SND_SOC_DAIFMT_RIGHT_J:
lrclk_pol = 1;
ctrl1 = ADAU17X1_SERIAL_PORT1_DELAY0;
break;
case SND_SOC_DAIFMT_DSP_A:
lrclk_pol = 1;
ctrl0 |= ADAU17X1_SERIAL_PORT0_PULSE_MODE;
ctrl1 = ADAU17X1_SERIAL_PORT1_DELAY1;
break;
case SND_SOC_DAIFMT_DSP_B:
lrclk_pol = 1;
ctrl0 |= ADAU17X1_SERIAL_PORT0_PULSE_MODE;
ctrl1 = ADAU17X1_SERIAL_PORT1_DELAY0;
break;
default:
return -EINVAL;
}
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_NB_NF:
break;
case SND_SOC_DAIFMT_IB_NF:
ctrl0 |= ADAU17X1_SERIAL_PORT0_BCLK_POL;
break;
case SND_SOC_DAIFMT_NB_IF:
lrclk_pol = !lrclk_pol;
break;
case SND_SOC_DAIFMT_IB_IF:
ctrl0 |= ADAU17X1_SERIAL_PORT0_BCLK_POL;
lrclk_pol = !lrclk_pol;
break;
default:
return -EINVAL;
}
if (lrclk_pol)
ctrl0 |= ADAU17X1_SERIAL_PORT0_LRCLK_POL;
regmap_write(adau->regmap, ADAU17X1_SERIAL_PORT0, ctrl0);
regmap_write(adau->regmap, ADAU17X1_SERIAL_PORT1, ctrl1);
adau->dai_fmt = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
return 0;
}
static int adau17x1_set_dai_tdm_slot(struct snd_soc_dai *dai,
unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width)
{
struct adau *adau = snd_soc_codec_get_drvdata(dai->codec);
unsigned int ser_ctrl0, ser_ctrl1;
unsigned int conv_ctrl0, conv_ctrl1;
/* I2S mode */
if (slots == 0) {
slots = 2;
rx_mask = 3;
tx_mask = 3;
slot_width = 32;
}
switch (slots) {
case 2:
ser_ctrl0 = ADAU17X1_SERIAL_PORT0_STEREO;
break;
case 4:
ser_ctrl0 = ADAU17X1_SERIAL_PORT0_TDM4;
break;
case 8:
if (adau->type == ADAU1361)
return -EINVAL;
ser_ctrl0 = ADAU17X1_SERIAL_PORT0_TDM8;
break;
default:
return -EINVAL;
}
switch (slot_width * slots) {
case 32:
if (adau->type == ADAU1761)
return -EINVAL;
ser_ctrl1 = ADAU17X1_SERIAL_PORT1_BCLK32;
break;
case 64:
ser_ctrl1 = ADAU17X1_SERIAL_PORT1_BCLK64;
break;
case 48:
ser_ctrl1 = ADAU17X1_SERIAL_PORT1_BCLK48;
break;
case 128:
ser_ctrl1 = ADAU17X1_SERIAL_PORT1_BCLK128;
break;
case 256:
if (adau->type == ADAU1361)
return -EINVAL;
ser_ctrl1 = ADAU17X1_SERIAL_PORT1_BCLK256;
break;
default:
return -EINVAL;
}
switch (rx_mask) {
case 0x03:
conv_ctrl1 = ADAU17X1_CONVERTER1_ADC_PAIR(1);
adau->tdm_slot[SNDRV_PCM_STREAM_CAPTURE] = 0;
break;
case 0x0c:
conv_ctrl1 = ADAU17X1_CONVERTER1_ADC_PAIR(2);
adau->tdm_slot[SNDRV_PCM_STREAM_CAPTURE] = 1;
break;
case 0x30:
conv_ctrl1 = ADAU17X1_CONVERTER1_ADC_PAIR(3);
adau->tdm_slot[SNDRV_PCM_STREAM_CAPTURE] = 2;
break;
case 0xc0:
conv_ctrl1 = ADAU17X1_CONVERTER1_ADC_PAIR(4);
adau->tdm_slot[SNDRV_PCM_STREAM_CAPTURE] = 3;
break;
default:
return -EINVAL;
}
switch (tx_mask) {
case 0x03:
conv_ctrl0 = ADAU17X1_CONVERTER0_DAC_PAIR(1);
adau->tdm_slot[SNDRV_PCM_STREAM_PLAYBACK] = 0;
break;
case 0x0c:
conv_ctrl0 = ADAU17X1_CONVERTER0_DAC_PAIR(2);
adau->tdm_slot[SNDRV_PCM_STREAM_PLAYBACK] = 1;
break;
case 0x30:
conv_ctrl0 = ADAU17X1_CONVERTER0_DAC_PAIR(3);
adau->tdm_slot[SNDRV_PCM_STREAM_PLAYBACK] = 2;
break;
case 0xc0:
conv_ctrl0 = ADAU17X1_CONVERTER0_DAC_PAIR(4);
adau->tdm_slot[SNDRV_PCM_STREAM_PLAYBACK] = 3;
break;
default:
return -EINVAL;
}
regmap_update_bits(adau->regmap, ADAU17X1_CONVERTER0,
ADAU17X1_CONVERTER0_DAC_PAIR_MASK, conv_ctrl0);
regmap_update_bits(adau->regmap, ADAU17X1_CONVERTER1,
ADAU17X1_CONVERTER1_ADC_PAIR_MASK, conv_ctrl1);
regmap_update_bits(adau->regmap, ADAU17X1_SERIAL_PORT0,
ADAU17X1_SERIAL_PORT0_TDM_MASK, ser_ctrl0);
regmap_update_bits(adau->regmap, ADAU17X1_SERIAL_PORT1,
ADAU17X1_SERIAL_PORT1_BCLK_MASK, ser_ctrl1);
if (!adau17x1_has_dsp(adau))
return 0;
if (adau->dsp_bypass[SNDRV_PCM_STREAM_PLAYBACK]) {
regmap_write(adau->regmap, ADAU17X1_SERIAL_INPUT_ROUTE,
(adau->tdm_slot[SNDRV_PCM_STREAM_PLAYBACK] * 2) + 1);
}
if (adau->dsp_bypass[SNDRV_PCM_STREAM_CAPTURE]) {
regmap_write(adau->regmap, ADAU17X1_SERIAL_OUTPUT_ROUTE,
(adau->tdm_slot[SNDRV_PCM_STREAM_CAPTURE] * 2) + 1);
}
return 0;
}
const struct snd_soc_dai_ops adau17x1_dai_ops = {
.hw_params = adau17x1_hw_params,
.set_sysclk = adau17x1_set_dai_sysclk,
.set_fmt = adau17x1_set_dai_fmt,
.set_pll = adau17x1_set_dai_pll,
.set_tdm_slot = adau17x1_set_dai_tdm_slot,
};
EXPORT_SYMBOL_GPL(adau17x1_dai_ops);
int adau17x1_set_micbias_voltage(struct snd_soc_codec *codec,
enum adau17x1_micbias_voltage micbias)
{
struct adau *adau = snd_soc_codec_get_drvdata(codec);
switch (micbias) {
case ADAU17X1_MICBIAS_0_90_AVDD:
case ADAU17X1_MICBIAS_0_65_AVDD:
break;
default:
return -EINVAL;
}
return regmap_write(adau->regmap, ADAU17X1_MICBIAS, micbias << 2);
}
EXPORT_SYMBOL_GPL(adau17x1_set_micbias_voltage);
bool adau17x1_readable_register(struct device *dev, unsigned int reg)
{
switch (reg) {
case ADAU17X1_CLOCK_CONTROL:
case ADAU17X1_PLL_CONTROL:
case ADAU17X1_REC_POWER_MGMT:
case ADAU17X1_MICBIAS:
case ADAU17X1_SERIAL_PORT0:
case ADAU17X1_SERIAL_PORT1:
case ADAU17X1_CONVERTER0:
case ADAU17X1_CONVERTER1:
case ADAU17X1_LEFT_INPUT_DIGITAL_VOL:
case ADAU17X1_RIGHT_INPUT_DIGITAL_VOL:
case ADAU17X1_ADC_CONTROL:
case ADAU17X1_PLAY_POWER_MGMT:
case ADAU17X1_DAC_CONTROL0:
case ADAU17X1_DAC_CONTROL1:
case ADAU17X1_DAC_CONTROL2:
case ADAU17X1_SERIAL_PORT_PAD:
case ADAU17X1_CONTROL_PORT_PAD0:
case ADAU17X1_CONTROL_PORT_PAD1:
case ADAU17X1_DSP_SAMPLING_RATE:
case ADAU17X1_SERIAL_INPUT_ROUTE:
case ADAU17X1_SERIAL_OUTPUT_ROUTE:
case ADAU17X1_DSP_ENABLE:
case ADAU17X1_DSP_RUN:
case ADAU17X1_SERIAL_SAMPLING_RATE:
return true;
default:
break;
}
return false;
}
EXPORT_SYMBOL_GPL(adau17x1_readable_register);
bool adau17x1_volatile_register(struct device *dev, unsigned int reg)
{
/* SigmaDSP parameter and program memory */
if (reg < 0x4000)
return true;
switch (reg) {
/* The PLL register is 6 bytes long */
case ADAU17X1_PLL_CONTROL:
case ADAU17X1_PLL_CONTROL + 1:
case ADAU17X1_PLL_CONTROL + 2:
case ADAU17X1_PLL_CONTROL + 3:
case ADAU17X1_PLL_CONTROL + 4:
case ADAU17X1_PLL_CONTROL + 5:
return true;
default:
break;
}
return false;
}
EXPORT_SYMBOL_GPL(adau17x1_volatile_register);
int adau17x1_load_firmware(struct adau *adau, struct device *dev,
const char *firmware)
{
int ret;
int dspsr;
ret = regmap_read(adau->regmap, ADAU17X1_DSP_SAMPLING_RATE, &dspsr);
if (ret)
return ret;
regmap_write(adau->regmap, ADAU17X1_DSP_ENABLE, 1);
regmap_write(adau->regmap, ADAU17X1_DSP_SAMPLING_RATE, 0xf);
ret = process_sigma_firmware_regmap(dev, adau->regmap, firmware);
if (ret) {
regmap_write(adau->regmap, ADAU17X1_DSP_ENABLE, 0);
return ret;
}
regmap_write(adau->regmap, ADAU17X1_DSP_SAMPLING_RATE, dspsr);
return 0;
}
EXPORT_SYMBOL_GPL(adau17x1_load_firmware);
int adau17x1_add_widgets(struct snd_soc_codec *codec)
{
struct adau *adau = snd_soc_codec_get_drvdata(codec);
int ret;
ret = snd_soc_add_codec_controls(codec, adau17x1_controls,
ARRAY_SIZE(adau17x1_controls));
if (ret)
return ret;
ret = snd_soc_dapm_new_controls(&codec->dapm, adau17x1_dapm_widgets,
ARRAY_SIZE(adau17x1_dapm_widgets));
if (ret)
return ret;
if (adau17x1_has_dsp(adau)) {
ret = snd_soc_dapm_new_controls(&codec->dapm,
adau17x1_dsp_dapm_widgets,
ARRAY_SIZE(adau17x1_dsp_dapm_widgets));
}
return ret;
}
EXPORT_SYMBOL_GPL(adau17x1_add_widgets);
int adau17x1_add_routes(struct snd_soc_codec *codec)
{
struct adau *adau = snd_soc_codec_get_drvdata(codec);
int ret;
ret = snd_soc_dapm_add_routes(&codec->dapm, adau17x1_dapm_routes,
ARRAY_SIZE(adau17x1_dapm_routes));
if (ret)
return ret;
if (adau17x1_has_dsp(adau)) {
ret = snd_soc_dapm_add_routes(&codec->dapm,
adau17x1_dsp_dapm_routes,
ARRAY_SIZE(adau17x1_dsp_dapm_routes));
} else {
ret = snd_soc_dapm_add_routes(&codec->dapm,
adau17x1_no_dsp_dapm_routes,
ARRAY_SIZE(adau17x1_no_dsp_dapm_routes));
}
return ret;
}
EXPORT_SYMBOL_GPL(adau17x1_add_routes);
int adau17x1_suspend(struct snd_soc_codec *codec)
{
codec->driver->set_bias_level(codec, SND_SOC_BIAS_OFF);
return 0;
}
EXPORT_SYMBOL_GPL(adau17x1_suspend);
int adau17x1_resume(struct snd_soc_codec *codec)
{
struct adau *adau = snd_soc_codec_get_drvdata(codec);
if (adau->switch_mode)
adau->switch_mode(codec->dev);
codec->driver->set_bias_level(codec, SND_SOC_BIAS_STANDBY);
regcache_sync(adau->regmap);
return 0;
}
EXPORT_SYMBOL_GPL(adau17x1_resume);
int adau17x1_probe(struct device *dev, struct regmap *regmap,
enum adau17x1_type type, void (*switch_mode)(struct device *dev))
{
struct adau *adau;
if (IS_ERR(regmap))
return PTR_ERR(regmap);
adau = devm_kzalloc(dev, sizeof(*adau), GFP_KERNEL);
if (!adau)
return -ENOMEM;
adau->regmap = regmap;
adau->switch_mode = switch_mode;
adau->type = type;
dev_set_drvdata(dev, adau);
if (switch_mode)
switch_mode(dev);
return 0;
}
EXPORT_SYMBOL_GPL(adau17x1_probe);
MODULE_DESCRIPTION("ASoC ADAU1X61/ADAU1X81 common code");
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,124 @@
#ifndef __ADAU17X1_H__
#define __ADAU17X1_H__
#include <linux/regmap.h>
#include <linux/platform_data/adau17x1.h>
enum adau17x1_type {
ADAU1361,
ADAU1761,
ADAU1381,
ADAU1781,
};
enum adau17x1_pll {
ADAU17X1_PLL,
};
enum adau17x1_pll_src {
ADAU17X1_PLL_SRC_MCLK,
};
enum adau17x1_clk_src {
ADAU17X1_CLK_SRC_MCLK,
ADAU17X1_CLK_SRC_PLL,
};
struct adau {
unsigned int sysclk;
unsigned int pll_freq;
enum adau17x1_clk_src clk_src;
enum adau17x1_type type;
void (*switch_mode)(struct device *dev);
unsigned int dai_fmt;
uint8_t pll_regs[6];
bool master;
unsigned int tdm_slot[2];
bool dsp_bypass[2];
struct regmap *regmap;
};
int adau17x1_add_widgets(struct snd_soc_codec *codec);
int adau17x1_add_routes(struct snd_soc_codec *codec);
int adau17x1_probe(struct device *dev, struct regmap *regmap,
enum adau17x1_type type, void (*switch_mode)(struct device *dev));
int adau17x1_set_micbias_voltage(struct snd_soc_codec *codec,
enum adau17x1_micbias_voltage micbias);
bool adau17x1_readable_register(struct device *dev, unsigned int reg);
bool adau17x1_volatile_register(struct device *dev, unsigned int reg);
int adau17x1_suspend(struct snd_soc_codec *codec);
int adau17x1_resume(struct snd_soc_codec *codec);
extern const struct snd_soc_dai_ops adau17x1_dai_ops;
int adau17x1_load_firmware(struct adau *adau, struct device *dev,
const char *firmware);
bool adau17x1_has_dsp(struct adau *adau);
#define ADAU17X1_CLOCK_CONTROL 0x4000
#define ADAU17X1_PLL_CONTROL 0x4002
#define ADAU17X1_REC_POWER_MGMT 0x4009
#define ADAU17X1_MICBIAS 0x4010
#define ADAU17X1_SERIAL_PORT0 0x4015
#define ADAU17X1_SERIAL_PORT1 0x4016
#define ADAU17X1_CONVERTER0 0x4017
#define ADAU17X1_CONVERTER1 0x4018
#define ADAU17X1_LEFT_INPUT_DIGITAL_VOL 0x401a
#define ADAU17X1_RIGHT_INPUT_DIGITAL_VOL 0x401b
#define ADAU17X1_ADC_CONTROL 0x4019
#define ADAU17X1_PLAY_POWER_MGMT 0x4029
#define ADAU17X1_DAC_CONTROL0 0x402a
#define ADAU17X1_DAC_CONTROL1 0x402b
#define ADAU17X1_DAC_CONTROL2 0x402c
#define ADAU17X1_SERIAL_PORT_PAD 0x402d
#define ADAU17X1_CONTROL_PORT_PAD0 0x402f
#define ADAU17X1_CONTROL_PORT_PAD1 0x4030
#define ADAU17X1_DSP_SAMPLING_RATE 0x40eb
#define ADAU17X1_SERIAL_INPUT_ROUTE 0x40f2
#define ADAU17X1_SERIAL_OUTPUT_ROUTE 0x40f3
#define ADAU17X1_DSP_ENABLE 0x40f5
#define ADAU17X1_DSP_RUN 0x40f6
#define ADAU17X1_SERIAL_SAMPLING_RATE 0x40f8
#define ADAU17X1_SERIAL_PORT0_BCLK_POL BIT(4)
#define ADAU17X1_SERIAL_PORT0_LRCLK_POL BIT(3)
#define ADAU17X1_SERIAL_PORT0_MASTER BIT(0)
#define ADAU17X1_SERIAL_PORT1_DELAY1 0x00
#define ADAU17X1_SERIAL_PORT1_DELAY0 0x01
#define ADAU17X1_SERIAL_PORT1_DELAY8 0x02
#define ADAU17X1_SERIAL_PORT1_DELAY16 0x03
#define ADAU17X1_SERIAL_PORT1_DELAY_MASK 0x03
#define ADAU17X1_CLOCK_CONTROL_INFREQ_MASK 0x6
#define ADAU17X1_CLOCK_CONTROL_CORECLK_SRC_PLL BIT(3)
#define ADAU17X1_CLOCK_CONTROL_SYSCLK_EN BIT(0)
#define ADAU17X1_SERIAL_PORT1_BCLK32 (0x0 << 5)
#define ADAU17X1_SERIAL_PORT1_BCLK48 (0x1 << 5)
#define ADAU17X1_SERIAL_PORT1_BCLK64 (0x2 << 5)
#define ADAU17X1_SERIAL_PORT1_BCLK128 (0x3 << 5)
#define ADAU17X1_SERIAL_PORT1_BCLK256 (0x4 << 5)
#define ADAU17X1_SERIAL_PORT1_BCLK_MASK (0x7 << 5)
#define ADAU17X1_SERIAL_PORT0_STEREO (0x0 << 1)
#define ADAU17X1_SERIAL_PORT0_TDM4 (0x1 << 1)
#define ADAU17X1_SERIAL_PORT0_TDM8 (0x2 << 1)
#define ADAU17X1_SERIAL_PORT0_TDM_MASK (0x3 << 1)
#define ADAU17X1_SERIAL_PORT0_PULSE_MODE BIT(5)
#define ADAU17X1_CONVERTER0_DAC_PAIR(x) (((x) - 1) << 5)
#define ADAU17X1_CONVERTER0_DAC_PAIR_MASK (0x3 << 5)
#define ADAU17X1_CONVERTER1_ADC_PAIR(x) ((x) - 1)
#define ADAU17X1_CONVERTER1_ADC_PAIR_MASK 0x3
#define ADAU17X1_CONVERTER0_CONVSR_MASK 0x7
#endif

View File

@ -763,14 +763,14 @@ static int cs42l56_set_sysclk(struct snd_soc_dai *codec_dai,
case CS42L56_MCLK_11P2896MHZ:
case CS42L56_MCLK_12MHZ:
case CS42L56_MCLK_12P288MHZ:
cs42l56->mclk_div2 = 1;
cs42l56->mclk_div2 = CS42L56_MCLK_DIV2;
cs42l56->mclk_prediv = 0;
break;
case CS42L56_MCLK_22P5792MHZ:
case CS42L56_MCLK_24MHZ:
case CS42L56_MCLK_24P576MHZ:
cs42l56->mclk_div2 = 1;
cs42l56->mclk_prediv = 1;
cs42l56->mclk_div2 = CS42L56_MCLK_DIV2;
cs42l56->mclk_prediv = CS42L56_MCLK_PREDIV;
break;
default:
return -EINVAL;
@ -844,57 +844,49 @@ static int cs42l56_digital_mute(struct snd_soc_dai *dai, int mute)
/* Hit the DSP Mixer first */
snd_soc_update_bits(codec, CS42L56_DSP_MUTE_CTL,
CS42L56_ADCAMIX_MUTE_MASK |
CS42L56_ADCBMIX_MUTE_MASK |
CS42L56_PCMAMIX_MUTE_MASK |
CS42L56_PCMBMIX_MUTE_MASK |
CS42L56_MSTB_MUTE_MASK |
CS42L56_MSTA_MUTE_MASK,
CS42L56_MUTE);
CS42L56_ADCBMIX_MUTE_MASK |
CS42L56_PCMAMIX_MUTE_MASK |
CS42L56_PCMBMIX_MUTE_MASK |
CS42L56_MSTB_MUTE_MASK |
CS42L56_MSTA_MUTE_MASK,
CS42L56_MUTE_ALL);
/* Mute ADC's */
snd_soc_update_bits(codec, CS42L56_MISC_ADC_CTL,
CS42L56_ADCA_MUTE_MASK |
CS42L56_ADCB_MUTE_MASK,
CS42L56_MUTE);
CS42L56_ADCA_MUTE_MASK |
CS42L56_ADCB_MUTE_MASK,
CS42L56_MUTE_ALL);
/* HP And LO */
snd_soc_update_bits(codec, CS42L56_HPA_VOLUME,
CS42L56_HP_MUTE_MASK,
CS42L56_MUTE);
CS42L56_HP_MUTE_MASK, CS42L56_MUTE_ALL);
snd_soc_update_bits(codec, CS42L56_HPB_VOLUME,
CS42L56_HP_MUTE_MASK,
CS42L56_MUTE);
CS42L56_HP_MUTE_MASK, CS42L56_MUTE_ALL);
snd_soc_update_bits(codec, CS42L56_LOA_VOLUME,
CS42L56_LO_MUTE_MASK,
CS42L56_MUTE);
CS42L56_LO_MUTE_MASK, CS42L56_MUTE_ALL);
snd_soc_update_bits(codec, CS42L56_LOB_VOLUME,
CS42L56_LO_MUTE_MASK,
CS42L56_MUTE);
CS42L56_LO_MUTE_MASK, CS42L56_MUTE_ALL);
} else {
snd_soc_update_bits(codec, CS42L56_DSP_MUTE_CTL,
CS42L56_ADCAMIX_MUTE_MASK |
CS42L56_ADCBMIX_MUTE_MASK |
CS42L56_PCMAMIX_MUTE_MASK |
CS42L56_PCMBMIX_MUTE_MASK |
CS42L56_MSTB_MUTE_MASK |
CS42L56_MSTA_MUTE_MASK,
CS42L56_UNMUTE);
CS42L56_ADCBMIX_MUTE_MASK |
CS42L56_PCMAMIX_MUTE_MASK |
CS42L56_PCMBMIX_MUTE_MASK |
CS42L56_MSTB_MUTE_MASK |
CS42L56_MSTA_MUTE_MASK,
CS42L56_UNMUTE);
snd_soc_update_bits(codec, CS42L56_MISC_ADC_CTL,
CS42L56_ADCA_MUTE_MASK |
CS42L56_ADCB_MUTE_MASK,
CS42L56_UNMUTE);
CS42L56_ADCA_MUTE_MASK |
CS42L56_ADCB_MUTE_MASK,
CS42L56_UNMUTE);
snd_soc_update_bits(codec, CS42L56_HPA_VOLUME,
CS42L56_HP_MUTE_MASK,
CS42L56_UNMUTE);
CS42L56_HP_MUTE_MASK, CS42L56_UNMUTE);
snd_soc_update_bits(codec, CS42L56_HPB_VOLUME,
CS42L56_HP_MUTE_MASK,
CS42L56_UNMUTE);
CS42L56_HP_MUTE_MASK, CS42L56_UNMUTE);
snd_soc_update_bits(codec, CS42L56_LOA_VOLUME,
CS42L56_LO_MUTE_MASK,
CS42L56_UNMUTE);
CS42L56_LO_MUTE_MASK, CS42L56_UNMUTE);
snd_soc_update_bits(codec, CS42L56_LOB_VOLUME,
CS42L56_LO_MUTE_MASK,
CS42L56_UNMUTE);
CS42L56_LO_MUTE_MASK, CS42L56_UNMUTE);
}
return 0;
}

View File

@ -80,19 +80,21 @@
#define CS42L56_PDN_HPB_MASK 0xc0
/* serial port and clk masks */
#define CS42L56_MASTER_MODE 1
#define CS42L56_MASTER_MODE 0x40
#define CS42L56_SLAVE_MODE 0
#define CS42L56_MS_MODE_MASK 0x40
#define CS42L56_SCLK_INV 1
#define CS42L56_SCLK_INV 0x20
#define CS42L56_SCLK_INV_MASK 0x20
#define CS42L56_SCLK_MCLK_MASK 0x18
#define CS42L56_MCLK_PREDIV 0x04
#define CS42L56_MCLK_PREDIV_MASK 0x04
#define CS42L56_MCLK_DIV2 0x02
#define CS42L56_MCLK_DIV2_MASK 0x02
#define CS42L56_MCLK_DIS_MASK 0x01
#define CS42L56_CLK_AUTO_MASK 0x20
#define CS42L56_CLK_RATIO_MASK 0x1f
#define CS42L56_DIG_FMT_I2S 0
#define CS42L56_DIG_FMT_LEFT_J 1
#define CS42L56_DIG_FMT_LEFT_J 0x08
#define CS42L56_DIG_FMT_MASK 0x08
/* Class H and misc ctl masks */
@ -116,7 +118,7 @@
#define CS42L56_DEEMPH_MASK 0x40
#define CS42L56_PLYBCK_GANG_MASK 0x10
#define CS42L56_PCM_INV_MASK 0x0c
#define CS42L56_MUTE 1
#define CS42L56_MUTE_ALL 0xff
#define CS42L56_UNMUTE 0
#define CS42L56_ADCAMIX_MUTE_MASK 0x40
#define CS42L56_ADCBMIX_MUTE_MASK 0x80

View File

@ -17,6 +17,7 @@
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/acpi.h>
#include <linux/clk.h>
#include <sound/jack.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@ -1547,19 +1548,19 @@ static const int lrclk_rates[] = {
};
static const int user_pclk_rates[] = {
13000000, 13000000
13000000, 13000000, 19200000, 19200000,
};
static const int user_lrclk_rates[] = {
44100, 48000
44100, 48000, 44100, 48000,
};
static const unsigned long long ni_value[] = {
3528, 768
3528, 768, 441, 8
};
static const unsigned long long mi_value[] = {
8125, 1625
8125, 1625, 1500, 25
};
static void max98090_configure_bclk(struct snd_soc_codec *codec)
@ -1800,6 +1801,19 @@ static int max98090_set_bias_level(struct snd_soc_codec *codec,
break;
case SND_SOC_BIAS_PREPARE:
/*
* SND_SOC_BIAS_PREPARE is called while preparing for a
* transition to ON or away from ON. If current bias_level
* is SND_SOC_BIAS_ON, then it is preparing for a transition
* away from ON. Disable the clock in that case, otherwise
* enable it.
*/
if (!IS_ERR(max98090->mclk)) {
if (codec->dapm.bias_level == SND_SOC_BIAS_ON)
clk_disable_unprepare(max98090->mclk);
else
clk_prepare_enable(max98090->mclk);
}
break;
case SND_SOC_BIAS_STANDBY:
@ -1929,6 +1943,11 @@ static int max98090_dai_set_sysclk(struct snd_soc_dai *dai,
if (freq == max98090->sysclk)
return 0;
if (!IS_ERR(max98090->mclk)) {
freq = clk_round_rate(max98090->mclk, freq);
clk_set_rate(max98090->mclk, freq);
}
/* Setup clocks for slave mode, and using the PLL
* PSCLK = 0x01 (when master clk is 10MHz to 20MHz)
* 0x02 (when master clk is 20MHz to 40MHz)..
@ -2213,6 +2232,10 @@ static int max98090_probe(struct snd_soc_codec *codec)
dev_dbg(codec->dev, "max98090_probe\n");
max98090->mclk = devm_clk_get(codec->dev, "mclk");
if (PTR_ERR(max98090->mclk) == -EPROBE_DEFER)
return -EPROBE_DEFER;
max98090->codec = codec;
/* Reset the codec, the DSP core, and disable all interrupts */

View File

@ -1524,6 +1524,7 @@ struct max98090_priv {
struct snd_soc_codec *codec;
enum max98090_type devtype;
struct max98090_pdata *pdata;
struct clk *mclk;
unsigned int sysclk;
unsigned int bclk;
unsigned int lrclk;

View File

@ -15,6 +15,7 @@
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/i2c.h>
#include <linux/clk.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@ -42,6 +43,7 @@ struct max98095_priv {
struct regmap *regmap;
enum max98095_type devtype;
struct max98095_pdata *pdata;
struct clk *mclk;
unsigned int sysclk;
struct max98095_cdata dai[3];
const char **eq_texts;
@ -1395,6 +1397,11 @@ static int max98095_dai_set_sysclk(struct snd_soc_dai *dai,
if (freq == max98095->sysclk)
return 0;
if (!IS_ERR(max98095->mclk)) {
freq = clk_round_rate(max98095->mclk, freq);
clk_set_rate(max98095->mclk, freq);
}
/* Setup clocks for slave mode, and using the PLL
* PSCLK = 0x01 (when master clk is 10MHz to 20MHz)
* 0x02 (when master clk is 20MHz to 40MHz)..
@ -1634,6 +1641,19 @@ static int max98095_set_bias_level(struct snd_soc_codec *codec,
break;
case SND_SOC_BIAS_PREPARE:
/*
* SND_SOC_BIAS_PREPARE is called while preparing for a
* transition to ON or away from ON. If current bias_level
* is SND_SOC_BIAS_ON, then it is preparing for a transition
* away from ON. Disable the clock in that case, otherwise
* enable it.
*/
if (!IS_ERR(max98095->mclk)) {
if (codec->dapm.bias_level == SND_SOC_BIAS_ON)
clk_disable_unprepare(max98095->mclk);
else
clk_prepare_enable(max98095->mclk);
}
break;
case SND_SOC_BIAS_STANDBY:
@ -2238,6 +2258,10 @@ static int max98095_probe(struct snd_soc_codec *codec)
struct i2c_client *client;
int ret = 0;
max98095->mclk = devm_clk_get(codec->dev, "mclk");
if (PTR_ERR(max98095->mclk) == -EPROBE_DEFER)
return -EPROBE_DEFER;
/* reset the codec, the DSP core, and disable all interrupts */
max98095_reset(codec);

View File

@ -0,0 +1,152 @@
/*
* rl6231.c - RL6231 class device shared support
*
* Copyright 2014 Realtek Semiconductor Corp.
*
* Author: Oder Chiou <oder_chiou@realtek.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/gpio.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#include <linux/spi/spi.h>
#include <linux/acpi.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 "rl6231.h"
/**
* rl6231_calc_dmic_clk - Calculate the parameter of dmic.
*
* @rate: base clock rate.
*
* Choose dmic clock between 1MHz and 3MHz.
* It is better for clock to approximate 3MHz.
*/
int rl6231_calc_dmic_clk(int rate)
{
int div[] = {2, 3, 4, 6, 8, 12}, idx = -EINVAL;
int i, red, bound, temp;
red = 3000000 * 12;
for (i = 0; i < ARRAY_SIZE(div); i++) {
bound = div[i] * 3000000;
if (rate > bound)
continue;
temp = bound - rate;
if (temp < red) {
red = temp;
idx = i;
}
}
return idx;
}
EXPORT_SYMBOL_GPL(rl6231_calc_dmic_clk);
/**
* rl6231_pll_calc - Calcualte PLL M/N/K code.
* @freq_in: external clock provided to codec.
* @freq_out: target clock which codec works on.
* @pll_code: Pointer to structure with M, N, K and bypass flag.
*
* Calcualte M/N/K code to configure PLL for codec.
*
* Returns 0 for success or negative error code.
*/
int rl6231_pll_calc(const unsigned int freq_in,
const unsigned int freq_out, struct rl6231_pll_code *pll_code)
{
int max_n = RL6231_PLL_N_MAX, max_m = RL6231_PLL_M_MAX;
int k, red, n_t, pll_out, in_t, out_t;
int n = 0, m = 0, m_t = 0;
int red_t = abs(freq_out - freq_in);
bool bypass = false;
if (RL6231_PLL_INP_MAX < freq_in || RL6231_PLL_INP_MIN > freq_in)
return -EINVAL;
k = 100000000 / freq_out - 2;
if (k > RL6231_PLL_K_MAX)
k = RL6231_PLL_K_MAX;
for (n_t = 0; n_t <= max_n; n_t++) {
in_t = freq_in / (k + 2);
pll_out = freq_out / (n_t + 2);
if (in_t < 0)
continue;
if (in_t == pll_out) {
bypass = true;
n = n_t;
goto code_find;
}
red = abs(in_t - pll_out);
if (red < red_t) {
bypass = true;
n = n_t;
m = m_t;
if (red == 0)
goto code_find;
red_t = red;
}
for (m_t = 0; m_t <= max_m; m_t++) {
out_t = in_t / (m_t + 2);
red = abs(out_t - pll_out);
if (red < red_t) {
bypass = false;
n = n_t;
m = m_t;
if (red == 0)
goto code_find;
red_t = red;
}
}
}
pr_debug("Only get approximation about PLL\n");
code_find:
pll_code->m_bp = bypass;
pll_code->m_code = m;
pll_code->n_code = n;
pll_code->k_code = k;
return 0;
}
EXPORT_SYMBOL_GPL(rl6231_pll_calc);
int rl6231_get_clk_info(int sclk, int rate)
{
int i, pd[] = {1, 2, 3, 4, 6, 8, 12, 16};
if (sclk <= 0 || rate <= 0)
return -EINVAL;
rate = rate << 8;
for (i = 0; i < ARRAY_SIZE(pd); i++)
if (sclk == rate * pd[i])
return i;
return -EINVAL;
}
EXPORT_SYMBOL_GPL(rl6231_get_clk_info);
MODULE_DESCRIPTION("RL6231 class device shared support");
MODULE_AUTHOR("Oder Chiou <oder_chiou@realtek.com>");
MODULE_LICENSE("GPL v2");

View File

@ -0,0 +1,34 @@
/*
* rl6231.h - RL6231 class device shared support
*
* Copyright 2014 Realtek Semiconductor Corp.
*
* Author: Oder Chiou <oder_chiou@realtek.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 __RL6231_H__
#define __RL6231_H__
#define RL6231_PLL_INP_MAX 40000000
#define RL6231_PLL_INP_MIN 256000
#define RL6231_PLL_N_MAX 0x1ff
#define RL6231_PLL_K_MAX 0x1f
#define RL6231_PLL_M_MAX 0xf
struct rl6231_pll_code {
bool m_bp; /* Indicates bypass m code or not. */
int m_code;
int n_code;
int k_code;
};
int rl6231_calc_dmic_clk(int rate);
int rl6231_pll_calc(const unsigned int freq_in,
const unsigned int freq_out, struct rl6231_pll_code *pll_code);
int rl6231_get_clk_info(int sclk, int rate);
#endif /* __RL6231_H__ */

View File

@ -31,6 +31,7 @@
#include <sound/initval.h>
#include <sound/tlv.h>
#include "rl6231.h"
#include "rt5640.h"
#define RT5640_DEVICE_ID 0x6231
@ -453,30 +454,16 @@ static const struct snd_kcontrol_new rt5640_specific_snd_controls[] = {
* @kcontrol: The kcontrol of this widget.
* @event: Event id.
*
* Choose dmic clock between 1MHz and 3MHz.
* It is better for clock to approximate 3MHz.
*/
static int set_dmic_clk(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = w->codec;
struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
int div[] = {2, 3, 4, 6, 8, 12};
int idx = -EINVAL, i;
int rate, red, bound, temp;
int idx = -EINVAL;
idx = rl6231_calc_dmic_clk(rt5640->sysclk);
rate = rt5640->sysclk;
red = 3000000 * 12;
for (i = 0; i < ARRAY_SIZE(div); i++) {
bound = div[i] * 3000000;
if (rate > bound)
continue;
temp = bound - rate;
if (temp < red) {
red = temp;
idx = i;
}
}
if (idx < 0)
dev_err(codec->dev, "Failed to set DMIC clock\n");
else
@ -1639,21 +1626,6 @@ static int get_sdp_info(struct snd_soc_codec *codec, int dai_id)
return ret;
}
static int get_clk_info(int sclk, int rate)
{
int i, pd[] = {1, 2, 3, 4, 6, 8, 12, 16};
if (sclk <= 0 || rate <= 0)
return -EINVAL;
rate = rate << 8;
for (i = 0; i < ARRAY_SIZE(pd); i++)
if (sclk == rate * pd[i])
return i;
return -EINVAL;
}
static int rt5640_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
{
@ -1663,7 +1635,7 @@ static int rt5640_hw_params(struct snd_pcm_substream *substream,
int dai_sel, pre_div, bclk_ms, frame_size;
rt5640->lrck[dai->id] = params_rate(params);
pre_div = get_clk_info(rt5640->sysclk, rt5640->lrck[dai->id]);
pre_div = rl6231_get_clk_info(rt5640->sysclk, rt5640->lrck[dai->id]);
if (pre_div < 0) {
dev_err(codec->dev, "Unsupported clock setting %d for DAI %d\n",
rt5640->lrck[dai->id], dai->id);
@ -1820,65 +1792,12 @@ static int rt5640_set_dai_sysclk(struct snd_soc_dai *dai,
return 0;
}
/**
* rt5640_pll_calc - Calculate PLL M/N/K code.
* @freq_in: external clock provided to codec.
* @freq_out: target clock which codec works on.
* @pll_code: Pointer to structure with M, N, K and bypass flag.
*
* Calculate M/N/K code to configure PLL for codec. And K is assigned to 2
* which make calculation more efficiently.
*
* Returns 0 for success or negative error code.
*/
static int rt5640_pll_calc(const unsigned int freq_in,
const unsigned int freq_out, struct rt5640_pll_code *pll_code)
{
int max_n = RT5640_PLL_N_MAX, max_m = RT5640_PLL_M_MAX;
int n = 0, m = 0, red, n_t, m_t, in_t, out_t;
int red_t = abs(freq_out - freq_in);
bool bypass = false;
if (RT5640_PLL_INP_MAX < freq_in || RT5640_PLL_INP_MIN > freq_in)
return -EINVAL;
for (n_t = 0; n_t <= max_n; n_t++) {
in_t = (freq_in >> 1) + (freq_in >> 2) * n_t;
if (in_t < 0)
continue;
if (in_t == freq_out) {
bypass = true;
n = n_t;
goto code_find;
}
for (m_t = 0; m_t <= max_m; m_t++) {
out_t = in_t / (m_t + 2);
red = abs(out_t - freq_out);
if (red < red_t) {
n = n_t;
m = m_t;
if (red == 0)
goto code_find;
red_t = red;
}
}
}
pr_debug("Only get approximation about PLL\n");
code_find:
pll_code->m_bp = bypass;
pll_code->m_code = m;
pll_code->n_code = n;
pll_code->k_code = 2;
return 0;
}
static int rt5640_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
unsigned int freq_in, unsigned int freq_out)
{
struct snd_soc_codec *codec = dai->codec;
struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
struct rt5640_pll_code *pll_code = &rt5640->pll_code;
struct rl6231_pll_code pll_code;
int ret, dai_sel;
if (source == rt5640->pll_src && freq_in == rt5640->pll_in &&
@ -1922,20 +1841,21 @@ static int rt5640_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
return -EINVAL;
}
ret = rt5640_pll_calc(freq_in, freq_out, pll_code);
ret = rl6231_pll_calc(freq_in, freq_out, &pll_code);
if (ret < 0) {
dev_err(codec->dev, "Unsupport input clock %d\n", freq_in);
return ret;
}
dev_dbg(codec->dev, "bypass=%d m=%d n=%d k=2\n", pll_code->m_bp,
(pll_code->m_bp ? 0 : pll_code->m_code), pll_code->n_code);
dev_dbg(codec->dev, "bypass=%d m=%d n=%d k=%d\n",
pll_code.m_bp, (pll_code.m_bp ? 0 : pll_code.m_code),
pll_code.n_code, pll_code.k_code);
snd_soc_write(codec, RT5640_PLL_CTRL1,
pll_code->n_code << RT5640_PLL_N_SFT | pll_code->k_code);
pll_code.n_code << RT5640_PLL_N_SFT | pll_code.k_code);
snd_soc_write(codec, RT5640_PLL_CTRL2,
(pll_code->m_bp ? 0 : pll_code->m_code) << RT5640_PLL_M_SFT |
pll_code->m_bp << RT5640_PLL_M_BP_SFT);
(pll_code.m_bp ? 0 : pll_code.m_code) << RT5640_PLL_M_SFT |
pll_code.m_bp << RT5640_PLL_M_BP_SFT);
rt5640->pll_in = freq_in;
rt5640->pll_out = freq_out;

View File

@ -2079,13 +2079,6 @@ enum {
RT5640_DMIC2,
};
struct rt5640_pll_code {
bool m_bp; /* Indicates bypass m code or not. */
int m_code;
int n_code;
int k_code;
};
struct rt5640_priv {
struct snd_soc_codec *codec;
struct rt5640_platform_data pdata;
@ -2097,7 +2090,6 @@ struct rt5640_priv {
int bclk[RT5640_AIFS];
int master[RT5640_AIFS];
struct rt5640_pll_code pll_code;
int pll_src;
int pll_in;
int pll_out;

View File

@ -26,6 +26,7 @@
#include <sound/initval.h>
#include <sound/tlv.h>
#include "rl6231.h"
#include "rt5645.h"
#define RT5645_DEVICE_ID 0x6308
@ -519,30 +520,15 @@ static const struct snd_kcontrol_new rt5645_snd_controls[] = {
* @kcontrol: The kcontrol of this widget.
* @event: Event id.
*
* Choose dmic clock between 1MHz and 3MHz.
* It is better for clock to approximate 3MHz.
*/
static int set_dmic_clk(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = w->codec;
struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
int div[] = {2, 3, 4, 6, 8, 12};
int idx = -EINVAL, i;
int rate, red, bound, temp;
int idx = -EINVAL;
rate = rt5645->sysclk;
red = 3000000 * 12;
for (i = 0; i < ARRAY_SIZE(div); i++) {
bound = div[i] * 3000000;
if (rate > bound)
continue;
temp = bound - rate;
if (temp < red) {
red = temp;
idx = i;
}
}
idx = rl6231_calc_dmic_clk(rt5645->sysclk);
if (idx < 0)
dev_err(codec->dev, "Failed to set DMIC clock\n");
@ -1800,21 +1786,6 @@ static const struct snd_soc_dapm_route rt5645_dapm_routes[] = {
{ "SPOR", NULL, "SPK amp" },
};
static int get_clk_info(int sclk, int rate)
{
int i, pd[] = {1, 2, 3, 4, 6, 8, 12, 16};
if (sclk <= 0 || rate <= 0)
return -EINVAL;
rate = rate << 8;
for (i = 0; i < ARRAY_SIZE(pd); i++)
if (sclk == rate * pd[i])
return i;
return -EINVAL;
}
static int rt5645_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
{
@ -1824,7 +1795,7 @@ static int rt5645_hw_params(struct snd_pcm_substream *substream,
int pre_div, bclk_ms, frame_size;
rt5645->lrck[dai->id] = params_rate(params);
pre_div = get_clk_info(rt5645->sysclk, rt5645->lrck[dai->id]);
pre_div = rl6231_get_clk_info(rt5645->sysclk, rt5645->lrck[dai->id]);
if (pre_div < 0) {
dev_err(codec->dev, "Unsupported clock setting\n");
return -EINVAL;
@ -1978,80 +1949,12 @@ static int rt5645_set_dai_sysclk(struct snd_soc_dai *dai,
return 0;
}
/**
* rt5645_pll_calc - Calcualte PLL M/N/K code.
* @freq_in: external clock provided to codec.
* @freq_out: target clock which codec works on.
* @pll_code: Pointer to structure with M, N, K and bypass flag.
*
* Calcualte M/N/K code to configure PLL for codec. And K is assigned to 2
* which make calculation more efficiently.
*
* Returns 0 for success or negative error code.
*/
static int rt5645_pll_calc(const unsigned int freq_in,
const unsigned int freq_out, struct rt5645_pll_code *pll_code)
{
int max_n = RT5645_PLL_N_MAX, max_m = RT5645_PLL_M_MAX;
int k, n = 0, m = 0, red, n_t, m_t, pll_out, in_t, out_t;
int red_t = abs(freq_out - freq_in);
bool bypass = false;
if (RT5645_PLL_INP_MAX < freq_in || RT5645_PLL_INP_MIN > freq_in)
return -EINVAL;
k = 100000000 / freq_out - 2;
if (k > RT5645_PLL_K_MAX)
k = RT5645_PLL_K_MAX;
for (n_t = 0; n_t <= max_n; n_t++) {
in_t = freq_in / (k + 2);
pll_out = freq_out / (n_t + 2);
if (in_t < 0)
continue;
if (in_t == pll_out) {
bypass = true;
n = n_t;
goto code_find;
}
red = abs(in_t - pll_out);
if (red < red_t) {
bypass = true;
n = n_t;
m = m_t;
if (red == 0)
goto code_find;
red_t = red;
}
for (m_t = 0; m_t <= max_m; m_t++) {
out_t = in_t / (m_t + 2);
red = abs(out_t - pll_out);
if (red < red_t) {
bypass = false;
n = n_t;
m = m_t;
if (red == 0)
goto code_find;
red_t = red;
}
}
}
pr_debug("Only get approximation about PLL\n");
code_find:
pll_code->m_bp = bypass;
pll_code->m_code = m;
pll_code->n_code = n;
pll_code->k_code = k;
return 0;
}
static int rt5645_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
unsigned int freq_in, unsigned int freq_out)
{
struct snd_soc_codec *codec = dai->codec;
struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
struct rt5645_pll_code pll_code;
struct rl6231_pll_code pll_code;
int ret;
if (source == rt5645->pll_src && freq_in == rt5645->pll_in &&
@ -2094,7 +1997,7 @@ static int rt5645_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
return -EINVAL;
}
ret = rt5645_pll_calc(freq_in, freq_out, &pll_code);
ret = rl6231_pll_calc(freq_in, freq_out, &pll_code);
if (ret < 0) {
dev_err(codec->dev, "Unsupport input clock %d\n", freq_in);
return ret;

View File

@ -2162,13 +2162,6 @@ enum {
RT5645_DMIC_DATA_GPIO11,
};
struct rt5645_pll_code {
bool m_bp; /* Indicates bypass m code or not. */
int m_code;
int n_code;
int k_code;
};
struct rt5645_priv {
struct snd_soc_codec *codec;
struct rt5645_platform_data pdata;

View File

@ -26,6 +26,7 @@
#include <sound/initval.h>
#include <sound/tlv.h>
#include "rl6231.h"
#include "rt5651.h"
#define RT5651_DEVICE_ID_VALUE 0x6281
@ -371,29 +372,16 @@ static const struct snd_kcontrol_new rt5651_snd_controls[] = {
* @kcontrol: The kcontrol of this widget.
* @event: Event id.
*
* Choose dmic clock between 1MHz and 3MHz.
* It is better for clock to approximate 3MHz.
*/
static int set_dmic_clk(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = w->codec;
struct rt5651_priv *rt5651 = snd_soc_codec_get_drvdata(codec);
int div[] = {2, 3, 4, 6, 8, 12}, idx = -EINVAL;
int i, rate, red, bound, temp;
int idx = -EINVAL;
idx = rl6231_calc_dmic_clk(rt5651->sysclk);
rate = rt5651->sysclk;
red = 3000000 * 12;
for (i = 0; i < ARRAY_SIZE(div); i++) {
bound = div[i] * 3000000;
if (rate > bound)
continue;
temp = bound - rate;
if (temp < red) {
red = temp;
idx = i;
}
}
if (idx < 0)
dev_err(codec->dev, "Failed to set DMIC clock\n");
else
@ -1350,21 +1338,6 @@ static const struct snd_soc_dapm_route rt5651_dapm_routes[] = {
{"PDMR", NULL, "PDM R Mux"},
};
static int get_clk_info(int sclk, int rate)
{
int i, pd[] = {1, 2, 3, 4, 6, 8, 12, 16};
if (sclk <= 0 || rate <= 0)
return -EINVAL;
rate = rate << 8;
for (i = 0; i < ARRAY_SIZE(pd); i++)
if (sclk == rate * pd[i])
return i;
return -EINVAL;
}
static int rt5651_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
{
@ -1374,7 +1347,7 @@ static int rt5651_hw_params(struct snd_pcm_substream *substream,
int pre_div, bclk_ms, frame_size;
rt5651->lrck[dai->id] = params_rate(params);
pre_div = get_clk_info(rt5651->sysclk, rt5651->lrck[dai->id]);
pre_div = rl6231_get_clk_info(rt5651->sysclk, rt5651->lrck[dai->id]);
if (pre_div < 0) {
dev_err(codec->dev, "Unsupported clock setting\n");
@ -1528,65 +1501,12 @@ static int rt5651_set_dai_sysclk(struct snd_soc_dai *dai,
return 0;
}
/**
* rt5651_pll_calc - Calcualte PLL M/N/K code.
* @freq_in: external clock provided to codec.
* @freq_out: target clock which codec works on.
* @pll_code: Pointer to structure with M, N, K and bypass flag.
*
* Calcualte M/N/K code to configure PLL for codec. And K is assigned to 2
* which make calculation more efficiently.
*
* Returns 0 for success or negative error code.
*/
static int rt5651_pll_calc(const unsigned int freq_in,
const unsigned int freq_out, struct rt5651_pll_code *pll_code)
{
int max_n = RT5651_PLL_N_MAX, max_m = RT5651_PLL_M_MAX;
int n = 0, m = 0, red, n_t, m_t, in_t, out_t;
int red_t = abs(freq_out - freq_in);
bool bypass = false;
if (RT5651_PLL_INP_MAX < freq_in || RT5651_PLL_INP_MIN > freq_in)
return -EINVAL;
for (n_t = 0; n_t <= max_n; n_t++) {
in_t = (freq_in >> 1) + (freq_in >> 2) * n_t;
if (in_t < 0)
continue;
if (in_t == freq_out) {
bypass = true;
n = n_t;
goto code_find;
}
for (m_t = 0; m_t <= max_m; m_t++) {
out_t = in_t / (m_t + 2);
red = abs(out_t - freq_out);
if (red < red_t) {
n = n_t;
m = m_t;
if (red == 0)
goto code_find;
red_t = red;
}
}
}
pr_debug("Only get approximation about PLL\n");
code_find:
pll_code->m_bp = bypass;
pll_code->m_code = m;
pll_code->n_code = n;
pll_code->k_code = 2;
return 0;
}
static int rt5651_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
unsigned int freq_in, unsigned int freq_out)
{
struct snd_soc_codec *codec = dai->codec;
struct rt5651_priv *rt5651 = snd_soc_codec_get_drvdata(codec);
struct rt5651_pll_code *pll_code = &rt5651->pll_code;
struct rl6231_pll_code pll_code;
int ret;
if (source == rt5651->pll_src && freq_in == rt5651->pll_in &&
@ -1621,20 +1541,21 @@ static int rt5651_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
return -EINVAL;
}
ret = rt5651_pll_calc(freq_in, freq_out, pll_code);
ret = rl6231_pll_calc(freq_in, freq_out, &pll_code);
if (ret < 0) {
dev_err(codec->dev, "Unsupport input clock %d\n", freq_in);
return ret;
}
dev_dbg(codec->dev, "bypass=%d m=%d n=%d k=2\n", pll_code->m_bp,
(pll_code->m_bp ? 0 : pll_code->m_code), pll_code->n_code);
dev_dbg(codec->dev, "bypass=%d m=%d n=%d k=%d\n",
pll_code.m_bp, (pll_code.m_bp ? 0 : pll_code.m_code),
pll_code.n_code, pll_code.k_code);
snd_soc_write(codec, RT5651_PLL_CTRL1,
pll_code->n_code << RT5651_PLL_N_SFT | pll_code->k_code);
pll_code.n_code << RT5651_PLL_N_SFT | pll_code.k_code);
snd_soc_write(codec, RT5651_PLL_CTRL2,
(pll_code->m_bp ? 0 : pll_code->m_code) << RT5651_PLL_M_SFT |
pll_code->m_bp << RT5651_PLL_M_BP_SFT);
(pll_code.m_bp ? 0 : pll_code.m_code) << RT5651_PLL_M_SFT |
pll_code.m_bp << RT5651_PLL_M_BP_SFT);
rt5651->pll_in = freq_in;
rt5651->pll_out = freq_out;

View File

@ -2069,7 +2069,6 @@ struct rt5651_priv {
int bclk[RT5651_AIFS];
int master[RT5651_AIFS];
struct rt5651_pll_code pll_code;
int pll_src;
int pll_in;
int pll_out;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -36,18 +36,32 @@
/* default value of sgtl5000 registers */
static const struct reg_default sgtl5000_reg_defaults[] = {
{ SGTL5000_CHIP_DIG_POWER, 0x0000 },
{ SGTL5000_CHIP_CLK_CTRL, 0x0008 },
{ SGTL5000_CHIP_I2S_CTRL, 0x0010 },
{ SGTL5000_CHIP_SSS_CTRL, 0x0010 },
{ SGTL5000_CHIP_ADCDAC_CTRL, 0x020c },
{ SGTL5000_CHIP_DAC_VOL, 0x3c3c },
{ SGTL5000_CHIP_PAD_STRENGTH, 0x015f },
{ SGTL5000_CHIP_ANA_ADC_CTRL, 0x0000 },
{ SGTL5000_CHIP_ANA_HP_CTRL, 0x1818 },
{ SGTL5000_CHIP_ANA_CTRL, 0x0111 },
{ SGTL5000_CHIP_LINREG_CTRL, 0x0000 },
{ SGTL5000_CHIP_REF_CTRL, 0x0000 },
{ SGTL5000_CHIP_MIC_CTRL, 0x0000 },
{ SGTL5000_CHIP_LINE_OUT_CTRL, 0x0000 },
{ SGTL5000_CHIP_LINE_OUT_VOL, 0x0404 },
{ SGTL5000_CHIP_ANA_POWER, 0x7060 },
{ SGTL5000_CHIP_PLL_CTRL, 0x5000 },
{ SGTL5000_CHIP_CLK_TOP_CTRL, 0x0000 },
{ SGTL5000_CHIP_ANA_STATUS, 0x0000 },
{ SGTL5000_CHIP_SHORT_CTRL, 0x0000 },
{ SGTL5000_CHIP_ANA_TEST2, 0x0000 },
{ SGTL5000_DAP_CTRL, 0x0000 },
{ SGTL5000_DAP_PEQ, 0x0000 },
{ SGTL5000_DAP_BASS_ENHANCE, 0x0040 },
{ SGTL5000_DAP_BASS_ENHANCE_CTRL, 0x051f },
{ SGTL5000_DAP_AUDIO_EQ, 0x0000 },
{ SGTL5000_DAP_SURROUND, 0x0040 },
{ SGTL5000_DAP_EQ_BASS_BAND0, 0x002f },
{ SGTL5000_DAP_EQ_BASS_BAND1, 0x002f },
@ -55,6 +69,7 @@ static const struct reg_default sgtl5000_reg_defaults[] = {
{ SGTL5000_DAP_EQ_BASS_BAND3, 0x002f },
{ SGTL5000_DAP_EQ_BASS_BAND4, 0x002f },
{ SGTL5000_DAP_MAIN_CHAN, 0x8000 },
{ SGTL5000_DAP_MIX_CHAN, 0x0000 },
{ SGTL5000_DAP_AVC_CTRL, 0x0510 },
{ SGTL5000_DAP_AVC_THRESHOLD, 0x1473 },
{ SGTL5000_DAP_AVC_ATTACK, 0x0028 },
@ -1068,71 +1083,11 @@ static int sgtl5000_suspend(struct snd_soc_codec *codec)
return 0;
}
/*
* restore all sgtl5000 registers,
* since a big hole between dap and regular registers,
* we will restore them respectively.
*/
static int sgtl5000_restore_regs(struct snd_soc_codec *codec)
{
u16 *cache = codec->reg_cache;
u16 reg;
/* restore regular registers */
for (reg = 0; reg <= SGTL5000_CHIP_SHORT_CTRL; reg += 2) {
/* These regs should restore in particular order */
if (reg == SGTL5000_CHIP_ANA_POWER ||
reg == SGTL5000_CHIP_CLK_CTRL ||
reg == SGTL5000_CHIP_LINREG_CTRL ||
reg == SGTL5000_CHIP_LINE_OUT_CTRL ||
reg == SGTL5000_CHIP_REF_CTRL)
continue;
snd_soc_write(codec, reg, cache[reg]);
}
/* restore dap registers */
for (reg = SGTL5000_DAP_REG_OFFSET; reg < SGTL5000_MAX_REG_OFFSET; reg += 2)
snd_soc_write(codec, reg, cache[reg]);
/*
* restore these regs according to the power setting sequence in
* sgtl5000_set_power_regs() and clock setting sequence in
* sgtl5000_set_clock().
*
* The order of restore is:
* 1. SGTL5000_CHIP_CLK_CTRL MCLK_FREQ bits (1:0) should be restore after
* SGTL5000_CHIP_ANA_POWER PLL bits set
* 2. SGTL5000_CHIP_LINREG_CTRL should be set before
* SGTL5000_CHIP_ANA_POWER LINREG_D restored
* 3. SGTL5000_CHIP_REF_CTRL controls Analog Ground Voltage,
* prefer to resotre it after SGTL5000_CHIP_ANA_POWER restored
*/
snd_soc_write(codec, SGTL5000_CHIP_LINREG_CTRL,
cache[SGTL5000_CHIP_LINREG_CTRL]);
snd_soc_write(codec, SGTL5000_CHIP_ANA_POWER,
cache[SGTL5000_CHIP_ANA_POWER]);
snd_soc_write(codec, SGTL5000_CHIP_CLK_CTRL,
cache[SGTL5000_CHIP_CLK_CTRL]);
snd_soc_write(codec, SGTL5000_CHIP_REF_CTRL,
cache[SGTL5000_CHIP_REF_CTRL]);
snd_soc_write(codec, SGTL5000_CHIP_LINE_OUT_CTRL,
cache[SGTL5000_CHIP_LINE_OUT_CTRL]);
return 0;
}
static int sgtl5000_resume(struct snd_soc_codec *codec)
{
/* Bring the codec back up to standby to enable regulators */
sgtl5000_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
/* Restore registers by cached in memory */
sgtl5000_restore_regs(codec);
return 0;
}
#else

View File

@ -109,7 +109,7 @@ static void enable_and_reset_codec(struct regmap *regmap,
{
regmap_update_bits(regmap, AUDIO_IC_CODEC_CTRL1,
codec_enable_bits | codec_reset_bits,
codec_enable_bits | ~codec_reset_bits);
codec_enable_bits);
msleep(20);
regmap_update_bits(regmap, AUDIO_IC_CODEC_CTRL1,
codec_reset_bits, codec_reset_bits);
@ -128,8 +128,7 @@ static int atlas6_codec_enable_and_reset_event(struct snd_soc_dapm_widget *w,
break;
case SND_SOC_DAPM_POST_PMD:
regmap_update_bits(sirf_audio_codec->regmap,
AUDIO_IC_CODEC_CTRL1, ATLAS6_CODEC_ENABLE_BITS,
~ATLAS6_CODEC_ENABLE_BITS);
AUDIO_IC_CODEC_CTRL1, ATLAS6_CODEC_ENABLE_BITS, 0);
break;
default:
break;
@ -151,8 +150,7 @@ static int prima2_codec_enable_and_reset_event(struct snd_soc_dapm_widget *w,
break;
case SND_SOC_DAPM_POST_PMD:
regmap_update_bits(sirf_audio_codec->regmap,
AUDIO_IC_CODEC_CTRL1, PRIMA2_CODEC_ENABLE_BITS,
~PRIMA2_CODEC_ENABLE_BITS);
AUDIO_IC_CODEC_CTRL1, PRIMA2_CODEC_ENABLE_BITS, 0);
break;
default:
break;

View File

@ -169,7 +169,7 @@ static int snd_soc_dapm_put_volsw_aic3x(struct snd_kcontrol *kcontrol,
mask <<= shift;
val <<= shift;
change = snd_soc_test_bits(codec, val, mask, reg);
change = snd_soc_test_bits(codec, reg, mask, val);
if (change) {
update.kcontrol = kcontrol;
update.reg = reg;

View File

@ -63,6 +63,7 @@ struct wm8804_priv {
struct regmap *regmap;
struct regulator_bulk_data supplies[WM8804_NUM_SUPPLIES];
struct notifier_block disable_nb[WM8804_NUM_SUPPLIES];
int mclk_div;
};
static int txsrc_get(struct snd_kcontrol *kcontrol,
@ -318,7 +319,7 @@ static struct {
#define FIXED_PLL_SIZE ((1ULL << 22) * 10)
static int pll_factors(struct pll_div *pll_div, unsigned int target,
unsigned int source)
unsigned int source, unsigned int mclk_div)
{
u64 Kpart;
unsigned long int K, Ndiv, Nmod, tmp;
@ -330,7 +331,8 @@ static int pll_factors(struct pll_div *pll_div, unsigned int target,
*/
for (i = 0; i < ARRAY_SIZE(post_table); i++) {
tmp = target * post_table[i].div;
if (tmp >= 90000000 && tmp <= 100000000) {
if ((tmp >= 90000000 && tmp <= 100000000) &&
(mclk_div == post_table[i].mclkdiv)) {
pll_div->freqmode = post_table[i].freqmode;
pll_div->mclkdiv = post_table[i].mclkdiv;
target *= post_table[i].div;
@ -387,8 +389,12 @@ static int wm8804_set_pll(struct snd_soc_dai *dai, int pll_id,
} else {
int ret;
struct pll_div pll_div;
struct wm8804_priv *wm8804;
ret = pll_factors(&pll_div, freq_out, freq_in);
wm8804 = snd_soc_codec_get_drvdata(codec);
ret = pll_factors(&pll_div, freq_out, freq_in,
wm8804->mclk_div);
if (ret)
return ret;
@ -452,6 +458,7 @@ static int wm8804_set_clkdiv(struct snd_soc_dai *dai,
int div_id, int div)
{
struct snd_soc_codec *codec;
struct wm8804_priv *wm8804;
codec = dai->codec;
switch (div_id) {
@ -459,6 +466,10 @@ static int wm8804_set_clkdiv(struct snd_soc_dai *dai,
snd_soc_update_bits(codec, WM8804_PLL5, 0x30,
(div & 0x3) << 4);
break;
case WM8804_MCLK_DIV:
wm8804 = snd_soc_codec_get_drvdata(codec);
wm8804->mclk_div = div;
break;
default:
dev_err(dai->dev, "Unknown clock divider: %d\n", div_id);
return -EINVAL;

View File

@ -57,5 +57,9 @@
#define WM8804_CLKOUT_SRC_OSCCLK 4
#define WM8804_CLKOUT_DIV 1
#define WM8804_MCLK_DIV 2
#define WM8804_MCLKDIV_256FS 0
#define WM8804_MCLKDIV_128FS 1
#endif /* _WM8804_H */

View File

@ -74,8 +74,7 @@ static const char *wm9713_rec_src[] =
"Mono Out", "Zh"};
static const char *wm9713_rec_gain[] = {"+1.5dB Steps", "+0.75dB Steps"};
static const char *wm9713_alc_select[] = {"None", "Left", "Right", "Stereo"};
static const char *wm9713_mono_pga[] = {"Vmid", "Zh", "Mono", "Inv",
"Mono Vmid", "Inv Vmid"};
static const char *wm9713_mono_pga[] = {"Vmid", "Zh", "Mono", "Inv"};
static const char *wm9713_spk_pga[] =
{"Vmid", "Zh", "Headphone", "Speaker", "Inv", "Headphone Vmid",
"Speaker Vmid", "Inv Vmid"};

View File

@ -1543,16 +1543,16 @@ static void wm_adsp2_boot_work(struct work_struct *work)
ret = regmap_read(dsp->regmap,
dsp->base + ADSP2_CLOCKING, &val);
if (ret != 0) {
dev_err(dsp->dev, "Failed to read clocking: %d\n", ret);
adsp_err(dsp, "Failed to read clocking: %d\n", ret);
return;
}
if ((val & ADSP2_CLK_SEL_MASK) >= 3) {
ret = regulator_enable(dsp->dvfs);
if (ret != 0) {
dev_err(dsp->dev,
"Failed to enable supply: %d\n",
ret);
adsp_err(dsp,
"Failed to enable supply: %d\n",
ret);
return;
}
@ -1560,9 +1560,9 @@ static void wm_adsp2_boot_work(struct work_struct *work)
1800000,
1800000);
if (ret != 0) {
dev_err(dsp->dev,
"Failed to raise supply: %d\n",
ret);
adsp_err(dsp,
"Failed to raise supply: %d\n",
ret);
return;
}
}
@ -1672,15 +1672,15 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
ret = regulator_set_voltage(dsp->dvfs, 1200000,
1800000);
if (ret != 0)
dev_warn(dsp->dev,
"Failed to lower supply: %d\n",
ret);
adsp_warn(dsp,
"Failed to lower supply: %d\n",
ret);
ret = regulator_disable(dsp->dvfs);
if (ret != 0)
dev_err(dsp->dev,
"Failed to enable supply: %d\n",
ret);
adsp_err(dsp,
"Failed to enable supply: %d\n",
ret);
}
list_for_each_entry(ctl, &dsp->ctl_list, list)
@ -1732,28 +1732,25 @@ int wm_adsp2_init(struct wm_adsp *adsp, bool dvfs)
adsp->dvfs = devm_regulator_get(adsp->dev, "DCVDD");
if (IS_ERR(adsp->dvfs)) {
ret = PTR_ERR(adsp->dvfs);
dev_err(adsp->dev, "Failed to get DCVDD: %d\n", ret);
adsp_err(adsp, "Failed to get DCVDD: %d\n", ret);
return ret;
}
ret = regulator_enable(adsp->dvfs);
if (ret != 0) {
dev_err(adsp->dev, "Failed to enable DCVDD: %d\n",
ret);
adsp_err(adsp, "Failed to enable DCVDD: %d\n", ret);
return ret;
}
ret = regulator_set_voltage(adsp->dvfs, 1200000, 1800000);
if (ret != 0) {
dev_err(adsp->dev, "Failed to initialise DVFS: %d\n",
ret);
adsp_err(adsp, "Failed to initialise DVFS: %d\n", ret);
return ret;
}
ret = regulator_disable(adsp->dvfs);
if (ret != 0) {
dev_err(adsp->dev, "Failed to disable DCVDD: %d\n",
ret);
adsp_err(adsp, "Failed to disable DCVDD: %d\n", ret);
return ret;
}
}

View File

@ -38,7 +38,7 @@ struct snd_soc_card_drvdata_davinci {
static int evm_startup(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_card *soc_card = rtd->codec->card;
struct snd_soc_card *soc_card = rtd->card;
struct snd_soc_card_drvdata_davinci *drvdata =
snd_soc_card_get_drvdata(soc_card);
@ -51,7 +51,7 @@ static int evm_startup(struct snd_pcm_substream *substream)
static void evm_shutdown(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_card *soc_card = rtd->codec->card;
struct snd_soc_card *soc_card = rtd->card;
struct snd_soc_card_drvdata_davinci *drvdata =
snd_soc_card_get_drvdata(soc_card);
@ -65,8 +65,7 @@ static int evm_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *codec_dai = rtd->codec_dai;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
struct snd_soc_codec *codec = rtd->codec;
struct snd_soc_card *soc_card = codec->card;
struct snd_soc_card *soc_card = rtd->card;
int ret = 0;
unsigned sysclk = ((struct snd_soc_card_drvdata_davinci *)
snd_soc_card_get_drvdata(soc_card))->sysclk;
@ -125,7 +124,7 @@ static int evm_aic3x_init(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_card *card = rtd->card;
struct snd_soc_codec *codec = rtd->codec;
struct device_node *np = codec->card->dev->of_node;
struct device_node *np = card->dev->of_node;
int ret;
/* Add davinci-evm specific widgets */

View File

@ -33,10 +33,10 @@
#include <sound/initval.h>
#include <sound/soc.h>
#include <sound/dmaengine_pcm.h>
#include <sound/omap-pcm.h>
#include "davinci-pcm.h"
#include "davinci-mcasp.h"
#include "../omap/omap-pcm.h"
#define MCASP_MAX_AFIFO_DEPTH 64

View File

@ -16,6 +16,7 @@ config SND_SOC_FSL_SSI
tristate "Synchronous Serial Interface module support"
select SND_SOC_IMX_PCM_DMA if SND_IMX_SOC != n
select SND_SOC_IMX_PCM_FIQ if SND_IMX_SOC != n && ARCH_MXC
select REGMAP_MMIO
help
Say Y if you want to add Synchronous Serial Interface (SSI)
support for the Freescale CPUs.
@ -207,12 +208,7 @@ config SND_SOC_PHYCORE_AC97
config SND_SOC_EUKREA_TLV320
tristate "Eukrea TLV320"
depends on MACH_EUKREA_MBIMX27_BASEBOARD \
|| MACH_EUKREA_MBIMXSD25_BASEBOARD \
|| MACH_EUKREA_MBIMXSD35_BASEBOARD \
|| MACH_EUKREA_MBIMXSD51_BASEBOARD \
|| (OF && ARM)
depends on I2C
depends on ARCH_MXC && I2C
select SND_SOC_TLV320AIC23_I2C
select SND_SOC_IMX_AUDMUX
select SND_SOC_IMX_SSI

File diff suppressed because it is too large Load Diff

View File

@ -12,32 +12,30 @@
#ifndef _MPC8610_I2S_H
#define _MPC8610_I2S_H
/* SSI Register Map */
struct ccsr_ssi {
__be32 stx0; /* 0x.0000 - SSI Transmit Data Register 0 */
__be32 stx1; /* 0x.0004 - SSI Transmit Data Register 1 */
__be32 srx0; /* 0x.0008 - SSI Receive Data Register 0 */
__be32 srx1; /* 0x.000C - SSI Receive Data Register 1 */
__be32 scr; /* 0x.0010 - SSI Control Register */
__be32 sisr; /* 0x.0014 - SSI Interrupt Status Register Mixed */
__be32 sier; /* 0x.0018 - SSI Interrupt Enable Register */
__be32 stcr; /* 0x.001C - SSI Transmit Configuration Register */
__be32 srcr; /* 0x.0020 - SSI Receive Configuration Register */
__be32 stccr; /* 0x.0024 - SSI Transmit Clock Control Register */
__be32 srccr; /* 0x.0028 - SSI Receive Clock Control Register */
__be32 sfcsr; /* 0x.002C - SSI FIFO Control/Status Register */
__be32 str; /* 0x.0030 - SSI Test Register */
__be32 sor; /* 0x.0034 - SSI Option Register */
__be32 sacnt; /* 0x.0038 - SSI AC97 Control Register */
__be32 sacadd; /* 0x.003C - SSI AC97 Command Address Register */
__be32 sacdat; /* 0x.0040 - SSI AC97 Command Data Register */
__be32 satag; /* 0x.0044 - SSI AC97 Tag Register */
__be32 stmsk; /* 0x.0048 - SSI Transmit Time Slot Mask Register */
__be32 srmsk; /* 0x.004C - SSI Receive Time Slot Mask Register */
__be32 saccst; /* 0x.0050 - SSI AC97 Channel Status Register */
__be32 saccen; /* 0x.0054 - SSI AC97 Channel Enable Register */
__be32 saccdis; /* 0x.0058 - SSI AC97 Channel Disable Register */
};
/* SSI registers */
#define CCSR_SSI_STX0 0x00
#define CCSR_SSI_STX1 0x04
#define CCSR_SSI_SRX0 0x08
#define CCSR_SSI_SRX1 0x0c
#define CCSR_SSI_SCR 0x10
#define CCSR_SSI_SISR 0x14
#define CCSR_SSI_SIER 0x18
#define CCSR_SSI_STCR 0x1c
#define CCSR_SSI_SRCR 0x20
#define CCSR_SSI_STCCR 0x24
#define CCSR_SSI_SRCCR 0x28
#define CCSR_SSI_SFCSR 0x2c
#define CCSR_SSI_STR 0x30
#define CCSR_SSI_SOR 0x34
#define CCSR_SSI_SACNT 0x38
#define CCSR_SSI_SACADD 0x3c
#define CCSR_SSI_SACDAT 0x40
#define CCSR_SSI_SATAG 0x44
#define CCSR_SSI_STMSK 0x48
#define CCSR_SSI_SRMSK 0x4c
#define CCSR_SSI_SACCST 0x50
#define CCSR_SSI_SACCEN 0x54
#define CCSR_SSI_SACCDIS 0x58
#define CCSR_SSI_SCR_SYNC_TX_FS 0x00001000
#define CCSR_SSI_SCR_RFR_CLK_DIS 0x00000800

View File

@ -24,9 +24,32 @@ struct simple_card_data {
struct asoc_simple_dai cpu_dai;
struct asoc_simple_dai codec_dai;
} *dai_props;
unsigned int mclk_fs;
struct snd_soc_dai_link dai_link[]; /* dynamically allocated */
};
static int asoc_simple_card_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *codec_dai = rtd->codec_dai;
struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
unsigned int mclk;
int ret = 0;
if (priv->mclk_fs) {
mclk = params_rate(params) * priv->mclk_fs;
ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk,
SND_SOC_CLOCK_IN);
}
return ret;
}
static struct snd_soc_ops asoc_simple_card_ops = {
.hw_params = asoc_simple_card_hw_params,
};
static int __asoc_simple_card_dai_init(struct snd_soc_dai *dai,
struct asoc_simple_dai *set)
{
@ -144,7 +167,8 @@ asoc_simple_card_sub_parse_of(struct device_node *np,
static int simple_card_dai_link_of(struct device_node *node,
struct device *dev,
struct snd_soc_dai_link *dai_link,
struct simple_dai_props *dai_props)
struct simple_dai_props *dai_props,
bool is_top_level_node)
{
struct device_node *np = NULL;
struct device_node *bitclkmaster = NULL;
@ -155,7 +179,8 @@ static int simple_card_dai_link_of(struct device_node *node,
char *prefix = "";
int ret;
prefix = "simple-audio-card,";
if (is_top_level_node)
prefix = "simple-audio-card,";
daifmt = snd_soc_of_parse_daifmt(node, prefix,
&bitclkmaster, &framemaster);
@ -249,6 +274,7 @@ static int simple_card_dai_link_of(struct device_node *node,
sprintf(name, "%s-%s", dai_link->cpu_dai_name,
dai_link->codec_dai_name);
dai_link->name = dai_link->stream_name = name;
dai_link->ops = &asoc_simple_card_ops;
dev_dbg(dev, "\tname : %s\n", dai_link->stream_name);
dev_dbg(dev, "\tcpu : %s / %04x / %d\n",
@ -298,6 +324,10 @@ static int asoc_simple_card_parse_of(struct device_node *node,
return ret;
}
/* Factor to mclk, used in hw_params() */
of_property_read_u32(node, "simple-audio-card,mclk-fs",
&priv->mclk_fs);
dev_dbg(dev, "New simple-card: %s\n", priv->snd_card.name ?
priv->snd_card.name : "");
@ -307,14 +337,15 @@ static int asoc_simple_card_parse_of(struct device_node *node,
for (i = 0; (np = of_get_next_child(node, np)); i++) {
dev_dbg(dev, "\tlink %d:\n", i);
ret = simple_card_dai_link_of(np, dev, dai_link + i,
dai_props + i);
dai_props + i, false);
if (ret < 0) {
of_node_put(np);
return ret;
}
}
} else {
ret = simple_card_dai_link_of(node, dev, dai_link, dai_props);
ret = simple_card_dai_link_of(node, dev, dai_link, dai_props,
true);
if (ret < 0)
return ret;
}

View File

@ -49,3 +49,12 @@ config SND_SOC_INTEL_BYT_RT5640_MACH
help
This adds audio driver for Intel Baytrail platform based boards
with the RT5640 audio codec.
config SND_SOC_INTEL_BYT_MAX98090_MACH
tristate "ASoC Audio driver for Intel Baytrail with MAX98090 codec"
depends on SND_SOC_INTEL_SST && X86_INTEL_LPSS && I2C
select SND_SOC_INTEL_BAYTRAIL
select SND_SOC_MAX98090
help
This adds audio driver for Intel Baytrail platform based boards
with the MAX98090 audio codec.

View File

@ -23,6 +23,8 @@ obj-$(CONFIG_SND_SOC_INTEL_BAYTRAIL) += snd-soc-sst-baytrail-pcm.o
# Machine support
snd-soc-sst-haswell-objs := haswell.o
snd-soc-sst-byt-rt5640-mach-objs := byt-rt5640.o
snd-soc-sst-byt-max98090-mach-objs := byt-max98090.o
obj-$(CONFIG_SND_SOC_INTEL_HASWELL_MACH) += snd-soc-sst-haswell.o
obj-$(CONFIG_SND_SOC_INTEL_BYT_RT5640_MACH) += snd-soc-sst-byt-rt5640-mach.o
obj-$(CONFIG_SND_SOC_INTEL_BYT_MAX98090_MACH) += snd-soc-sst-byt-max98090-mach.o

View File

@ -0,0 +1,203 @@
/*
* Intel Baytrail SST MAX98090 machine driver
* Copyright (c) 2014, Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/acpi.h>
#include <linux/device.h>
#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/slab.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/jack.h>
#include "../codecs/max98090.h"
struct byt_max98090_private {
struct snd_soc_jack jack;
};
static const struct snd_soc_dapm_widget byt_max98090_widgets[] = {
SND_SOC_DAPM_HP("Headphone", NULL),
SND_SOC_DAPM_MIC("Headset Mic", NULL),
SND_SOC_DAPM_MIC("Int Mic", NULL),
SND_SOC_DAPM_SPK("Ext Spk", NULL),
};
static const struct snd_soc_dapm_route byt_max98090_audio_map[] = {
{"IN34", NULL, "Headset Mic"},
{"IN34", NULL, "MICBIAS"},
{"MICBIAS", NULL, "Headset Mic"},
{"DMICL", NULL, "Int Mic"},
{"Headphone", NULL, "HPL"},
{"Headphone", NULL, "HPR"},
{"Ext Spk", NULL, "SPKL"},
{"Ext Spk", NULL, "SPKR"},
};
static const struct snd_kcontrol_new byt_max98090_controls[] = {
SOC_DAPM_PIN_SWITCH("Headphone"),
SOC_DAPM_PIN_SWITCH("Headset Mic"),
SOC_DAPM_PIN_SWITCH("Int Mic"),
SOC_DAPM_PIN_SWITCH("Ext Spk"),
};
static struct snd_soc_jack_pin hs_jack_pins[] = {
{
.pin = "Headphone",
.mask = SND_JACK_HEADPHONE,
},
{
.pin = "Headset Mic",
.mask = SND_JACK_MICROPHONE,
},
{
.pin = "Ext Spk",
.mask = SND_JACK_LINEOUT,
},
{
.pin = "Int Mic",
.mask = SND_JACK_LINEIN,
},
};
static struct snd_soc_jack_gpio hs_jack_gpios[] = {
{
.name = "hp-gpio",
.idx = 0,
.report = SND_JACK_HEADPHONE | SND_JACK_LINEOUT,
.debounce_time = 200,
},
{
.name = "mic-gpio",
.idx = 1,
.report = SND_JACK_MICROPHONE | SND_JACK_LINEIN,
.debounce_time = 200,
},
};
static int byt_max98090_init(struct snd_soc_pcm_runtime *runtime)
{
int ret;
struct snd_soc_codec *codec = runtime->codec;
struct snd_soc_card *card = runtime->card;
struct byt_max98090_private *drv = snd_soc_card_get_drvdata(card);
struct snd_soc_jack *jack = &drv->jack;
card->dapm.idle_bias_off = true;
ret = snd_soc_dai_set_sysclk(runtime->codec_dai,
M98090_REG_SYSTEM_CLOCK,
25000000, SND_SOC_CLOCK_IN);
if (ret < 0) {
dev_err(card->dev, "Can't set codec clock %d\n", ret);
return ret;
}
/* Enable jack detection */
ret = snd_soc_jack_new(codec, "Headphone", SND_JACK_HEADPHONE, jack);
if (ret)
return ret;
ret = snd_soc_jack_add_pins(jack, ARRAY_SIZE(hs_jack_pins),
hs_jack_pins);
if (ret)
return ret;
ret = snd_soc_jack_add_gpiods(card->dev->parent, jack,
ARRAY_SIZE(hs_jack_gpios),
hs_jack_gpios);
if (ret)
return ret;
return max98090_mic_detect(codec, jack);
}
static struct snd_soc_dai_link byt_max98090_dais[] = {
{
.name = "Baytrail Audio",
.stream_name = "Audio",
.cpu_dai_name = "baytrail-pcm-audio",
.codec_dai_name = "HiFi",
.codec_name = "i2c-193C9890:00",
.platform_name = "baytrail-pcm-audio",
.init = byt_max98090_init,
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBS_CFS,
},
};
static struct snd_soc_card byt_max98090_card = {
.name = "byt-max98090",
.dai_link = byt_max98090_dais,
.num_links = ARRAY_SIZE(byt_max98090_dais),
.dapm_widgets = byt_max98090_widgets,
.num_dapm_widgets = ARRAY_SIZE(byt_max98090_widgets),
.dapm_routes = byt_max98090_audio_map,
.num_dapm_routes = ARRAY_SIZE(byt_max98090_audio_map),
.controls = byt_max98090_controls,
.num_controls = ARRAY_SIZE(byt_max98090_controls),
};
static int byt_max98090_probe(struct platform_device *pdev)
{
int ret_val = 0;
struct byt_max98090_private *priv;
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_ATOMIC);
if (!priv) {
dev_err(&pdev->dev, "allocation failed\n");
return -ENOMEM;
}
byt_max98090_card.dev = &pdev->dev;
snd_soc_card_set_drvdata(&byt_max98090_card, priv);
ret_val = devm_snd_soc_register_card(&pdev->dev, &byt_max98090_card);
if (ret_val) {
dev_err(&pdev->dev,
"snd_soc_register_card failed %d\n", ret_val);
return ret_val;
}
return ret_val;
}
static int byt_max98090_remove(struct platform_device *pdev)
{
struct snd_soc_card *card = platform_get_drvdata(pdev);
struct byt_max98090_private *priv = snd_soc_card_get_drvdata(card);
snd_soc_jack_free_gpios(&priv->jack, ARRAY_SIZE(hs_jack_gpios),
hs_jack_gpios);
return 0;
}
static struct platform_driver byt_max98090_driver = {
.probe = byt_max98090_probe,
.remove = byt_max98090_remove,
.driver = {
.name = "byt-max98090",
.owner = THIS_MODULE,
.pm = &snd_soc_pm_ops,
},
};
module_platform_driver(byt_max98090_driver)
MODULE_DESCRIPTION("ASoC Intel(R) Baytrail Machine driver");
MODULE_AUTHOR("Omair Md Abdullah, Jarkko Nikula");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:byt-max98090");

View File

@ -132,43 +132,20 @@ static struct snd_soc_card byt_rt5640_card = {
.num_dapm_routes = ARRAY_SIZE(byt_rt5640_audio_map),
};
#ifdef CONFIG_PM_SLEEP
static const struct dev_pm_ops byt_rt5640_pm_ops = {
.suspend = snd_soc_suspend,
.resume = snd_soc_resume,
};
#define BYT_RT5640_PM_OPS (&byt_rt5640_pm_ops)
#else
#define BYT_RT5640_PM_OPS NULL
#endif
static int byt_rt5640_probe(struct platform_device *pdev)
{
struct snd_soc_card *card = &byt_rt5640_card;
struct device *dev = &pdev->dev;
card->dev = &pdev->dev;
dev_set_drvdata(dev, card);
return snd_soc_register_card(card);
}
static int byt_rt5640_remove(struct platform_device *pdev)
{
struct snd_soc_card *card = platform_get_drvdata(pdev);
snd_soc_unregister_card(card);
return 0;
return devm_snd_soc_register_card(&pdev->dev, card);
}
static struct platform_driver byt_rt5640_audio = {
.probe = byt_rt5640_probe,
.remove = byt_rt5640_remove,
.driver = {
.name = "byt-rt5640",
.owner = THIS_MODULE,
.pm = BYT_RT5640_PM_OPS,
.pm = &snd_soc_pm_ops,
},
};
module_platform_driver(byt_rt5640_audio)

View File

@ -202,18 +202,11 @@ static int haswell_audio_probe(struct platform_device *pdev)
{
haswell_rt5640.dev = &pdev->dev;
return snd_soc_register_card(&haswell_rt5640);
}
static int haswell_audio_remove(struct platform_device *pdev)
{
snd_soc_unregister_card(&haswell_rt5640);
return 0;
return devm_snd_soc_register_card(&pdev->dev, &haswell_rt5640);
}
static struct platform_driver haswell_audio = {
.probe = haswell_audio_probe,
.remove = haswell_audio_remove,
.driver = {
.name = "haswell-audio",
.owner = THIS_MODULE,

View File

@ -247,6 +247,7 @@ static struct sst_acpi_desc sst_acpi_broadwell_desc = {
static struct sst_acpi_mach baytrail_machines[] = {
{ "10EC5640", "byt-rt5640", "intel/fw_sst_0f28.bin-i2s_master" },
{ "193C9890", "byt-max98090", "intel/fw_sst_0f28.bin-i2s_master" },
{}
};

View File

@ -22,7 +22,6 @@
#include <linux/export.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/list.h>
#include <linux/platform_device.h>
#include <linux/kthread.h>
#include <linux/firmware.h>
@ -892,7 +891,7 @@ int sst_byt_dsp_init(struct device *dev, struct sst_pdata *pdata)
/* start the IPC message thread */
init_kthread_worker(&byt->kworker);
byt->tx_thread = kthread_run(kthread_worker_fn,
&byt->kworker,
&byt->kworker, "%s",
dev_name(byt->dev));
if (IS_ERR(byt->tx_thread)) {
err = PTR_ERR(byt->tx_thread);
@ -907,7 +906,7 @@ int sst_byt_dsp_init(struct device *dev, struct sst_pdata *pdata)
byt->dsp = sst_dsp_new(dev, &byt_dev, pdata);
if (byt->dsp == NULL) {
err = -ENODEV;
goto err_free_msg;
goto dsp_err;
}
/* keep the DSP in reset state for base FW loading */
@ -940,6 +939,8 @@ boot_err:
sst_fw_free(byt_sst_fw);
fw_err:
sst_dsp_free(byt->dsp);
dsp_err:
kthread_stop(byt->tx_thread);
err_free_msg:
kfree(byt->msg);
@ -954,6 +955,7 @@ void sst_byt_dsp_free(struct device *dev, struct sst_pdata *pdata)
sst_dsp_reset(byt->dsp);
sst_fw_free_all(byt->dsp);
sst_dsp_free(byt->dsp);
kthread_stop(byt->tx_thread);
kfree(byt->msg);
}
EXPORT_SYMBOL_GPL(sst_byt_dsp_free);

View File

@ -180,6 +180,7 @@ static int sst_byt_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
pcm_data->hw_ptr = 0;
sst_byt_stream_start(byt, pcm_data->stream, 0);
break;
case SNDRV_PCM_TRIGGER_RESUME:

View File

@ -25,7 +25,6 @@
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/sched.h>
#include <linux/list.h>
#include <linux/platform_device.h>
#include <linux/kthread.h>
#include <linux/firmware.h>
@ -1730,17 +1729,17 @@ int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata)
ret = msg_empty_list_init(hsw);
if (ret < 0)
goto list_err;
return -ENOMEM;
/* start the IPC message thread */
init_kthread_worker(&hsw->kworker);
hsw->tx_thread = kthread_run(kthread_worker_fn,
&hsw->kworker,
&hsw->kworker, "%s",
dev_name(hsw->dev));
if (IS_ERR(hsw->tx_thread)) {
ret = PTR_ERR(hsw->tx_thread);
dev_err(hsw->dev, "error: failed to create message TX task\n");
goto list_err;
goto err_free_msg;
}
init_kthread_work(&hsw->kwork, ipc_tx_msgs);
@ -1750,7 +1749,7 @@ int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata)
hsw->dsp = sst_dsp_new(dev, &hsw_dev, pdata);
if (hsw->dsp == NULL) {
ret = -ENODEV;
goto list_err;
goto dsp_err;
}
/* keep the DSP in reset state for base FW loading */
@ -1794,8 +1793,11 @@ boot_err:
sst_fw_free(hsw_sst_fw);
fw_err:
sst_dsp_free(hsw->dsp);
dsp_err:
kthread_stop(hsw->tx_thread);
err_free_msg:
kfree(hsw->msg);
list_err:
return ret;
}
EXPORT_SYMBOL_GPL(sst_hsw_dsp_init);
@ -1808,6 +1810,7 @@ void sst_hsw_dsp_free(struct device *dev, struct sst_pdata *pdata)
sst_fw_free_all(hsw->dsp);
sst_dsp_free(hsw->dsp);
kfree(hsw->scratch);
kthread_stop(hsw->tx_thread);
kfree(hsw->msg);
}
EXPORT_SYMBOL_GPL(sst_hsw_dsp_free);

View File

@ -17,7 +17,6 @@
#include <linux/module.h>
#include <linux/dma-mapping.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <asm/page.h>
#include <asm/pgtable.h>

View File

@ -527,6 +527,15 @@ static int ams_delta_cx20442_init(struct snd_soc_pcm_runtime *rtd)
return 0;
}
static int ams_delta_card_remove(struct snd_soc_pcm_runtime *rtd)
{
snd_soc_jack_free_gpios(&ams_delta_hook_switch,
ARRAY_SIZE(ams_delta_hook_switch_gpios),
ams_delta_hook_switch_gpios);
return 0;
}
/* DAI glue - connects codec <--> CPU */
static struct snd_soc_dai_link ams_delta_dai_link = {
.name = "CX20442",
@ -543,6 +552,7 @@ static struct snd_soc_dai_link ams_delta_dai_link = {
static struct snd_soc_card ams_delta_audio_card = {
.name = "AMS_DELTA",
.owner = THIS_MODULE,
.remove = ams_delta_card_remove,
.dai_link = &ams_delta_dai_link,
.num_links = 1,
@ -579,10 +589,6 @@ static int ams_delta_remove(struct platform_device *pdev)
dev_warn(&pdev->dev,
"failed to unregister V253 line discipline\n");
snd_soc_jack_free_gpios(&ams_delta_hook_switch,
ARRAY_SIZE(ams_delta_hook_switch_gpios),
ams_delta_hook_switch_gpios);
snd_soc_unregister_card(card);
card->dev = NULL;
return 0;

View File

@ -40,9 +40,9 @@
#include <sound/initval.h>
#include <sound/soc.h>
#include <sound/dmaengine_pcm.h>
#include <sound/omap-pcm.h>
#include "omap-dmic.h"
#include "omap-pcm.h"
struct omap_dmic {
struct device *dev;

View File

@ -34,9 +34,9 @@
#include <sound/asoundef.h>
#include <sound/dmaengine_pcm.h>
#include <video/omapdss.h>
#include <sound/omap-pcm.h>
#include "omap-hdmi.h"
#include "omap-pcm.h"
#define DRV_NAME "omap-hdmi-audio-dai"

View File

@ -34,11 +34,11 @@
#include <sound/initval.h>
#include <sound/soc.h>
#include <sound/dmaengine_pcm.h>
#include <sound/omap-pcm.h>
#include <linux/platform_data/asoc-ti-mcbsp.h>
#include "mcbsp.h"
#include "omap-mcbsp.h"
#include "omap-pcm.h"
#define OMAP_MCBSP_RATES (SNDRV_PCM_RATE_8000_96000)

View File

@ -40,9 +40,9 @@
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/dmaengine_pcm.h>
#include <sound/omap-pcm.h>
#include "omap-mcpdm.h"
#include "omap-pcm.h"
struct mcpdm_link_config {
u32 link_mask; /* channel mask for the direction */

View File

@ -231,6 +231,19 @@ static int omap_twl4030_init(struct snd_soc_pcm_runtime *rtd)
return ret;
}
static int omap_twl4030_card_remove(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_card *card = rtd->card;
struct omap_twl4030 *priv = snd_soc_card_get_drvdata(card);
if (priv->jack_detect > 0)
snd_soc_jack_free_gpios(&priv->hs_jack,
ARRAY_SIZE(hs_jack_gpios),
hs_jack_gpios);
return 0;
}
/* Digital audio interface glue - connects codec <--> CPU */
static struct snd_soc_dai_link omap_twl4030_dai_links[] = {
{
@ -258,6 +271,7 @@ static struct snd_soc_dai_link omap_twl4030_dai_links[] = {
/* Audio machine driver */
static struct snd_soc_card omap_twl4030_card = {
.owner = THIS_MODULE,
.remove = omap_twl4030_card_remove,
.dai_link = omap_twl4030_dai_links,
.num_links = ARRAY_SIZE(omap_twl4030_dai_links),
@ -353,19 +367,6 @@ static int omap_twl4030_probe(struct platform_device *pdev)
return 0;
}
static int omap_twl4030_remove(struct platform_device *pdev)
{
struct snd_soc_card *card = platform_get_drvdata(pdev);
struct omap_twl4030 *priv = snd_soc_card_get_drvdata(card);
if (priv->jack_detect > 0)
snd_soc_jack_free_gpios(&priv->hs_jack,
ARRAY_SIZE(hs_jack_gpios),
hs_jack_gpios);
return 0;
}
static const struct of_device_id omap_twl4030_of_match[] = {
{.compatible = "ti,omap-twl4030", },
{ },
@ -380,7 +381,6 @@ static struct platform_driver omap_twl4030_driver = {
.of_match_table = omap_twl4030_of_match,
},
.probe = omap_twl4030_probe,
.remove = omap_twl4030_remove,
};
module_platform_driver(omap_twl4030_driver);

View File

@ -334,6 +334,14 @@ static int rx51_aic34_init(struct snd_soc_pcm_runtime *rtd)
return err;
}
static int rx51_card_remove(struct snd_soc_pcm_runtime *rtd)
{
snd_soc_jack_free_gpios(&rx51_av_jack, ARRAY_SIZE(rx51_av_jack_gpios),
rx51_av_jack_gpios);
return 0;
}
/* Digital audio interface glue - connects codec <--> CPU */
static struct snd_soc_dai_link rx51_dai[] = {
{
@ -368,6 +376,7 @@ static struct snd_soc_codec_conf rx51_codec_conf[] = {
static struct snd_soc_card rx51_sound_card = {
.name = "RX-51",
.owner = THIS_MODULE,
.remove = rx51_card_remove,
.dai_link = rx51_dai,
.num_links = ARRAY_SIZE(rx51_dai),
.aux_dev = rx51_aux_dev,
@ -499,14 +508,6 @@ static int rx51_soc_probe(struct platform_device *pdev)
return 0;
}
static int rx51_soc_remove(struct platform_device *pdev)
{
snd_soc_jack_free_gpios(&rx51_av_jack, ARRAY_SIZE(rx51_av_jack_gpios),
rx51_av_jack_gpios);
return 0;
}
#if defined(CONFIG_OF)
static const struct of_device_id rx51_audio_of_match[] = {
{ .compatible = "nokia,n900-audio", },
@ -522,7 +523,6 @@ static struct platform_driver rx51_soc_driver = {
.of_match_table = of_match_ptr(rx51_audio_of_match),
},
.probe = rx51_soc_probe,
.remove = rx51_soc_remove,
};
module_platform_driver(rx51_soc_driver);

View File

@ -152,6 +152,13 @@ static int hx4700_ak4641_init(struct snd_soc_pcm_runtime *rtd)
return err;
}
static int hx4700_card_remove(struct snd_soc_pcm_runtime *rtd)
{
snd_soc_jack_free_gpios(&hs_jack, 1, &hs_jack_gpio);
return 0;
}
/* hx4700 digital audio interface glue - connects codec <--> CPU */
static struct snd_soc_dai_link hx4700_dai = {
.name = "ak4641",
@ -170,6 +177,7 @@ static struct snd_soc_dai_link hx4700_dai = {
static struct snd_soc_card snd_soc_card_hx4700 = {
.name = "iPAQ hx4700",
.owner = THIS_MODULE,
.remove = hx4700_card_remove,
.dai_link = &hx4700_dai,
.num_links = 1,
.dapm_widgets = hx4700_dapm_widgets,
@ -206,7 +214,6 @@ static int hx4700_audio_probe(struct platform_device *pdev)
static int hx4700_audio_remove(struct platform_device *pdev)
{
snd_soc_jack_free_gpios(&hs_jack, 1, &hs_jack_gpio);
snd_soc_unregister_card(&snd_soc_card_hx4700);
gpio_set_value(GPIO92_HX4700_HP_DRIVER, 0);

View File

@ -808,6 +808,7 @@ static const struct snd_soc_component_driver pxa_ssp_component = {
#ifdef CONFIG_OF
static const struct of_device_id pxa_ssp_of_ids[] = {
{ .compatible = "mrvl,pxa-ssp-dai" },
{}
};
#endif

View File

@ -189,6 +189,14 @@ static int h1940_uda1380_init(struct snd_soc_pcm_runtime *rtd)
return 0;
}
static int h1940_uda1380_card_remove(struct snd_soc_pcm_runtime *rtd)
{
snd_soc_jack_free_gpios(&hp_jack, ARRAY_SIZE(hp_jack_gpios),
hp_jack_gpios);
return 0;
}
/* s3c24xx digital audio interface glue - connects codec <--> CPU */
static struct snd_soc_dai_link h1940_uda1380_dai[] = {
{
@ -206,6 +214,7 @@ static struct snd_soc_dai_link h1940_uda1380_dai[] = {
static struct snd_soc_card h1940_asoc = {
.name = "h1940",
.owner = THIS_MODULE,
.remove = h1940_uda1380_card_remove,
.dai_link = h1940_uda1380_dai,
.num_links = ARRAY_SIZE(h1940_uda1380_dai),
@ -257,8 +266,6 @@ err_out:
static void __exit h1940_exit(void)
{
platform_device_unregister(s3c24xx_snd_device);
snd_soc_jack_free_gpios(&hp_jack, ARRAY_SIZE(hp_jack_gpios),
hp_jack_gpios);
gpio_free(S3C_GPIO_END + 9);
}

View File

@ -488,7 +488,7 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai,
clk_id = 1;
if (!any_active(i2s)) {
if (i2s->op_clk) {
if (i2s->op_clk && !IS_ERR(i2s->op_clk)) {
if ((clk_id && !(mod & MOD_IMS_SYSMUX)) ||
(!clk_id && (mod & MOD_IMS_SYSMUX))) {
clk_disable_unprepare(i2s->op_clk);
@ -506,6 +506,10 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai,
else
i2s->op_clk = clk_get(&i2s->pdev->dev,
"i2s_opclk0");
if (WARN_ON(IS_ERR(i2s->op_clk)))
return PTR_ERR(i2s->op_clk);
clk_prepare_enable(i2s->op_clk);
i2s->rclk_srcrate = clk_get_rate(i2s->op_clk);
@ -672,8 +676,8 @@ static int i2s_hw_params(struct snd_pcm_substream *substream,
if (is_manager(i2s))
mod &= ~MOD_BLC_MASK;
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S8:
switch (params_width(params)) {
case 8:
if (is_secondary(i2s))
mod |= MOD_BLCS_8BIT;
else
@ -681,7 +685,7 @@ static int i2s_hw_params(struct snd_pcm_substream *substream,
if (is_manager(i2s))
mod |= MOD_BLC_8BIT;
break;
case SNDRV_PCM_FORMAT_S16_LE:
case 16:
if (is_secondary(i2s))
mod |= MOD_BLCS_16BIT;
else
@ -689,7 +693,7 @@ static int i2s_hw_params(struct snd_pcm_substream *substream,
if (is_manager(i2s))
mod |= MOD_BLC_16BIT;
break;
case SNDRV_PCM_FORMAT_S24_LE:
case 24:
if (is_secondary(i2s))
mod |= MOD_BLCS_24BIT;
else

View File

@ -283,8 +283,8 @@ static int s3c_pcm_hw_params(struct snd_pcm_substream *substream,
dev_dbg(pcm->dev, "Entered %s\n", __func__);
/* Strictly check for sample size */
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE:
switch (params_width(params)) {
case 16:
break;
default:
return -EINVAL;

View File

@ -31,6 +31,7 @@
#include "s3c24xx-i2s.h"
static int rx1950_uda1380_init(struct snd_soc_pcm_runtime *rtd);
static int rx1950_uda1380_card_remove(struct snd_soc_pcm_runtime *rtd);
static int rx1950_startup(struct snd_pcm_substream *substream);
static int rx1950_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params);
@ -116,6 +117,7 @@ static const struct snd_soc_dapm_route audio_map[] = {
static struct snd_soc_card rx1950_asoc = {
.name = "rx1950",
.owner = THIS_MODULE,
.remove = rx1950_uda1380_card_remove,
.dai_link = rx1950_uda1380_dai,
.num_links = ARRAY_SIZE(rx1950_uda1380_dai),
@ -234,6 +236,14 @@ static int rx1950_uda1380_init(struct snd_soc_pcm_runtime *rtd)
return 0;
}
static int rx1950_uda1380_card_remove(struct snd_soc_pcm_runtime *rtd)
{
snd_soc_jack_free_gpios(&hp_jack, ARRAY_SIZE(hp_jack_gpios),
hp_jack_gpios);
return 0;
}
static int __init rx1950_init(void)
{
int ret;
@ -278,8 +288,6 @@ err_gpio:
static void __exit rx1950_exit(void)
{
platform_device_unregister(s3c24xx_snd_device);
snd_soc_jack_free_gpios(&hp_jack, ARRAY_SIZE(hp_jack_gpios),
hp_jack_gpios);
gpio_free(S3C2410_GPA(1));
}

View File

@ -322,13 +322,13 @@ static int s3c_i2sv2_hw_params(struct snd_pcm_substream *substream,
iismod &= ~S3C64XX_IISMOD_BLC_MASK;
/* Sample size */
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S8:
switch (params_width(params)) {
case 8:
iismod |= S3C64XX_IISMOD_BLC_8BIT;
break;
case SNDRV_PCM_FORMAT_S16_LE:
case 16:
break;
case SNDRV_PCM_FORMAT_S24_LE:
case 24:
iismod |= S3C64XX_IISMOD_BLC_24BIT;
break;
}

View File

@ -120,11 +120,11 @@ static int s3c2412_i2s_hw_params(struct snd_pcm_substream *substream,
iismod = readl(i2s->regs + S3C2412_IISMOD);
pr_debug("%s: r: IISMOD: %x\n", __func__, iismod);
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S8:
switch (params_width(params)) {
case 8:
iismod |= S3C2412_IISMOD_8BIT;
break;
case SNDRV_PCM_FORMAT_S16_LE:
case 16:
iismod &= ~S3C2412_IISMOD_8BIT;
break;
}

View File

@ -248,12 +248,12 @@ static int s3c24xx_i2s_hw_params(struct snd_pcm_substream *substream,
iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);
pr_debug("hw_params r: IISMOD: %x\n", iismod);
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S8:
switch (params_width(params)) {
case 8:
iismod &= ~S3C2410_IISMOD_16BIT;
dma_data->dma_size = 1;
break;
case SNDRV_PCM_FORMAT_S16_LE:
case 16:
iismod |= S3C2410_IISMOD_16BIT;
dma_data->dma_size = 2;
break;

View File

@ -182,6 +182,14 @@ static int smartq_wm8987_init(struct snd_soc_pcm_runtime *rtd)
return err;
}
static int smartq_wm8987_card_remove(struct snd_soc_pcm_runtime *rtd)
{
snd_soc_jack_free_gpios(&smartq_jack, ARRAY_SIZE(smartq_jack_gpios),
smartq_jack_gpios);
return 0;
}
static struct snd_soc_dai_link smartq_dai[] = {
{
.name = "wm8987",
@ -198,6 +206,7 @@ static struct snd_soc_dai_link smartq_dai[] = {
static struct snd_soc_card snd_soc_smartq = {
.name = "SmartQ",
.owner = THIS_MODULE,
.remove = smartq_wm8987_card_remove,
.dai_link = smartq_dai,
.num_links = ARRAY_SIZE(smartq_dai),
@ -259,8 +268,6 @@ err_unregister_device:
static void __exit smartq_exit(void)
{
gpio_free(S3C64XX_GPK(12));
snd_soc_jack_free_gpios(&smartq_jack, ARRAY_SIZE(smartq_jack_gpios),
smartq_jack_gpios);
platform_device_unregister(smartq_snd_device);
}

View File

@ -37,13 +37,11 @@ static int smdk_hw_params(struct snd_pcm_substream *substream,
unsigned int pll_out;
int bfs, rfs, ret;
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_U8:
case SNDRV_PCM_FORMAT_S8:
switch (params_width(params)) {
case 8:
bfs = 16;
break;
case SNDRV_PCM_FORMAT_U16_LE:
case SNDRV_PCM_FORMAT_S16_LE:
case 16:
bfs = 32;
break;
default:

View File

@ -57,7 +57,7 @@ static int smdk_hw_params(struct snd_pcm_substream *substream,
int ret;
/* AIF1CLK should be >=3MHz for optimal performance */
if (params_format(params) == SNDRV_PCM_FORMAT_S24_LE)
if (params_width(params) == 24)
pll_out = params_rate(params) * 384;
else if (params_rate(params) == 8000 || params_rate(params) == 11025)
pll_out = params_rate(params) * 512;

View File

@ -211,8 +211,8 @@ static int spdif_hw_params(struct snd_pcm_substream *substream,
con |= CON_PCM_DATA;
con &= ~CON_PCM_MASK;
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE:
switch (params_width(params)) {
case 16:
con |= CON_PCM_16BIT;
break;
default:

View File

@ -255,11 +255,81 @@ int rsnd_dma_available(struct rsnd_dma *dma)
return !!dma->chan;
}
#define DMA_NAME_SIZE 16
#define MOD_MAX 4 /* MEM/SSI/SRC/DVC */
static int _rsnd_dma_of_name(char *dma_name, struct rsnd_mod *mod)
{
if (mod)
return snprintf(dma_name, DMA_NAME_SIZE / 2, "%s%d",
rsnd_mod_name(mod), rsnd_mod_id(mod));
else
return snprintf(dma_name, DMA_NAME_SIZE / 2, "mem");
}
static void rsnd_dma_of_name(struct rsnd_dma *dma,
int is_play, char *dma_name)
{
struct rsnd_mod *this = rsnd_dma_to_mod(dma);
struct rsnd_dai_stream *io = rsnd_mod_to_io(this);
struct rsnd_mod *ssi = rsnd_io_to_mod_ssi(io);
struct rsnd_mod *src = rsnd_io_to_mod_src(io);
struct rsnd_mod *dvc = rsnd_io_to_mod_dvc(io);
struct rsnd_mod *mod[MOD_MAX];
struct rsnd_mod *src_mod, *dst_mod;
int i, index;
for (i = 0; i < MOD_MAX; i++)
mod[i] = NULL;
/*
* in play case...
*
* src -> dst
*
* mem -> SSI
* mem -> SRC -> SSI
* mem -> SRC -> DVC -> SSI
*/
mod[0] = NULL; /* for "mem" */
index = 1;
for (i = 1; i < MOD_MAX; i++) {
if (!src) {
mod[i] = ssi;
break;
} else if (!dvc) {
mod[i] = src;
src = NULL;
} else {
mod[i] = dvc;
dvc = NULL;
}
if (mod[i] == this)
index = i;
}
if (is_play) {
src_mod = mod[index - 1];
dst_mod = mod[index];
} else {
src_mod = mod[index];
dst_mod = mod[index + 1];
}
index = 0;
index = _rsnd_dma_of_name(dma_name + index, src_mod);
*(dma_name + index++) = '_';
index = _rsnd_dma_of_name(dma_name + index, dst_mod);
}
int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma,
int is_play, int id)
{
struct device *dev = rsnd_priv_to_dev(priv);
struct dma_slave_config cfg;
char dma_name[DMA_NAME_SIZE];
dma_cap_mask_t mask;
int ret;
@ -271,18 +341,23 @@ int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma,
dma_cap_zero(mask);
dma_cap_set(DMA_SLAVE, mask);
if (dev->of_node)
rsnd_dma_of_name(dma, is_play, dma_name);
else
snprintf(dma_name, DMA_NAME_SIZE,
is_play ? "tx" : "rx");
dev_dbg(dev, "dma name : %s\n", dma_name);
dma->chan = dma_request_slave_channel_compat(mask, shdma_chan_filter,
(void *)id, dev,
is_play ? "tx" : "rx");
dma_name);
if (!dma->chan) {
dev_err(dev, "can't get dma channel\n");
return -EIO;
}
cfg.slave_id = id;
cfg.dst_addr = 0; /* use default addr when playback */
cfg.src_addr = 0; /* use default addr when capture */
cfg.direction = is_play ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM;
rsnd_gen_dma_addr(priv, dma, &cfg, is_play, id);
ret = dmaengine_slave_config(dma->chan, &cfg);
if (ret < 0)
@ -956,7 +1031,7 @@ static int rsnd_probe(struct platform_device *pdev)
return -ENODEV;
}
priv->dev = dev;
priv->pdev = pdev;
priv->info = info;
spin_lock_init(&priv->lock);

View File

@ -13,6 +13,9 @@
#define RSND_DVC_NAME_SIZE 16
#define RSND_DVC_VOLUME_MAX 100
#define RSND_DVC_VOLUME_NUM 2
#define DVC_NAME "dvc"
struct rsnd_dvc {
struct rsnd_dvc_platform_info *info; /* rcar_snd.h */
struct rsnd_mod mod;
@ -43,6 +46,17 @@ static void rsnd_dvc_volume_update(struct rsnd_mod *mod)
rsnd_mod_write(mod, DVC_VOL1R, vol[1]);
}
static int rsnd_dvc_probe_gen2(struct rsnd_mod *mod,
struct rsnd_dai *rdai)
{
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
struct device *dev = rsnd_priv_to_dev(priv);
dev_dbg(dev, "%s (Gen2) is probed\n", rsnd_mod_name(mod));
return 0;
}
static int rsnd_dvc_init(struct rsnd_mod *dvc_mod,
struct rsnd_dai *rdai)
{
@ -208,7 +222,8 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod,
}
static struct rsnd_mod_ops rsnd_dvc_ops = {
.name = "dvc (gen2)",
.name = DVC_NAME,
.probe = rsnd_dvc_probe_gen2,
.init = rsnd_dvc_init,
.quit = rsnd_dvc_quit,
.start = rsnd_dvc_start,
@ -255,7 +270,8 @@ int rsnd_dvc_probe(struct platform_device *pdev,
priv->dvc = dvc;
for_each_rsnd_dvc(dvc, priv, i) {
snprintf(name, RSND_DVC_NAME_SIZE, "dvc.%d", i);
snprintf(name, RSND_DVC_NAME_SIZE, "%s.%d",
DVC_NAME, i);
clk = devm_clk_get(dev, name);
if (IS_ERR(clk))

View File

@ -155,6 +155,101 @@ static int rsnd_gen_regmap_init(struct rsnd_priv *priv,
return 0;
}
/*
* DMA read/write register offset
*
* RSND_xxx_I_N for Audio DMAC input
* RSND_xxx_O_N for Audio DMAC output
* RSND_xxx_I_P for Audio DMAC peri peri input
* RSND_xxx_O_P for Audio DMAC peri peri output
*
* ex) R-Car H2 case
* mod / DMAC in / DMAC out / DMAC PP in / DMAC pp out
* SSI : 0xec541000 / 0xec241008 / 0xec24100c / 0xec400000 / 0xec400000
* SCU : 0xec500000 / 0xec000000 / 0xec004000 / 0xec300000 / 0xec304000
* CMD : 0xec500000 / 0xec008000 0xec308000
*/
#define RDMA_SSI_I_N(addr, i) (addr ##_reg - 0x00300000 + (0x40 * i) + 0x8)
#define RDMA_SSI_O_N(addr, i) (addr ##_reg - 0x00300000 + (0x40 * i) + 0xc)
#define RDMA_SSI_I_P(addr, i) (addr ##_reg - 0x00141000 + (0x1000 * i))
#define RDMA_SSI_O_P(addr, i) (addr ##_reg - 0x00141000 + (0x1000 * i))
#define RDMA_SRC_I_N(addr, i) (addr ##_reg - 0x00500000 + (0x400 * i))
#define RDMA_SRC_O_N(addr, i) (addr ##_reg - 0x004fc000 + (0x400 * i))
#define RDMA_SRC_I_P(addr, i) (addr ##_reg - 0x00200000 + (0x400 * i))
#define RDMA_SRC_O_P(addr, i) (addr ##_reg - 0x001fc000 + (0x400 * i))
#define RDMA_CMD_O_N(addr, i) (addr ##_reg - 0x004f8000 + (0x400 * i))
#define RDMA_CMD_O_P(addr, i) (addr ##_reg - 0x001f8000 + (0x400 * i))
void rsnd_gen_dma_addr(struct rsnd_priv *priv,
struct rsnd_dma *dma,
struct dma_slave_config *cfg,
int is_play, int slave_id)
{
struct platform_device *pdev = rsnd_priv_to_pdev(priv);
struct device *dev = rsnd_priv_to_dev(priv);
struct rsnd_mod *mod = rsnd_dma_to_mod(dma);
struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
dma_addr_t ssi_reg = platform_get_resource(pdev,
IORESOURCE_MEM, RSND_GEN2_SSI)->start;
dma_addr_t src_reg = platform_get_resource(pdev,
IORESOURCE_MEM, RSND_GEN2_SCU)->start;
int is_ssi = !!(rsnd_io_to_mod_ssi(io) == mod);
int use_src = !!rsnd_io_to_mod_src(io);
int use_dvc = !!rsnd_io_to_mod_dvc(io);
int id = rsnd_mod_id(mod);
struct dma_addr {
dma_addr_t src_addr;
dma_addr_t dst_addr;
} dma_addrs[2][2][3] = {
{ /* SRC */
/* Capture */
{{ 0, 0 },
{ RDMA_SRC_O_N(src, id), 0 },
{ RDMA_CMD_O_N(src, id), 0 }},
/* Playback */
{{ 0, 0, },
{ 0, RDMA_SRC_I_N(src, id) },
{ 0, RDMA_SRC_I_N(src, id) }}
}, { /* SSI */
/* Capture */
{{ RDMA_SSI_O_N(ssi, id), 0 },
{ RDMA_SSI_O_P(ssi, id), RDMA_SRC_I_P(src, id) },
{ RDMA_SSI_O_P(ssi, id), RDMA_SRC_I_P(src, id) }},
/* Playback */
{{ 0, RDMA_SSI_I_N(ssi, id) },
{ RDMA_SRC_O_P(src, id), RDMA_SSI_I_P(ssi, id) },
{ RDMA_CMD_O_P(src, id), RDMA_SSI_I_P(ssi, id) }}
}
};
cfg->slave_id = slave_id;
cfg->src_addr = 0;
cfg->dst_addr = 0;
cfg->direction = is_play ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM;
/*
* gen1 uses default DMA addr
*/
if (rsnd_is_gen1(priv))
return;
/* it shouldn't happen */
if (use_dvc & !use_src) {
dev_err(dev, "DVC is selected without SRC\n");
return;
}
cfg->src_addr = dma_addrs[is_ssi][is_play][use_src + use_dvc].src_addr;
cfg->dst_addr = dma_addrs[is_ssi][is_play][use_src + use_dvc].dst_addr;
dev_dbg(dev, "dma%d addr - src : %x / dst : %x\n",
id, cfg->src_addr, cfg->dst_addr);
}
/*
* Gen2
*/

View File

@ -281,6 +281,11 @@ int rsnd_gen_probe(struct platform_device *pdev,
void __iomem *rsnd_gen_reg_get(struct rsnd_priv *priv,
struct rsnd_mod *mod,
enum rsnd_reg reg);
void rsnd_gen_dma_addr(struct rsnd_priv *priv,
struct rsnd_dma *dma,
struct dma_slave_config *cfg,
int is_play, int slave_id);
#define rsnd_is_gen1(s) (((s)->info->flags & RSND_GEN_MASK) == RSND_GEN1)
#define rsnd_is_gen2(s) (((s)->info->flags & RSND_GEN_MASK) == RSND_GEN2)
@ -317,7 +322,7 @@ struct rsnd_of_data {
struct rsnd_priv {
struct device *dev;
struct platform_device *pdev;
struct rcar_snd_info *info;
spinlock_t lock;
@ -357,7 +362,8 @@ struct rsnd_priv {
int rdai_nr;
};
#define rsnd_priv_to_dev(priv) ((priv)->dev)
#define rsnd_priv_to_pdev(priv) ((priv)->pdev)
#define rsnd_priv_to_dev(priv) (&(rsnd_priv_to_pdev(priv)->dev))
#define rsnd_priv_to_info(priv) ((priv)->info)
#define rsnd_lock(priv, flags) spin_lock_irqsave(&priv->lock, flags)
#define rsnd_unlock(priv, flags) spin_unlock_irqrestore(&priv->lock, flags)

View File

@ -10,6 +10,8 @@
*/
#include "rsnd.h"
#define SRC_NAME "src"
struct rsnd_src {
struct rsnd_src_platform_info *info; /* rcar_snd.h */
struct rsnd_mod mod;
@ -268,10 +270,6 @@ static int rsnd_src_stop(struct rsnd_mod *mod,
return 0;
}
static struct rsnd_mod_ops rsnd_src_non_ops = {
.name = "src (non)",
};
/*
* Gen1 functions
*/
@ -393,6 +391,17 @@ static int rsnd_src_set_convert_rate_gen1(struct rsnd_mod *mod,
return 0;
}
static int rsnd_src_probe_gen1(struct rsnd_mod *mod,
struct rsnd_dai *rdai)
{
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
struct device *dev = rsnd_priv_to_dev(priv);
dev_dbg(dev, "%s (Gen1) is probed\n", rsnd_mod_name(mod));
return 0;
}
static int rsnd_src_init_gen1(struct rsnd_mod *mod,
struct rsnd_dai *rdai)
{
@ -438,7 +447,8 @@ static int rsnd_src_stop_gen1(struct rsnd_mod *mod,
}
static struct rsnd_mod_ops rsnd_src_gen1_ops = {
.name = "sru (gen1)",
.name = SRC_NAME,
.probe = rsnd_src_probe_gen1,
.init = rsnd_src_init_gen1,
.quit = rsnd_src_quit,
.start = rsnd_src_start_gen1,
@ -502,6 +512,8 @@ static int rsnd_src_probe_gen2(struct rsnd_mod *mod,
if (ret < 0)
dev_err(dev, "SRC DMA failed\n");
dev_dbg(dev, "%s (Gen2) is probed\n", rsnd_mod_name(mod));
return ret;
}
@ -562,7 +574,7 @@ static int rsnd_src_stop_gen2(struct rsnd_mod *mod,
}
static struct rsnd_mod_ops rsnd_src_gen2_ops = {
.name = "src (gen2)",
.name = SRC_NAME,
.probe = rsnd_src_probe_gen2,
.remove = rsnd_src_remove_gen2,
.init = rsnd_src_init_gen2,
@ -598,18 +610,21 @@ static void rsnd_of_parse_src(struct platform_device *pdev,
nr = of_get_child_count(src_node);
if (!nr)
return;
goto rsnd_of_parse_src_end;
src_info = devm_kzalloc(dev,
sizeof(struct rsnd_src_platform_info) * nr,
GFP_KERNEL);
if (!src_info) {
dev_err(dev, "src info allocation error\n");
return;
goto rsnd_of_parse_src_end;
}
info->src_info = src_info;
info->src_info_nr = nr;
rsnd_of_parse_src_end:
of_node_put(src_node);
}
int rsnd_src_probe(struct platform_device *pdev,
@ -624,6 +639,16 @@ int rsnd_src_probe(struct platform_device *pdev,
char name[RSND_SRC_NAME_SIZE];
int i, nr;
ops = NULL;
if (rsnd_is_gen1(priv))
ops = &rsnd_src_gen1_ops;
if (rsnd_is_gen2(priv))
ops = &rsnd_src_gen2_ops;
if (!ops) {
dev_err(dev, "unknown Generation\n");
return -EIO;
}
rsnd_of_parse_src(pdev, of_data, priv);
/*
@ -643,7 +668,8 @@ int rsnd_src_probe(struct platform_device *pdev,
priv->src = src;
for_each_rsnd_src(src, priv, i) {
snprintf(name, RSND_SRC_NAME_SIZE, "src.%d", i);
snprintf(name, RSND_SRC_NAME_SIZE, "%s.%d",
SRC_NAME, i);
clk = devm_clk_get(dev, name);
if (IS_ERR(clk))
@ -652,12 +678,6 @@ int rsnd_src_probe(struct platform_device *pdev,
src->info = &info->src_info[i];
src->clk = clk;
ops = &rsnd_src_non_ops;
if (rsnd_is_gen1(priv))
ops = &rsnd_src_gen1_ops;
if (rsnd_is_gen2(priv))
ops = &rsnd_src_gen2_ops;
rsnd_mod_init(priv, &src->mod, ops, RSND_MOD_SRC, i);
dev_dbg(dev, "SRC%d probed\n", i);

View File

@ -57,6 +57,8 @@
*/
#define CONT (1 << 8) /* WS Continue Function */
#define SSI_NAME "ssi"
struct rsnd_ssi {
struct clk *clk;
struct rsnd_ssi_platform_info *info; /* rcar_snd.h */
@ -373,6 +375,8 @@ static int rsnd_ssi_pio_probe(struct rsnd_mod *mod,
if (ret)
dev_err(dev, "SSI request interrupt failed\n");
dev_dbg(dev, "%s (PIO) is probed\n", rsnd_mod_name(mod));
return ret;
}
@ -405,7 +409,7 @@ static int rsnd_ssi_pio_stop(struct rsnd_mod *mod,
}
static struct rsnd_mod_ops rsnd_ssi_pio_ops = {
.name = "ssi (pio)",
.name = SSI_NAME,
.probe = rsnd_ssi_pio_probe,
.init = rsnd_ssi_init,
.quit = rsnd_ssi_quit,
@ -430,6 +434,8 @@ static int rsnd_ssi_dma_probe(struct rsnd_mod *mod,
if (ret < 0)
dev_err(dev, "SSI DMA failed\n");
dev_dbg(dev, "%s (DMA) is probed\n", rsnd_mod_name(mod));
return ret;
}
@ -480,7 +486,7 @@ static int rsnd_ssi_dma_stop(struct rsnd_mod *mod,
}
static struct rsnd_mod_ops rsnd_ssi_dma_ops = {
.name = "ssi (dma)",
.name = SSI_NAME,
.probe = rsnd_ssi_dma_probe,
.remove = rsnd_ssi_dma_remove,
.init = rsnd_ssi_init,
@ -493,7 +499,7 @@ static struct rsnd_mod_ops rsnd_ssi_dma_ops = {
* Non SSI
*/
static struct rsnd_mod_ops rsnd_ssi_non_ops = {
.name = "ssi (non)",
.name = SSI_NAME,
};
/*
@ -554,14 +560,14 @@ static void rsnd_of_parse_ssi(struct platform_device *pdev,
nr = of_get_child_count(node);
if (!nr)
return;
goto rsnd_of_parse_ssi_end;
ssi_info = devm_kzalloc(dev,
sizeof(struct rsnd_ssi_platform_info) * nr,
GFP_KERNEL);
if (!ssi_info) {
dev_err(dev, "ssi info allocation error\n");
return;
goto rsnd_of_parse_ssi_end;
}
info->ssi_info = ssi_info;
@ -583,7 +589,16 @@ static void rsnd_of_parse_ssi(struct platform_device *pdev,
* irq
*/
ssi_info->pio_irq = irq_of_parse_and_map(np, 0);
/*
* DMA
*/
ssi_info->dma_id = of_get_property(np, "pio-transfer", NULL) ?
0 : 1;
}
rsnd_of_parse_ssi_end:
of_node_put(node);
}
int rsnd_ssi_probe(struct platform_device *pdev,
@ -617,7 +632,8 @@ int rsnd_ssi_probe(struct platform_device *pdev,
for_each_rsnd_ssi(ssi, priv, i) {
pinfo = &info->ssi_info[i];
snprintf(name, RSND_SSI_NAME_SIZE, "ssi.%d", i);
snprintf(name, RSND_SSI_NAME_SIZE, "%s.%d",
SSI_NAME, i);
clk = devm_clk_get(dev, name);
if (IS_ERR(clk))

View File

@ -72,6 +72,9 @@ int snd_soc_cache_init(struct snd_soc_codec *codec)
reg_size = codec_drv->reg_cache_size * codec_drv->reg_word_size;
if (!reg_size)
return 0;
mutex_init(&codec->cache_rw_mutex);
dev_dbg(codec->dev, "ASoC: Initializing cache for %s codec\n",

View File

@ -14,6 +14,7 @@
#include <sound/jack.h>
#include <sound/soc.h>
#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/interrupt.h>
#include <linux/workqueue.h>
#include <linux/delay.h>
@ -240,7 +241,7 @@ static void snd_soc_jack_gpio_detect(struct snd_soc_jack_gpio *gpio)
int enable;
int report;
enable = gpio_get_value_cansleep(gpio->gpio);
enable = gpiod_get_value_cansleep(gpio->desc);
if (gpio->invert)
enable = !enable;
@ -297,31 +298,50 @@ int snd_soc_jack_add_gpios(struct snd_soc_jack *jack, int count,
int i, ret;
for (i = 0; i < count; i++) {
if (!gpio_is_valid(gpios[i].gpio)) {
dev_err(jack->codec->dev, "ASoC: Invalid gpio %d\n",
gpios[i].gpio);
ret = -EINVAL;
goto undo;
}
if (!gpios[i].name) {
dev_err(jack->codec->dev, "ASoC: No name for gpio %d\n",
gpios[i].gpio);
dev_err(jack->codec->dev,
"ASoC: No name for gpio at index %d\n", i);
ret = -EINVAL;
goto undo;
}
ret = gpio_request(gpios[i].gpio, gpios[i].name);
if (ret)
goto undo;
if (gpios[i].gpiod_dev) {
/* GPIO descriptor */
gpios[i].desc = gpiod_get_index(gpios[i].gpiod_dev,
gpios[i].name,
gpios[i].idx);
if (IS_ERR(gpios[i].desc)) {
ret = PTR_ERR(gpios[i].desc);
dev_err(gpios[i].gpiod_dev,
"ASoC: Cannot get gpio at index %d: %d",
i, ret);
goto undo;
}
} else {
/* legacy GPIO number */
if (!gpio_is_valid(gpios[i].gpio)) {
dev_err(jack->codec->dev,
"ASoC: Invalid gpio %d\n",
gpios[i].gpio);
ret = -EINVAL;
goto undo;
}
ret = gpio_direction_input(gpios[i].gpio);
ret = gpio_request(gpios[i].gpio, gpios[i].name);
if (ret)
goto undo;
gpios[i].desc = gpio_to_desc(gpios[i].gpio);
}
ret = gpiod_direction_input(gpios[i].desc);
if (ret)
goto err;
INIT_DELAYED_WORK(&gpios[i].work, gpio_work);
gpios[i].jack = jack;
ret = request_any_context_irq(gpio_to_irq(gpios[i].gpio),
ret = request_any_context_irq(gpiod_to_irq(gpios[i].desc),
gpio_handler,
IRQF_TRIGGER_RISING |
IRQF_TRIGGER_FALLING,
@ -331,15 +351,15 @@ int snd_soc_jack_add_gpios(struct snd_soc_jack *jack, int count,
goto err;
if (gpios[i].wake) {
ret = irq_set_irq_wake(gpio_to_irq(gpios[i].gpio), 1);
ret = irq_set_irq_wake(gpiod_to_irq(gpios[i].desc), 1);
if (ret != 0)
dev_err(jack->codec->dev, "ASoC: "
"Failed to mark GPIO %d as wake source: %d\n",
gpios[i].gpio, ret);
dev_err(jack->codec->dev,
"ASoC: Failed to mark GPIO at index %d as wake source: %d\n",
i, ret);
}
/* Expose GPIO value over sysfs for diagnostic purposes */
gpio_export(gpios[i].gpio, false);
gpiod_export(gpios[i].desc, false);
/* Update initial jack status */
schedule_delayed_work(&gpios[i].work,
@ -357,6 +377,30 @@ undo:
}
EXPORT_SYMBOL_GPL(snd_soc_jack_add_gpios);
/**
* snd_soc_jack_add_gpiods - Associate GPIO descriptor pins with an ASoC jack
*
* @gpiod_dev: GPIO consumer device
* @jack: ASoC jack
* @count: number of pins
* @gpios: array of gpio pins
*
* This function will request gpio, set data direction and request irq
* for each gpio in the array.
*/
int snd_soc_jack_add_gpiods(struct device *gpiod_dev,
struct snd_soc_jack *jack,
int count, struct snd_soc_jack_gpio *gpios)
{
int i;
for (i = 0; i < count; i++)
gpios[i].gpiod_dev = gpiod_dev;
return snd_soc_jack_add_gpios(jack, count, gpios);
}
EXPORT_SYMBOL_GPL(snd_soc_jack_add_gpiods);
/**
* snd_soc_jack_free_gpios - Release GPIO pins' resources of an ASoC jack
*
@ -372,10 +416,10 @@ void snd_soc_jack_free_gpios(struct snd_soc_jack *jack, int count,
int i;
for (i = 0; i < count; i++) {
gpio_unexport(gpios[i].gpio);
free_irq(gpio_to_irq(gpios[i].gpio), &gpios[i]);
gpiod_unexport(gpios[i].desc);
free_irq(gpiod_to_irq(gpios[i].desc), &gpios[i]);
cancel_delayed_work_sync(&gpios[i].work);
gpio_free(gpios[i].gpio);
gpiod_put(gpios[i].desc);
gpios[i].jack = NULL;
}
}

View File

@ -125,6 +125,18 @@ static int tegra_alc5632_asoc_init(struct snd_soc_pcm_runtime *rtd)
return 0;
}
static int tegra_alc5632_card_remove(struct snd_soc_card *card)
{
struct tegra_alc5632 *machine = snd_soc_card_get_drvdata(card);
if (gpio_is_valid(machine->gpio_hp_det)) {
snd_soc_jack_free_gpios(&tegra_alc5632_hs_jack, 1,
&tegra_alc5632_hp_jack_gpio);
}
return 0;
}
static struct snd_soc_dai_link tegra_alc5632_dai = {
.name = "ALC5632",
.stream_name = "ALC5632 PCM",
@ -139,6 +151,7 @@ static struct snd_soc_dai_link tegra_alc5632_dai = {
static struct snd_soc_card snd_soc_tegra_alc5632 = {
.name = "tegra-alc5632",
.owner = THIS_MODULE,
.remove = tegra_alc5632_card_remove,
.dai_link = &tegra_alc5632_dai,
.num_links = 1,
.controls = tegra_alc5632_controls,
@ -223,9 +236,6 @@ static int tegra_alc5632_remove(struct platform_device *pdev)
struct snd_soc_card *card = platform_get_drvdata(pdev);
struct tegra_alc5632 *machine = snd_soc_card_get_drvdata(card);
snd_soc_jack_free_gpios(&tegra_alc5632_hs_jack, 1,
&tegra_alc5632_hp_jack_gpio);
snd_soc_unregister_card(card);
tegra_asoc_utils_fini(&machine->util_data);

View File

@ -145,6 +145,18 @@ static int tegra_max98090_asoc_init(struct snd_soc_pcm_runtime *rtd)
return 0;
}
static int tegra_max98090_card_remove(struct snd_soc_card *card)
{
struct tegra_max98090 *machine = snd_soc_card_get_drvdata(card);
if (gpio_is_valid(machine->gpio_hp_det)) {
snd_soc_jack_free_gpios(&tegra_max98090_hp_jack, 1,
&tegra_max98090_hp_jack_gpio);
}
return 0;
}
static struct snd_soc_dai_link tegra_max98090_dai = {
.name = "max98090",
.stream_name = "max98090 PCM",
@ -158,6 +170,7 @@ static struct snd_soc_dai_link tegra_max98090_dai = {
static struct snd_soc_card snd_soc_tegra_max98090 = {
.name = "tegra-max98090",
.owner = THIS_MODULE,
.remove = tegra_max98090_card_remove,
.dai_link = &tegra_max98090_dai,
.num_links = 1,
.controls = tegra_max98090_controls,
@ -241,9 +254,6 @@ static int tegra_max98090_remove(struct platform_device *pdev)
struct snd_soc_card *card = platform_get_drvdata(pdev);
struct tegra_max98090 *machine = snd_soc_card_get_drvdata(card);
snd_soc_jack_free_gpios(&tegra_max98090_hp_jack, 1,
&tegra_max98090_hp_jack_gpio);
snd_soc_unregister_card(card);
tegra_asoc_utils_fini(&machine->util_data);

View File

@ -128,6 +128,18 @@ static int tegra_rt5640_asoc_init(struct snd_soc_pcm_runtime *rtd)
return 0;
}
static int tegra_rt5640_card_remove(struct snd_soc_card *card)
{
struct tegra_rt5640 *machine = snd_soc_card_get_drvdata(card);
if (gpio_is_valid(machine->gpio_hp_det)) {
snd_soc_jack_free_gpios(&tegra_rt5640_hp_jack, 1,
&tegra_rt5640_hp_jack_gpio);
}
return 0;
}
static struct snd_soc_dai_link tegra_rt5640_dai = {
.name = "RT5640",
.stream_name = "RT5640 PCM",
@ -141,6 +153,7 @@ static struct snd_soc_dai_link tegra_rt5640_dai = {
static struct snd_soc_card snd_soc_tegra_rt5640 = {
.name = "tegra-rt5640",
.owner = THIS_MODULE,
.remove = tegra_rt5640_card_remove,
.dai_link = &tegra_rt5640_dai,
.num_links = 1,
.controls = tegra_rt5640_controls,
@ -224,9 +237,6 @@ static int tegra_rt5640_remove(struct platform_device *pdev)
struct snd_soc_card *card = platform_get_drvdata(pdev);
struct tegra_rt5640 *machine = snd_soc_card_get_drvdata(card);
snd_soc_jack_free_gpios(&tegra_rt5640_hp_jack, 1,
&tegra_rt5640_hp_jack_gpio);
snd_soc_unregister_card(card);
tegra_asoc_utils_fini(&machine->util_data);

View File

@ -206,6 +206,12 @@ static int tegra_wm8903_remove(struct snd_soc_card *card)
struct snd_soc_pcm_runtime *rtd = &(card->rtd[0]);
struct snd_soc_dai *codec_dai = rtd->codec_dai;
struct snd_soc_codec *codec = codec_dai->codec;
struct tegra_wm8903 *machine = snd_soc_card_get_drvdata(card);
if (gpio_is_valid(machine->gpio_hp_det)) {
snd_soc_jack_free_gpios(&tegra_wm8903_hp_jack, 1,
&tegra_wm8903_hp_jack_gpio);
}
wm8903_mic_detect(codec, NULL, 0, 0);
@ -228,9 +234,7 @@ static struct snd_soc_card snd_soc_tegra_wm8903 = {
.owner = THIS_MODULE,
.dai_link = &tegra_wm8903_dai,
.num_links = 1,
.remove = tegra_wm8903_remove,
.controls = tegra_wm8903_controls,
.num_controls = ARRAY_SIZE(tegra_wm8903_controls),
.dapm_widgets = tegra_wm8903_dapm_widgets,
@ -368,9 +372,6 @@ static int tegra_wm8903_driver_remove(struct platform_device *pdev)
struct snd_soc_card *card = platform_get_drvdata(pdev);
struct tegra_wm8903 *machine = snd_soc_card_get_drvdata(card);
snd_soc_jack_free_gpios(&tegra_wm8903_hp_jack, 1,
&tegra_wm8903_hp_jack_gpio);
snd_soc_unregister_card(card);
tegra_asoc_utils_fini(&machine->util_data);