mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-08 03:40:35 +09:00
audio: add pcm186x, ssm3515, ssm3525 codec driver
PD#145486: audio: add codec driver Change-Id: Ief27ff9892ffabba81f676f4bed2ec6b4ead74f5 Signed-off-by: Peipeng Zhao <peipeng.zhao@amlogic.com>
This commit is contained in:
11
MAINTAINERS
11
MAINTAINERS
@@ -13929,4 +13929,13 @@ AMLOGIC Security Support
|
||||
M: Peifu Jiang <peifu.jiang@amlogic.com>
|
||||
F: include/linux/amlogic/meson-secure.h
|
||||
F: arch/arm/mach-meson/meson-smc.S
|
||||
F: arch/arm/mach-meson/meson-secure.c
|
||||
F: arch/arm/mach-meson/meson-secure.c
|
||||
|
||||
AMLOGIC Audio codec driver
|
||||
M: Peipeng Zhao <peipeng.zhao@amlogic.com>
|
||||
F: sound/soc/codecs/amlogic/pcm186x-i2c.c
|
||||
F: sound/soc/codecs/amlogic/pcm186x-spi.c
|
||||
F: sound/soc/codecs/amlogic/pcm186x.c
|
||||
F: sound/soc/codecs/amlogic/pcm186x.h
|
||||
F: sound/soc/codecs/amlogic/ssm3515.c
|
||||
F: sound/soc/codecs/amlogic/ssm3525.c
|
||||
|
||||
@@ -376,6 +376,9 @@ CONFIG_AMLOGIC_SND_CODEC_PDM_DUMMY_CODEC=y
|
||||
CONFIG_AMLOGIC_SND_CODEC_AMLT9015=y
|
||||
CONFIG_AMLOGIC_SND_SOC_TAS5707=y
|
||||
CONFIG_AMLOGIC_SND_SOC_TLV320ADC3101=y
|
||||
CONFIG_AMLOGIC_SND_SOC_PCM186X=y
|
||||
CONFIG_AMLOGIC_SND_SOC_SSM3515=y
|
||||
CONFIG_AMLOGIC_SND_SOC_SSM3525=y
|
||||
CONFIG_AMLOGIC_SND_SOC=y
|
||||
CONFIG_AMLOGIC_SND_SOC_MESON=y
|
||||
CONFIG_AMLOGIC_SND_SOC_AUGE=y
|
||||
|
||||
@@ -304,6 +304,7 @@ static const unsigned int i2sout_ch45_pins[] = { PIN(GPIOZ_6, EE_OFF) };
|
||||
static const unsigned int dvp_d4_pins[] = { PIN(GPIOZ_6, EE_OFF) };
|
||||
|
||||
|
||||
static const unsigned int i2sout_ch67_pins[] = { PIN(GPIOZ_7, EE_OFF) };
|
||||
static const unsigned int eth_rxd3_pins[] = { PIN(GPIOZ_7, EE_OFF) };
|
||||
static const unsigned int eth_rgmii_tx_clk_pins[] = { PIN(GPIOZ_8, EE_OFF) };
|
||||
static const unsigned int eth_tx_en_pins[] = { PIN(GPIOZ_9, EE_OFF) };
|
||||
@@ -551,6 +552,7 @@ static struct meson_pmx_group meson_gxl_periphs_groups[] = {
|
||||
GROUP(i2sout_ch45, 3, 25), /*z6*/
|
||||
GROUP(dvp_d4, 3, 11), /*z6*/
|
||||
|
||||
GROUP(i2sout_ch67, 3, 24), /*z7*/
|
||||
|
||||
GROUP(spi_sclk_0, 4, 4),/*z11*/
|
||||
GROUP(spi_miso_0, 4, 3),/*z12*/
|
||||
@@ -859,10 +861,15 @@ static const char * const i2c_d_groups[] = {
|
||||
};
|
||||
|
||||
static const char * const i2s_groups[] = {
|
||||
"i2s_am_clk", "i2s_ao_clk_out", "i2s_lr_clk_out", "i2sout_ch01",
|
||||
"i2s_am_clk", "i2s_ao_clk_out", "i2s_lr_clk_out",
|
||||
"i2sout_ch01", "i2sout_ch23_z5", "i2sout_ch45", "i2sout_ch67",
|
||||
"i2sin_ch23", "i2sin_ch45", "i2sin_ch67",
|
||||
};
|
||||
|
||||
static const char * const pdm_groups[] = {
|
||||
"pdm_in", "pdm_clk",
|
||||
};
|
||||
|
||||
static const char * const sdio_groups[] = {
|
||||
"sdio_d0", "sdio_d1", "sdio_d2", "sdio_d3", "sdio_clk", "sdio_cmd",
|
||||
};
|
||||
@@ -886,6 +893,7 @@ static struct meson_pmx_func meson_gxl_periphs_functions[] = {
|
||||
FUNCTION(spdif_in),
|
||||
FUNCTION(spdif_in_1),
|
||||
FUNCTION(i2s),
|
||||
FUNCTION(pdm),
|
||||
FUNCTION(spi),
|
||||
FUNCTION(i2c_a),
|
||||
FUNCTION(i2c_b),
|
||||
|
||||
@@ -574,16 +574,32 @@ static int aml_card_dai_parse_of(struct device *dev,
|
||||
if (ret < 0)
|
||||
goto parse_error;
|
||||
|
||||
/* get codec dai->name */
|
||||
ret = snd_soc_of_get_dai_name(codec_node, &dai_link->codec_dai_name);
|
||||
if (ret < 0)
|
||||
goto parse_error;
|
||||
|
||||
dai_link->name = dai_link->stream_name = dai_link->cpu_dai_name;
|
||||
dai_link->codec_of_node = of_parse_phandle(codec_node, "sound-dai", 0);
|
||||
dai_link->platform_of_node = plat_node;
|
||||
dai_link->init = init;
|
||||
ret = of_count_phandle_with_args(codec_node, "sound-dai",
|
||||
"#sound-dai-cells");
|
||||
pr_info("%s codec_node count:%d\n", __func__, ret);
|
||||
if (ret <= 1) {
|
||||
ret = snd_soc_of_get_dai_name(codec_node,
|
||||
&dai_link->codec_dai_name);
|
||||
if (ret < 0)
|
||||
goto parse_error;
|
||||
|
||||
dai_link->name = dai_link->stream_name =
|
||||
dai_link->cpu_dai_name;
|
||||
dai_link->codec_of_node =
|
||||
of_parse_phandle(codec_node, "sound-dai", 0);
|
||||
dai_link->platform_of_node = plat_node;
|
||||
dai_link->init = init;
|
||||
} else {
|
||||
ret = snd_soc_of_get_dai_link_codecs(dev, codec_node, dai_link);
|
||||
if (ret < 0) {
|
||||
pr_err("failed get_dai_link_codecs from codec_node\n");
|
||||
goto parse_error;
|
||||
}
|
||||
dai_link->init = init;
|
||||
dai_link->platform_of_node = plat_node;
|
||||
/*"multicodec";*/
|
||||
dai_link->name = dai_link->stream_name = dai_link->cpu_dai_name;
|
||||
}
|
||||
return 0;
|
||||
|
||||
parse_error:
|
||||
|
||||
@@ -78,5 +78,40 @@ config AMLOGIC_SND_SOC_TLV320ADC3101
|
||||
help
|
||||
Enable Support for Texas INstruments TLV320ADC3101 CODEC.
|
||||
Select this if your TLV320ADC3101 is connected via an I2C bus.
|
||||
Enable Support for Texas INstruments TLV320ADC3101 CODEC.
|
||||
Select this if your TLV320ADC3101 is connected via an I2C bus.
|
||||
|
||||
config AMLOGIC_SND_SOC_PCM186X
|
||||
bool "Texas Instruments PCM186X "
|
||||
depends on AMLOGIC_SND_SOC_CODECS
|
||||
depends on I2C
|
||||
default n
|
||||
help
|
||||
Enable support for Texas Instruments PCM186X CODEC.
|
||||
Select this if your PCM186X is connected via an I2C bus.
|
||||
Enable support for Texas Instruments PCM186X CODEC.
|
||||
Select this if your PCM186X is connected via an I2C bus.
|
||||
|
||||
config AMLOGIC_SND_SOC_SSM3525
|
||||
bool "Analog Devices SSM3525 "
|
||||
depends on AMLOGIC_SND_SOC_CODECS
|
||||
depends on I2C
|
||||
default n
|
||||
help
|
||||
Enable support for SSM3525 CODEC.
|
||||
Select this if your SSM3525 is connected via an I2C bus.
|
||||
Enable support for SSM3525 CODEC.
|
||||
Select this if your SSM3525 is connected via an I2C bus.
|
||||
|
||||
config AMLOGIC_SND_SOC_SSM3515
|
||||
bool "Analog Devices SSM3515 "
|
||||
depends on AMLOGIC_SND_SOC_CODECS
|
||||
depends on I2C
|
||||
default n
|
||||
help
|
||||
Enable support for SSM3515 CODEC.
|
||||
Select this if SSM3515 is connected via an I2C bus.
|
||||
Enable support for SSM3515 CODEC.
|
||||
Select this if SSM3515 is connected via an I2C bus.
|
||||
|
||||
#endif #AMLOGIC_SND_SOC_CODECS
|
||||
|
||||
@@ -8,6 +8,9 @@ snd-soc-pmu3-objs := aml_pmu3.o
|
||||
#Third part codecs
|
||||
snd-soc-tas5707-objs := tas5707.o
|
||||
snd-soc-tlv320adc3101-objs := tlv320adc3101.o
|
||||
snd-soc-pcm186x-objs := pcm186x.o pcm186x-i2c.o pcm186x-spi.o
|
||||
snd-soc-ssm3515-objs := ssm3515.o
|
||||
snd-soc-ssm3525-objs := ssm3525.o
|
||||
|
||||
# Amlogic
|
||||
obj-$(CONFIG_AMLOGIC_SND_CODEC_DUMMY_CODEC) += snd-soc-dummy_codec.o
|
||||
@@ -18,4 +21,7 @@ obj-$(CONFIG_AMLOGIC_SND_CODEC_PMU3) += snd-soc-pmu3.o
|
||||
|
||||
#Third part codecs
|
||||
obj-$(CONFIG_AMLOGIC_SND_SOC_TAS5707) += snd-soc-tas5707.o
|
||||
obj-$(CONFIG_AMLOGIC_SND_SOC_TLV320ADC3101) += snd-soc-tlv320adc3101.o
|
||||
obj-$(CONFIG_AMLOGIC_SND_SOC_TLV320ADC3101) += snd-soc-tlv320adc3101.o
|
||||
obj-$(CONFIG_AMLOGIC_SND_SOC_PCM186X) += snd-soc-pcm186x.o
|
||||
obj-$(CONFIG_AMLOGIC_SND_SOC_SSM3515) += snd-soc-ssm3515.o
|
||||
obj-$(CONFIG_AMLOGIC_SND_SOC_SSM3525) += snd-soc-ssm3525.o
|
||||
|
||||
80
sound/soc/codecs/amlogic/pcm186x-i2c.c
Normal file
80
sound/soc/codecs/amlogic/pcm186x-i2c.c
Normal file
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* pcm186x.c - Texas Instruments PCM186x Universal Audio ADC - I2C
|
||||
*
|
||||
* Copyright (C) 2015-2016 Texas Instruments Incorporated - http://www.ti.com
|
||||
*
|
||||
* Author: Andreas Dannenberg <dannenberg@ti.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/i2c.h>
|
||||
|
||||
#include "pcm186x.h"
|
||||
|
||||
static int pcm186x_i2c_probe(struct i2c_client *i2c,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
const enum pcm186x_type type = (enum pcm186x_type)id->driver_data;
|
||||
int irq = i2c->irq;
|
||||
struct regmap *regmap;
|
||||
|
||||
dev_info(&i2c->dev, "%s() i2c->addr=%d\n", __func__, i2c->addr);
|
||||
|
||||
regmap = devm_regmap_init_i2c(i2c, &pcm186x_regmap);
|
||||
if (IS_ERR(regmap))
|
||||
return PTR_ERR(regmap);
|
||||
|
||||
return pcm186x_probe(&i2c->dev, type, irq, regmap);
|
||||
}
|
||||
|
||||
static int pcm186x_i2c_remove(struct i2c_client *i2c)
|
||||
{
|
||||
pcm186x_remove(&i2c->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id pcm186x_i2c_id[] = {
|
||||
{ " pcm1862", PCM1862 },
|
||||
{ " pcm1863", PCM1863 },
|
||||
{ " pcm1864", PCM1864 },
|
||||
{ " pcm1865", PCM1865 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, pcm186x_i2c_id);
|
||||
|
||||
static const struct of_device_id pcm186x_of_match[] = {
|
||||
{ .compatible = "ti, pcm1862", },
|
||||
{ .compatible = "ti, pcm1863", },
|
||||
{ .compatible = "ti, pcm1864", },
|
||||
{ .compatible = "ti, pcm1865", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, pcm186x_of_match);
|
||||
|
||||
static struct i2c_driver pcm186x_i2c_driver = {
|
||||
.probe = pcm186x_i2c_probe,
|
||||
.remove = pcm186x_i2c_remove,
|
||||
.id_table = pcm186x_i2c_id,
|
||||
.driver = {
|
||||
.name = "pcm186x",
|
||||
.of_match_table = pcm186x_of_match,
|
||||
.pm = &pcm186x_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
module_i2c_driver(pcm186x_i2c_driver);
|
||||
|
||||
MODULE_DESCRIPTION("PCM186x Universal Audio ADC driver - I2C");
|
||||
MODULE_AUTHOR("Andreas Dannenberg <dannenberg@ti.com>");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
79
sound/soc/codecs/amlogic/pcm186x-spi.c
Normal file
79
sound/soc/codecs/amlogic/pcm186x-spi.c
Normal file
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* pcm186x.c - Texas Instruments PCM186x Universal Audio ADC - SPI
|
||||
*
|
||||
* Copyright (C) 2015-2016 Texas Instruments Incorporated - http://www.ti.com
|
||||
*
|
||||
* Author: Andreas Dannenberg <dannenberg@ti.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
#include "pcm186x.h"
|
||||
|
||||
static int pcm186x_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
const enum pcm186x_type type =
|
||||
(enum pcm186x_type)spi_get_device_id(spi)->driver_data;
|
||||
int irq = spi->irq;
|
||||
struct regmap *regmap;
|
||||
|
||||
dev_info(&spi->dev, "%s()\n", __func__);
|
||||
|
||||
regmap = devm_regmap_init_spi(spi, &pcm186x_regmap);
|
||||
if (IS_ERR(regmap))
|
||||
return PTR_ERR(regmap);
|
||||
|
||||
return pcm186x_probe(&spi->dev, type, irq, regmap);
|
||||
}
|
||||
|
||||
static int pcm186x_spi_remove(struct spi_device *spi)
|
||||
{
|
||||
pcm186x_remove(&spi->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_device_id pcm186x_spi_id[] = {
|
||||
{ " pcm1862", PCM1862 },
|
||||
{ " pcm1863", PCM1863 },
|
||||
{ " pcm1864", PCM1864 },
|
||||
{ " pcm1865", PCM1865 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, pcm186x_spi_id);
|
||||
|
||||
static const struct of_device_id pcm186x_of_match[] = {
|
||||
{ .compatible = "ti, pcm1862", },
|
||||
{ .compatible = "ti, pcm1863", },
|
||||
{ .compatible = "ti, pcm1864", },
|
||||
{ .compatible = "ti, pcm1865", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, pcm186x_of_match);
|
||||
|
||||
static struct spi_driver pcm186x_spi_driver = {
|
||||
.probe = pcm186x_spi_probe,
|
||||
.remove = pcm186x_spi_remove,
|
||||
.id_table = pcm186x_spi_id,
|
||||
.driver = {
|
||||
.name = "pcm186x",
|
||||
.of_match_table = pcm186x_of_match,
|
||||
.pm = &pcm186x_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
module_spi_driver(pcm186x_spi_driver);
|
||||
|
||||
MODULE_DESCRIPTION("PCM186x Universal Audio ADC driver - SPI");
|
||||
MODULE_AUTHOR("Andreas Dannenberg <dannenberg@ti.com>");
|
||||
MODULE_LICENSE("GPL");
|
||||
1358
sound/soc/codecs/amlogic/pcm186x.c
Normal file
1358
sound/soc/codecs/amlogic/pcm186x.c
Normal file
File diff suppressed because it is too large
Load Diff
265
sound/soc/codecs/amlogic/pcm186x.h
Normal file
265
sound/soc/codecs/amlogic/pcm186x.h
Normal file
@@ -0,0 +1,265 @@
|
||||
/*
|
||||
* pcm186x.h - Texas Instruments PCM186x Universal Audio ADC
|
||||
*
|
||||
* Copyright (C) 2015-2016 Texas Instruments Incorporated - http://www.ti.com
|
||||
*
|
||||
* Author: Andreas Dannenberg <dannenberg@ti.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef _PCM186X_H_
|
||||
#define _PCM186X_H_
|
||||
|
||||
#include <linux/pm.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
enum pcm186x_type {
|
||||
PCM1862,
|
||||
PCM1863,
|
||||
PCM1864,
|
||||
PCM1865,
|
||||
};
|
||||
|
||||
#define PCM186X_PAGE_LEN 0x0100
|
||||
#define PCM186X_PAGE_BASE(n) (PCM186X_PAGE_LEN * n)
|
||||
|
||||
/* The page selection register address is the same on all pages */
|
||||
#define PCM186X_PAGE 0
|
||||
|
||||
/* Register Definitions - Page 0 */
|
||||
#define PCM186X_PGA_VAL_CH1_L (PCM186X_PAGE_BASE(0) + 1)
|
||||
#define PCM186X_PGA_VAL_CH1_R (PCM186X_PAGE_BASE(0) + 2)
|
||||
#define PCM186X_PGA_VAL_CH2_L (PCM186X_PAGE_BASE(0) + 3)
|
||||
#define PCM186X_PGA_VAL_CH2_R (PCM186X_PAGE_BASE(0) + 4)
|
||||
#define PCM186X_PGA_CTRL (PCM186X_PAGE_BASE(0) + 5)
|
||||
#define PCM186X_ADC1_INPUT_SEL_L (PCM186X_PAGE_BASE(0) + 6)
|
||||
#define PCM186X_ADC1_INPUT_SEL_R (PCM186X_PAGE_BASE(0) + 7)
|
||||
#define PCM186X_ADC2_INPUT_SEL_L (PCM186X_PAGE_BASE(0) + 8)
|
||||
#define PCM186X_ADC2_INPUT_SEL_R (PCM186X_PAGE_BASE(0) + 9)
|
||||
#define PCM186X_AUXADC_INPUT_SEL (PCM186X_PAGE_BASE(0) + 10)
|
||||
#define PCM186X_PCM_CFG (PCM186X_PAGE_BASE(0) + 11)
|
||||
#define PCM186X_TDM_TX_SEL (PCM186X_PAGE_BASE(0) + 12)
|
||||
#define PCM186X_TDM_TX_OFFSET (PCM186X_PAGE_BASE(0) + 13)
|
||||
#define PCM186X_TDM_RX_OFFSET (PCM186X_PAGE_BASE(0) + 14)
|
||||
#define PCM186X_DPGA_VAL_CH1_L (PCM186X_PAGE_BASE(0) + 15)
|
||||
#define PCM186X_GPIO1_0_CTRL (PCM186X_PAGE_BASE(0) + 16)
|
||||
#define PCM186X_GPIO3_2_CTRL (PCM186X_PAGE_BASE(0) + 17)
|
||||
#define PCM186X_GPIO1_0_DIR_CTRL (PCM186X_PAGE_BASE(0) + 18)
|
||||
#define PCM186X_GPIO3_2_DIR_CTRL (PCM186X_PAGE_BASE(0) + 19)
|
||||
#define PCM186X_GPIO_IN_OUT (PCM186X_PAGE_BASE(0) + 20)
|
||||
#define PCM186X_GPIO_PULL_CTRL (PCM186X_PAGE_BASE(0) + 21)
|
||||
#define PCM186X_DPGA_VAL_CH1_R (PCM186X_PAGE_BASE(0) + 22)
|
||||
#define PCM186X_DPGA_VAL_CH2_L (PCM186X_PAGE_BASE(0) + 23)
|
||||
#define PCM186X_DPGA_VAL_CH2_R (PCM186X_PAGE_BASE(0) + 24)
|
||||
#define PCM186X_DPGA_GAIN_CTRL (PCM186X_PAGE_BASE(0) + 25)
|
||||
#define PCM186X_DPGA_MIC_CTRL (PCM186X_PAGE_BASE(0) + 26)
|
||||
#define PCM186X_DIN_RESAMP_CTRL (PCM186X_PAGE_BASE(0) + 27)
|
||||
#define PCM186X_CLK_CTRL (PCM186X_PAGE_BASE(0) + 32)
|
||||
#define PCM186X_DSP1_CLK_DIV (PCM186X_PAGE_BASE(0) + 33)
|
||||
#define PCM186X_DSP2_CLK_DIV (PCM186X_PAGE_BASE(0) + 34)
|
||||
#define PCM186X_ADC_CLK_DIV (PCM186X_PAGE_BASE(0) + 35)
|
||||
#define PCM186X_PLL_SCK_DIV (PCM186X_PAGE_BASE(0) + 37)
|
||||
#define PCM186X_BCK_DIV (PCM186X_PAGE_BASE(0) + 38)
|
||||
#define PCM186X_LRK_DIV (PCM186X_PAGE_BASE(0) + 39)
|
||||
#define PCM186X_PLL_CTRL (PCM186X_PAGE_BASE(0) + 40)
|
||||
#define PCM186X_PLL_P_DIV (PCM186X_PAGE_BASE(0) + 41)
|
||||
#define PCM186X_PLL_R_DIV (PCM186X_PAGE_BASE(0) + 42)
|
||||
#define PCM186X_PLL_J_DIV (PCM186X_PAGE_BASE(0) + 43)
|
||||
#define PCM186X_PLL_D_DIV_LSB (PCM186X_PAGE_BASE(0) + 44)
|
||||
#define PCM186X_PLL_D_DIV_MSB (PCM186X_PAGE_BASE(0) + 45)
|
||||
#define PCM186X_SIGDET_MODE (PCM186X_PAGE_BASE(0) + 48)
|
||||
#define PCM186X_SIGDET_MASK (PCM186X_PAGE_BASE(0) + 49)
|
||||
#define PCM186X_SIGDET_STAT (PCM186X_PAGE_BASE(0) + 50)
|
||||
#define PCM186X_SIGDET_LOSS_TIME (PCM186X_PAGE_BASE(0) + 52)
|
||||
#define PCM186X_SIGDET_SCAN_TIME (PCM186X_PAGE_BASE(0) + 53)
|
||||
#define PCM186X_SIGDET_INT_INTVL (PCM186X_PAGE_BASE(0) + 54)
|
||||
#define PCM186X_SIGDET_DC_REF_CH1_L (PCM186X_PAGE_BASE(0) + 64)
|
||||
#define PCM186X_SIGDET_DC_DIFF_CH1_L (PCM186X_PAGE_BASE(0) + 65)
|
||||
#define PCM186X_SIGDET_DC_LEV_CH1_L (PCM186X_PAGE_BASE(0) + 66)
|
||||
#define PCM186X_SIGDET_DC_REF_CH1_R (PCM186X_PAGE_BASE(0) + 67)
|
||||
#define PCM186X_SIGDET_DC_DIFF_CH1_R (PCM186X_PAGE_BASE(0) + 68)
|
||||
#define PCM186X_SIGDET_DC_LEV_CH1_R (PCM186X_PAGE_BASE(0) + 69)
|
||||
#define PCM186X_SIGDET_DC_REF_CH2_L (PCM186X_PAGE_BASE(0) + 70)
|
||||
#define PCM186X_SIGDET_DC_DIFF_CH2_L (PCM186X_PAGE_BASE(0) + 71)
|
||||
#define PCM186X_SIGDET_DC_LEV_CH2_L (PCM186X_PAGE_BASE(0) + 72)
|
||||
#define PCM186X_SIGDET_DC_REF_CH2_R (PCM186X_PAGE_BASE(0) + 73)
|
||||
#define PCM186X_SIGDET_DC_DIFF_CH2_R (PCM186X_PAGE_BASE(0) + 74)
|
||||
#define PCM186X_SIGDET_DC_LEV_CH2_R (PCM186X_PAGE_BASE(0) + 75)
|
||||
#define PCM186X_SIGDET_DC_REF_CH3_L (PCM186X_PAGE_BASE(0) + 76)
|
||||
#define PCM186X_SIGDET_DC_DIFF_CH3_L (PCM186X_PAGE_BASE(0) + 77)
|
||||
#define PCM186X_SIGDET_DC_LEV_CH3_L (PCM186X_PAGE_BASE(0) + 78)
|
||||
#define PCM186X_SIGDET_DC_REF_CH3_R (PCM186X_PAGE_BASE(0) + 79)
|
||||
#define PCM186X_SIGDET_DC_DIFF_CH3_R (PCM186X_PAGE_BASE(0) + 80)
|
||||
#define PCM186X_SIGDET_DC_LEV_CH3_R (PCM186X_PAGE_BASE(0) + 81)
|
||||
#define PCM186X_SIGDET_DC_REF_CH4_L (PCM186X_PAGE_BASE(0) + 82)
|
||||
#define PCM186X_SIGDET_DC_DIFF_CH4_L (PCM186X_PAGE_BASE(0) + 83)
|
||||
#define PCM186X_SIGDET_DC_LEV_CH4_L (PCM186X_PAGE_BASE(0) + 84)
|
||||
#define PCM186X_SIGDET_DC_REF_CH4_R (PCM186X_PAGE_BASE(0) + 85)
|
||||
#define PCM186X_SIGDET_DC_DIFF_CH4_R (PCM186X_PAGE_BASE(0) + 86)
|
||||
#define PCM186X_SIGDET_DC_LEV_CH4_R (PCM186X_PAGE_BASE(0) + 87)
|
||||
#define PCM186X_AUXADC_DATA_CTRL (PCM186X_PAGE_BASE(0) + 88)
|
||||
#define PCM186X_AUXADC_DATA_LSB (PCM186X_PAGE_BASE(0) + 89)
|
||||
#define PCM186X_AUXADC_DATA_MSB (PCM186X_PAGE_BASE(0) + 90)
|
||||
#define PCM186X_INT_ENABLE (PCM186X_PAGE_BASE(0) + 96)
|
||||
#define PCM186X_INT_FLAG (PCM186X_PAGE_BASE(0) + 97)
|
||||
#define PCM186X_INT_POL_WIDTH (PCM186X_PAGE_BASE(0) + 98)
|
||||
#define PCM186X_POWER_CTRL (PCM186X_PAGE_BASE(0) + 112)
|
||||
#define PCM186X_FILTER_MUTE_CTRL (PCM186X_PAGE_BASE(0) + 113)
|
||||
#define PCM186X_DEVICE_STATUS (PCM186X_PAGE_BASE(0) + 114)
|
||||
#define PCM186X_FSAMPLE_STATUS (PCM186X_PAGE_BASE(0) + 115)
|
||||
#define PCM186X_DIV_STATUS (PCM186X_PAGE_BASE(0) + 116)
|
||||
#define PCM186X_CLK_STATUS (PCM186X_PAGE_BASE(0) + 117)
|
||||
#define PCM186X_SUPPLY_STATUS (PCM186X_PAGE_BASE(0) + 120)
|
||||
|
||||
/* Register Definitions - Page 1 */
|
||||
#define PCM186X_MMAP_STAT_CTRL (PCM186X_PAGE_BASE(1) + 1)
|
||||
#define PCM186X_MMAP_ADDRESS (PCM186X_PAGE_BASE(1) + 2)
|
||||
#define PCM186X_MEM_WDATA0 (PCM186X_PAGE_BASE(1) + 4)
|
||||
#define PCM186X_MEM_WDATA1 (PCM186X_PAGE_BASE(1) + 5)
|
||||
#define PCM186X_MEM_WDATA2 (PCM186X_PAGE_BASE(1) + 6)
|
||||
#define PCM186X_MEM_WDATA3 (PCM186X_PAGE_BASE(1) + 7)
|
||||
#define PCM186X_MEM_RDATA0 (PCM186X_PAGE_BASE(1) + 8)
|
||||
#define PCM186X_MEM_RDATA1 (PCM186X_PAGE_BASE(1) + 9)
|
||||
#define PCM186X_MEM_RDATA2 (PCM186X_PAGE_BASE(1) + 10)
|
||||
#define PCM186X_MEM_RDATA3 (PCM186X_PAGE_BASE(1) + 11)
|
||||
|
||||
/* Register Definitions - Page 3 */
|
||||
#define PCM186X_OSC_PWR_DOWN_CTRL (PCM186X_PAGE_BASE(3) + 18)
|
||||
#define PCM186X_MIC_BIAS_CTRL (PCM186X_PAGE_BASE(3) + 21)
|
||||
|
||||
/* Register Definitions - Page 253 */
|
||||
#define PCM186X_CURR_TRIM_CTRL (PCM186X_PAGE_BASE(253) + 20)
|
||||
|
||||
#define PCM186X_MAX_REGISTER PCM186X_CURR_TRIM_CTRL
|
||||
|
||||
/*
|
||||
* Register definitions for DSP accessed indirectly through PCM186X_MEM_X using
|
||||
* a special sequence. Each of the registers holds 24 bits. See device data
|
||||
* sheet for details.
|
||||
*/
|
||||
#define PCM186X_DSP_MIX1_CH1L 0x00
|
||||
#define PCM186X_DSP_MIX1_CH1R 0x01
|
||||
#define PCM186X_DSP_MIX1_CH2L 0x02
|
||||
#define PCM186X_DSP_MIX1_CH2R 0x03
|
||||
#define PCM186X_DSP_MIX1_I2SL 0x04
|
||||
#define PCM186X_DSP_MIX1_I2SR 0x05
|
||||
#define PCM186X_DSP_MIX2_CH1L 0x06
|
||||
#define PCM186X_DSP_MIX2_CH1R 0x07
|
||||
#define PCM186X_DSP_MIX2_CH2L 0x08
|
||||
#define PCM186X_DSP_MIX2_CH2R 0x09
|
||||
#define PCM186X_DSP_MIX2_I2SL 0x0a
|
||||
#define PCM186X_DSP_MIX2_I2SR 0x0b
|
||||
#define PCM186X_DSP_MIX3_CH1L 0x0c
|
||||
#define PCM186X_DSP_MIX3_CH1R 0x0d
|
||||
#define PCM186X_DSP_MIX3_CH2L 0x0e
|
||||
#define PCM186X_DSP_MIX3_CH2R 0x0f
|
||||
#define PCM186X_DSP_MIX3_I2SL 0x10
|
||||
#define PCM186X_DSP_MIX3_I2SR 0x11
|
||||
#define PCM186X_DSP_MIX4_CH1L 0x12
|
||||
#define PCM186X_DSP_MIX4_CH1R 0x13
|
||||
#define PCM186X_DSP_MIX4_CH2L 0x14
|
||||
#define PCM186X_DSP_MIX4_CH2R 0x15
|
||||
#define PCM186X_DSP_MIX4_I2SL 0x16
|
||||
#define PCM186X_DSP_MIX4_I2SR 0x17
|
||||
#define PCM186X_DSP_LPF_B0 0x20
|
||||
#define PCM186X_DSP_LPF_B1 0x21
|
||||
#define PCM186X_DSP_LPF_B2 0x22
|
||||
#define PCM186X_DSP_LPF_A1 0x23
|
||||
#define PCM186X_DSP_LPF_A2 0x24
|
||||
#define PCM186X_DSP_HPF_B0 0x25
|
||||
#define PCM186X_DSP_HPF_B1 0x26
|
||||
#define PCM186X_DSP_HPF_B2 0x27
|
||||
#define PCM186X_DSP_HPF_A1 0x28
|
||||
#define PCM186X_DSP_HPF_A2 0x29
|
||||
#define PCM186X_DSP_LOSS_THRESH 0x2c
|
||||
#define PCM186X_DSP_RES_THRESH 0x2d
|
||||
|
||||
/* PCM186X_PAGE */
|
||||
#define PCM186X_PAGE_0 0x00
|
||||
#define PCM186X_PAGE_1 0x01
|
||||
#define PCM186X_PAGE_3 0x03
|
||||
#define PCM186X_RESET 0xff
|
||||
|
||||
/* PCM186X_ADCX_INPUT_SEL_X */
|
||||
#define PCM186X_ADC_INPUT_SEL_POL BIT(7)
|
||||
#define PCM186X_ADC_INPUT_SEL_MASK GENMASK(5, 0)
|
||||
|
||||
/* PCM186X_PCM_CFG */
|
||||
#define PCM186X_PCM_CFG_RX_WLEN_32 (0x0 << 6)
|
||||
#define PCM186X_PCM_CFG_RX_WLEN_24 (0x1 << 6)
|
||||
#define PCM186X_PCM_CFG_RX_WLEN_20 (0x2 << 6)
|
||||
#define PCM186X_PCM_CFG_RX_WLEN_16 (0x3 << 6)
|
||||
#define PCM186X_PCM_CFG_RX_WLEN_MASK (0x3 << 6)
|
||||
#define PCM186X_PCM_CFG_TDM_LRCK_MODE BIT(4)
|
||||
#define PCM186X_PCM_CFG_TX_WLEN_32 (0x0 << 2)
|
||||
#define PCM186X_PCM_CFG_TX_WLEN_24 (0x1 << 2)
|
||||
#define PCM186X_PCM_CFG_TX_WLEN_20 (0x2 << 2)
|
||||
#define PCM186X_PCM_CFG_TX_WLEN_16 (0x3 << 2)
|
||||
#define PCM186X_PCM_CFG_TX_WLEN_MASK (0x3 << 2)
|
||||
#define PCM186X_PCM_CFG_FMT_I2S 0x00
|
||||
#define PCM186X_PCM_CFG_FMT_LEFTJ 0x01
|
||||
#define PCM186X_PCM_CFG_FMT_RIGHTJ 0x02
|
||||
#define PCM186X_PCM_CFG_FMT_TDM 0x03
|
||||
#define PCM186X_PCM_CFG_FMT_MASK 0x03
|
||||
|
||||
/* PCM186X_TDM_TX_SEL */
|
||||
#define PCM186X_TDM_TX_SEL_2CH 0x00
|
||||
#define PCM186X_TDM_TX_SEL_4CH 0x01
|
||||
#define PCM186X_TDM_TX_SEL_6CH 0x02
|
||||
#define PCM186X_TDM_TX_SEL_MASK 0x03
|
||||
|
||||
/* PCM186X_CLK_CTRL */
|
||||
#define PCM186X_CLK_CTRL_SCK_XI_SEL1 BIT(7)
|
||||
#define PCM186X_CLK_CTRL_SCK_XI_SEL0 BIT(6)
|
||||
#define PCM186X_CLK_CTRL_SCK_SRC_PLL BIT(5)
|
||||
#define PCM186X_CLK_CTRL_MST_MODE BIT(4)
|
||||
#define PCM186X_CLK_CTRL_ADC_SRC_PLL BIT(3)
|
||||
#define PCM186X_CLK_CTRL_DSP2_SRC_PLL BIT(2)
|
||||
#define PCM186X_CLK_CTRL_DSP1_SRC_PLL BIT(1)
|
||||
#define PCM186X_CLK_CTRL_CLKDET_EN BIT(0)
|
||||
|
||||
/* PCM186X_PLL_CTRL */
|
||||
#define PCM186X_PLL_CTRL_LOCK BIT(4)
|
||||
#define PCM186X_PLL_CTRL_REF_SEL BIT(1)
|
||||
#define PCM186X_PLL_CTRL_EN BIT(0)
|
||||
|
||||
/* PCM186X_POWER_CTRL */
|
||||
#define PCM186X_PWR_CTRL_PWRDN BIT(2)
|
||||
#define PCM186X_PWR_CTRL_SLEEP BIT(1)
|
||||
#define PCM186X_PWR_CTRL_STBY BIT(0)
|
||||
|
||||
/* PCM186X_CLK_STATUS */
|
||||
#define PCM186X_CLK_STATUS_LRCKHLT BIT(6)
|
||||
#define PCM186X_CLK_STATUS_BCKHLT BIT(5)
|
||||
#define PCM186X_CLK_STATUS_SCKHLT BIT(4)
|
||||
#define PCM186X_CLK_STATUS_LRCKERR BIT(2)
|
||||
#define PCM186X_CLK_STATUS_BCKERR BIT(1)
|
||||
#define PCM186X_CLK_STATUS_SCKERR BIT(0)
|
||||
|
||||
/* PCM186X_SUPPLY_STATUS */
|
||||
#define PCM186X_SUPPLY_STATUS_DVDD BIT(2)
|
||||
#define PCM186X_SUPPLY_STATUS_AVDD BIT(1)
|
||||
#define PCM186X_SUPPLY_STATUS_LDO BIT(0)
|
||||
|
||||
/* PCM186X_MMAP_STAT_CTRL */
|
||||
#define PCM186X_MMAP_STAT_DONE BIT(4)
|
||||
#define PCM186X_MMAP_STAT_BUSY BIT(2)
|
||||
#define PCM186X_MMAP_STAT_R_REQ BIT(1)
|
||||
#define PCM186X_MMAP_STAT_W_REQ BIT(0)
|
||||
|
||||
extern const struct dev_pm_ops pcm186x_pm_ops;
|
||||
extern const struct regmap_config pcm186x_regmap;
|
||||
|
||||
int pcm186x_probe(struct device *dev, enum pcm186x_type type, int irq,
|
||||
struct regmap *regmap);
|
||||
int pcm186x_remove(struct device *dev);
|
||||
|
||||
#endif /* _PCM186X_H_ */
|
||||
652
sound/soc/codecs/amlogic/ssm3515.c
Normal file
652
sound/soc/codecs/amlogic/ssm3515.c
Normal file
@@ -0,0 +1,652 @@
|
||||
/*
|
||||
* SSM3515 amplifier audio driver
|
||||
*
|
||||
* Copyright 2014 Google Chromium project.
|
||||
* Author: Anatol Pomozov <anatol@chromium.org>
|
||||
*
|
||||
* Based on code copyright/by:
|
||||
* Copyright 2013 Analog Devices Inc.
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/initval.h>
|
||||
#include <sound/tlv.h>
|
||||
|
||||
#define SSM3515_REG_POWER_CTRL 0x00
|
||||
#define SSM3515_REG_AMP_SNS_CTRL 0x01
|
||||
#define SSM3515_REG_DAC_CTRL 0x02
|
||||
#define SSM3515_REG_DAC_VOLUME 0x03
|
||||
#define SSM3515_REG_SAI_CTRL_1 0x04
|
||||
#define SSM3515_REG_SAI_CTRL_2 0x05
|
||||
#define SSM3515_REG_BATTERY_V_OUT 0x06
|
||||
#define SSM3515_REG_LIMITER_CTRL_1 0x07
|
||||
#define SSM3515_REG_LIMITER_CTRL_2 0x08
|
||||
#define SSM3515_REG_LIMITER_CTRL_3 0x09
|
||||
#define SSM3515_REG_STATUS_1 0x0A
|
||||
#define SSM3515_REG_FAULT_CTRL 0x0B
|
||||
|
||||
/* POWER_CTRL */
|
||||
#define SSM3515_POWER_APWDN_EN BIT(7)
|
||||
#define SSM3515_POWER_BSNS_PWDN BIT(6)
|
||||
#define SSM3515_POWER_S_RESET BIT(1)
|
||||
#define SSM3515_POWER_SPWDN BIT(0)
|
||||
|
||||
/* DAC_CTRL */
|
||||
#define SSM3515_DAC_HV BIT(7)
|
||||
#define SSM3515_DAC_MUTE BIT(6)
|
||||
#define SSM3515_DAC_HPF BIT(5)
|
||||
#define SSM3515_DAC_LPM BIT(4)
|
||||
#define SSM3515_DAC_FS_MASK 0x7
|
||||
#define SSM3515_DAC_FS_8000_12000 0x0
|
||||
#define SSM3515_DAC_FS_16000_24000 0x1
|
||||
#define SSM3515_DAC_FS_32000_48000 0x2
|
||||
#define SSM3515_DAC_FS_64000_96000 0x3
|
||||
#define SSM3515_DAC_FS_128000_192000 0x4
|
||||
|
||||
/* SAI_CTRL_1 */
|
||||
#define SSM3515_SAI_CTRL_1_BCLK BIT(6)
|
||||
#define SSM3515_SAI_CTRL_1_TDM_BLCKS_MASK (0x3 << 3)
|
||||
#define SSM3515_SAI_CTRL_1_TDM_BLCKS_32 (0x0 << 3)
|
||||
#define SSM3515_SAI_CTRL_1_TDM_BLCKS_48 (0x1 << 3)
|
||||
#define SSM3515_SAI_CTRL_1_TDM_BLCKS_64 (0x2 << 3)
|
||||
#define SSM3515_SAI_CTRL_1_FSYNC BIT(2)
|
||||
#define SSM3515_SAI_CTRL_1_LJ BIT(1)
|
||||
#define SSM3515_SAI_CTRL_1_TDM BIT(0)
|
||||
|
||||
/* SAI_CTRL_2 */
|
||||
#define SSM3515_SAI_CTRL_2_AUTO_SLOT BIT(3)
|
||||
#define SSM3515_SAI_CTRL_2_TDM_SLOT_MASK 0x7
|
||||
#define SSM3515_SAI_CTRL_2_TDM_SLOT(x) (x)
|
||||
|
||||
static int device_num;
|
||||
struct ssm3515 *g_ssm3515;
|
||||
|
||||
struct ssm3515 {
|
||||
struct regmap *regmap;
|
||||
unsigned int slots_mask;
|
||||
const char *amp_cfg;
|
||||
int total_num;
|
||||
};
|
||||
|
||||
static const struct reg_default ssm3515_reg_defaults[] = {
|
||||
{ SSM3515_REG_POWER_CTRL, 0x81 },
|
||||
{ SSM3515_REG_AMP_SNS_CTRL, 0x03 },
|
||||
{ SSM3515_REG_DAC_CTRL, 0x22 },
|
||||
{ SSM3515_REG_DAC_VOLUME, 0x40 },
|
||||
{ SSM3515_REG_SAI_CTRL_1, 0x11 },
|
||||
};
|
||||
|
||||
|
||||
static bool ssm3515_readable_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case SSM3515_REG_POWER_CTRL ... SSM3515_REG_FAULT_CTRL:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static bool ssm3515_writeable_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case SSM3515_REG_POWER_CTRL ... SSM3515_REG_SAI_CTRL_2:
|
||||
case SSM3515_REG_LIMITER_CTRL_1 ... SSM3515_REG_LIMITER_CTRL_3:
|
||||
case SSM3515_REG_FAULT_CTRL:
|
||||
return true;
|
||||
/*The datasheet states that soft reset register is read-only,*/
|
||||
/*but logically it is write-only. */
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool ssm3515_volatile_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case SSM3515_REG_BATTERY_V_OUT:
|
||||
case SSM3515_REG_STATUS_1:
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static int WL2_amplifier_power_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
int value = 0;
|
||||
int ret;
|
||||
int i;
|
||||
struct ssm3515 *ssm3515 = NULL;
|
||||
|
||||
for (i = 0; i < device_num; i++) {
|
||||
if (!strcmp(g_ssm3515[i].amp_cfg, "WL2")) {
|
||||
ssm3515 = g_ssm3515+i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
ret = regmap_read(ssm3515->regmap, SSM3515_REG_POWER_CTRL, &value);
|
||||
if (ret)
|
||||
return ret;
|
||||
ucontrol->value.integer.value[0] = value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int WL2_amplifier_power_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
int value, ret, i;
|
||||
struct ssm3515 *ssm3515 = NULL;
|
||||
|
||||
for (i = 0; i < device_num; i++) {
|
||||
if (!strcmp(g_ssm3515[i].amp_cfg, "WL2")) {
|
||||
ssm3515 = g_ssm3515+i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
value = ucontrol->value.integer.value[0];
|
||||
ret = regmap_write(ssm3515->regmap, SSM3515_REG_POWER_CTRL, value);
|
||||
if (ret)
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int WR2_amplifier_power_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
int value = 0;
|
||||
int ret, i;
|
||||
struct ssm3515 *ssm3515 = NULL;
|
||||
|
||||
for (i = 0; i < device_num; i++) {
|
||||
if (!strcmp(g_ssm3515[i].amp_cfg, "WR2")) {
|
||||
ssm3515 = g_ssm3515+i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
ret = regmap_read(ssm3515->regmap, SSM3515_REG_POWER_CTRL, &value);
|
||||
if (ret)
|
||||
return ret;
|
||||
ucontrol->value.integer.value[0] = value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int WR2_amplifier_power_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
int value, ret, i;
|
||||
struct ssm3515 *ssm3515 = NULL;
|
||||
|
||||
for (i = 0; i < device_num; i++) {
|
||||
if (!strcmp(g_ssm3515[i].amp_cfg, "WR2")) {
|
||||
ssm3515 = g_ssm3515+i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
value = ucontrol->value.integer.value[0];
|
||||
ret = regmap_write(ssm3515->regmap, SSM3515_REG_POWER_CTRL, value);
|
||||
if (ret)
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int TL1_amplifier_power_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
int value = 0;
|
||||
int ret, i;
|
||||
struct ssm3515 *ssm3515 = NULL;
|
||||
|
||||
for (i = 0; i < device_num; i++) {
|
||||
if (!strcmp(g_ssm3515[i].amp_cfg, "TL1")) {
|
||||
ssm3515 = g_ssm3515+i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
ret = regmap_read(ssm3515->regmap, SSM3515_REG_POWER_CTRL, &value);
|
||||
if (ret)
|
||||
return ret;
|
||||
ucontrol->value.integer.value[0] = value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int TL1_amplifier_power_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
int value, ret, i;
|
||||
struct ssm3515 *ssm3515 = NULL;
|
||||
|
||||
for (i = 0; i < device_num; i++) {
|
||||
if (!strcmp(g_ssm3515[i].amp_cfg, "TL1")) {
|
||||
ssm3515 = g_ssm3515+i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
value = ucontrol->value.integer.value[0];
|
||||
ret = regmap_write(ssm3515->regmap, SSM3515_REG_POWER_CTRL, value);
|
||||
if (ret)
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int TR1_amplifier_power_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
int value = 0;
|
||||
int ret, i;
|
||||
struct ssm3515 *ssm3515 = NULL;
|
||||
|
||||
for (i = 0; i < device_num; i++) {
|
||||
if (!strcmp(g_ssm3515[i].amp_cfg, "TR1")) {
|
||||
ssm3515 = g_ssm3515+i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
ret = regmap_read(ssm3515->regmap, SSM3515_REG_POWER_CTRL, &value);
|
||||
if (ret)
|
||||
return ret;
|
||||
ucontrol->value.integer.value[0] = value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int TR1_amplifier_power_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
int value, ret, i;
|
||||
struct ssm3515 *ssm3515 = NULL;
|
||||
|
||||
for (i = 0; i < device_num; i++) {
|
||||
if (!strcmp(g_ssm3515[i].amp_cfg, "TR1")) {
|
||||
ssm3515 = g_ssm3515+i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
value = ucontrol->value.integer.value[0];
|
||||
ret = regmap_write(ssm3515->regmap, SSM3515_REG_POWER_CTRL, value);
|
||||
if (ret)
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_kcontrol_new ssm3515_snd_controls[] = {
|
||||
SOC_SINGLE_EXT("WR2 Amp Power Control", SND_SOC_NOPM, 0, 0xff, 0,
|
||||
WR2_amplifier_power_get, WR2_amplifier_power_put),
|
||||
SOC_SINGLE_EXT("WL2 Amp Power Control", SND_SOC_NOPM, 0, 0xff, 0,
|
||||
WL2_amplifier_power_get, WL2_amplifier_power_put),
|
||||
SOC_SINGLE_EXT("TL1 Amp Power Control", SND_SOC_NOPM, 0, 0xff, 0,
|
||||
TL1_amplifier_power_get, TL1_amplifier_power_put),
|
||||
SOC_SINGLE_EXT("TR1 Amp Power Control", SND_SOC_NOPM, 0, 0xff, 0,
|
||||
TR1_amplifier_power_get, TR1_amplifier_power_put),
|
||||
};
|
||||
|
||||
static int ssm3515_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 ssm3515 *ssm3515 = snd_soc_codec_get_drvdata(codec);
|
||||
unsigned int rate = params_rate(params);
|
||||
unsigned int dacfs;
|
||||
|
||||
if (rate >= 8000 && rate <= 12000)
|
||||
dacfs = SSM3515_DAC_FS_8000_12000;
|
||||
else if (rate >= 16000 && rate <= 24000)
|
||||
dacfs = SSM3515_DAC_FS_16000_24000;
|
||||
else if (rate >= 32000 && rate <= 48000)
|
||||
dacfs = SSM3515_DAC_FS_32000_48000;
|
||||
else if (rate >= 64000 && rate <= 96000)
|
||||
dacfs = SSM3515_DAC_FS_64000_96000;
|
||||
else if (rate >= 128000 && rate <= 192000)
|
||||
dacfs = SSM3515_DAC_FS_128000_192000;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
return regmap_update_bits(ssm3515->regmap, SSM3515_REG_DAC_CTRL,
|
||||
SSM3515_DAC_FS_MASK, dacfs);
|
||||
}
|
||||
|
||||
static int ssm3515_mute(struct snd_soc_dai *dai, int mute)
|
||||
{
|
||||
struct ssm3515 *ssm3515 = snd_soc_codec_get_drvdata(dai->codec);
|
||||
unsigned int val;
|
||||
|
||||
val = mute ? SSM3515_DAC_MUTE : 0;
|
||||
return regmap_update_bits(ssm3515->regmap, SSM3515_REG_DAC_CTRL,
|
||||
SSM3515_DAC_MUTE, val);
|
||||
}
|
||||
|
||||
static int ssm3515_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
|
||||
unsigned int rx_mask, int slots, int width)
|
||||
{
|
||||
struct ssm3515 *ssm3515 = snd_soc_dai_get_drvdata(dai);
|
||||
unsigned int blcks;
|
||||
int slot;
|
||||
int ret;
|
||||
|
||||
tx_mask = ssm3515->slots_mask;
|
||||
if (tx_mask == 0)
|
||||
return -EINVAL;
|
||||
|
||||
slot = __ffs(tx_mask);
|
||||
if (tx_mask != BIT(slot))
|
||||
return -EINVAL;
|
||||
|
||||
switch (width) {
|
||||
case 32:
|
||||
blcks = SSM3515_SAI_CTRL_1_TDM_BLCKS_32;
|
||||
break;
|
||||
case 48:
|
||||
blcks = SSM3515_SAI_CTRL_1_TDM_BLCKS_48;
|
||||
break;
|
||||
case 64:
|
||||
blcks = SSM3515_SAI_CTRL_1_TDM_BLCKS_64;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = regmap_update_bits(ssm3515->regmap, SSM3515_REG_SAI_CTRL_2,
|
||||
SSM3515_SAI_CTRL_2_AUTO_SLOT | SSM3515_SAI_CTRL_2_TDM_SLOT_MASK,
|
||||
SSM3515_SAI_CTRL_2_TDM_SLOT(slot));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return regmap_update_bits(ssm3515->regmap, SSM3515_REG_SAI_CTRL_1,
|
||||
SSM3515_SAI_CTRL_1_TDM_BLCKS_MASK, blcks);
|
||||
}
|
||||
|
||||
static int ssm3515_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
|
||||
{
|
||||
#if 0
|
||||
struct ssm3515 *ssm3515 = snd_soc_dai_get_drvdata(dai);
|
||||
unsigned int ctrl1 = 0;
|
||||
bool invert_fclk;
|
||||
|
||||
fmt = fmt | SND_SOC_DAIFMT_DSP_A |
|
||||
SND_SOC_DAIFMT_CBS_CFS |
|
||||
SND_SOC_DAIFMT_NB_NF |
|
||||
SND_SOC_DAIFMT_CONT;
|
||||
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
|
||||
case SND_SOC_DAIFMT_CBS_CFS:
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
|
||||
case SND_SOC_DAIFMT_NB_NF:
|
||||
invert_fclk = false;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_IB_NF:
|
||||
ctrl1 |= SSM3515_SAI_CTRL_1_BCLK;
|
||||
invert_fclk = false;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_NB_IF:
|
||||
ctrl1 |= SSM3515_SAI_CTRL_1_FSYNC;
|
||||
invert_fclk = true;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_IB_IF:
|
||||
ctrl1 |= SSM3515_SAI_CTRL_1_BCLK;
|
||||
invert_fclk = true;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
|
||||
case SND_SOC_DAIFMT_I2S:
|
||||
break;
|
||||
case SND_SOC_DAIFMT_LEFT_J:
|
||||
ctrl1 |= SSM3515_SAI_CTRL_1_LJ;
|
||||
invert_fclk = !invert_fclk;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_DSP_A:
|
||||
ctrl1 |= SSM3515_SAI_CTRL_1_TDM;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_DSP_B:
|
||||
ctrl1 |= SSM3515_SAI_CTRL_1_TDM | SSM3515_SAI_CTRL_1_LJ;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (invert_fclk)
|
||||
ctrl1 |= SSM3515_SAI_CTRL_1_FSYNC;
|
||||
|
||||
return regmap_update_bits(ssm3515->regmap, SSM3515_REG_SAI_CTRL_1,
|
||||
SSM3515_SAI_CTRL_1_BCLK |
|
||||
SSM3515_SAI_CTRL_1_FSYNC |
|
||||
SSM3515_SAI_CTRL_1_LJ |
|
||||
SSM3515_SAI_CTRL_1_TDM,
|
||||
ctrl1);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ssm3515_set_power(struct ssm3515 *ssm3515, bool enable)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (!enable) {
|
||||
ret = regmap_update_bits(ssm3515->regmap,
|
||||
SSM3515_REG_POWER_CTRL,
|
||||
SSM3515_POWER_SPWDN, SSM3515_POWER_SPWDN);
|
||||
}
|
||||
|
||||
if (enable) {
|
||||
ret = regmap_write(ssm3515->regmap, SSM3515_REG_POWER_CTRL,
|
||||
SSM3515_POWER_S_RESET);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_update_bits(ssm3515->regmap,
|
||||
SSM3515_REG_POWER_CTRL,
|
||||
SSM3515_POWER_SPWDN, 0x00);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ssm3515_set_bias_level(struct snd_soc_codec *codec,
|
||||
enum snd_soc_bias_level level)
|
||||
{
|
||||
struct ssm3515 *ssm3515 = snd_soc_codec_get_drvdata(codec);
|
||||
int ret = 0;
|
||||
|
||||
switch (level) {
|
||||
case SND_SOC_BIAS_ON:
|
||||
break;
|
||||
case SND_SOC_BIAS_PREPARE:
|
||||
break;
|
||||
case SND_SOC_BIAS_STANDBY:
|
||||
if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF)
|
||||
ret = ssm3515_set_power(ssm3515, true);
|
||||
break;
|
||||
case SND_SOC_BIAS_OFF:
|
||||
ret = ssm3515_set_power(ssm3515, false);
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct snd_soc_dai_ops ssm3515_dai_ops = {
|
||||
.hw_params = ssm3515_hw_params,
|
||||
.digital_mute = ssm3515_mute,
|
||||
.set_fmt = ssm3515_set_dai_fmt,
|
||||
.set_tdm_slot = ssm3515_set_tdm_slot,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_driver ssm3515_dai = {
|
||||
.name = "ssm3515-hifi",
|
||||
.playback = {
|
||||
.stream_name = "Playback",
|
||||
.channels_min = 1,
|
||||
.channels_max = 8,
|
||||
.rates = SNDRV_PCM_RATE_8000_192000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |
|
||||
SNDRV_PCM_FMTBIT_S32,
|
||||
},
|
||||
.capture = {
|
||||
.stream_name = "Capture ",
|
||||
.channels_min = 1,
|
||||
.channels_max = 8,
|
||||
.rates = SNDRV_PCM_RATE_8000_192000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |
|
||||
SNDRV_PCM_FMTBIT_S32,
|
||||
},
|
||||
.ops = &ssm3515_dai_ops,
|
||||
};
|
||||
|
||||
static struct snd_soc_codec_driver ssm3515_codec_nocomponent = {
|
||||
.set_bias_level = ssm3515_set_bias_level,
|
||||
.idle_bias_off = true,
|
||||
};
|
||||
|
||||
static struct snd_soc_codec_driver ssm3515_codec_driver = {
|
||||
.set_bias_level = ssm3515_set_bias_level,
|
||||
.idle_bias_off = true,
|
||||
|
||||
.component_driver = {
|
||||
.controls = ssm3515_snd_controls,
|
||||
.num_controls = ARRAY_SIZE(ssm3515_snd_controls),
|
||||
},
|
||||
};
|
||||
|
||||
static const struct regmap_config ssm3515_regmap_config = {
|
||||
.val_bits = 8,
|
||||
.reg_bits = 8,
|
||||
|
||||
.max_register = SSM3515_REG_FAULT_CTRL,
|
||||
.readable_reg = ssm3515_readable_reg,
|
||||
.writeable_reg = ssm3515_writeable_reg,
|
||||
.volatile_reg = ssm3515_volatile_reg,
|
||||
/*
|
||||
* TODO: Activate register map cache. It has been temporarily deactivated to
|
||||
* eliminate a potential source of trouble during driver development.
|
||||
*/
|
||||
/*.cache_type = REGCACHE_RBTREE,*/
|
||||
/*.reg_defaults = ssm3515_reg_defaults,*/
|
||||
/*.num_reg_defaults = ARRAY_SIZE(ssm3515_reg_defaults),*/
|
||||
};
|
||||
|
||||
static int ssm3515_i2c_probe(struct i2c_client *i2c,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct ssm3515 *ssm3515;
|
||||
int ret, i;
|
||||
|
||||
device_num++;
|
||||
|
||||
ssm3515 = devm_kzalloc(&i2c->dev, sizeof(*ssm3515), GFP_KERNEL);
|
||||
if (ssm3515 == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
i2c_set_clientdata(i2c, ssm3515);
|
||||
|
||||
ssm3515->regmap = devm_regmap_init_i2c(i2c, &ssm3515_regmap_config);
|
||||
if (IS_ERR(ssm3515->regmap))
|
||||
return PTR_ERR(ssm3515->regmap);
|
||||
|
||||
ret = ssm3515_set_power(ssm3515, false);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_write(ssm3515->regmap, SSM3515_REG_POWER_CTRL,
|
||||
SSM3515_POWER_S_RESET);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_write(ssm3515->regmap, SSM3515_REG_POWER_CTRL,
|
||||
SSM3515_POWER_S_RESET);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ssm3515_reg_defaults); i++) {
|
||||
ret = regmap_write(ssm3515->regmap, ssm3515_reg_defaults[i].reg,
|
||||
ssm3515_reg_defaults[i].def);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
if (i2c->dev.of_node) {
|
||||
const struct device_node *np = i2c->dev.of_node;
|
||||
u32 val;
|
||||
|
||||
if (of_property_read_u32(np, "slots_mask", &val) >= 0)
|
||||
ssm3515->slots_mask = val;
|
||||
|
||||
of_property_read_string(np, "amp_config", &ssm3515->amp_cfg);
|
||||
|
||||
if (of_property_read_u32(np, "total_num", &val) >= 0)
|
||||
ssm3515->total_num = val;
|
||||
}
|
||||
#endif
|
||||
if (device_num == 1) {
|
||||
g_ssm3515 = kzalloc(sizeof(struct ssm3515) *
|
||||
ssm3515->total_num, GFP_KERNEL);
|
||||
if (g_ssm3515 == NULL)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
g_ssm3515[device_num-1] = *ssm3515;
|
||||
|
||||
if (device_num == ssm3515->total_num) {
|
||||
ret = snd_soc_register_codec(&i2c->dev, &ssm3515_codec_driver,
|
||||
&ssm3515_dai, 1);
|
||||
} else {
|
||||
ret = snd_soc_register_codec(&i2c->dev,
|
||||
&ssm3515_codec_nocomponent, &ssm3515_dai, 1);
|
||||
}
|
||||
if (ret)
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ssm3515_i2c_remove(struct i2c_client *client)
|
||||
{
|
||||
snd_soc_unregister_codec(&client->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id ssm3515_dt_ids[] = {
|
||||
{ .compatible = "adi, ssm3515", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ssm3515_dt_ids);
|
||||
#endif
|
||||
|
||||
static const struct i2c_device_id ssm3515_i2c_ids[] = {
|
||||
{ " ssm3515", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, ssm3515_i2c_ids);
|
||||
|
||||
|
||||
static struct i2c_driver ssm3515_driver = {
|
||||
.driver = {
|
||||
.name = "ssm3515",
|
||||
.of_match_table = of_match_ptr(ssm3515_dt_ids),
|
||||
},
|
||||
.probe = ssm3515_i2c_probe,
|
||||
.remove = ssm3515_i2c_remove,
|
||||
.id_table = ssm3515_i2c_ids,
|
||||
};
|
||||
module_i2c_driver(ssm3515_driver);
|
||||
|
||||
MODULE_DESCRIPTION("ASoC SSM3515 driver");
|
||||
MODULE_AUTHOR("Anatol Pomozov <anatol@chromium.org>");
|
||||
MODULE_LICENSE("GPL");
|
||||
602
sound/soc/codecs/amlogic/ssm3525.c
Normal file
602
sound/soc/codecs/amlogic/ssm3525.c
Normal file
@@ -0,0 +1,602 @@
|
||||
/*
|
||||
* SSM4567 amplifier audio driver
|
||||
*
|
||||
* Copyright 2014 Google Chromium project.
|
||||
* Author: Anatol Pomozov <anatol@chromium.org>
|
||||
*
|
||||
* Based on code copyright/by:
|
||||
* Copyright 2013 Analog Devices Inc.
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/initval.h>
|
||||
#include <sound/tlv.h>
|
||||
|
||||
#define SSM3525_REG_VENDER_ID 0x00
|
||||
#define SSM3525_REG_DEVICE_ID1 0x01
|
||||
#define SSM3525_REG_DEVICE_ID2 0x02
|
||||
#define SSM3525_REG_REVISION_ID 0x03
|
||||
#define SSM3525_REG_REGULATOR_ENABLE 0x04
|
||||
#define SSM3525_REG_AMP_SNS_CTRL 0x05
|
||||
#define SSM3525_REG_DAC_CTRL 0x06
|
||||
#define SSM3525_REG_DAC_VOLUME 0x07
|
||||
#define SSM3525_REG_LIMITER_CTRL_1 0x08
|
||||
#define SSM3525_REG_LIMITER_CTRL_2 0x09
|
||||
#define SSM3525_REG_LIMITER_CTRL_3 0x0A
|
||||
#define SSM3525_REG_VBAT_LIM_CTRL_1 0x0B
|
||||
#define SSM3525_REG_VBAT_LIM_CTRL_2 0x0C
|
||||
#define SSM3525_REG_VBAT_LIM_CTRL_3 0x0D
|
||||
#define SSM3525_REG_LIMITER_LINK 0x0E
|
||||
#define SSM3525_REG_DAC_FLIP 0x0F
|
||||
#define SSM3525_REG_FAULT_CTRL 0x10
|
||||
#define SSM3525_REG_STATUS 0x11
|
||||
#define SSM3525_REG_TEMP 0x12
|
||||
#define SSM3525_REG_BATTERY_V_OUT 0x13
|
||||
|
||||
#define SSM3525_REG_POWER_CTRL 0x20
|
||||
#define SSM3525_REG_PDM_CTRL 0x21
|
||||
#define SSM3525_REG_SAI_CTRL_1 0x22
|
||||
#define SSM3525_REG_SAI_CTRL_2 0x23
|
||||
#define SSM3525_REG_SAI_PLACEMENT_1 0x24
|
||||
#define SSM3525_REG_SAI_PLACEMENT_2 0x25
|
||||
#define SSM3525_REG_SAI_PLACEMENT_3 0x26
|
||||
#define SSM3525_REG_SAI_PLACEMENT_4 0x27
|
||||
#define SSM3525_REG_SAI_PLACEMENT_5 0x28
|
||||
#define SSM3525_REG_SAI_PLACEMENT_6 0x29
|
||||
|
||||
#define SSM3525_REG_AGC_PLACEMENT_1 0x2A
|
||||
#define SSM3525_REG_AGC_PLACEMENT_2 0x2B
|
||||
#define SSM3525_REG_AGC_PLACEMENT_3 0x2C
|
||||
#define SSM3525_REG_AGC_PLACEMENT_4 0x2D
|
||||
#define SSM3525_REG_SOFT_RESET 0x2E
|
||||
|
||||
/* POWER_CTRL */
|
||||
#define SSM3525_POWER_APWDN_EN BIT(7)
|
||||
#define SSM3525_POWER_BSNS_PWDN BIT(6)
|
||||
#define SSM3525_POWER_VSNS_PWDN BIT(5)
|
||||
#define SSM3525_POWER_ISNS_PWDN BIT(4)
|
||||
#define SSM3525_POWER_BOOST_PWDN BIT(3)
|
||||
#define SSM3525_POWER_AMP_PWDN BIT(2)
|
||||
#define SSM3525_POWER_VBAT_ONLY BIT(1)
|
||||
#define SSM3525_POWER_SPWDN BIT(0)
|
||||
|
||||
/* DAC_CTRL */
|
||||
#define SSM3525_DAC_HV BIT(7)
|
||||
#define SSM3525_DAC_MUTE BIT(6)
|
||||
#define SSM3525_DAC_HPF BIT(5)
|
||||
#define SSM3525_DAC_LPM BIT(4)
|
||||
#define SSM3525_DAC_FS_MASK 0x7
|
||||
#define SSM3525_DAC_FS_8000_12000 0x0
|
||||
#define SSM3525_DAC_FS_16000_24000 0x1
|
||||
#define SSM3525_DAC_FS_32000_48000 0x2
|
||||
#define SSM3525_DAC_FS_64000_96000 0x3
|
||||
#define SSM3525_DAC_FS_128000_192000 0x4
|
||||
|
||||
/* SAI_CTRL_1 */
|
||||
#define SSM3525_SAI_CTRL_1_BCLK BIT(6)
|
||||
#define SSM3525_SAI_CTRL_1_TDM_BLCKS_MASK (0x3 << 4)
|
||||
#define SSM3525_SAI_CTRL_1_TDM_BLCKS_32 (0x0 << 4)
|
||||
#define SSM3525_SAI_CTRL_1_TDM_BLCKS_48 (0x1 << 4)
|
||||
#define SSM3525_SAI_CTRL_1_TDM_BLCKS_64 (0x2 << 4)
|
||||
#define SSM3525_SAI_CTRL_1_FSYNC BIT(3)
|
||||
#define SSM3525_SAI_CTRL_1_LJ BIT(2)
|
||||
#define SSM3525_SAI_CTRL_1_TDM BIT(1)
|
||||
#define SSM3525_SAI_CTRL_1_PDM BIT(0)
|
||||
|
||||
/* SAI_CTRL_2 */
|
||||
#define SSM3525_SAI_CTRL_2_AUTO_SLOT BIT(3)
|
||||
#define SSM3525_SAI_CTRL_2_TDM_SLOT_MASK 0x7
|
||||
#define SSM3525_SAI_CTRL_2_TDM_SLOT(x) (x)
|
||||
|
||||
static int device_num;
|
||||
struct ssm3525 *g_ssm3525;
|
||||
|
||||
struct ssm3525 {
|
||||
struct regmap *regmap;
|
||||
unsigned int slots_mask;
|
||||
const char *amp_cfg;
|
||||
int total_num;
|
||||
};
|
||||
|
||||
static const struct reg_default ssm3525_reg_defaults[] = {
|
||||
{ SSM3525_REG_REGULATOR_ENABLE, 0x01 },
|
||||
{ SSM3525_REG_AMP_SNS_CTRL, 0x23 },
|
||||
{ SSM3525_REG_DAC_CTRL, 0x22 },
|
||||
{ SSM3525_REG_SAI_PLACEMENT_1, 0x01 },
|
||||
{ SSM3525_REG_SAI_PLACEMENT_2, 0x20 },
|
||||
{ SSM3525_REG_SAI_PLACEMENT_3, 0x28 },
|
||||
{ SSM3525_REG_SAI_PLACEMENT_4, 0x28 },
|
||||
{ SSM3525_REG_SAI_PLACEMENT_5, 0x08 },
|
||||
{ SSM3525_REG_SAI_PLACEMENT_6, 0x08 },
|
||||
};
|
||||
|
||||
|
||||
static bool ssm3525_readable_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case SSM3525_REG_VENDER_ID ... SSM3525_REG_SOFT_RESET:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static bool ssm3525_writeable_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case SSM3525_REG_REGULATOR_ENABLE ... SSM3525_REG_FAULT_CTRL:
|
||||
case SSM3525_REG_POWER_CTRL ... SSM3525_REG_SOFT_RESET:
|
||||
/*The datasheet states that soft reset register is read-only,*/
|
||||
/*but logically it is write-only. */
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool ssm3525_volatile_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case SSM3525_REG_VENDER_ID ... SSM3525_REG_SOFT_RESET:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static int WL1_amplifier_power_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
int value = 0;
|
||||
int ret;
|
||||
int i;
|
||||
struct ssm3525 *ssm3525 = NULL;
|
||||
|
||||
for (i = 0; i < device_num; i++) {
|
||||
if (!strcmp(g_ssm3525[i].amp_cfg, "WL1")) {
|
||||
ssm3525 = g_ssm3525+i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
ret = regmap_read(ssm3525->regmap, SSM3525_REG_POWER_CTRL, &value);
|
||||
if (ret)
|
||||
return ret;
|
||||
ucontrol->value.integer.value[0] = value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int WL1_amplifier_power_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
int value, ret, i;
|
||||
struct ssm3525 *ssm3525 = NULL;
|
||||
|
||||
for (i = 0; i < device_num; i++) {
|
||||
if (!strcmp(g_ssm3525[i].amp_cfg, "WL1")) {
|
||||
ssm3525 = g_ssm3525+i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
value = ucontrol->value.integer.value[0];
|
||||
ret = regmap_write(ssm3525->regmap, SSM3525_REG_POWER_CTRL, value);
|
||||
if (ret)
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int WR1_amplifier_power_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
int value = 0;
|
||||
int ret, i;
|
||||
struct ssm3525 *ssm3525 = NULL;
|
||||
|
||||
for (i = 0; i < device_num; i++) {
|
||||
if (!strcmp(g_ssm3525[i].amp_cfg, "WR1")) {
|
||||
ssm3525 = g_ssm3525+i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
ret = regmap_read(ssm3525->regmap, SSM3525_REG_POWER_CTRL, &value);
|
||||
if (ret)
|
||||
return ret;
|
||||
ucontrol->value.integer.value[0] = value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int WR1_amplifier_power_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
int value, ret, i;
|
||||
struct ssm3525 *ssm3525 = NULL;
|
||||
|
||||
for (i = 0; i < device_num; i++) {
|
||||
if (!strcmp(g_ssm3525[i].amp_cfg, "WR1")) {
|
||||
ssm3525 = g_ssm3525+i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
value = ucontrol->value.integer.value[0];
|
||||
ret = regmap_write(ssm3525->regmap, SSM3525_REG_POWER_CTRL, value);
|
||||
if (ret)
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_kcontrol_new ssm3525_snd_controls[] = {
|
||||
SOC_SINGLE_EXT("WL1 Amp Power Control", SND_SOC_NOPM, 0, 0xff, 0,
|
||||
WL1_amplifier_power_get, WL1_amplifier_power_put),
|
||||
SOC_SINGLE_EXT("WR1 Amp Power Control", SND_SOC_NOPM, 0, 0xff, 0,
|
||||
WR1_amplifier_power_get, WR1_amplifier_power_put),
|
||||
};
|
||||
static int ssm3525_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 ssm3525 *ssm3525 = snd_soc_codec_get_drvdata(codec);
|
||||
unsigned int rate = params_rate(params);
|
||||
unsigned int dacfs;
|
||||
|
||||
if (rate >= 8000 && rate <= 12000)
|
||||
dacfs = SSM3525_DAC_FS_8000_12000;
|
||||
else if (rate >= 16000 && rate <= 24000)
|
||||
dacfs = SSM3525_DAC_FS_16000_24000;
|
||||
else if (rate >= 32000 && rate <= 48000)
|
||||
dacfs = SSM3525_DAC_FS_32000_48000;
|
||||
else if (rate >= 64000 && rate <= 96000)
|
||||
dacfs = SSM3525_DAC_FS_64000_96000;
|
||||
else if (rate >= 128000 && rate <= 192000)
|
||||
dacfs = SSM3525_DAC_FS_128000_192000;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
return regmap_update_bits(ssm3525->regmap, SSM3525_REG_DAC_CTRL,
|
||||
SSM3525_DAC_FS_MASK, dacfs);
|
||||
}
|
||||
|
||||
static int ssm3525_mute(struct snd_soc_dai *dai, int mute)
|
||||
{
|
||||
struct ssm3525 *ssm3525 = snd_soc_codec_get_drvdata(dai->codec);
|
||||
unsigned int val;
|
||||
|
||||
val = mute ? SSM3525_DAC_MUTE : 0;
|
||||
return regmap_update_bits(ssm3525->regmap, SSM3525_REG_DAC_CTRL,
|
||||
SSM3525_DAC_MUTE, val);
|
||||
}
|
||||
|
||||
static int ssm3525_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
|
||||
unsigned int rx_mask, int slots, int width)
|
||||
{
|
||||
struct ssm3525 *ssm3525 = snd_soc_dai_get_drvdata(dai);
|
||||
unsigned int blcks;
|
||||
int slot;
|
||||
int ret;
|
||||
|
||||
tx_mask = ssm3525->slots_mask;
|
||||
|
||||
if (tx_mask == 0)
|
||||
return -EINVAL;
|
||||
|
||||
slot = __ffs(tx_mask);
|
||||
if (tx_mask != BIT(slot))
|
||||
return -EINVAL;
|
||||
|
||||
switch (width) {
|
||||
case 32:
|
||||
blcks = SSM3525_SAI_CTRL_1_TDM_BLCKS_32;
|
||||
break;
|
||||
case 48:
|
||||
blcks = SSM3525_SAI_CTRL_1_TDM_BLCKS_48;
|
||||
break;
|
||||
case 64:
|
||||
blcks = SSM3525_SAI_CTRL_1_TDM_BLCKS_64;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = regmap_update_bits(ssm3525->regmap, SSM3525_REG_SAI_CTRL_2,
|
||||
SSM3525_SAI_CTRL_2_AUTO_SLOT | SSM3525_SAI_CTRL_2_TDM_SLOT_MASK,
|
||||
SSM3525_SAI_CTRL_2_TDM_SLOT(slot));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return regmap_update_bits(ssm3525->regmap, SSM3525_REG_SAI_CTRL_1,
|
||||
SSM3525_SAI_CTRL_1_TDM_BLCKS_MASK, blcks);
|
||||
}
|
||||
|
||||
static int ssm3525_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
|
||||
{
|
||||
#if 0
|
||||
struct ssm3525 *ssm3525 = snd_soc_dai_get_drvdata(dai);
|
||||
unsigned int ctrl1 = 0;
|
||||
bool invert_fclk;
|
||||
|
||||
|
||||
fmt = fmt | SND_SOC_DAIFMT_DSP_A |
|
||||
SND_SOC_DAIFMT_CBS_CFS |
|
||||
SND_SOC_DAIFMT_NB_NF |
|
||||
SND_SOC_DAIFMT_CONT;
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
|
||||
case SND_SOC_DAIFMT_CBS_CFS:
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
|
||||
case SND_SOC_DAIFMT_NB_NF:
|
||||
invert_fclk = false;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_IB_NF:
|
||||
ctrl1 |= SSM3525_SAI_CTRL_1_BCLK;
|
||||
invert_fclk = false;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_NB_IF:
|
||||
ctrl1 |= SSM3525_SAI_CTRL_1_FSYNC;
|
||||
invert_fclk = true;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_IB_IF:
|
||||
ctrl1 |= SSM3525_SAI_CTRL_1_BCLK;
|
||||
invert_fclk = true;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
|
||||
case SND_SOC_DAIFMT_I2S:
|
||||
break;
|
||||
case SND_SOC_DAIFMT_LEFT_J:
|
||||
ctrl1 |= SSM3525_SAI_CTRL_1_LJ;
|
||||
invert_fclk = !invert_fclk;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_DSP_A:
|
||||
ctrl1 |= SSM3525_SAI_CTRL_1_TDM;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_DSP_B:
|
||||
ctrl1 |= SSM3525_SAI_CTRL_1_TDM | SSM3525_SAI_CTRL_1_LJ;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_PDM:
|
||||
ctrl1 |= SSM3525_SAI_CTRL_1_PDM;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (invert_fclk)
|
||||
ctrl1 |= SSM3525_SAI_CTRL_1_FSYNC;
|
||||
|
||||
return regmap_update_bits(ssm3525->regmap, SSM3525_REG_SAI_CTRL_1,
|
||||
SSM3525_SAI_CTRL_1_BCLK |
|
||||
SSM3525_SAI_CTRL_1_FSYNC |
|
||||
SSM3525_SAI_CTRL_1_LJ |
|
||||
SSM3525_SAI_CTRL_1_TDM |
|
||||
SSM3525_SAI_CTRL_1_PDM,
|
||||
ctrl1);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ssm3525_set_power(struct ssm3525 *ssm3525, bool enable)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (!enable) {
|
||||
ret = regmap_update_bits(ssm3525->regmap,
|
||||
SSM3525_REG_POWER_CTRL,
|
||||
SSM3525_POWER_SPWDN, SSM3525_POWER_SPWDN);
|
||||
}
|
||||
|
||||
|
||||
if (enable) {
|
||||
ret = regmap_write(ssm3525->regmap, SSM3525_REG_SOFT_RESET,
|
||||
0x00);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_update_bits(ssm3525->regmap,
|
||||
SSM3525_REG_POWER_CTRL,
|
||||
SSM3525_POWER_SPWDN, 0x00);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ssm3525_set_bias_level(struct snd_soc_codec *codec,
|
||||
enum snd_soc_bias_level level)
|
||||
{
|
||||
struct ssm3525 *ssm3525 = snd_soc_codec_get_drvdata(codec);
|
||||
int ret = 0;
|
||||
|
||||
switch (level) {
|
||||
case SND_SOC_BIAS_ON:
|
||||
break;
|
||||
case SND_SOC_BIAS_PREPARE:
|
||||
break;
|
||||
case SND_SOC_BIAS_STANDBY:
|
||||
if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF)
|
||||
ret = ssm3525_set_power(ssm3525, true);
|
||||
break;
|
||||
case SND_SOC_BIAS_OFF:
|
||||
ret = ssm3525_set_power(ssm3525, false);
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct snd_soc_dai_ops ssm3525_dai_ops = {
|
||||
.hw_params = ssm3525_hw_params,
|
||||
.digital_mute = ssm3525_mute,
|
||||
.set_fmt = ssm3525_set_dai_fmt,
|
||||
.set_tdm_slot = ssm3525_set_tdm_slot,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_driver ssm3525_dai = {
|
||||
.name = "ssm3525-hifi",
|
||||
.playback = {
|
||||
.stream_name = "Playback",
|
||||
.channels_min = 1,
|
||||
.channels_max = 8,
|
||||
.rates = SNDRV_PCM_RATE_8000_192000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |
|
||||
SNDRV_PCM_FMTBIT_S32,
|
||||
},
|
||||
.capture = {
|
||||
.stream_name = "Capture Sense",
|
||||
.channels_min = 1,
|
||||
.channels_max = 8,
|
||||
.rates = SNDRV_PCM_RATE_8000_192000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |
|
||||
SNDRV_PCM_FMTBIT_S32,
|
||||
},
|
||||
.ops = &ssm3525_dai_ops,
|
||||
};
|
||||
|
||||
static struct snd_soc_codec_driver ssm3525_codec_nocomponent = {
|
||||
.set_bias_level = ssm3525_set_bias_level,
|
||||
.idle_bias_off = true,
|
||||
};
|
||||
|
||||
static struct snd_soc_codec_driver ssm3525_codec_driver = {
|
||||
.set_bias_level = ssm3525_set_bias_level,
|
||||
.idle_bias_off = true,
|
||||
|
||||
.component_driver = {
|
||||
.controls = ssm3525_snd_controls,
|
||||
.num_controls = ARRAY_SIZE(ssm3525_snd_controls),
|
||||
},
|
||||
};
|
||||
|
||||
static const struct regmap_config ssm3525_regmap_config = {
|
||||
.val_bits = 8,
|
||||
.reg_bits = 8,
|
||||
|
||||
.max_register = SSM3525_REG_SOFT_RESET,
|
||||
.readable_reg = ssm3525_readable_reg,
|
||||
.writeable_reg = ssm3525_writeable_reg,
|
||||
.volatile_reg = ssm3525_volatile_reg,
|
||||
/*
|
||||
* TODO: Activate register map cache. It has been temporarily deactivated to
|
||||
* eliminate a potential source of trouble during driver development.
|
||||
*/
|
||||
/*.cache_type = REGCACHE_RBTREE,*/
|
||||
/*.reg_defaults = ssm3525_reg_defaults,*/
|
||||
/*.num_reg_defaults = ARRAY_SIZE(ssm3525_reg_defaults),*/
|
||||
};
|
||||
|
||||
static int ssm3525_i2c_probe(struct i2c_client *i2c,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct ssm3525 *ssm3525;
|
||||
int ret, i;
|
||||
|
||||
device_num++;
|
||||
ssm3525 = devm_kzalloc(&i2c->dev, sizeof(*ssm3525), GFP_KERNEL);
|
||||
if (ssm3525 == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
i2c_set_clientdata(i2c, ssm3525);
|
||||
|
||||
ssm3525->regmap = devm_regmap_init_i2c(i2c, &ssm3525_regmap_config);
|
||||
if (IS_ERR(ssm3525->regmap))
|
||||
return PTR_ERR(ssm3525->regmap);
|
||||
|
||||
ret = regmap_write(ssm3525->regmap, SSM3525_REG_SOFT_RESET, 0x01);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = regmap_write(ssm3525->regmap, SSM3525_REG_SOFT_RESET, 0x00);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ssm3525_reg_defaults); i++) {
|
||||
ret = regmap_write(ssm3525->regmap,
|
||||
ssm3525_reg_defaults[i].reg,
|
||||
ssm3525_reg_defaults[i].def);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
#ifdef CONFIG_OF
|
||||
if (i2c->dev.of_node) {
|
||||
const struct device_node *np = i2c->dev.of_node;
|
||||
u32 val;
|
||||
|
||||
if (of_property_read_u32(np, "slots_mask", &val) >= 0)
|
||||
ssm3525->slots_mask = val;
|
||||
|
||||
of_property_read_string(np, "amp_config", &ssm3525->amp_cfg);
|
||||
|
||||
if (of_property_read_u32(np, "total_num", &val) >= 0)
|
||||
ssm3525->total_num = val;
|
||||
}
|
||||
#endif
|
||||
if (device_num == 1) {
|
||||
g_ssm3525 = kzalloc(sizeof(struct ssm3525) *
|
||||
ssm3525->total_num, GFP_KERNEL);
|
||||
if (g_ssm3525 == NULL)
|
||||
return -ENOMEM;
|
||||
}
|
||||
g_ssm3525[device_num-1] = *ssm3525;
|
||||
|
||||
if (device_num == ssm3525->total_num) {
|
||||
ret = snd_soc_register_codec(&i2c->dev, &ssm3525_codec_driver,
|
||||
&ssm3525_dai, 1);
|
||||
|
||||
} else {
|
||||
ret = snd_soc_register_codec(&i2c->dev,
|
||||
&ssm3525_codec_nocomponent,
|
||||
&ssm3525_dai, 1);
|
||||
}
|
||||
if (ret)
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ssm3525_i2c_remove(struct i2c_client *client)
|
||||
{
|
||||
snd_soc_unregister_codec(&client->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id ssm3525_dt_ids[] = {
|
||||
{ .compatible = "adi, ssm3525", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ssm3525_dt_ids);
|
||||
#endif
|
||||
static const struct i2c_device_id ssm3525_i2c_ids[] = {
|
||||
{ "ssm3525", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, ssm3525_i2c_ids);
|
||||
|
||||
|
||||
static struct i2c_driver ssm3525_driver = {
|
||||
.driver = {
|
||||
.name = "ssm3525",
|
||||
.of_match_table = of_match_ptr(ssm3525_dt_ids),
|
||||
},
|
||||
.probe = ssm3525_i2c_probe,
|
||||
.remove = ssm3525_i2c_remove,
|
||||
.id_table = ssm3525_i2c_ids,
|
||||
};
|
||||
module_i2c_driver(ssm3525_driver);
|
||||
|
||||
MODULE_DESCRIPTION("ASoC SSM3525 driver");
|
||||
MODULE_AUTHOR("Anatol Pomozov <anatol@chromium.org>");
|
||||
MODULE_LICENSE("GPL");
|
||||
Reference in New Issue
Block a user