Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
linux
Commits
bfe617d3
Commit
bfe617d3
authored
Jun 17, 2013
by
Mark Brown
Browse files
Options
Browse Files
Download
Plain Diff
Merge remote-tracking branch 'asoc/topic/ssm2518' into asoc-next
parents
5f5eb4ef
1aad4e57
Changes
6
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
924 additions
and
0 deletions
+924
-0
Documentation/devicetree/bindings/sound/ssm2518.txt
Documentation/devicetree/bindings/sound/ssm2518.txt
+20
-0
include/linux/platform_data/ssm2518.h
include/linux/platform_data/ssm2518.h
+22
-0
sound/soc/codecs/Kconfig
sound/soc/codecs/Kconfig
+4
-0
sound/soc/codecs/Makefile
sound/soc/codecs/Makefile
+2
-0
sound/soc/codecs/ssm2518.c
sound/soc/codecs/ssm2518.c
+856
-0
sound/soc/codecs/ssm2518.h
sound/soc/codecs/ssm2518.h
+20
-0
No files found.
Documentation/devicetree/bindings/sound/ssm2518.txt
0 → 100644
View file @
bfe617d3
SSM2518 audio amplifier
This device supports I2C only.
Required properties:
- compatible : Must be "adi,ssm2518"
- reg : the I2C address of the device. This will either be 0x34 (ADDR pin low)
or 0x35 (ADDR pin high)
Optional properties:
- gpios : GPIO connected to the nSD pin. If the property is not present it is
assumed that the nSD pin is hardwired to always on.
Example:
ssm2518: ssm2518@34 {
compatible = "adi,ssm2518";
reg = <0x34>;
gpios = <&gpio 5 0>;
};
include/linux/platform_data/ssm2518.h
0 → 100644
View file @
bfe617d3
/*
* SSM2518 amplifier audio driver
*
* Copyright 2013 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
*
* Licensed under the GPL-2.
*/
#ifndef __LINUX_PLATFORM_DATA_SSM2518_H__
#define __LINUX_PLATFORM_DATA_SSM2518_H__
/**
* struct ssm2518_platform_data - Platform data for the ssm2518 driver
* @enable_gpio: GPIO connected to the nSD pin. Set to -1 if the nSD pin is
* hardwired.
*/
struct
ssm2518_platform_data
{
int
enable_gpio
;
};
#endif
sound/soc/codecs/Kconfig
View file @
bfe617d3
...
...
@@ -61,6 +61,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_SI476X if MFD_SI476X_CORE
select SND_SOC_SN95031 if INTEL_SCU_IPC
select SND_SOC_SPDIF
select SND_SOC_SSM2518 if I2C
select SND_SOC_SSM2602 if SND_SOC_I2C_AND_SPI
select SND_SOC_STA32X if I2C
select SND_SOC_STA529 if I2C
...
...
@@ -317,6 +318,9 @@ config SND_SOC_SN95031
config SND_SOC_SPDIF
tristate
config SND_SOC_SSM2518
tristate
config SND_SOC_SSM2602
tristate
...
...
sound/soc/codecs/Makefile
View file @
bfe617d3
...
...
@@ -53,6 +53,7 @@ snd-soc-si476x-objs := si476x.o
snd-soc-sn95031-objs
:=
sn95031.o
snd-soc-spdif-tx-objs
:=
spdif_transmitter.o
snd-soc-spdif-rx-objs
:=
spdif_receiver.o
snd-soc-ssm2518-objs
:=
ssm2518.o
snd-soc-ssm2602-objs
:=
ssm2602.o
snd-soc-sta32x-objs
:=
sta32x.o
snd-soc-sta529-objs
:=
sta529.o
...
...
@@ -178,6 +179,7 @@ obj-$(CONFIG_SND_SOC_SIGMADSP) += snd-soc-sigmadsp.o
obj-$(CONFIG_SND_SOC_SI476X)
+=
snd-soc-si476x.o
obj-$(CONFIG_SND_SOC_SN95031)
+=
snd-soc-sn95031.o
obj-$(CONFIG_SND_SOC_SPDIF)
+=
snd-soc-spdif-rx.o snd-soc-spdif-tx.o
obj-$(CONFIG_SND_SOC_SSM2518)
+=
snd-soc-ssm2518.o
obj-$(CONFIG_SND_SOC_SSM2602)
+=
snd-soc-ssm2602.o
obj-$(CONFIG_SND_SOC_STA32X)
+=
snd-soc-sta32x.o
obj-$(CONFIG_SND_SOC_STA529)
+=
snd-soc-sta529.o
...
...
sound/soc/codecs/ssm2518.c
0 → 100644
View file @
bfe617d3
/*
* SSM2518 amplifier audio driver
*
* Copyright 2013 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
*
* Licensed under the GPL-2.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/platform_data/ssm2518.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/initval.h>
#include <sound/tlv.h>
#include "ssm2518.h"
#define SSM2518_REG_POWER1 0x00
#define SSM2518_REG_CLOCK 0x01
#define SSM2518_REG_SAI_CTRL1 0x02
#define SSM2518_REG_SAI_CTRL2 0x03
#define SSM2518_REG_CHAN_MAP 0x04
#define SSM2518_REG_LEFT_VOL 0x05
#define SSM2518_REG_RIGHT_VOL 0x06
#define SSM2518_REG_MUTE_CTRL 0x07
#define SSM2518_REG_FAULT_CTRL 0x08
#define SSM2518_REG_POWER2 0x09
#define SSM2518_REG_DRC_1 0x0a
#define SSM2518_REG_DRC_2 0x0b
#define SSM2518_REG_DRC_3 0x0c
#define SSM2518_REG_DRC_4 0x0d
#define SSM2518_REG_DRC_5 0x0e
#define SSM2518_REG_DRC_6 0x0f
#define SSM2518_REG_DRC_7 0x10
#define SSM2518_REG_DRC_8 0x11
#define SSM2518_REG_DRC_9 0x12
#define SSM2518_POWER1_RESET BIT(7)
#define SSM2518_POWER1_NO_BCLK BIT(5)
#define SSM2518_POWER1_MCS_MASK (0xf << 1)
#define SSM2518_POWER1_MCS_64FS (0x0 << 1)
#define SSM2518_POWER1_MCS_128FS (0x1 << 1)
#define SSM2518_POWER1_MCS_256FS (0x2 << 1)
#define SSM2518_POWER1_MCS_384FS (0x3 << 1)
#define SSM2518_POWER1_MCS_512FS (0x4 << 1)
#define SSM2518_POWER1_MCS_768FS (0x5 << 1)
#define SSM2518_POWER1_MCS_100FS (0x6 << 1)
#define SSM2518_POWER1_MCS_200FS (0x7 << 1)
#define SSM2518_POWER1_MCS_400FS (0x8 << 1)
#define SSM2518_POWER1_SPWDN BIT(0)
#define SSM2518_CLOCK_ASR BIT(0)
#define SSM2518_SAI_CTRL1_FMT_MASK (0x3 << 5)
#define SSM2518_SAI_CTRL1_FMT_I2S (0x0 << 5)
#define SSM2518_SAI_CTRL1_FMT_LJ (0x1 << 5)
#define SSM2518_SAI_CTRL1_FMT_RJ_24BIT (0x2 << 5)
#define SSM2518_SAI_CTRL1_FMT_RJ_16BIT (0x3 << 5)
#define SSM2518_SAI_CTRL1_SAI_MASK (0x7 << 2)
#define SSM2518_SAI_CTRL1_SAI_I2S (0x0 << 2)
#define SSM2518_SAI_CTRL1_SAI_TDM_2 (0x1 << 2)
#define SSM2518_SAI_CTRL1_SAI_TDM_4 (0x2 << 2)
#define SSM2518_SAI_CTRL1_SAI_TDM_8 (0x3 << 2)
#define SSM2518_SAI_CTRL1_SAI_TDM_16 (0x4 << 2)
#define SSM2518_SAI_CTRL1_SAI_MONO (0x5 << 2)
#define SSM2518_SAI_CTRL1_FS_MASK (0x3)
#define SSM2518_SAI_CTRL1_FS_8000_12000 (0x0)
#define SSM2518_SAI_CTRL1_FS_16000_24000 (0x1)
#define SSM2518_SAI_CTRL1_FS_32000_48000 (0x2)
#define SSM2518_SAI_CTRL1_FS_64000_96000 (0x3)
#define SSM2518_SAI_CTRL2_BCLK_INTERAL BIT(7)
#define SSM2518_SAI_CTRL2_LRCLK_PULSE BIT(6)
#define SSM2518_SAI_CTRL2_LRCLK_INVERT BIT(5)
#define SSM2518_SAI_CTRL2_MSB BIT(4)
#define SSM2518_SAI_CTRL2_SLOT_WIDTH_MASK (0x3 << 2)
#define SSM2518_SAI_CTRL2_SLOT_WIDTH_32 (0x0 << 2)
#define SSM2518_SAI_CTRL2_SLOT_WIDTH_24 (0x1 << 2)
#define SSM2518_SAI_CTRL2_SLOT_WIDTH_16 (0x2 << 2)
#define SSM2518_SAI_CTRL2_BCLK_INVERT BIT(1)
#define SSM2518_CHAN_MAP_RIGHT_SLOT_OFFSET 4
#define SSM2518_CHAN_MAP_RIGHT_SLOT_MASK 0xf0
#define SSM2518_CHAN_MAP_LEFT_SLOT_OFFSET 0
#define SSM2518_CHAN_MAP_LEFT_SLOT_MASK 0x0f
#define SSM2518_MUTE_CTRL_ANA_GAIN BIT(5)
#define SSM2518_MUTE_CTRL_MUTE_MASTER BIT(0)
#define SSM2518_POWER2_APWDN BIT(0)
#define SSM2518_DAC_MUTE BIT(6)
#define SSM2518_DAC_FS_MASK 0x07
#define SSM2518_DAC_FS_8000 0x00
#define SSM2518_DAC_FS_16000 0x01
#define SSM2518_DAC_FS_32000 0x02
#define SSM2518_DAC_FS_64000 0x03
#define SSM2518_DAC_FS_128000 0x04
struct
ssm2518
{
struct
regmap
*
regmap
;
bool
right_j
;
unsigned
int
sysclk
;
const
struct
snd_pcm_hw_constraint_list
*
constraints
;
int
enable_gpio
;
};
static
const
struct
reg_default
ssm2518_reg_defaults
[]
=
{
{
0x00
,
0x05
},
{
0x01
,
0x00
},
{
0x02
,
0x02
},
{
0x03
,
0x00
},
{
0x04
,
0x10
},
{
0x05
,
0x40
},
{
0x06
,
0x40
},
{
0x07
,
0x81
},
{
0x08
,
0x0c
},
{
0x09
,
0x99
},
{
0x0a
,
0x7c
},
{
0x0b
,
0x5b
},
{
0x0c
,
0x57
},
{
0x0d
,
0x89
},
{
0x0e
,
0x8c
},
{
0x0f
,
0x77
},
{
0x10
,
0x26
},
{
0x11
,
0x1c
},
{
0x12
,
0x97
},
};
static
const
DECLARE_TLV_DB_MINMAX_MUTE
(
ssm2518_vol_tlv
,
-
7125
,
2400
);
static
const
DECLARE_TLV_DB_SCALE
(
ssm2518_compressor_tlv
,
-
3400
,
200
,
0
);
static
const
DECLARE_TLV_DB_SCALE
(
ssm2518_expander_tlv
,
-
8100
,
300
,
0
);
static
const
DECLARE_TLV_DB_SCALE
(
ssm2518_noise_gate_tlv
,
-
9600
,
300
,
0
);
static
const
DECLARE_TLV_DB_SCALE
(
ssm2518_post_drc_tlv
,
-
2400
,
300
,
0
);
static
const
DECLARE_TLV_DB_RANGE
(
ssm2518_limiter_tlv
,
0
,
7
,
TLV_DB_SCALE_ITEM
(
-
2200
,
200
,
0
),
7
,
15
,
TLV_DB_SCALE_ITEM
(
-
800
,
100
,
0
),
);
static
const
char
*
const
ssm2518_drc_peak_detector_attack_time_text
[]
=
{
"0 ms"
,
"0.1 ms"
,
"0.19 ms"
,
"0.37 ms"
,
"0.75 ms"
,
"1.5 ms"
,
"3 ms"
,
"6 ms"
,
"12 ms"
,
"24 ms"
,
"48 ms"
,
"96 ms"
,
"192 ms"
,
"384 ms"
,
"768 ms"
,
"1536 ms"
,
};
static
const
char
*
const
ssm2518_drc_peak_detector_release_time_text
[]
=
{
"0 ms"
,
"1.5 ms"
,
"3 ms"
,
"6 ms"
,
"12 ms"
,
"24 ms"
,
"48 ms"
,
"96 ms"
,
"192 ms"
,
"384 ms"
,
"768 ms"
,
"1536 ms"
,
"3072 ms"
,
"6144 ms"
,
"12288 ms"
,
"24576 ms"
};
static
const
char
*
const
ssm2518_drc_hold_time_text
[]
=
{
"0 ms"
,
"0.67 ms"
,
"1.33 ms"
,
"2.67 ms"
,
"5.33 ms"
,
"10.66 ms"
,
"21.32 ms"
,
"42.64 ms"
,
"85.28 ms"
,
"170.56 ms"
,
"341.12 ms"
,
"682.24 ms"
,
"1364 ms"
,
};
static
const
SOC_ENUM_SINGLE_DECL
(
ssm2518_drc_peak_detector_attack_time_enum
,
SSM2518_REG_DRC_2
,
4
,
ssm2518_drc_peak_detector_attack_time_text
);
static
const
SOC_ENUM_SINGLE_DECL
(
ssm2518_drc_peak_detector_release_time_enum
,
SSM2518_REG_DRC_2
,
0
,
ssm2518_drc_peak_detector_release_time_text
);
static
const
SOC_ENUM_SINGLE_DECL
(
ssm2518_drc_attack_time_enum
,
SSM2518_REG_DRC_6
,
4
,
ssm2518_drc_peak_detector_attack_time_text
);
static
const
SOC_ENUM_SINGLE_DECL
(
ssm2518_drc_decay_time_enum
,
SSM2518_REG_DRC_6
,
0
,
ssm2518_drc_peak_detector_release_time_text
);
static
const
SOC_ENUM_SINGLE_DECL
(
ssm2518_drc_hold_time_enum
,
SSM2518_REG_DRC_7
,
4
,
ssm2518_drc_hold_time_text
);
static
const
SOC_ENUM_SINGLE_DECL
(
ssm2518_drc_noise_gate_hold_time_enum
,
SSM2518_REG_DRC_7
,
0
,
ssm2518_drc_hold_time_text
);
static
const
SOC_ENUM_SINGLE_DECL
(
ssm2518_drc_rms_averaging_time_enum
,
SSM2518_REG_DRC_9
,
0
,
ssm2518_drc_peak_detector_release_time_text
);
static
const
struct
snd_kcontrol_new
ssm2518_snd_controls
[]
=
{
SOC_SINGLE
(
"Playback De-emphasis Switch"
,
SSM2518_REG_MUTE_CTRL
,
4
,
1
,
0
),
SOC_DOUBLE_R_TLV
(
"Master Playback Volume"
,
SSM2518_REG_LEFT_VOL
,
SSM2518_REG_RIGHT_VOL
,
0
,
0xff
,
1
,
ssm2518_vol_tlv
),
SOC_DOUBLE
(
"Master Playback Switch"
,
SSM2518_REG_MUTE_CTRL
,
2
,
1
,
1
,
1
),
SOC_SINGLE
(
"Amp Low Power Mode Switch"
,
SSM2518_REG_POWER2
,
4
,
1
,
0
),
SOC_SINGLE
(
"DAC Low Power Mode Switch"
,
SSM2518_REG_POWER2
,
3
,
1
,
0
),
SOC_SINGLE
(
"DRC Limiter Switch"
,
SSM2518_REG_DRC_1
,
5
,
1
,
0
),
SOC_SINGLE
(
"DRC Compressor Switch"
,
SSM2518_REG_DRC_1
,
4
,
1
,
0
),
SOC_SINGLE
(
"DRC Expander Switch"
,
SSM2518_REG_DRC_1
,
3
,
1
,
0
),
SOC_SINGLE
(
"DRC Noise Gate Switch"
,
SSM2518_REG_DRC_1
,
2
,
1
,
0
),
SOC_DOUBLE
(
"DRC Switch"
,
SSM2518_REG_DRC_1
,
0
,
1
,
1
,
0
),
SOC_SINGLE_TLV
(
"DRC Limiter Threshold Volume"
,
SSM2518_REG_DRC_3
,
4
,
15
,
1
,
ssm2518_limiter_tlv
),
SOC_SINGLE_TLV
(
"DRC Compressor Lower Threshold Volume"
,
SSM2518_REG_DRC_3
,
0
,
15
,
1
,
ssm2518_compressor_tlv
),
SOC_SINGLE_TLV
(
"DRC Expander Upper Threshold Volume"
,
SSM2518_REG_DRC_4
,
4
,
15
,
1
,
ssm2518_expander_tlv
),
SOC_SINGLE_TLV
(
"DRC Noise Gate Threshold Volume"
,
SSM2518_REG_DRC_4
,
0
,
15
,
1
,
ssm2518_noise_gate_tlv
),
SOC_SINGLE_TLV
(
"DRC Upper Output Threshold Volume"
,
SSM2518_REG_DRC_5
,
4
,
15
,
1
,
ssm2518_limiter_tlv
),
SOC_SINGLE_TLV
(
"DRC Lower Output Threshold Volume"
,
SSM2518_REG_DRC_5
,
0
,
15
,
1
,
ssm2518_noise_gate_tlv
),
SOC_SINGLE_TLV
(
"DRC Post Volume"
,
SSM2518_REG_DRC_8
,
2
,
15
,
1
,
ssm2518_post_drc_tlv
),
SOC_ENUM
(
"DRC Peak Detector Attack Time"
,
ssm2518_drc_peak_detector_attack_time_enum
),
SOC_ENUM
(
"DRC Peak Detector Release Time"
,
ssm2518_drc_peak_detector_release_time_enum
),
SOC_ENUM
(
"DRC Attack Time"
,
ssm2518_drc_attack_time_enum
),
SOC_ENUM
(
"DRC Decay Time"
,
ssm2518_drc_decay_time_enum
),
SOC_ENUM
(
"DRC Hold Time"
,
ssm2518_drc_hold_time_enum
),
SOC_ENUM
(
"DRC Noise Gate Hold Time"
,
ssm2518_drc_noise_gate_hold_time_enum
),
SOC_ENUM
(
"DRC RMS Averaging Time"
,
ssm2518_drc_rms_averaging_time_enum
),
};
static
const
struct
snd_soc_dapm_widget
ssm2518_dapm_widgets
[]
=
{
SND_SOC_DAPM_DAC
(
"DACL"
,
"HiFi Playback"
,
SSM2518_REG_POWER2
,
1
,
1
),
SND_SOC_DAPM_DAC
(
"DACR"
,
"HiFi Playback"
,
SSM2518_REG_POWER2
,
2
,
1
),
SND_SOC_DAPM_OUTPUT
(
"OUTL"
),
SND_SOC_DAPM_OUTPUT
(
"OUTR"
),
};
static
const
struct
snd_soc_dapm_route
ssm2518_routes
[]
=
{
{
"OUTL"
,
NULL
,
"DACL"
},
{
"OUTR"
,
NULL
,
"DACR"
},
};
struct
ssm2518_mcs_lut
{
unsigned
int
rate
;
const
unsigned
int
*
sysclks
;
};
static
const
unsigned
int
ssm2518_sysclks_2048000
[]
=
{
2048000
,
4096000
,
8192000
,
12288000
,
16384000
,
24576000
,
3200000
,
6400000
,
12800000
,
0
};
static
const
unsigned
int
ssm2518_sysclks_2822000
[]
=
{
2822000
,
5644800
,
11289600
,
16934400
,
22579200
,
33868800
,
4410000
,
8820000
,
17640000
,
0
};
static
const
unsigned
int
ssm2518_sysclks_3072000
[]
=
{
3072000
,
6144000
,
12288000
,
16384000
,
24576000
,
38864000
,
4800000
,
9600000
,
19200000
,
0
};
static
const
struct
ssm2518_mcs_lut
ssm2518_mcs_lut
[]
=
{
{
8000
,
ssm2518_sysclks_2048000
,
},
{
11025
,
ssm2518_sysclks_2822000
,
},
{
12000
,
ssm2518_sysclks_3072000
,
},
{
16000
,
ssm2518_sysclks_2048000
,
},
{
24000
,
ssm2518_sysclks_3072000
,
},
{
22050
,
ssm2518_sysclks_2822000
,
},
{
32000
,
ssm2518_sysclks_2048000
,
},
{
44100
,
ssm2518_sysclks_2822000
,
},
{
48000
,
ssm2518_sysclks_3072000
,
},
{
96000
,
ssm2518_sysclks_3072000
,
},
};
static
const
unsigned
int
ssm2518_rates_2048000
[]
=
{
8000
,
16000
,
32000
,
};
static
const
struct
snd_pcm_hw_constraint_list
ssm2518_constraints_2048000
=
{
.
list
=
ssm2518_rates_2048000
,
.
count
=
ARRAY_SIZE
(
ssm2518_rates_2048000
),
};
static
const
unsigned
int
ssm2518_rates_2822000
[]
=
{
11025
,
22050
,
44100
,
};
static
const
struct
snd_pcm_hw_constraint_list
ssm2518_constraints_2822000
=
{
.
list
=
ssm2518_rates_2822000
,
.
count
=
ARRAY_SIZE
(
ssm2518_rates_2822000
),
};
static
const
unsigned
int
ssm2518_rates_3072000
[]
=
{
12000
,
24000
,
48000
,
96000
,
};
static
const
struct
snd_pcm_hw_constraint_list
ssm2518_constraints_3072000
=
{
.
list
=
ssm2518_rates_3072000
,
.
count
=
ARRAY_SIZE
(
ssm2518_rates_3072000
),
};
static
const
unsigned
int
ssm2518_rates_12288000
[]
=
{
8000
,
12000
,
16000
,
24000
,
32000
,
48000
,
96000
,
};
static
const
struct
snd_pcm_hw_constraint_list
ssm2518_constraints_12288000
=
{
.
list
=
ssm2518_rates_12288000
,
.
count
=
ARRAY_SIZE
(
ssm2518_rates_12288000
),
};
static
unsigned
int
ssm2518_lookup_mcs
(
struct
ssm2518
*
ssm2518
,
unsigned
int
rate
)
{
const
unsigned
int
*
sysclks
=
NULL
;
int
i
;
for
(
i
=
0
;
i
<
ARRAY_SIZE
(
ssm2518_mcs_lut
);
i
++
)
{
if
(
ssm2518_mcs_lut
[
i
].
rate
==
rate
)
{
sysclks
=
ssm2518_mcs_lut
[
i
].
sysclks
;
break
;
}
}
if
(
!
sysclks
)
return
-
EINVAL
;
for
(
i
=
0
;
sysclks
[
i
];
i
++
)
{
if
(
sysclks
[
i
]
==
ssm2518
->
sysclk
)
return
i
;
}
return
-
EINVAL
;
}
static
int
ssm2518_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
ssm2518
*
ssm2518
=
snd_soc_codec_get_drvdata
(
codec
);
unsigned
int
rate
=
params_rate
(
params
);
unsigned
int
ctrl1
,
ctrl1_mask
;
int
mcs
;
int
ret
;
mcs
=
ssm2518_lookup_mcs
(
ssm2518
,
rate
);
if
(
mcs
<
0
)
return
mcs
;
ctrl1_mask
=
SSM2518_SAI_CTRL1_FS_MASK
;
if
(
rate
>=
8000
&&
rate
<=
12000
)
ctrl1
=
SSM2518_SAI_CTRL1_FS_8000_12000
;
else
if
(
rate
>=
16000
&&
rate
<=
24000
)
ctrl1
=
SSM2518_SAI_CTRL1_FS_16000_24000
;
else
if
(
rate
>=
32000
&&
rate
<=
48000
)
ctrl1
=
SSM2518_SAI_CTRL1_FS_32000_48000
;
else
if
(
rate
>=
64000
&&
rate
<=
96000
)
ctrl1
=
SSM2518_SAI_CTRL1_FS_64000_96000
;
else
return
-
EINVAL
;
if
(
ssm2518
->
right_j
)
{
switch
(
params_format
(
params
))
{
case
SNDRV_PCM_FORMAT_S16_LE
:
ctrl1
|=
SSM2518_SAI_CTRL1_FMT_RJ_16BIT
;
break
;
case
SNDRV_PCM_FORMAT_S24_LE
:
ctrl1
|=
SSM2518_SAI_CTRL1_FMT_RJ_24BIT
;
break
;
default:
return
-
EINVAL
;
}
ctrl1_mask
|=
SSM2518_SAI_CTRL1_FMT_MASK
;
}
/* Disable auto samplerate detection */
ret
=
regmap_update_bits
(
ssm2518
->
regmap
,
SSM2518_REG_CLOCK
,
SSM2518_CLOCK_ASR
,
SSM2518_CLOCK_ASR
);
if
(
ret
<
0
)
return
ret
;
ret
=
regmap_update_bits
(
ssm2518
->
regmap
,
SSM2518_REG_SAI_CTRL1
,
ctrl1_mask
,
ctrl1
);
if
(
ret
<
0
)
return
ret
;
return
regmap_update_bits
(
ssm2518
->
regmap
,
SSM2518_REG_POWER1
,
SSM2518_POWER1_MCS_MASK
,
mcs
<<
1
);
}
static
int
ssm2518_mute
(
struct
snd_soc_dai
*
dai
,
int
mute
)
{
struct
ssm2518
*
ssm2518
=
snd_soc_codec_get_drvdata
(
dai
->
codec
);
unsigned
int
val
;
if
(
mute
)
val
=
SSM2518_MUTE_CTRL_MUTE_MASTER
;
else
val
=
0
;
return
regmap_update_bits
(
ssm2518
->
regmap
,
SSM2518_REG_MUTE_CTRL
,
SSM2518_MUTE_CTRL_MUTE_MASTER
,
val
);
}
static
int
ssm2518_set_dai_fmt
(
struct
snd_soc_dai
*
dai
,
unsigned
int
fmt
)
{
struct
ssm2518
*
ssm2518
=
snd_soc_codec_get_drvdata
(
dai
->
codec
);
unsigned
int
ctrl1
=
0
,
ctrl2
=
0
;
bool
invert_fclk
;
int
ret
;
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
:
ctrl2
|=
SSM2518_SAI_CTRL2_BCLK_INVERT
;
invert_fclk
=
false
;
break
;
case
SND_SOC_DAIFMT_NB_IF
:
invert_fclk
=
true
;
break
;
case
SND_SOC_DAIFMT_IB_IF
:
ctrl2
|=
SSM2518_SAI_CTRL2_BCLK_INVERT
;
invert_fclk
=
true
;
break
;
default:
return
-
EINVAL
;
}
ssm2518
->
right_j
=
false
;
switch
(
fmt
&
SND_SOC_DAIFMT_FORMAT_MASK
)
{
case
SND_SOC_DAIFMT_I2S
:
ctrl1
|=
SSM2518_SAI_CTRL1_FMT_I2S
;
break
;
case
SND_SOC_DAIFMT_LEFT_J
:
ctrl1
|=
SSM2518_SAI_CTRL1_FMT_LJ
;
invert_fclk
=
!
invert_fclk
;
break
;
case
SND_SOC_DAIFMT_RIGHT_J
:
ctrl1
|=
SSM2518_SAI_CTRL1_FMT_RJ_24BIT
;
ssm2518
->
right_j
=
true
;
invert_fclk
=
!
invert_fclk
;
break
;
case
SND_SOC_DAIFMT_DSP_A
:
ctrl2
|=
SSM2518_SAI_CTRL2_LRCLK_PULSE
;
ctrl1
|=
SSM2518_SAI_CTRL1_FMT_I2S
;
invert_fclk
=
false
;
break
;
case
SND_SOC_DAIFMT_DSP_B
:
ctrl2
|=
SSM2518_SAI_CTRL2_LRCLK_PULSE
;
ctrl1
|=
SSM2518_SAI_CTRL1_FMT_LJ
;
invert_fclk
=
false
;
break
;
default:
return
-
EINVAL
;
}
if
(
invert_fclk
)
ctrl2
|=
SSM2518_SAI_CTRL2_LRCLK_INVERT
;
ret
=
regmap_write
(
ssm2518
->
regmap
,
SSM2518_REG_SAI_CTRL1
,
ctrl1
);
if
(
ret
)
return
ret
;
return
regmap_write
(
ssm2518
->
regmap
,
SSM2518_REG_SAI_CTRL2
,
ctrl2
);
}
static
int
ssm2518_set_power
(
struct
ssm2518
*
ssm2518
,
bool
enable
)
{
int
ret
=
0
;
if
(
!
enable
)
{
ret
=
regmap_update_bits
(
ssm2518
->
regmap
,
SSM2518_REG_POWER1
,
SSM2518_POWER1_SPWDN
,
SSM2518_POWER1_SPWDN
);
regcache_mark_dirty
(
ssm2518
->
regmap
);
}
if
(
gpio_is_valid
(
ssm2518
->
enable_gpio
))
gpio_set_value
(
ssm2518
->
enable_gpio
,
enable
);
regcache_cache_only
(
ssm2518
->
regmap
,
!
enable
);
if
(
enable
)
{
ret
=
regmap_update_bits
(
ssm2518
->
regmap
,
SSM2518_REG_POWER1
,
SSM2518_POWER1_SPWDN
|
SSM2518_POWER1_RESET
,
0x00
);
regcache_sync
(
ssm2518
->
regmap
);
}
return
ret
;
}
static
int
ssm2518_set_bias_level
(
struct
snd_soc_codec
*
codec
,
enum
snd_soc_bias_level
level
)
{
struct
ssm2518
*
ssm2518
=
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
(
codec
->
dapm
.
bias_level
==
SND_SOC_BIAS_OFF
)
ret
=
ssm2518_set_power
(
ssm2518
,
true
);
break
;
case
SND_SOC_BIAS_OFF
:
ret
=
ssm2518_set_power
(
ssm2518
,
false
);
break
;
}
if
(
ret
)
return
ret
;
codec
->
dapm
.
bias_level
=
level
;
return
0
;
}
static
int
ssm2518_set_tdm_slot
(
struct
snd_soc_dai
*
dai
,
unsigned
int
tx_mask
,
unsigned
int
rx_mask
,
int
slots
,
int
width
)
{
struct
ssm2518
*
ssm2518
=
snd_soc_codec_get_drvdata
(
dai
->
codec
);
unsigned
int
ctrl1
,
ctrl2
;
int
left_slot
,
right_slot
;
int
ret
;
if
(
slots
==
0
)
return
regmap_update_bits
(
ssm2518
->
regmap
,
SSM2518_REG_SAI_CTRL1
,
SSM2518_SAI_CTRL1_SAI_MASK
,
SSM2518_SAI_CTRL1_SAI_I2S
);
if
(
tx_mask
==
0
||
rx_mask
!=
0
)
return
-
EINVAL
;
if
(
slots
==
1
)
{
if
(
tx_mask
!=
1
)
return
-
EINVAL
;
left_slot
=
0
;
right_slot
=
0
;
}
else
{
/* We assume the left channel < right channel */
left_slot
=
ffs
(
tx_mask
);
tx_mask
&=
~
(
1
<<
tx_mask
);
if
(
tx_mask
==
0
)
{
right_slot
=
left_slot
;
}
else
{
right_slot
=
ffs
(
tx_mask
);
tx_mask
&=
~
(
1
<<
tx_mask
);
}
}
if
(
tx_mask
!=
0
||
left_slot
>=
slots
||
right_slot
>=
slots
)
return
-
EINVAL
;
switch
(
width
)
{
case
16
:
ctrl2
=
SSM2518_SAI_CTRL2_SLOT_WIDTH_16
;
break
;
case
24
:
ctrl2
=
SSM2518_SAI_CTRL2_SLOT_WIDTH_24
;
break
;
case
32
:
ctrl2
=
SSM2518_SAI_CTRL2_SLOT_WIDTH_32
;
break
;
default:
return
-
EINVAL
;
}
switch
(
slots
)
{
case
1
:
ctrl1
=
SSM2518_SAI_CTRL1_SAI_MONO
;
break
;
case
2
:
ctrl1
=
SSM2518_SAI_CTRL1_SAI_TDM_2
;
break
;
case
4
:
ctrl1
=
SSM2518_SAI_CTRL1_SAI_TDM_4
;
break
;
case
8
:
ctrl1
=
SSM2518_SAI_CTRL1_SAI_TDM_8
;
break
;
case
16
:
ctrl1
=
SSM2518_SAI_CTRL1_SAI_TDM_16
;
break
;
default:
return
-
EINVAL
;
}
ret
=
regmap_write
(
ssm2518
->
regmap
,
SSM2518_REG_CHAN_MAP
,
(
left_slot
<<
SSM2518_CHAN_MAP_LEFT_SLOT_OFFSET
)
|
(
right_slot
<<
SSM2518_CHAN_MAP_RIGHT_SLOT_OFFSET
));
if
(
ret
)
return
ret
;
ret
=
regmap_update_bits
(
ssm2518
->
regmap
,
SSM2518_REG_SAI_CTRL1
,
SSM2518_SAI_CTRL1_SAI_MASK
,
ctrl1
);
if
(
ret
)
return
ret
;
return
regmap_update_bits
(
ssm2518
->
regmap
,
SSM2518_REG_SAI_CTRL2
,
SSM2518_SAI_CTRL2_SLOT_WIDTH_MASK
,
ctrl2
);
}
static
int
ssm2518_startup
(
struct
snd_pcm_substream
*
substream
,
struct
snd_soc_dai
*
dai
)
{
struct
ssm2518
*
ssm2518
=
snd_soc_codec_get_drvdata
(
dai
->
codec
);
if
(
ssm2518
->
constraints
)
snd_pcm_hw_constraint_list
(
substream
->
runtime
,
0
,
SNDRV_PCM_HW_PARAM_RATE
,
ssm2518
->
constraints
);
return
0
;
}
#define SSM2518_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32)
static
const
struct
snd_soc_dai_ops
ssm2518_dai_ops
=
{
.
startup
=
ssm2518_startup
,
.
hw_params
=
ssm2518_hw_params
,
.
digital_mute
=
ssm2518_mute
,
.
set_fmt
=
ssm2518_set_dai_fmt
,
.
set_tdm_slot
=
ssm2518_set_tdm_slot
,
};
static
struct
snd_soc_dai_driver
ssm2518_dai
=
{
.
name
=
"ssm2518-hifi"
,
.
playback
=
{
.
stream_name
=
"Playback"
,
.
channels_min
=
2
,
.
channels_max
=
2
,
.
rates
=
SNDRV_PCM_RATE_8000_96000
,
.
formats
=
SSM2518_FORMATS
,
},
.
ops
=
&
ssm2518_dai_ops
,
};
static
int
ssm2518_probe
(
struct
snd_soc_codec
*
codec
)
{
struct
ssm2518
*
ssm2518
=
snd_soc_codec_get_drvdata
(
codec
);
int
ret
;
codec
->
control_data
=
ssm2518
->
regmap
;
ret
=
snd_soc_codec_set_cache_io
(
codec
,
0
,
0
,
SND_SOC_REGMAP
);
if
(
ret
<
0
)
{
dev_err
(
codec
->
dev
,
"Failed to set cache I/O: %d
\n
"
,
ret
);
return
ret
;
}
return
ssm2518_set_bias_level
(
codec
,
SND_SOC_BIAS_OFF
);
}
static
int
ssm2518_remove
(
struct
snd_soc_codec
*
codec
)
{
ssm2518_set_bias_level
(
codec
,
SND_SOC_BIAS_OFF
);
return
0
;
}
static
int
ssm2518_set_sysclk
(
struct
snd_soc_codec
*
codec
,
int
clk_id
,
int
source
,
unsigned
int
freq
,
int
dir
)
{
struct
ssm2518
*
ssm2518
=
snd_soc_codec_get_drvdata
(
codec
);
unsigned
int
val
;
if
(
clk_id
!=
SSM2518_SYSCLK
)
return
-
EINVAL
;
switch
(
source
)
{
case
SSM2518_SYSCLK_SRC_MCLK
:
val
=
0
;
break
;
case
SSM2518_SYSCLK_SRC_BCLK
:
/* In this case the bitclock is used as the system clock, and
* the bitclock signal needs to be connected to the MCLK pin and
* the BCLK pin is left unconnected */
val
=
SSM2518_POWER1_NO_BCLK
;
break
;
default:
return
-
EINVAL
;
}
switch
(
freq
)
{
case
0
:
ssm2518
->
constraints
=
NULL
;
break
;
case
2048000
:
case
4096000
:
case
8192000
:
case
3200000
:
case
6400000
:
case
12800000
:
ssm2518
->
constraints
=
&
ssm2518_constraints_2048000
;
break
;
case
2822000
:
case
5644800
:
case
11289600
:
case
16934400
:
case
22579200
:
case
33868800
:
case
4410000
:
case
8820000
:
case
17640000
:
ssm2518
->
constraints
=
&
ssm2518_constraints_2822000
;
break
;
case
3072000
:
case
6144000
:
case
38864000
:
case
4800000
:
case
9600000
:
case
19200000
:
ssm2518
->
constraints
=
&
ssm2518_constraints_3072000
;
break
;
case
12288000
:
case
16384000
:
case
24576000
:
ssm2518
->
constraints
=
&
ssm2518_constraints_12288000
;
break
;
default:
return
-
EINVAL
;
}
ssm2518
->
sysclk
=
freq
;
return
regmap_update_bits
(
ssm2518
->
regmap
,
SSM2518_REG_POWER1
,
SSM2518_POWER1_NO_BCLK
,
val
);
}
static
struct
snd_soc_codec_driver
ssm2518_codec_driver
=
{
.
probe
=
ssm2518_probe
,
.
remove
=
ssm2518_remove
,
.
set_bias_level
=
ssm2518_set_bias_level
,
.
set_sysclk
=
ssm2518_set_sysclk
,
.
idle_bias_off
=
true
,
.
controls
=
ssm2518_snd_controls
,
.
num_controls
=
ARRAY_SIZE
(
ssm2518_snd_controls
),
.
dapm_widgets
=
ssm2518_dapm_widgets
,
.
num_dapm_widgets
=
ARRAY_SIZE
(
ssm2518_dapm_widgets
),
.
dapm_routes
=
ssm2518_routes
,
.
num_dapm_routes
=
ARRAY_SIZE
(
ssm2518_routes
),
};
static
bool
ssm2518_register_volatile
(
struct
device
*
dev
,
unsigned
int
reg
)
{
return
false
;
}
static
const
struct
regmap_config
ssm2518_regmap_config
=
{
.
val_bits
=
8
,
.
reg_bits
=
8
,
.
max_register
=
SSM2518_REG_DRC_9
,
.
volatile_reg
=
ssm2518_register_volatile
,
.
cache_type
=
REGCACHE_RBTREE
,
.
reg_defaults
=
ssm2518_reg_defaults
,
.
num_reg_defaults
=
ARRAY_SIZE
(
ssm2518_reg_defaults
),
};
static
int
ssm2518_i2c_probe
(
struct
i2c_client
*
i2c
,
const
struct
i2c_device_id
*
id
)
{
struct
ssm2518_platform_data
*
pdata
=
i2c
->
dev
.
platform_data
;
struct
ssm2518
*
ssm2518
;
int
ret
;
ssm2518
=
devm_kzalloc
(
&
i2c
->
dev
,
sizeof
(
*
ssm2518
),
GFP_KERNEL
);
if
(
ssm2518
==
NULL
)
return
-
ENOMEM
;
if
(
pdata
)
{
ssm2518
->
enable_gpio
=
pdata
->
enable_gpio
;
}
else
if
(
i2c
->
dev
.
of_node
)
{
ssm2518
->
enable_gpio
=
of_get_gpio
(
i2c
->
dev
.
of_node
,
0
);
if
(
ssm2518
->
enable_gpio
<
0
&&
ssm2518
->
enable_gpio
!=
-
ENOENT
)
return
ssm2518
->
enable_gpio
;
}
else
{
ssm2518
->
enable_gpio
=
-
1
;
}
if
(
gpio_is_valid
(
ssm2518
->
enable_gpio
))
{
ret
=
devm_gpio_request_one
(
&
i2c
->
dev
,
ssm2518
->
enable_gpio
,
GPIOF_OUT_INIT_HIGH
,
"SSM2518 nSD"
);
if
(
ret
)
return
ret
;
}
i2c_set_clientdata
(
i2c
,
ssm2518
);
ssm2518
->
regmap
=
devm_regmap_init_i2c
(
i2c
,
&
ssm2518_regmap_config
);
if
(
IS_ERR
(
ssm2518
->
regmap
))
return
PTR_ERR
(
ssm2518
->
regmap
);
/*
* The reset bit is obviously volatile, but we need to be able to cache
* the other bits in the register, so we can't just mark the whole
* register as volatile. Since this is the only place where we'll ever
* touch the reset bit just bypass the cache for this operation.
*/
regcache_cache_bypass
(
ssm2518
->
regmap
,
true
);
ret
=
regmap_write
(
ssm2518
->
regmap
,
SSM2518_REG_POWER1
,
SSM2518_POWER1_RESET
);
regcache_cache_bypass
(
ssm2518
->
regmap
,
false
);
if
(
ret
)
return
ret
;
ret
=
regmap_update_bits
(
ssm2518
->
regmap
,
SSM2518_REG_POWER2
,
SSM2518_POWER2_APWDN
,
0x00
);
if
(
ret
)
return
ret
;
ret
=
ssm2518_set_power
(
ssm2518
,
false
);
if
(
ret
)
return
ret
;
return
snd_soc_register_codec
(
&
i2c
->
dev
,
&
ssm2518_codec_driver
,
&
ssm2518_dai
,
1
);
}
static
int
ssm2518_i2c_remove
(
struct
i2c_client
*
client
)
{
snd_soc_unregister_codec
(
&
client
->
dev
);
return
0
;
}
static
const
struct
i2c_device_id
ssm2518_i2c_ids
[]
=
{
{
"ssm2518"
,
0
},
{
}
};
MODULE_DEVICE_TABLE
(
i2c
,
ssm2518_i2c_ids
);
static
struct
i2c_driver
ssm2518_driver
=
{
.
driver
=
{
.
name
=
"ssm2518"
,
.
owner
=
THIS_MODULE
,
},
.
probe
=
ssm2518_i2c_probe
,
.
remove
=
ssm2518_i2c_remove
,
.
id_table
=
ssm2518_i2c_ids
,
};
module_i2c_driver
(
ssm2518_driver
);
MODULE_DESCRIPTION
(
"ASoC SSM2518 driver"
);
MODULE_AUTHOR
(
"Lars-Peter Clausen <lars@metafoo.de>"
);
MODULE_LICENSE
(
"GPL"
);
sound/soc/codecs/ssm2518.h
0 → 100644
View file @
bfe617d3
/*
* SSM2518 amplifier audio driver
*
* Copyright 2013 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
*
* Licensed under the GPL-2.
*/
#ifndef __SND_SOC_CODECS_SSM2518_H__
#define __SND_SOC_CODECS_SSM2518_H__
#define SSM2518_SYSCLK 0
enum
ssm2518_sysclk_src
{
SSM2518_SYSCLK_SRC_MCLK
=
0
,
SSM2518_SYSCLK_SRC_BCLK
=
1
,
};
#endif
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment