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
Kirill Smelkov
linux
Commits
52db65f0
Commit
52db65f0
authored
Mar 12, 2014
by
Mark Brown
Browse files
Options
Browse Files
Download
Plain Diff
Merge remote-tracking branch 'asoc/topic/pcm512x' into asoc-next
parents
3b93f059
e97db9ab
Changes
7
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
953 additions
and
0 deletions
+953
-0
Documentation/devicetree/bindings/sound/pcm512x.txt
Documentation/devicetree/bindings/sound/pcm512x.txt
+30
-0
sound/soc/codecs/Kconfig
sound/soc/codecs/Kconfig
+17
-0
sound/soc/codecs/Makefile
sound/soc/codecs/Makefile
+6
-0
sound/soc/codecs/pcm512x-i2c.c
sound/soc/codecs/pcm512x-i2c.c
+71
-0
sound/soc/codecs/pcm512x-spi.c
sound/soc/codecs/pcm512x-spi.c
+69
-0
sound/soc/codecs/pcm512x.c
sound/soc/codecs/pcm512x.c
+589
-0
sound/soc/codecs/pcm512x.h
sound/soc/codecs/pcm512x.h
+171
-0
No files found.
Documentation/devicetree/bindings/sound/pcm512x.txt
0 → 100644
View file @
52db65f0
PCM512x audio CODECs
These devices support both I2C and SPI (configured with pin strapping
on the board).
Required properties:
- compatible : One of "ti,pcm5121" or "ti,pcm5122"
- reg : the I2C address of the device for I2C, the chip select
number for SPI.
- AVDD-supply, DVDD-supply, and CPVDD-supply : power supplies for the
device, as covered in bindings/regulator/regulator.txt
Optional properties:
- clocks : A clock specifier for the clock connected as SCLK. If this
is absent the device will be configured to clock from BCLK.
Example:
pcm5122: pcm5122@4c {
compatible = "ti,pcm5122";
reg = <0x4c>;
AVDD-supply = <®_3v3_analog>;
DVDD-supply = <®_1v8>;
CPVDD-supply = <®_3v3>;
};
sound/soc/codecs/Kconfig
View file @
52db65f0
...
...
@@ -59,6 +59,8 @@ config SND_SOC_ALL_CODECS
select SND_SOC_PCM1681 if I2C
select SND_SOC_PCM1792A if SPI_MASTER
select SND_SOC_PCM3008
select SND_SOC_PCM512x_I2C if I2C
select SND_SOC_PCM512x_SPI if SPI_MASTER
select SND_SOC_RT5631 if I2C
select SND_SOC_RT5640 if I2C
select SND_SOC_SGTL5000 if I2C
...
...
@@ -313,6 +315,21 @@ config SND_SOC_PCM1792A
config SND_SOC_PCM3008
tristate
config SND_SOC_PCM512x
tristate
config SND_SOC_PCM512x_I2C
tristate "Texas Instruments PCM512x CODECs - I2C"
depends on I2C
select SND_SOC_PCM512x
select REGMAP_I2C
config SND_SOC_PCM512x_SPI
tristate "Texas Instruments PCM512x CODECs - SPI"
depends on SPI_MASTER
select SND_SOC_PCM512x
select REGMAP_SPI
config SND_SOC_RT5631
tristate
...
...
sound/soc/codecs/Makefile
View file @
52db65f0
...
...
@@ -46,6 +46,9 @@ snd-soc-hdmi-codec-objs := hdmi.o
snd-soc-pcm1681-objs
:=
pcm1681.o
snd-soc-pcm1792a-codec-objs
:=
pcm1792a.o
snd-soc-pcm3008-objs
:=
pcm3008.o
snd-soc-pcm512x-objs
:=
pcm512x.o
snd-soc-pcm512x-i2c-objs
:=
pcm512x-i2c.o
snd-soc-pcm512x-spi-objs
:=
pcm512x-spi.o
snd-soc-rt5631-objs
:=
rt5631.o
snd-soc-rt5640-objs
:=
rt5640.o
snd-soc-sgtl5000-objs
:=
sgtl5000.o
...
...
@@ -179,6 +182,9 @@ obj-$(CONFIG_SND_SOC_HDMI_CODEC) += snd-soc-hdmi-codec.o
obj-$(CONFIG_SND_SOC_PCM1681)
+=
snd-soc-pcm1681.o
obj-$(CONFIG_SND_SOC_PCM1792A)
+=
snd-soc-pcm1792a-codec.o
obj-$(CONFIG_SND_SOC_PCM3008)
+=
snd-soc-pcm3008.o
obj-$(CONFIG_SND_SOC_PCM512x)
+=
snd-soc-pcm512x.o
obj-$(CONFIG_SND_SOC_PCM512x_I2C)
+=
snd-soc-pcm512x-i2c.o
obj-$(CONFIG_SND_SOC_PCM512x_SPI)
+=
snd-soc-pcm512x-spi.o
obj-$(CONFIG_SND_SOC_RT5631)
+=
snd-soc-rt5631.o
obj-$(CONFIG_SND_SOC_RT5640)
+=
snd-soc-rt5640.o
obj-$(CONFIG_SND_SOC_SGTL5000)
+=
snd-soc-sgtl5000.o
...
...
sound/soc/codecs/pcm512x-i2c.c
0 → 100644
View file @
52db65f0
/*
* Driver for the PCM512x CODECs
*
* Author: Mark Brown <broonie@linaro.org>
* Copyright 2014 Linaro Ltd
*
* 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 "pcm512x.h"
static
int
pcm512x_i2c_probe
(
struct
i2c_client
*
i2c
,
const
struct
i2c_device_id
*
id
)
{
struct
regmap
*
regmap
;
regmap
=
devm_regmap_init_i2c
(
i2c
,
&
pcm512x_regmap
);
if
(
IS_ERR
(
regmap
))
return
PTR_ERR
(
regmap
);
return
pcm512x_probe
(
&
i2c
->
dev
,
regmap
);
}
static
int
pcm512x_i2c_remove
(
struct
i2c_client
*
i2c
)
{
pcm512x_remove
(
&
i2c
->
dev
);
return
0
;
}
static
const
struct
i2c_device_id
pcm512x_i2c_id
[]
=
{
{
"pcm5121"
,
},
{
"pcm5122"
,
},
{
}
};
MODULE_DEVICE_TABLE
(
i2c
,
pcm512x_i2c_id
);
static
const
struct
of_device_id
pcm512x_of_match
[]
=
{
{
.
compatible
=
"ti,pcm5121"
,
},
{
.
compatible
=
"ti,pcm5122"
,
},
{
}
};
MODULE_DEVICE_TABLE
(
of
,
pcm512x_of_match
);
static
struct
i2c_driver
pcm512x_i2c_driver
=
{
.
probe
=
pcm512x_i2c_probe
,
.
remove
=
pcm512x_i2c_remove
,
.
id_table
=
pcm512x_i2c_id
,
.
driver
=
{
.
name
=
"pcm512x"
,
.
owner
=
THIS_MODULE
,
.
of_match_table
=
pcm512x_of_match
,
.
pm
=
&
pcm512x_pm_ops
,
},
};
module_i2c_driver
(
pcm512x_i2c_driver
);
MODULE_DESCRIPTION
(
"ASoC PCM512x codec driver - I2C"
);
MODULE_AUTHOR
(
"Mark Brown <broonie@linaro.org>"
);
MODULE_LICENSE
(
"GPL v2"
);
sound/soc/codecs/pcm512x-spi.c
0 → 100644
View file @
52db65f0
/*
* Driver for the PCM512x CODECs
*
* Author: Mark Brown <broonie@linaro.org>
* Copyright 2014 Linaro Ltd
*
* 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 "pcm512x.h"
static
int
pcm512x_spi_probe
(
struct
spi_device
*
spi
)
{
struct
regmap
*
regmap
;
int
ret
;
regmap
=
devm_regmap_init_spi
(
spi
,
&
pcm512x_regmap
);
if
(
IS_ERR
(
regmap
))
{
ret
=
PTR_ERR
(
regmap
);
return
ret
;
}
return
pcm512x_probe
(
&
spi
->
dev
,
regmap
);
}
static
int
pcm512x_spi_remove
(
struct
spi_device
*
spi
)
{
pcm512x_remove
(
&
spi
->
dev
);
return
0
;
}
static
const
struct
spi_device_id
pcm512x_spi_id
[]
=
{
{
"pcm5121"
,
},
{
"pcm5122"
,
},
{
},
};
MODULE_DEVICE_TABLE
(
spi
,
pcm512x_spi_id
);
static
const
struct
of_device_id
pcm512x_of_match
[]
=
{
{
.
compatible
=
"ti,pcm5121"
,
},
{
.
compatible
=
"ti,pcm5122"
,
},
{
}
};
MODULE_DEVICE_TABLE
(
of
,
pcm512x_of_match
);
static
struct
spi_driver
pcm512x_spi_driver
=
{
.
probe
=
pcm512x_spi_probe
,
.
remove
=
pcm512x_spi_remove
,
.
id_table
=
pcm512x_spi_id
,
.
driver
=
{
.
name
=
"pcm512x"
,
.
owner
=
THIS_MODULE
,
.
of_match_table
=
pcm512x_of_match
,
.
pm
=
&
pcm512x_pm_ops
,
},
};
module_spi_driver
(
pcm512x_spi_driver
);
sound/soc/codecs/pcm512x.c
0 → 100644
View file @
52db65f0
/*
* Driver for the PCM512x CODECs
*
* Author: Mark Brown <broonie@linaro.org>
* Copyright 2014 Linaro Ltd
*
* 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/clk.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <sound/tlv.h>
#include "pcm512x.h"
#define PCM512x_NUM_SUPPLIES 3
static
const
char
*
const
pcm512x_supply_names
[
PCM512x_NUM_SUPPLIES
]
=
{
"AVDD"
,
"DVDD"
,
"CPVDD"
,
};
struct
pcm512x_priv
{
struct
regmap
*
regmap
;
struct
clk
*
sclk
;
struct
regulator_bulk_data
supplies
[
PCM512x_NUM_SUPPLIES
];
struct
notifier_block
supply_nb
[
PCM512x_NUM_SUPPLIES
];
};
/*
* We can't use the same notifier block for more than one supply and
* there's no way I can see to get from a callback to the caller
* except container_of().
*/
#define PCM512x_REGULATOR_EVENT(n) \
static int pcm512x_regulator_event_##n(struct notifier_block *nb, \
unsigned long event, void *data) \
{ \
struct pcm512x_priv *pcm512x = container_of(nb, struct pcm512x_priv, \
supply_nb[n]); \
if (event & REGULATOR_EVENT_DISABLE) { \
regcache_mark_dirty(pcm512x->regmap); \
regcache_cache_only(pcm512x->regmap, true); \
} \
return 0; \
}
PCM512x_REGULATOR_EVENT
(
0
)
PCM512x_REGULATOR_EVENT
(
1
)
PCM512x_REGULATOR_EVENT
(
2
)
static
const
struct
reg_default
pcm512x_reg_defaults
[]
=
{
{
PCM512x_RESET
,
0x00
},
{
PCM512x_POWER
,
0x00
},
{
PCM512x_MUTE
,
0x00
},
{
PCM512x_DSP
,
0x00
},
{
PCM512x_PLL_REF
,
0x00
},
{
PCM512x_DAC_ROUTING
,
0x11
},
{
PCM512x_DSP_PROGRAM
,
0x01
},
{
PCM512x_CLKDET
,
0x00
},
{
PCM512x_AUTO_MUTE
,
0x00
},
{
PCM512x_ERROR_DETECT
,
0x00
},
{
PCM512x_DIGITAL_VOLUME_1
,
0x00
},
{
PCM512x_DIGITAL_VOLUME_2
,
0x30
},
{
PCM512x_DIGITAL_VOLUME_3
,
0x30
},
{
PCM512x_DIGITAL_MUTE_1
,
0x22
},
{
PCM512x_DIGITAL_MUTE_2
,
0x00
},
{
PCM512x_DIGITAL_MUTE_3
,
0x07
},
{
PCM512x_OUTPUT_AMPLITUDE
,
0x00
},
{
PCM512x_ANALOG_GAIN_CTRL
,
0x00
},
{
PCM512x_UNDERVOLTAGE_PROT
,
0x00
},
{
PCM512x_ANALOG_MUTE_CTRL
,
0x00
},
{
PCM512x_ANALOG_GAIN_BOOST
,
0x00
},
{
PCM512x_VCOM_CTRL_1
,
0x00
},
{
PCM512x_VCOM_CTRL_2
,
0x01
},
};
static
bool
pcm512x_readable
(
struct
device
*
dev
,
unsigned
int
reg
)
{
switch
(
reg
)
{
case
PCM512x_RESET
:
case
PCM512x_POWER
:
case
PCM512x_MUTE
:
case
PCM512x_PLL_EN
:
case
PCM512x_SPI_MISO_FUNCTION
:
case
PCM512x_DSP
:
case
PCM512x_GPIO_EN
:
case
PCM512x_BCLK_LRCLK_CFG
:
case
PCM512x_DSP_GPIO_INPUT
:
case
PCM512x_MASTER_MODE
:
case
PCM512x_PLL_REF
:
case
PCM512x_PLL_COEFF_0
:
case
PCM512x_PLL_COEFF_1
:
case
PCM512x_PLL_COEFF_2
:
case
PCM512x_PLL_COEFF_3
:
case
PCM512x_PLL_COEFF_4
:
case
PCM512x_DSP_CLKDIV
:
case
PCM512x_DAC_CLKDIV
:
case
PCM512x_NCP_CLKDIV
:
case
PCM512x_OSR_CLKDIV
:
case
PCM512x_MASTER_CLKDIV_1
:
case
PCM512x_MASTER_CLKDIV_2
:
case
PCM512x_FS_SPEED_MODE
:
case
PCM512x_IDAC_1
:
case
PCM512x_IDAC_2
:
case
PCM512x_ERROR_DETECT
:
case
PCM512x_I2S_1
:
case
PCM512x_I2S_2
:
case
PCM512x_DAC_ROUTING
:
case
PCM512x_DSP_PROGRAM
:
case
PCM512x_CLKDET
:
case
PCM512x_AUTO_MUTE
:
case
PCM512x_DIGITAL_VOLUME_1
:
case
PCM512x_DIGITAL_VOLUME_2
:
case
PCM512x_DIGITAL_VOLUME_3
:
case
PCM512x_DIGITAL_MUTE_1
:
case
PCM512x_DIGITAL_MUTE_2
:
case
PCM512x_DIGITAL_MUTE_3
:
case
PCM512x_GPIO_OUTPUT_1
:
case
PCM512x_GPIO_OUTPUT_2
:
case
PCM512x_GPIO_OUTPUT_3
:
case
PCM512x_GPIO_OUTPUT_4
:
case
PCM512x_GPIO_OUTPUT_5
:
case
PCM512x_GPIO_OUTPUT_6
:
case
PCM512x_GPIO_CONTROL_1
:
case
PCM512x_GPIO_CONTROL_2
:
case
PCM512x_OVERFLOW
:
case
PCM512x_RATE_DET_1
:
case
PCM512x_RATE_DET_2
:
case
PCM512x_RATE_DET_3
:
case
PCM512x_RATE_DET_4
:
case
PCM512x_ANALOG_MUTE_DET
:
case
PCM512x_GPIN
:
case
PCM512x_DIGITAL_MUTE_DET
:
case
PCM512x_OUTPUT_AMPLITUDE
:
case
PCM512x_ANALOG_GAIN_CTRL
:
case
PCM512x_UNDERVOLTAGE_PROT
:
case
PCM512x_ANALOG_MUTE_CTRL
:
case
PCM512x_ANALOG_GAIN_BOOST
:
case
PCM512x_VCOM_CTRL_1
:
case
PCM512x_VCOM_CTRL_2
:
case
PCM512x_CRAM_CTRL
:
return
true
;
default:
/* There are 256 raw register addresses */
return
reg
<
0xff
;
}
}
static
bool
pcm512x_volatile
(
struct
device
*
dev
,
unsigned
int
reg
)
{
switch
(
reg
)
{
case
PCM512x_PLL_EN
:
case
PCM512x_OVERFLOW
:
case
PCM512x_RATE_DET_1
:
case
PCM512x_RATE_DET_2
:
case
PCM512x_RATE_DET_3
:
case
PCM512x_RATE_DET_4
:
case
PCM512x_ANALOG_MUTE_DET
:
case
PCM512x_GPIN
:
case
PCM512x_DIGITAL_MUTE_DET
:
case
PCM512x_CRAM_CTRL
:
return
true
;
default:
/* There are 256 raw register addresses */
return
reg
<
0xff
;
}
}
static
const
DECLARE_TLV_DB_SCALE
(
digital_tlv
,
-
10350
,
50
,
1
);
static
const
DECLARE_TLV_DB_SCALE
(
analog_tlv
,
-
600
,
600
,
0
);
static
const
DECLARE_TLV_DB_SCALE
(
boost_tlv
,
0
,
80
,
0
);
static
const
char
*
const
pcm512x_dsp_program_texts
[]
=
{
"FIR interpolation with de-emphasis"
,
"Low latency IIR with de-emphasis"
,
"Fixed process flow"
,
"High attenuation with de-emphasis"
,
"Ringing-less low latency FIR"
,
};
static
const
unsigned
int
pcm512x_dsp_program_values
[]
=
{
1
,
2
,
3
,
5
,
7
,
};
static
SOC_VALUE_ENUM_SINGLE_DECL
(
pcm512x_dsp_program
,
PCM512x_DSP_PROGRAM
,
0
,
0x1f
,
pcm512x_dsp_program_texts
,
pcm512x_dsp_program_values
);
static
const
char
*
const
pcm512x_clk_missing_text
[]
=
{
"1s"
,
"2s"
,
"3s"
,
"4s"
,
"5s"
,
"6s"
,
"7s"
,
"8s"
};
static
const
struct
soc_enum
pcm512x_clk_missing
=
SOC_ENUM_SINGLE
(
PCM512x_CLKDET
,
0
,
8
,
pcm512x_clk_missing_text
);
static
const
char
*
const
pcm512x_autom_text
[]
=
{
"21ms"
,
"106ms"
,
"213ms"
,
"533ms"
,
"1.07s"
,
"2.13s"
,
"5.33s"
,
"10.66s"
};
static
const
struct
soc_enum
pcm512x_autom_l
=
SOC_ENUM_SINGLE
(
PCM512x_AUTO_MUTE
,
PCM512x_ATML_SHIFT
,
8
,
pcm512x_autom_text
);
static
const
struct
soc_enum
pcm512x_autom_r
=
SOC_ENUM_SINGLE
(
PCM512x_AUTO_MUTE
,
PCM512x_ATMR_SHIFT
,
8
,
pcm512x_autom_text
);
static
const
char
*
const
pcm512x_ramp_rate_text
[]
=
{
"1 sample/update"
,
"2 samples/update"
,
"4 samples/update"
,
"Immediate"
};
static
const
struct
soc_enum
pcm512x_vndf
=
SOC_ENUM_SINGLE
(
PCM512x_DIGITAL_MUTE_1
,
PCM512x_VNDF_SHIFT
,
4
,
pcm512x_ramp_rate_text
);
static
const
struct
soc_enum
pcm512x_vnuf
=
SOC_ENUM_SINGLE
(
PCM512x_DIGITAL_MUTE_1
,
PCM512x_VNUF_SHIFT
,
4
,
pcm512x_ramp_rate_text
);
static
const
struct
soc_enum
pcm512x_vedf
=
SOC_ENUM_SINGLE
(
PCM512x_DIGITAL_MUTE_2
,
PCM512x_VEDF_SHIFT
,
4
,
pcm512x_ramp_rate_text
);
static
const
char
*
const
pcm512x_ramp_step_text
[]
=
{
"4dB/step"
,
"2dB/step"
,
"1dB/step"
,
"0.5dB/step"
};
static
const
struct
soc_enum
pcm512x_vnds
=
SOC_ENUM_SINGLE
(
PCM512x_DIGITAL_MUTE_1
,
PCM512x_VNDS_SHIFT
,
4
,
pcm512x_ramp_step_text
);
static
const
struct
soc_enum
pcm512x_vnus
=
SOC_ENUM_SINGLE
(
PCM512x_DIGITAL_MUTE_1
,
PCM512x_VNUS_SHIFT
,
4
,
pcm512x_ramp_step_text
);
static
const
struct
soc_enum
pcm512x_veds
=
SOC_ENUM_SINGLE
(
PCM512x_DIGITAL_MUTE_2
,
PCM512x_VEDS_SHIFT
,
4
,
pcm512x_ramp_step_text
);
static
const
struct
snd_kcontrol_new
pcm512x_controls
[]
=
{
SOC_DOUBLE_R_TLV
(
"Playback Digital Volume"
,
PCM512x_DIGITAL_VOLUME_2
,
PCM512x_DIGITAL_VOLUME_3
,
0
,
255
,
1
,
digital_tlv
),
SOC_DOUBLE_TLV
(
"Playback Volume"
,
PCM512x_ANALOG_GAIN_CTRL
,
PCM512x_LAGN_SHIFT
,
PCM512x_RAGN_SHIFT
,
1
,
1
,
analog_tlv
),
SOC_DOUBLE_TLV
(
"Playback Boost Volume"
,
PCM512x_ANALOG_GAIN_BOOST
,
PCM512x_AGBL_SHIFT
,
PCM512x_AGBR_SHIFT
,
1
,
0
,
boost_tlv
),
SOC_DOUBLE
(
"Playback Digital Switch"
,
PCM512x_MUTE
,
PCM512x_RQML_SHIFT
,
PCM512x_RQMR_SHIFT
,
1
,
1
),
SOC_SINGLE
(
"Deemphasis Switch"
,
PCM512x_DSP
,
PCM512x_DEMP_SHIFT
,
1
,
1
),
SOC_VALUE_ENUM
(
"DSP Program"
,
pcm512x_dsp_program
),
SOC_ENUM
(
"Clock Missing Period"
,
pcm512x_clk_missing
),
SOC_ENUM
(
"Auto Mute Time Left"
,
pcm512x_autom_l
),
SOC_ENUM
(
"Auto Mute Time Right"
,
pcm512x_autom_r
),
SOC_SINGLE
(
"Auto Mute Mono Switch"
,
PCM512x_DIGITAL_MUTE_3
,
PCM512x_ACTL_SHIFT
,
1
,
0
),
SOC_DOUBLE
(
"Auto Mute Switch"
,
PCM512x_DIGITAL_MUTE_3
,
PCM512x_AMLE_SHIFT
,
PCM512x_AMLR_SHIFT
,
1
,
0
),
SOC_ENUM
(
"Volume Ramp Down Rate"
,
pcm512x_vndf
),
SOC_ENUM
(
"Volume Ramp Down Step"
,
pcm512x_vnds
),
SOC_ENUM
(
"Volume Ramp Up Rate"
,
pcm512x_vnuf
),
SOC_ENUM
(
"Volume Ramp Up Step"
,
pcm512x_vnus
),
SOC_ENUM
(
"Volume Ramp Down Emergency Rate"
,
pcm512x_vedf
),
SOC_ENUM
(
"Volume Ramp Down Emergency Step"
,
pcm512x_veds
),
};
static
const
struct
snd_soc_dapm_widget
pcm512x_dapm_widgets
[]
=
{
SND_SOC_DAPM_DAC
(
"DACL"
,
NULL
,
SND_SOC_NOPM
,
0
,
0
),
SND_SOC_DAPM_DAC
(
"DACR"
,
NULL
,
SND_SOC_NOPM
,
0
,
0
),
SND_SOC_DAPM_OUTPUT
(
"OUTL"
),
SND_SOC_DAPM_OUTPUT
(
"OUTR"
),
};
static
const
struct
snd_soc_dapm_route
pcm512x_dapm_routes
[]
=
{
{
"DACL"
,
NULL
,
"Playback"
},
{
"DACR"
,
NULL
,
"Playback"
},
{
"OUTL"
,
NULL
,
"DACL"
},
{
"OUTR"
,
NULL
,
"DACR"
},
};
static
int
pcm512x_set_bias_level
(
struct
snd_soc_codec
*
codec
,
enum
snd_soc_bias_level
level
)
{
struct
pcm512x_priv
*
pcm512x
=
dev_get_drvdata
(
codec
->
dev
);
int
ret
;
switch
(
level
)
{
case
SND_SOC_BIAS_ON
:
case
SND_SOC_BIAS_PREPARE
:
break
;
case
SND_SOC_BIAS_STANDBY
:
ret
=
regmap_update_bits
(
pcm512x
->
regmap
,
PCM512x_POWER
,
PCM512x_RQST
,
0
);
if
(
ret
!=
0
)
{
dev_err
(
codec
->
dev
,
"Failed to remove standby: %d
\n
"
,
ret
);
return
ret
;
}
break
;
case
SND_SOC_BIAS_OFF
:
ret
=
regmap_update_bits
(
pcm512x
->
regmap
,
PCM512x_POWER
,
PCM512x_RQST
,
PCM512x_RQST
);
if
(
ret
!=
0
)
{
dev_err
(
codec
->
dev
,
"Failed to request standby: %d
\n
"
,
ret
);
return
ret
;
}
break
;
}
codec
->
dapm
.
bias_level
=
level
;
return
0
;
}
static
struct
snd_soc_dai_driver
pcm512x_dai
=
{
.
name
=
"pcm512x-hifi"
,
.
playback
=
{
.
stream_name
=
"Playback"
,
.
channels_min
=
2
,
.
channels_max
=
2
,
.
rates
=
SNDRV_PCM_RATE_8000_192000
,
.
formats
=
SNDRV_PCM_FMTBIT_S16_LE
|
SNDRV_PCM_FMTBIT_S24_LE
|
SNDRV_PCM_FMTBIT_S32_LE
},
};
static
struct
snd_soc_codec_driver
pcm512x_codec_driver
=
{
.
set_bias_level
=
pcm512x_set_bias_level
,
.
idle_bias_off
=
true
,
.
controls
=
pcm512x_controls
,
.
num_controls
=
ARRAY_SIZE
(
pcm512x_controls
),
.
dapm_widgets
=
pcm512x_dapm_widgets
,
.
num_dapm_widgets
=
ARRAY_SIZE
(
pcm512x_dapm_widgets
),
.
dapm_routes
=
pcm512x_dapm_routes
,
.
num_dapm_routes
=
ARRAY_SIZE
(
pcm512x_dapm_routes
),
};
static
const
struct
regmap_range_cfg
pcm512x_range
=
{
.
name
=
"Pages"
,
.
range_min
=
PCM512x_VIRT_BASE
,
.
range_max
=
PCM512x_MAX_REGISTER
,
.
selector_reg
=
PCM512x_PAGE
,
.
selector_mask
=
0xff
,
.
window_start
=
0
,
.
window_len
=
0x100
,
};
const
struct
regmap_config
pcm512x_regmap
=
{
.
reg_bits
=
8
,
.
val_bits
=
8
,
.
readable_reg
=
pcm512x_readable
,
.
volatile_reg
=
pcm512x_volatile
,
.
ranges
=
&
pcm512x_range
,
.
num_ranges
=
1
,
.
max_register
=
PCM512x_MAX_REGISTER
,
.
reg_defaults
=
pcm512x_reg_defaults
,
.
num_reg_defaults
=
ARRAY_SIZE
(
pcm512x_reg_defaults
),
.
cache_type
=
REGCACHE_RBTREE
,
};
EXPORT_SYMBOL_GPL
(
pcm512x_regmap
);
int
pcm512x_probe
(
struct
device
*
dev
,
struct
regmap
*
regmap
)
{
struct
pcm512x_priv
*
pcm512x
;
int
i
,
ret
;
pcm512x
=
devm_kzalloc
(
dev
,
sizeof
(
struct
pcm512x_priv
),
GFP_KERNEL
);
if
(
!
pcm512x
)
return
-
ENOMEM
;
dev_set_drvdata
(
dev
,
pcm512x
);
pcm512x
->
regmap
=
regmap
;
for
(
i
=
0
;
i
<
ARRAY_SIZE
(
pcm512x
->
supplies
);
i
++
)
pcm512x
->
supplies
[
i
].
supply
=
pcm512x_supply_names
[
i
];
ret
=
devm_regulator_bulk_get
(
dev
,
ARRAY_SIZE
(
pcm512x
->
supplies
),
pcm512x
->
supplies
);
if
(
ret
!=
0
)
{
dev_err
(
dev
,
"Failed to get supplies: %d
\n
"
,
ret
);
return
ret
;
}
pcm512x
->
supply_nb
[
0
].
notifier_call
=
pcm512x_regulator_event_0
;
pcm512x
->
supply_nb
[
1
].
notifier_call
=
pcm512x_regulator_event_1
;
pcm512x
->
supply_nb
[
2
].
notifier_call
=
pcm512x_regulator_event_2
;
for
(
i
=
0
;
i
<
ARRAY_SIZE
(
pcm512x
->
supplies
);
i
++
)
{
ret
=
regulator_register_notifier
(
pcm512x
->
supplies
[
i
].
consumer
,
&
pcm512x
->
supply_nb
[
i
]);
if
(
ret
!=
0
)
{
dev_err
(
dev
,
"Failed to register regulator notifier: %d
\n
"
,
ret
);
}
}
ret
=
regulator_bulk_enable
(
ARRAY_SIZE
(
pcm512x
->
supplies
),
pcm512x
->
supplies
);
if
(
ret
!=
0
)
{
dev_err
(
dev
,
"Failed to enable supplies: %d
\n
"
,
ret
);
return
ret
;
}
/* Reset the device, verifying I/O in the process for I2C */
ret
=
regmap_write
(
regmap
,
PCM512x_RESET
,
PCM512x_RSTM
|
PCM512x_RSTR
);
if
(
ret
!=
0
)
{
dev_err
(
dev
,
"Failed to reset device: %d
\n
"
,
ret
);
goto
err
;
}
ret
=
regmap_write
(
regmap
,
PCM512x_RESET
,
0
);
if
(
ret
!=
0
)
{
dev_err
(
dev
,
"Failed to reset device: %d
\n
"
,
ret
);
goto
err
;
}
pcm512x
->
sclk
=
devm_clk_get
(
dev
,
NULL
);
if
(
IS_ERR
(
pcm512x
->
sclk
))
{
if
(
PTR_ERR
(
pcm512x
->
sclk
)
==
-
EPROBE_DEFER
)
return
-
EPROBE_DEFER
;
dev_info
(
dev
,
"No SCLK, using BCLK: %ld
\n
"
,
PTR_ERR
(
pcm512x
->
sclk
));
/* Disable reporting of missing SCLK as an error */
regmap_update_bits
(
regmap
,
PCM512x_ERROR_DETECT
,
PCM512x_IDCH
,
PCM512x_IDCH
);
/* Switch PLL input to BCLK */
regmap_update_bits
(
regmap
,
PCM512x_PLL_REF
,
PCM512x_SREF
,
PCM512x_SREF
);
}
else
{
ret
=
clk_prepare_enable
(
pcm512x
->
sclk
);
if
(
ret
!=
0
)
{
dev_err
(
dev
,
"Failed to enable SCLK: %d
\n
"
,
ret
);
return
ret
;
}
}
/* Default to standby mode */
ret
=
regmap_update_bits
(
pcm512x
->
regmap
,
PCM512x_POWER
,
PCM512x_RQST
,
PCM512x_RQST
);
if
(
ret
!=
0
)
{
dev_err
(
dev
,
"Failed to request standby: %d
\n
"
,
ret
);
goto
err_clk
;
}
pm_runtime_set_active
(
dev
);
pm_runtime_enable
(
dev
);
pm_runtime_idle
(
dev
);
ret
=
snd_soc_register_codec
(
dev
,
&
pcm512x_codec_driver
,
&
pcm512x_dai
,
1
);
if
(
ret
!=
0
)
{
dev_err
(
dev
,
"Failed to register CODEC: %d
\n
"
,
ret
);
goto
err_pm
;
}
return
0
;
err_pm:
pm_runtime_disable
(
dev
);
err_clk:
if
(
!
IS_ERR
(
pcm512x
->
sclk
))
clk_disable_unprepare
(
pcm512x
->
sclk
);
err:
regulator_bulk_disable
(
ARRAY_SIZE
(
pcm512x
->
supplies
),
pcm512x
->
supplies
);
return
ret
;
}
EXPORT_SYMBOL_GPL
(
pcm512x_probe
);
void
pcm512x_remove
(
struct
device
*
dev
)
{
struct
pcm512x_priv
*
pcm512x
=
dev_get_drvdata
(
dev
);
snd_soc_unregister_codec
(
dev
);
pm_runtime_disable
(
dev
);
if
(
!
IS_ERR
(
pcm512x
->
sclk
))
clk_disable_unprepare
(
pcm512x
->
sclk
);
regulator_bulk_disable
(
ARRAY_SIZE
(
pcm512x
->
supplies
),
pcm512x
->
supplies
);
}
EXPORT_SYMBOL_GPL
(
pcm512x_remove
);
static
int
pcm512x_suspend
(
struct
device
*
dev
)
{
struct
pcm512x_priv
*
pcm512x
=
dev_get_drvdata
(
dev
);
int
ret
;
ret
=
regmap_update_bits
(
pcm512x
->
regmap
,
PCM512x_POWER
,
PCM512x_RQPD
,
PCM512x_RQPD
);
if
(
ret
!=
0
)
{
dev_err
(
dev
,
"Failed to request power down: %d
\n
"
,
ret
);
return
ret
;
}
ret
=
regulator_bulk_disable
(
ARRAY_SIZE
(
pcm512x
->
supplies
),
pcm512x
->
supplies
);
if
(
ret
!=
0
)
{
dev_err
(
dev
,
"Failed to disable supplies: %d
\n
"
,
ret
);
return
ret
;
}
if
(
!
IS_ERR
(
pcm512x
->
sclk
))
clk_disable_unprepare
(
pcm512x
->
sclk
);
return
0
;
}
static
int
pcm512x_resume
(
struct
device
*
dev
)
{
struct
pcm512x_priv
*
pcm512x
=
dev_get_drvdata
(
dev
);
int
ret
;
if
(
!
IS_ERR
(
pcm512x
->
sclk
))
{
ret
=
clk_prepare_enable
(
pcm512x
->
sclk
);
if
(
ret
!=
0
)
{
dev_err
(
dev
,
"Failed to enable SCLK: %d
\n
"
,
ret
);
return
ret
;
}
}
ret
=
regulator_bulk_enable
(
ARRAY_SIZE
(
pcm512x
->
supplies
),
pcm512x
->
supplies
);
if
(
ret
!=
0
)
{
dev_err
(
dev
,
"Failed to enable supplies: %d
\n
"
,
ret
);
return
ret
;
}
regcache_cache_only
(
pcm512x
->
regmap
,
false
);
ret
=
regcache_sync
(
pcm512x
->
regmap
);
if
(
ret
!=
0
)
{
dev_err
(
dev
,
"Failed to sync cache: %d
\n
"
,
ret
);
return
ret
;
}
ret
=
regmap_update_bits
(
pcm512x
->
regmap
,
PCM512x_POWER
,
PCM512x_RQPD
,
0
);
if
(
ret
!=
0
)
{
dev_err
(
dev
,
"Failed to remove power down: %d
\n
"
,
ret
);
return
ret
;
}
return
0
;
}
const
struct
dev_pm_ops
pcm512x_pm_ops
=
{
SET_RUNTIME_PM_OPS
(
pcm512x_suspend
,
pcm512x_resume
,
NULL
)
};
EXPORT_SYMBOL_GPL
(
pcm512x_pm_ops
);
MODULE_DESCRIPTION
(
"ASoC PCM512x codec driver"
);
MODULE_AUTHOR
(
"Mark Brown <broonie@linaro.org>"
);
MODULE_LICENSE
(
"GPL v2"
);
sound/soc/codecs/pcm512x.h
0 → 100644
View file @
52db65f0
/*
* Driver for the PCM512x CODECs
*
* Author: Mark Brown <broonie@linaro.org>
* Copyright 2014 Linaro Ltd
*
* 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 _SND_SOC_PCM512X
#define _SND_SOC_PCM512X
#include <linux/pm.h>
#include <linux/regmap.h>
#define PCM512x_VIRT_BASE 0x100
#define PCM512x_PAGE_LEN 0x100
#define PCM512x_PAGE_BASE(n) (PCM512x_VIRT_BASE + (PCM512x_PAGE_LEN * n))
#define PCM512x_PAGE 0
#define PCM512x_RESET (PCM512x_PAGE_BASE(0) + 1)
#define PCM512x_POWER (PCM512x_PAGE_BASE(0) + 2)
#define PCM512x_MUTE (PCM512x_PAGE_BASE(0) + 3)
#define PCM512x_PLL_EN (PCM512x_PAGE_BASE(0) + 4)
#define PCM512x_SPI_MISO_FUNCTION (PCM512x_PAGE_BASE(0) + 6)
#define PCM512x_DSP (PCM512x_PAGE_BASE(0) + 7)
#define PCM512x_GPIO_EN (PCM512x_PAGE_BASE(0) + 8)
#define PCM512x_BCLK_LRCLK_CFG (PCM512x_PAGE_BASE(0) + 9)
#define PCM512x_DSP_GPIO_INPUT (PCM512x_PAGE_BASE(0) + 10)
#define PCM512x_MASTER_MODE (PCM512x_PAGE_BASE(0) + 12)
#define PCM512x_PLL_REF (PCM512x_PAGE_BASE(0) + 13)
#define PCM512x_PLL_COEFF_0 (PCM512x_PAGE_BASE(0) + 20)
#define PCM512x_PLL_COEFF_1 (PCM512x_PAGE_BASE(0) + 21)
#define PCM512x_PLL_COEFF_2 (PCM512x_PAGE_BASE(0) + 22)
#define PCM512x_PLL_COEFF_3 (PCM512x_PAGE_BASE(0) + 23)
#define PCM512x_PLL_COEFF_4 (PCM512x_PAGE_BASE(0) + 24)
#define PCM512x_DSP_CLKDIV (PCM512x_PAGE_BASE(0) + 27)
#define PCM512x_DAC_CLKDIV (PCM512x_PAGE_BASE(0) + 28)
#define PCM512x_NCP_CLKDIV (PCM512x_PAGE_BASE(0) + 29)
#define PCM512x_OSR_CLKDIV (PCM512x_PAGE_BASE(0) + 30)
#define PCM512x_MASTER_CLKDIV_1 (PCM512x_PAGE_BASE(0) + 32)
#define PCM512x_MASTER_CLKDIV_2 (PCM512x_PAGE_BASE(0) + 33)
#define PCM512x_FS_SPEED_MODE (PCM512x_PAGE_BASE(0) + 34)
#define PCM512x_IDAC_1 (PCM512x_PAGE_BASE(0) + 35)
#define PCM512x_IDAC_2 (PCM512x_PAGE_BASE(0) + 36)
#define PCM512x_ERROR_DETECT (PCM512x_PAGE_BASE(0) + 37)
#define PCM512x_I2S_1 (PCM512x_PAGE_BASE(0) + 40)
#define PCM512x_I2S_2 (PCM512x_PAGE_BASE(0) + 41)
#define PCM512x_DAC_ROUTING (PCM512x_PAGE_BASE(0) + 42)
#define PCM512x_DSP_PROGRAM (PCM512x_PAGE_BASE(0) + 43)
#define PCM512x_CLKDET (PCM512x_PAGE_BASE(0) + 44)
#define PCM512x_AUTO_MUTE (PCM512x_PAGE_BASE(0) + 59)
#define PCM512x_DIGITAL_VOLUME_1 (PCM512x_PAGE_BASE(0) + 60)
#define PCM512x_DIGITAL_VOLUME_2 (PCM512x_PAGE_BASE(0) + 61)
#define PCM512x_DIGITAL_VOLUME_3 (PCM512x_PAGE_BASE(0) + 62)
#define PCM512x_DIGITAL_MUTE_1 (PCM512x_PAGE_BASE(0) + 63)
#define PCM512x_DIGITAL_MUTE_2 (PCM512x_PAGE_BASE(0) + 64)
#define PCM512x_DIGITAL_MUTE_3 (PCM512x_PAGE_BASE(0) + 65)
#define PCM512x_GPIO_OUTPUT_1 (PCM512x_PAGE_BASE(0) + 80)
#define PCM512x_GPIO_OUTPUT_2 (PCM512x_PAGE_BASE(0) + 81)
#define PCM512x_GPIO_OUTPUT_3 (PCM512x_PAGE_BASE(0) + 82)
#define PCM512x_GPIO_OUTPUT_4 (PCM512x_PAGE_BASE(0) + 83)
#define PCM512x_GPIO_OUTPUT_5 (PCM512x_PAGE_BASE(0) + 84)
#define PCM512x_GPIO_OUTPUT_6 (PCM512x_PAGE_BASE(0) + 85)
#define PCM512x_GPIO_CONTROL_1 (PCM512x_PAGE_BASE(0) + 86)
#define PCM512x_GPIO_CONTROL_2 (PCM512x_PAGE_BASE(0) + 87)
#define PCM512x_OVERFLOW (PCM512x_PAGE_BASE(0) + 90)
#define PCM512x_RATE_DET_1 (PCM512x_PAGE_BASE(0) + 91)
#define PCM512x_RATE_DET_2 (PCM512x_PAGE_BASE(0) + 92)
#define PCM512x_RATE_DET_3 (PCM512x_PAGE_BASE(0) + 93)
#define PCM512x_RATE_DET_4 (PCM512x_PAGE_BASE(0) + 94)
#define PCM512x_ANALOG_MUTE_DET (PCM512x_PAGE_BASE(0) + 108)
#define PCM512x_GPIN (PCM512x_PAGE_BASE(0) + 119)
#define PCM512x_DIGITAL_MUTE_DET (PCM512x_PAGE_BASE(0) + 120)
#define PCM512x_OUTPUT_AMPLITUDE (PCM512x_PAGE_BASE(1) + 1)
#define PCM512x_ANALOG_GAIN_CTRL (PCM512x_PAGE_BASE(1) + 2)
#define PCM512x_UNDERVOLTAGE_PROT (PCM512x_PAGE_BASE(1) + 5)
#define PCM512x_ANALOG_MUTE_CTRL (PCM512x_PAGE_BASE(1) + 6)
#define PCM512x_ANALOG_GAIN_BOOST (PCM512x_PAGE_BASE(1) + 7)
#define PCM512x_VCOM_CTRL_1 (PCM512x_PAGE_BASE(1) + 8)
#define PCM512x_VCOM_CTRL_2 (PCM512x_PAGE_BASE(1) + 9)
#define PCM512x_CRAM_CTRL (PCM512x_PAGE_BASE(44) + 1)
#define PCM512x_MAX_REGISTER (PCM512x_PAGE_BASE(44) + 1)
/* Page 0, Register 1 - reset */
#define PCM512x_RSTR (1 << 0)
#define PCM512x_RSTM (1 << 4)
/* Page 0, Register 2 - power */
#define PCM512x_RQPD (1 << 0)
#define PCM512x_RQPD_SHIFT 0
#define PCM512x_RQST (1 << 4)
#define PCM512x_RQST_SHIFT 4
/* Page 0, Register 3 - mute */
#define PCM512x_RQMR_SHIFT 0
#define PCM512x_RQML_SHIFT 4
/* Page 0, Register 4 - PLL */
#define PCM512x_PLCE (1 << 0)
#define PCM512x_RLCE_SHIFT 0
#define PCM512x_PLCK (1 << 4)
#define PCM512x_PLCK_SHIFT 4
/* Page 0, Register 7 - DSP */
#define PCM512x_SDSL (1 << 0)
#define PCM512x_SDSL_SHIFT 0
#define PCM512x_DEMP (1 << 4)
#define PCM512x_DEMP_SHIFT 4
/* Page 0, Register 13 - PLL reference */
#define PCM512x_SREF (1 << 4)
/* Page 0, Register 37 - Error detection */
#define PCM512x_IPLK (1 << 0)
#define PCM512x_DCAS (1 << 1)
#define PCM512x_IDCM (1 << 2)
#define PCM512x_IDCH (1 << 3)
#define PCM512x_IDSK (1 << 4)
#define PCM512x_IDBK (1 << 5)
#define PCM512x_IDFS (1 << 6)
/* Page 0, Register 42 - DAC routing */
#define PCM512x_AUPR_SHIFT 0
#define PCM512x_AUPL_SHIFT 4
/* Page 0, Register 59 - auto mute */
#define PCM512x_ATMR_SHIFT 0
#define PCM512x_ATML_SHIFT 4
/* Page 0, Register 63 - ramp rates */
#define PCM512x_VNDF_SHIFT 6
#define PCM512x_VNDS_SHIFT 4
#define PCM512x_VNUF_SHIFT 2
#define PCM512x_VNUS_SHIFT 0
/* Page 0, Register 64 - emergency ramp rates */
#define PCM512x_VEDF_SHIFT 6
#define PCM512x_VEDS_SHIFT 4
/* Page 0, Register 65 - Digital mute enables */
#define PCM512x_ACTL_SHIFT 2
#define PCM512x_AMLE_SHIFT 1
#define PCM512x_AMLR_SHIFT 0
/* Page 1, Register 2 - analog volume control */
#define PCM512x_RAGN_SHIFT 0
#define PCM512x_LAGN_SHIFT 4
/* Page 1, Register 7 - analog boost control */
#define PCM512x_AGBR_SHIFT 0
#define PCM512x_AGBL_SHIFT 4
extern
const
struct
dev_pm_ops
pcm512x_pm_ops
;
extern
const
struct
regmap_config
pcm512x_regmap
;
int
pcm512x_probe
(
struct
device
*
dev
,
struct
regmap
*
regmap
);
void
pcm512x_remove
(
struct
device
*
dev
);
#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