Merge remote-tracking branches 'asoc/topic/rcar', 'asoc/topic/rockchip', 'asoc/topic/rt298', 'asoc/topic/rt5514' and 'asoc/topic/rt5616' into asoc-next

This commit is contained in:
Mark Brown 2016-03-13 15:17:35 +07:00
30 changed files with 2967 additions and 920 deletions

View File

@ -1,6 +1,337 @@
Renesas R-Car sound
=============================================
* Modules
=============================================
Renesas R-Car sound is constructed from below modules
(for Gen2 or later)
SCU : Sampling Rate Converter Unit
- SRC : Sampling Rate Converter
- CMD
- CTU : Channel Transfer Unit
- MIX : Mixer
- DVC : Digital Volume and Mute Function
SSIU : Serial Sound Interface Unit
SSI : Serial Sound Interface
See detail of each module's channels, connection, limitation on datasheet
=============================================
* Multi channel
=============================================
Multi channel is supported by Multi-SSI, or TDM-SSI.
Multi-SSI : 6ch case, you can use stereo x 3 SSI
TDM-SSI : 6ch case, you can use TDM
=============================================
* Enable/Disable each modules
=============================================
See datasheet to check SRC/CTU/MIX/DVC connect-limitation.
DT controls enabling/disabling module.
${LINUX}/arch/arm/boot/dts/r8a7790-lager.dts can be good example.
This is example of
Playback: [MEM] -> [SRC2] -> [DVC0] -> [SSIU0/SSI0] -> [codec]
Capture: [MEM] <- [DVC1] <- [SRC3] <- [SSIU1/SSI1] <- [codec]
&rcar_sound {
...
rcar_sound,dai {
dai0 {
playback = <&ssi0 &src2 &dvc0>;
capture = <&ssi1 &src3 &dvc1>;
};
};
};
You can use below.
${LINUX}/arch/arm/boot/dts/r8a7790.dts can be good example.
&src0 &ctu00 &mix0 &dvc0 &ssi0
&src1 &ctu01 &mix1 &dvc1 &ssi1
&src2 &ctu02 &ssi2
&src3 &ctu03 &ssi3
&src4 &ssi4
&src5 &ctu10 &ssi5
&src6 &ctu11 &ssi6
&src7 &ctu12 &ssi7
&src8 &ctu13 &ssi8
&src9 &ssi9
=============================================
* SRC (Sampling Rate Converter)
=============================================
[xx]Hz [yy]Hz
------> [SRC] ------>
SRC can convert [xx]Hz to [yy]Hz. Then, it has below 2 modes
Asynchronous mode: input data / output data are based on different clocks.
you can use this mode on Playback / Capture
Synchronous mode: input data / output data are based on same clocks.
This mode will be used if system doesn't have its input clock,
for example digital TV case.
you can use this mode on Playback
------------------
** Asynchronous mode
------------------
You need to use "renesas,rsrc-card" sound card for it.
example)
sound {
compatible = "renesas,rsrc-card";
...
/*
* SRC Asynchronous mode setting
* Playback:
* All input data will be converted to 48kHz
* Capture:
* Inputed 48kHz data will be converted to
* system specified Hz
*/
convert-rate = <48000>;
...
cpu {
sound-dai = <&rcar_sound>;
};
codec {
...
};
};
------------------
** Synchronous mode
------------------
> amixer set "SRC Out Rate" on
> aplay xxxx.wav
> amixer set "SRC Out Rate" 48000
> amixer set "SRC Out Rate" 44100
=============================================
* CTU (Channel Transfer Unit)
=============================================
[xx]ch [yy]ch
------> [CTU] -------->
CTU can convert [xx]ch to [yy]ch, or exchange outputed channel.
CTU conversion needs matrix settings.
For more detail information, see below
Renesas R-Car datasheet
- Sampling Rate Converter Unit (SCU)
- SCU Operation
- CMD Block
- Functional Blocks in CMD
Renesas R-Car datasheet
- Sampling Rate Converter Unit (SCU)
- Register Description
- CTUn Scale Value exx Register (CTUn_SVxxR)
${LINUX}/sound/soc/sh/rcar/ctu.c
- comment of header
You need to use "renesas,rsrc-card" sound card for it.
example)
sound {
compatible = "renesas,rsrc-card";
...
/*
* CTU setting
* All input data will be converted to 2ch
* as output data
*/
convert-channels = <2>;
...
cpu {
sound-dai = <&rcar_sound>;
};
codec {
...
};
};
Ex) Exchange output channel
Input -> Output
1ch -> 0ch
0ch -> 1ch
example of using matrix
output 0ch = (input 0ch x 0) + (input 1ch x 1)
output 1ch = (input 0ch x 1) + (input 1ch x 0)
amixer set "CTU Reset" on
amixer set "CTU Pass" 9,10
amixer set "CTU SV0" 0,4194304
amixer set "CTU SV1" 4194304,0
example of changing connection
amixer set "CTU Reset" on
amixer set "CTU Pass" 2,1
=============================================
* MIX (Mixer)
=============================================
MIX merges 2 sounds path. You can see 2 sound interface on system,
and these sounds will be merged by MIX.
aplay -D plughw:0,0 xxxx.wav &
aplay -D plughw:0,1 yyyy.wav
You need to use "renesas,rsrc-card" sound card for it.
Ex)
[MEM] -> [SRC1] -> [CTU02] -+-> [MIX0] -> [DVC0] -> [SSI0]
|
[MEM] -> [SRC2] -> [CTU03] -+
sound {
compatible = "renesas,rsrc-card";
...
cpu@0 {
sound-dai = <&rcar_sound 0>;
};
cpu@1 {
sound-dai = <&rcar_sound 1>;
};
codec {
...
};
};
&rcar_sound {
...
rcar_sound,dai {
dai0 {
playback = <&src1 &ctu02 &mix0 &dvc0 &ssi0>;
};
dai1 {
playback = <&src2 &ctu03 &mix0 &dvc0 &ssi0>;
};
};
};
=============================================
* DVC (Digital Volume and Mute Function)
=============================================
DVC controls Playback/Capture volume.
Playback Volume
amixer set "DVC Out" 100%
Capture Volume
amixer set "DVC In" 100%
Playback Mute
amixer set "DVC Out Mute" on
Capture Mute
amixer set "DVC In Mute" on
Volume Ramp
amixer set "DVC Out Ramp Up Rate" "0.125 dB/64 steps"
amixer set "DVC Out Ramp Down Rate" "0.125 dB/512 steps"
amixer set "DVC Out Ramp" on
aplay xxx.wav &
amixer set "DVC Out" 80% // Volume Down
amixer set "DVC Out" 100% // Volume Up
=============================================
* SSIU (Serial Sound Interface Unit)
=============================================
There is no DT settings for SSIU, because SSIU will be automatically
selected via SSI.
SSIU can avoid some under/over run error, because it has some buffer.
But you can't use it if SSI was PIO mode.
In DMA mode, you can select not to use SSIU by using "no-busif" on DT.
&ssi0 {
no-busif;
};
=============================================
* SSI (Serial Sound Interface)
=============================================
** PIO mode
You can use PIO mode which is for connection check by using.
Note: The system will drop non-SSI modules in PIO mode
even though if DT is selecting other modules.
&ssi0 {
pio-transfer
};
** DMA mode without SSIU
You can use DMA without SSIU.
Note: under/over run, or noise are likely to occur
&ssi0 {
no-busif;
};
** PIN sharing
Each SSI can share WS pin. It is based on platform.
This is example if SSI1 want to share WS pin with SSI0
&ssi1 {
shared-pin;
};
** Multi-SSI
You can use Multi-SSI.
This is example of SSI0/SSI1/SSI2 (= for 6ch)
&rcar_sound {
...
rcar_sound,dai {
dai0 {
playback = <&ssi0 &ssi1 &ssi2 &src0 &dvc0>;
};
};
};
** TDM-SSI
You can use TDM with SSI.
This is example of TDM 6ch.
Driver can automatically switches TDM <-> stereo mode in this case.
rsnd_tdm: sound {
compatible = "simple-audio-card";
...
simple-audio-card,cpu {
/* system can use TDM 6ch */
dai-tdm-slot-num = <6>;
sound-dai = <&rcar_sound>;
};
simple-audio-card,codec {
...
};
};
=============================================
Required properties:
=============================================
- compatible : "renesas,rcar_sound-<soctype>", fallbacks
"renesas,rcar_sound-gen1" if generation1, and
"renesas,rcar_sound-gen2" if generation2
@ -64,7 +395,10 @@ DAI subnode properties:
- playback : list of playback modules
- capture : list of capture modules
=============================================
Example:
=============================================
rcar_sound: sound@ec500000 {
#sound-dai-cells = <1>;
@ -250,7 +584,9 @@ rcar_sound: sound@ec500000 {
};
};
=============================================
Example: simple sound card
=============================================
rsnd_ak4643: sound {
compatible = "simple-audio-card";
@ -290,7 +626,9 @@ Example: simple sound card
shared-pin;
};
=============================================
Example: simple sound card for TDM
=============================================
rsnd_tdm: sound {
compatible = "simple-audio-card";
@ -309,7 +647,9 @@ Example: simple sound card for TDM
};
};
=============================================
Example: simple sound card for Multi channel
=============================================
&rcar_sound {
pinctrl-0 = <&sound_pins &sound_clk_pins>;

View File

@ -30,6 +30,7 @@ Optional subnode properties:
- frame-inversion : bool property. Add this if the
dai-link uses frame clock inversion.
- convert-rate : platform specified sampling rate convert
- convert-channels : platform specified converted channel size (2 - 8 ch)
- audio-prefix : see audio-routing
- audio-routing : A list of the connections between audio components.
Each entry is a pair of strings, the first being the connection's sink,

View File

@ -9,6 +9,7 @@ Required properties:
- "rockchip,rk3066-i2s": for rk3066
- "rockchip,rk3188-i2s", "rockchip,rk3066-i2s": for rk3188
- "rockchip,rk3288-i2s", "rockchip,rk3066-i2s": for rk3288
- "rockchip,rk3399-i2s", "rockchip,rk3066-i2s": for rk3399
- reg: physical base address of the controller and length of memory mapped
region.
- interrupts: should contain the I2S interrupt.

View File

@ -7,8 +7,12 @@ a fibre cable.
Required properties:
- compatible: should be one of the following:
- "rockchip,rk3288-spdif", "rockchip,rk3188-spdif" or
"rockchip,rk3066-spdif"
- "rockchip,rk3066-spdif"
- "rockchip,rk3188-spdif"
- "rockchip,rk3288-spdif"
- "rockchip,rk3366-spdif"
- "rockchip,rk3368-spdif"
- "rockchip,rk3399-spdif"
- reg: physical base address of the controller and length of memory mapped
region.
- interrupts: should contain the SPDIF interrupt.

View File

@ -0,0 +1,25 @@
RT5514 audio CODEC
This device supports I2C only.
Required properties:
- compatible : "realtek,rt5514".
- reg : The I2C address of the device.
Pins on the device (for linking into audio routes) for RT5514:
* DMIC1L
* DMIC1R
* DMIC2L
* DMIC2R
* AMICL
* AMICR
Example:
codec: rt5514@57 {
compatible = "realtek,rt5514";
reg = <0x57>;
};

View File

@ -8,6 +8,12 @@ Required properties:
- reg : The I2C address of the device.
Optional properties:
- clocks: The phandle of the master clock to the CODEC.
- clock-names: Should be "mclk".
Pins on the device (for linking into audio routes) for RT5616:
* IN1P

View File

@ -1692,100 +1692,63 @@ int regmap_raw_write(struct regmap *map, unsigned int reg,
EXPORT_SYMBOL_GPL(regmap_raw_write);
/**
* regmap_field_write(): Write a value to a single register field
*
* @field: Register field to write to
* @val: Value to be written
*
* A value of zero will be returned on success, a negative errno will
* be returned in error cases.
*/
int regmap_field_write(struct regmap_field *field, unsigned int val)
{
return regmap_update_bits(field->regmap, field->reg,
field->mask, val << field->shift);
}
EXPORT_SYMBOL_GPL(regmap_field_write);
/**
* regmap_field_update_bits(): Perform a read/modify/write cycle
* on the register field
* regmap_field_update_bits_base():
* Perform a read/modify/write cycle on the register field
* with change, async, force option
*
* @field: Register field to write to
* @mask: Bitmask to change
* @val: Value to be written
* @change: Boolean indicating if a write was done
* @async: Boolean indicating asynchronously
* @force: Boolean indicating use force update
*
* A value of zero will be returned on success, a negative errno will
* be returned in error cases.
*/
int regmap_field_update_bits(struct regmap_field *field, unsigned int mask, unsigned int val)
int regmap_field_update_bits_base(struct regmap_field *field,
unsigned int mask, unsigned int val,
bool *change, bool async, bool force)
{
mask = (mask << field->shift) & field->mask;
return regmap_update_bits(field->regmap, field->reg,
mask, val << field->shift);
return regmap_update_bits_base(field->regmap, field->reg,
mask, val << field->shift,
change, async, force);
}
EXPORT_SYMBOL_GPL(regmap_field_update_bits);
EXPORT_SYMBOL_GPL(regmap_field_update_bits_base);
/**
* regmap_fields_write(): Write a value to a single register field with port ID
*
* @field: Register field to write to
* @id: port ID
* @val: Value to be written
*
* A value of zero will be returned on success, a negative errno will
* be returned in error cases.
*/
int regmap_fields_write(struct regmap_field *field, unsigned int id,
unsigned int val)
{
if (id >= field->id_size)
return -EINVAL;
return regmap_update_bits(field->regmap,
field->reg + (field->id_offset * id),
field->mask, val << field->shift);
}
EXPORT_SYMBOL_GPL(regmap_fields_write);
int regmap_fields_force_write(struct regmap_field *field, unsigned int id,
unsigned int val)
{
if (id >= field->id_size)
return -EINVAL;
return regmap_write_bits(field->regmap,
field->reg + (field->id_offset * id),
field->mask, val << field->shift);
}
EXPORT_SYMBOL_GPL(regmap_fields_force_write);
/**
* regmap_fields_update_bits(): Perform a read/modify/write cycle
* on the register field
* regmap_fields_update_bits_base():
* Perform a read/modify/write cycle on the register field
* with change, async, force option
*
* @field: Register field to write to
* @id: port ID
* @mask: Bitmask to change
* @val: Value to be written
* @change: Boolean indicating if a write was done
* @async: Boolean indicating asynchronously
* @force: Boolean indicating use force update
*
* A value of zero will be returned on success, a negative errno will
* be returned in error cases.
*/
int regmap_fields_update_bits(struct regmap_field *field, unsigned int id,
unsigned int mask, unsigned int val)
int regmap_fields_update_bits_base(struct regmap_field *field, unsigned int id,
unsigned int mask, unsigned int val,
bool *change, bool async, bool force)
{
if (id >= field->id_size)
return -EINVAL;
mask = (mask << field->shift) & field->mask;
return regmap_update_bits(field->regmap,
field->reg + (field->id_offset * id),
mask, val << field->shift);
return regmap_update_bits_base(field->regmap,
field->reg + (field->id_offset * id),
mask, val << field->shift,
change, async, force);
}
EXPORT_SYMBOL_GPL(regmap_fields_update_bits);
EXPORT_SYMBOL_GPL(regmap_fields_update_bits_base);
/*
* regmap_bulk_write(): Write multiple registers to the device
@ -2653,27 +2616,44 @@ static int _regmap_update_bits(struct regmap *map, unsigned int reg,
}
/**
* regmap_update_bits: Perform a read/modify/write cycle on the register map
* regmap_update_bits_base:
* Perform a read/modify/write cycle on the
* register map with change, async, force option
*
* @map: Register map to update
* @reg: Register to update
* @mask: Bitmask to change
* @val: New value for bitmask
* @change: Boolean indicating if a write was done
* @async: Boolean indicating asynchronously
* @force: Boolean indicating use force update
*
* if async was true,
* With most buses the read must be done synchronously so this is most
* useful for devices with a cache which do not need to interact with
* the hardware to determine the current register value.
*
* Returns zero for success, a negative number on error.
*/
int regmap_update_bits(struct regmap *map, unsigned int reg,
unsigned int mask, unsigned int val)
int regmap_update_bits_base(struct regmap *map, unsigned int reg,
unsigned int mask, unsigned int val,
bool *change, bool async, bool force)
{
int ret;
map->lock(map->lock_arg);
ret = _regmap_update_bits(map, reg, mask, val, NULL, false);
map->async = async;
ret = _regmap_update_bits(map, reg, mask, val, change, force);
map->async = false;
map->unlock(map->lock_arg);
return ret;
}
EXPORT_SYMBOL_GPL(regmap_update_bits);
EXPORT_SYMBOL_GPL(regmap_update_bits_base);
/**
* regmap_write_bits: Perform a read/modify/write cycle on the register map
@ -2698,102 +2678,6 @@ int regmap_write_bits(struct regmap *map, unsigned int reg,
}
EXPORT_SYMBOL_GPL(regmap_write_bits);
/**
* regmap_update_bits_async: Perform a read/modify/write cycle on the register
* map asynchronously
*
* @map: Register map to update
* @reg: Register to update
* @mask: Bitmask to change
* @val: New value for bitmask
*
* With most buses the read must be done synchronously so this is most
* useful for devices with a cache which do not need to interact with
* the hardware to determine the current register value.
*
* Returns zero for success, a negative number on error.
*/
int regmap_update_bits_async(struct regmap *map, unsigned int reg,
unsigned int mask, unsigned int val)
{
int ret;
map->lock(map->lock_arg);
map->async = true;
ret = _regmap_update_bits(map, reg, mask, val, NULL, false);
map->async = false;
map->unlock(map->lock_arg);
return ret;
}
EXPORT_SYMBOL_GPL(regmap_update_bits_async);
/**
* regmap_update_bits_check: Perform a read/modify/write cycle on the
* register map and report if updated
*
* @map: Register map to update
* @reg: Register to update
* @mask: Bitmask to change
* @val: New value for bitmask
* @change: Boolean indicating if a write was done
*
* Returns zero for success, a negative number on error.
*/
int regmap_update_bits_check(struct regmap *map, unsigned int reg,
unsigned int mask, unsigned int val,
bool *change)
{
int ret;
map->lock(map->lock_arg);
ret = _regmap_update_bits(map, reg, mask, val, change, false);
map->unlock(map->lock_arg);
return ret;
}
EXPORT_SYMBOL_GPL(regmap_update_bits_check);
/**
* regmap_update_bits_check_async: Perform a read/modify/write cycle on the
* register map asynchronously and report if
* updated
*
* @map: Register map to update
* @reg: Register to update
* @mask: Bitmask to change
* @val: New value for bitmask
* @change: Boolean indicating if a write was done
*
* With most buses the read must be done synchronously so this is most
* useful for devices with a cache which do not need to interact with
* the hardware to determine the current register value.
*
* Returns zero for success, a negative number on error.
*/
int regmap_update_bits_check_async(struct regmap *map, unsigned int reg,
unsigned int mask, unsigned int val,
bool *change)
{
int ret;
map->lock(map->lock_arg);
map->async = true;
ret = _regmap_update_bits(map, reg, mask, val, change, false);
map->async = false;
map->unlock(map->lock_arg);
return ret;
}
EXPORT_SYMBOL_GPL(regmap_update_bits_check_async);
void regmap_async_complete_cb(struct regmap_async *async, int ret)
{
struct regmap *map = async->map;

View File

@ -65,6 +65,33 @@ struct reg_sequence {
unsigned int delay_us;
};
#define regmap_update_bits(map, reg, mask, val) \
regmap_update_bits_base(map, reg, mask, val, NULL, false, false)
#define regmap_update_bits_async(map, reg, mask, val)\
regmap_update_bits_base(map, reg, mask, val, NULL, true, false)
#define regmap_update_bits_check(map, reg, mask, val, change)\
regmap_update_bits_base(map, reg, mask, val, change, false, false)
#define regmap_update_bits_check_async(map, reg, mask, val, change)\
regmap_update_bits_base(map, reg, mask, val, change, true, false)
#define regmap_field_write(field, val) \
regmap_field_update_bits_base(field, ~0, val, NULL, false, false)
#define regmap_field_force_write(field, val) \
regmap_field_update_bits_base(field, ~0, val, NULL, false, true)
#define regmap_field_update_bits(field, mask, val)\
regmap_field_update_bits_base(field, mask, val, NULL, false, false)
#define regmap_field_force_update_bits(field, mask, val) \
regmap_field_update_bits_base(field, mask, val, NULL, false, true)
#define regmap_fields_write(field, id, val) \
regmap_fields_update_bits_base(field, id, ~0, val, NULL, false, false)
#define regmap_fields_force_write(field, id, val) \
regmap_fields_update_bits_base(field, id, ~0, val, NULL, false, true)
#define regmap_fields_update_bits(field, id, mask, val)\
regmap_fields_update_bits_base(field, id, mask, val, NULL, false, false)
#define regmap_fields_force_update_bits(field, id, mask, val) \
regmap_fields_update_bits_base(field, id, mask, val, NULL, false, true)
#ifdef CONFIG_REGMAP
enum regmap_endian {
@ -691,18 +718,11 @@ int regmap_raw_read(struct regmap *map, unsigned int reg,
void *val, size_t val_len);
int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val,
size_t val_count);
int regmap_update_bits(struct regmap *map, unsigned int reg,
unsigned int mask, unsigned int val);
int regmap_update_bits_base(struct regmap *map, unsigned int reg,
unsigned int mask, unsigned int val,
bool *change, bool async, bool force);
int regmap_write_bits(struct regmap *map, unsigned int reg,
unsigned int mask, unsigned int val);
int regmap_update_bits_async(struct regmap *map, unsigned int reg,
unsigned int mask, unsigned int val);
int regmap_update_bits_check(struct regmap *map, unsigned int reg,
unsigned int mask, unsigned int val,
bool *change);
int regmap_update_bits_check_async(struct regmap *map, unsigned int reg,
unsigned int mask, unsigned int val,
bool *change);
int regmap_get_val_bytes(struct regmap *map);
int regmap_get_max_register(struct regmap *map);
int regmap_get_reg_stride(struct regmap *map);
@ -770,18 +790,14 @@ struct regmap_field *devm_regmap_field_alloc(struct device *dev,
void devm_regmap_field_free(struct device *dev, struct regmap_field *field);
int regmap_field_read(struct regmap_field *field, unsigned int *val);
int regmap_field_write(struct regmap_field *field, unsigned int val);
int regmap_field_update_bits(struct regmap_field *field,
unsigned int mask, unsigned int val);
int regmap_fields_write(struct regmap_field *field, unsigned int id,
unsigned int val);
int regmap_fields_force_write(struct regmap_field *field, unsigned int id,
unsigned int val);
int regmap_field_update_bits_base(struct regmap_field *field,
unsigned int mask, unsigned int val,
bool *change, bool async, bool force);
int regmap_fields_read(struct regmap_field *field, unsigned int id,
unsigned int *val);
int regmap_fields_update_bits(struct regmap_field *field, unsigned int id,
unsigned int mask, unsigned int val);
int regmap_fields_update_bits_base(struct regmap_field *field, unsigned int id,
unsigned int mask, unsigned int val,
bool *change, bool async, bool force);
/**
* Description of an IRQ for the generic regmap irq_chip.
@ -937,8 +953,9 @@ static inline int regmap_bulk_read(struct regmap *map, unsigned int reg,
return -EINVAL;
}
static inline int regmap_update_bits(struct regmap *map, unsigned int reg,
unsigned int mask, unsigned int val)
static inline int regmap_update_bits_base(struct regmap *map, unsigned int reg,
unsigned int mask, unsigned int val,
bool *change, bool async, bool force)
{
WARN_ONCE(1, "regmap API is disabled");
return -EINVAL;
@ -951,28 +968,18 @@ static inline int regmap_write_bits(struct regmap *map, unsigned int reg,
return -EINVAL;
}
static inline int regmap_update_bits_async(struct regmap *map,
unsigned int reg,
unsigned int mask, unsigned int val)
static inline int regmap_field_update_bits_base(struct regmap_field *field,
unsigned int mask, unsigned int val,
bool *change, bool async, bool force)
{
WARN_ONCE(1, "regmap API is disabled");
return -EINVAL;
}
static inline int regmap_update_bits_check(struct regmap *map,
unsigned int reg,
unsigned int mask, unsigned int val,
bool *change)
{
WARN_ONCE(1, "regmap API is disabled");
return -EINVAL;
}
static inline int regmap_update_bits_check_async(struct regmap *map,
unsigned int reg,
unsigned int mask,
unsigned int val,
bool *change)
static inline int regmap_fields_update_bits_base(struct regmap_field *field,
unsigned int id,
unsigned int mask, unsigned int val,
bool *change, bool async, bool force)
{
WARN_ONCE(1, "regmap API is disabled");
return -EINVAL;

View File

@ -98,6 +98,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_PCM512x_SPI if SPI_MASTER
select SND_SOC_RT286 if I2C
select SND_SOC_RT298 if I2C
select SND_SOC_RT5514 if I2C
select SND_SOC_RT5616 if I2C
select SND_SOC_RT5631 if I2C
select SND_SOC_RT5640 if I2C
@ -501,6 +502,7 @@ config SND_SOC_ICS43432
config SND_SOC_INNO_RK3036
tristate "Inno codec driver for RK3036 SoC"
select REGMAP_MMIO
config SND_SOC_ISABELLE
tristate
@ -590,6 +592,7 @@ config SND_SOC_PCM512x_SPI
config SND_SOC_RL6231
tristate
default y if SND_SOC_RT5514=y
default y if SND_SOC_RT5616=y
default y if SND_SOC_RT5640=y
default y if SND_SOC_RT5645=y
@ -597,6 +600,7 @@ config SND_SOC_RL6231
default y if SND_SOC_RT5659=y
default y if SND_SOC_RT5670=y
default y if SND_SOC_RT5677=y
default m if SND_SOC_RT5514=m
default m if SND_SOC_RT5616=m
default m if SND_SOC_RT5640=m
default m if SND_SOC_RT5645=m
@ -620,9 +624,12 @@ config SND_SOC_RT298
tristate
depends on I2C
config SND_SOC_RT5616
config SND_SOC_RT5514
tristate
config SND_SOC_RT5616
tristate "Realtek RT5616 CODEC"
config SND_SOC_RT5631
tristate "Realtek ALC5631/RT5631 CODEC"
depends on I2C

View File

@ -96,6 +96,7 @@ snd-soc-rl6231-objs := rl6231.o
snd-soc-rl6347a-objs := rl6347a.o
snd-soc-rt286-objs := rt286.o
snd-soc-rt298-objs := rt298.o
snd-soc-rt5514-objs := rt5514.o
snd-soc-rt5616-objs := rt5616.o
snd-soc-rt5631-objs := rt5631.o
snd-soc-rt5640-objs := rt5640.o
@ -304,6 +305,7 @@ obj-$(CONFIG_SND_SOC_RL6231) += snd-soc-rl6231.o
obj-$(CONFIG_SND_SOC_RL6347A) += snd-soc-rl6347a.o
obj-$(CONFIG_SND_SOC_RT286) += snd-soc-rt286.o
obj-$(CONFIG_SND_SOC_RT298) += snd-soc-rt298.o
obj-$(CONFIG_SND_SOC_RT5514) += snd-soc-rt5514.o
obj-$(CONFIG_SND_SOC_RT5616) += snd-soc-rt5616.o
obj-$(CONFIG_SND_SOC_RT5631) += snd-soc-rt5631.o
obj-$(CONFIG_SND_SOC_RT5640) += snd-soc-rt5640.o

View File

@ -1224,7 +1224,12 @@ static int rt298_i2c_probe(struct i2c_client *i2c,
regmap_write(rt298->regmap, RT298_MISC_CTRL1, 0x0000);
regmap_update_bits(rt298->regmap,
RT298_WIND_FILTER_CTRL, 0x0082, 0x0082);
regmap_update_bits(rt298->regmap, RT298_IRQ_CTRL, 0x2, 0x2);
regmap_write(rt298->regmap, RT298_UNSOLICITED_INLINE_CMD, 0x81);
regmap_write(rt298->regmap, RT298_UNSOLICITED_HP_OUT, 0x82);
regmap_write(rt298->regmap, RT298_UNSOLICITED_MIC1, 0x84);
regmap_update_bits(rt298->regmap, RT298_IRQ_FLAG_CTRL, 0x2, 0x2);
rt298->is_hp_in = -1;
if (rt298->i2c->irq) {

View File

@ -34,6 +34,7 @@
#define RT298_HP_OUT 0x21
#define RT298_MIXER_IN1 0x22
#define RT298_MIXER_IN2 0x23
#define RT298_INLINE_CMD 0x55
#define RT298_SET_PIN_SFT 6
#define RT298_SET_PIN_ENABLE 0x40
@ -124,6 +125,12 @@
VERB_CMD(AC_VERB_SET_COEF_INDEX, RT298_VENDOR_REGISTERS, 0)
#define RT298_PROC_COEF\
VERB_CMD(AC_VERB_SET_PROC_COEF, RT298_VENDOR_REGISTERS, 0)
#define RT298_UNSOLICITED_INLINE_CMD\
VERB_CMD(AC_VERB_SET_UNSOLICITED_ENABLE, RT298_INLINE_CMD, 0)
#define RT298_UNSOLICITED_HP_OUT\
VERB_CMD(AC_VERB_SET_UNSOLICITED_ENABLE, RT298_HP_OUT, 0)
#define RT298_UNSOLICITED_MIC1\
VERB_CMD(AC_VERB_SET_UNSOLICITED_ENABLE, RT298_MIC1, 0)
/* Index registers */
#define RT298_A_BIAS_CTRL1 0x01
@ -148,6 +155,7 @@
#define RT298_DEPOP_CTRL2 0x67
#define RT298_DEPOP_CTRL3 0x68
#define RT298_DEPOP_CTRL4 0x69
#define RT298_IRQ_FLAG_CTRL 0x7c
/* SPDIF (0x06) */
#define RT298_SPDIF_SEL_SFT 0

982
sound/soc/codecs/rt5514.c Normal file
View File

@ -0,0 +1,982 @@
/*
* rt5514.c -- RT5514 ALSA SoC audio codec driver
*
* Copyright 2015 Realtek Semiconductor Corp.
* Author: Oder Chiou <oder_chiou@realtek.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/regmap.h>
#include <linux/i2c.h>
#include <linux/platform_device.h>
#include <linux/firmware.h>
#include <linux/gpio.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <sound/initval.h>
#include <sound/tlv.h>
#include "rl6231.h"
#include "rt5514.h"
static const struct reg_sequence rt5514_i2c_patch[] = {
{0x1800101c, 0x00000000},
{0x18001100, 0x0000031f},
{0x18001104, 0x00000007},
{0x18001108, 0x00000000},
{0x1800110c, 0x00000000},
{0x18001110, 0x00000000},
{0x18001114, 0x00000001},
{0x18001118, 0x00000000},
{0x18002f08, 0x00000006},
{0x18002f00, 0x00055149},
{0x18002f00, 0x0005514b},
{0x18002f00, 0x00055149},
{0xfafafafa, 0x00000001},
{0x18002f10, 0x00000001},
{0x18002f10, 0x00000000},
{0x18002f10, 0x00000001},
{0xfafafafa, 0x00000001},
{0x18002000, 0x000010ec},
{0xfafafafa, 0x00000000},
};
static const struct reg_sequence rt5514_patch[] = {
{RT5514_DIG_IO_CTRL, 0x00000040},
{RT5514_CLK_CTRL1, 0x38020041},
{RT5514_SRC_CTRL, 0x44000eee},
{RT5514_ANA_CTRL_LDO10, 0x00028604},
{RT5514_ANA_CTRL_ADCFED, 0x00000800},
};
static const struct reg_default rt5514_reg[] = {
{RT5514_RESET, 0x00000000},
{RT5514_PWR_ANA1, 0x00808880},
{RT5514_PWR_ANA2, 0x00220000},
{RT5514_I2S_CTRL1, 0x00000330},
{RT5514_I2S_CTRL2, 0x20000000},
{RT5514_VAD_CTRL6, 0xc00007d2},
{RT5514_EXT_VAD_CTRL, 0x80000080},
{RT5514_DIG_IO_CTRL, 0x00000040},
{RT5514_PAD_CTRL1, 0x00804000},
{RT5514_DMIC_DATA_CTRL, 0x00000005},
{RT5514_DIG_SOURCE_CTRL, 0x00000002},
{RT5514_SRC_CTRL, 0x44000eee},
{RT5514_DOWNFILTER2_CTRL1, 0x0000882f},
{RT5514_PLL_SOURCE_CTRL, 0x00000004},
{RT5514_CLK_CTRL1, 0x38020041},
{RT5514_CLK_CTRL2, 0x00000000},
{RT5514_PLL3_CALIB_CTRL1, 0x00400200},
{RT5514_PLL3_CALIB_CTRL5, 0x40220012},
{RT5514_DELAY_BUF_CTRL1, 0x7fff006a},
{RT5514_DELAY_BUF_CTRL3, 0x00000000},
{RT5514_DOWNFILTER0_CTRL1, 0x00020c2f},
{RT5514_DOWNFILTER0_CTRL2, 0x00020c2f},
{RT5514_DOWNFILTER0_CTRL3, 0x00000362},
{RT5514_DOWNFILTER1_CTRL1, 0x00020c2f},
{RT5514_DOWNFILTER1_CTRL2, 0x00020c2f},
{RT5514_DOWNFILTER1_CTRL3, 0x00000362},
{RT5514_ANA_CTRL_LDO10, 0x00028604},
{RT5514_ANA_CTRL_LDO18_16, 0x02000345},
{RT5514_ANA_CTRL_ADC12, 0x0000a2a8},
{RT5514_ANA_CTRL_ADC21, 0x00001180},
{RT5514_ANA_CTRL_ADC22, 0x0000aaa8},
{RT5514_ANA_CTRL_ADC23, 0x00151427},
{RT5514_ANA_CTRL_MICBST, 0x00002000},
{RT5514_ANA_CTRL_ADCFED, 0x00000800},
{RT5514_ANA_CTRL_INBUF, 0x00000143},
{RT5514_ANA_CTRL_VREF, 0x00008d50},
{RT5514_ANA_CTRL_PLL3, 0x0000000e},
{RT5514_ANA_CTRL_PLL1_1, 0x00000000},
{RT5514_ANA_CTRL_PLL1_2, 0x00030220},
{RT5514_DMIC_LP_CTRL, 0x00000000},
{RT5514_MISC_CTRL_DSP, 0x00000000},
{RT5514_DSP_CTRL1, 0x00055149},
{RT5514_DSP_CTRL3, 0x00000006},
{RT5514_DSP_CTRL4, 0x00000001},
{RT5514_VENDOR_ID1, 0x00000001},
{RT5514_VENDOR_ID2, 0x10ec5514},
};
static bool rt5514_volatile_register(struct device *dev, unsigned int reg)
{
switch (reg) {
case RT5514_VENDOR_ID1:
case RT5514_VENDOR_ID2:
return true;
default:
return false;
}
}
static bool rt5514_readable_register(struct device *dev, unsigned int reg)
{
switch (reg) {
case RT5514_RESET:
case RT5514_PWR_ANA1:
case RT5514_PWR_ANA2:
case RT5514_I2S_CTRL1:
case RT5514_I2S_CTRL2:
case RT5514_VAD_CTRL6:
case RT5514_EXT_VAD_CTRL:
case RT5514_DIG_IO_CTRL:
case RT5514_PAD_CTRL1:
case RT5514_DMIC_DATA_CTRL:
case RT5514_DIG_SOURCE_CTRL:
case RT5514_SRC_CTRL:
case RT5514_DOWNFILTER2_CTRL1:
case RT5514_PLL_SOURCE_CTRL:
case RT5514_CLK_CTRL1:
case RT5514_CLK_CTRL2:
case RT5514_PLL3_CALIB_CTRL1:
case RT5514_PLL3_CALIB_CTRL5:
case RT5514_DELAY_BUF_CTRL1:
case RT5514_DELAY_BUF_CTRL3:
case RT5514_DOWNFILTER0_CTRL1:
case RT5514_DOWNFILTER0_CTRL2:
case RT5514_DOWNFILTER0_CTRL3:
case RT5514_DOWNFILTER1_CTRL1:
case RT5514_DOWNFILTER1_CTRL2:
case RT5514_DOWNFILTER1_CTRL3:
case RT5514_ANA_CTRL_LDO10:
case RT5514_ANA_CTRL_LDO18_16:
case RT5514_ANA_CTRL_ADC12:
case RT5514_ANA_CTRL_ADC21:
case RT5514_ANA_CTRL_ADC22:
case RT5514_ANA_CTRL_ADC23:
case RT5514_ANA_CTRL_MICBST:
case RT5514_ANA_CTRL_ADCFED:
case RT5514_ANA_CTRL_INBUF:
case RT5514_ANA_CTRL_VREF:
case RT5514_ANA_CTRL_PLL3:
case RT5514_ANA_CTRL_PLL1_1:
case RT5514_ANA_CTRL_PLL1_2:
case RT5514_DMIC_LP_CTRL:
case RT5514_MISC_CTRL_DSP:
case RT5514_DSP_CTRL1:
case RT5514_DSP_CTRL3:
case RT5514_DSP_CTRL4:
case RT5514_VENDOR_ID1:
case RT5514_VENDOR_ID2:
return true;
default:
return false;
}
}
static bool rt5514_i2c_readable_register(struct device *dev,
unsigned int reg)
{
switch (reg) {
case RT5514_DSP_MAPPING | RT5514_RESET:
case RT5514_DSP_MAPPING | RT5514_PWR_ANA1:
case RT5514_DSP_MAPPING | RT5514_PWR_ANA2:
case RT5514_DSP_MAPPING | RT5514_I2S_CTRL1:
case RT5514_DSP_MAPPING | RT5514_I2S_CTRL2:
case RT5514_DSP_MAPPING | RT5514_VAD_CTRL6:
case RT5514_DSP_MAPPING | RT5514_EXT_VAD_CTRL:
case RT5514_DSP_MAPPING | RT5514_DIG_IO_CTRL:
case RT5514_DSP_MAPPING | RT5514_PAD_CTRL1:
case RT5514_DSP_MAPPING | RT5514_DMIC_DATA_CTRL:
case RT5514_DSP_MAPPING | RT5514_DIG_SOURCE_CTRL:
case RT5514_DSP_MAPPING | RT5514_SRC_CTRL:
case RT5514_DSP_MAPPING | RT5514_DOWNFILTER2_CTRL1:
case RT5514_DSP_MAPPING | RT5514_PLL_SOURCE_CTRL:
case RT5514_DSP_MAPPING | RT5514_CLK_CTRL1:
case RT5514_DSP_MAPPING | RT5514_CLK_CTRL2:
case RT5514_DSP_MAPPING | RT5514_PLL3_CALIB_CTRL1:
case RT5514_DSP_MAPPING | RT5514_PLL3_CALIB_CTRL5:
case RT5514_DSP_MAPPING | RT5514_DELAY_BUF_CTRL1:
case RT5514_DSP_MAPPING | RT5514_DELAY_BUF_CTRL3:
case RT5514_DSP_MAPPING | RT5514_DOWNFILTER0_CTRL1:
case RT5514_DSP_MAPPING | RT5514_DOWNFILTER0_CTRL2:
case RT5514_DSP_MAPPING | RT5514_DOWNFILTER0_CTRL3:
case RT5514_DSP_MAPPING | RT5514_DOWNFILTER1_CTRL1:
case RT5514_DSP_MAPPING | RT5514_DOWNFILTER1_CTRL2:
case RT5514_DSP_MAPPING | RT5514_DOWNFILTER1_CTRL3:
case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_LDO10:
case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_LDO18_16:
case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_ADC12:
case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_ADC21:
case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_ADC22:
case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_ADC23:
case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_MICBST:
case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_ADCFED:
case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_INBUF:
case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_VREF:
case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_PLL3:
case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_PLL1_1:
case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_PLL1_2:
case RT5514_DSP_MAPPING | RT5514_DMIC_LP_CTRL:
case RT5514_DSP_MAPPING | RT5514_MISC_CTRL_DSP:
case RT5514_DSP_MAPPING | RT5514_DSP_CTRL1:
case RT5514_DSP_MAPPING | RT5514_DSP_CTRL3:
case RT5514_DSP_MAPPING | RT5514_DSP_CTRL4:
case RT5514_DSP_MAPPING | RT5514_VENDOR_ID1:
case RT5514_DSP_MAPPING | RT5514_VENDOR_ID2:
return true;
default:
return false;
}
}
/* {-3, 0, +3, +4.5, +7.5, +9.5, +12, +14, +17} dB */
static const DECLARE_TLV_DB_RANGE(bst_tlv,
0, 2, TLV_DB_SCALE_ITEM(-300, 300, 0),
3, 3, TLV_DB_SCALE_ITEM(450, 0, 0),
4, 4, TLV_DB_SCALE_ITEM(750, 0, 0),
5, 5, TLV_DB_SCALE_ITEM(950, 0, 0),
6, 6, TLV_DB_SCALE_ITEM(1200, 0, 0),
7, 7, TLV_DB_SCALE_ITEM(1400, 0, 0),
8, 8, TLV_DB_SCALE_ITEM(1700, 0, 0)
);
static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -17625, 375, 0);
static const struct snd_kcontrol_new rt5514_snd_controls[] = {
SOC_DOUBLE_TLV("MIC Boost Volume", RT5514_ANA_CTRL_MICBST,
RT5514_SEL_BSTL_SFT, RT5514_SEL_BSTR_SFT, 8, 0, bst_tlv),
SOC_DOUBLE_R_TLV("ADC1 Capture Volume", RT5514_DOWNFILTER0_CTRL1,
RT5514_DOWNFILTER0_CTRL2, RT5514_AD_GAIN_SFT, 127, 0,
adc_vol_tlv),
SOC_DOUBLE_R_TLV("ADC2 Capture Volume", RT5514_DOWNFILTER1_CTRL1,
RT5514_DOWNFILTER1_CTRL2, RT5514_AD_GAIN_SFT, 127, 0,
adc_vol_tlv),
};
/* ADC Mixer*/
static const struct snd_kcontrol_new rt5514_sto1_adc_l_mix[] = {
SOC_DAPM_SINGLE("DMIC Switch", RT5514_DOWNFILTER0_CTRL1,
RT5514_AD_DMIC_MIX_BIT, 1, 1),
SOC_DAPM_SINGLE("ADC Switch", RT5514_DOWNFILTER0_CTRL1,
RT5514_AD_AD_MIX_BIT, 1, 1),
};
static const struct snd_kcontrol_new rt5514_sto1_adc_r_mix[] = {
SOC_DAPM_SINGLE("DMIC Switch", RT5514_DOWNFILTER0_CTRL2,
RT5514_AD_DMIC_MIX_BIT, 1, 1),
SOC_DAPM_SINGLE("ADC Switch", RT5514_DOWNFILTER0_CTRL2,
RT5514_AD_AD_MIX_BIT, 1, 1),
};
static const struct snd_kcontrol_new rt5514_sto2_adc_l_mix[] = {
SOC_DAPM_SINGLE("DMIC Switch", RT5514_DOWNFILTER1_CTRL1,
RT5514_AD_DMIC_MIX_BIT, 1, 1),
SOC_DAPM_SINGLE("ADC Switch", RT5514_DOWNFILTER1_CTRL1,
RT5514_AD_AD_MIX_BIT, 1, 1),
};
static const struct snd_kcontrol_new rt5514_sto2_adc_r_mix[] = {
SOC_DAPM_SINGLE("DMIC Switch", RT5514_DOWNFILTER1_CTRL2,
RT5514_AD_DMIC_MIX_BIT, 1, 1),
SOC_DAPM_SINGLE("ADC Switch", RT5514_DOWNFILTER1_CTRL2,
RT5514_AD_AD_MIX_BIT, 1, 1),
};
/* DMIC Source */
static const char * const rt5514_dmic_src[] = {
"DMIC1", "DMIC2"
};
static const SOC_ENUM_SINGLE_DECL(
rt5514_stereo1_dmic_enum, RT5514_DIG_SOURCE_CTRL,
RT5514_AD0_DMIC_INPUT_SEL_SFT, rt5514_dmic_src);
static const struct snd_kcontrol_new rt5514_sto1_dmic_mux =
SOC_DAPM_ENUM("Stereo1 DMIC Source", rt5514_stereo1_dmic_enum);
static const SOC_ENUM_SINGLE_DECL(
rt5514_stereo2_dmic_enum, RT5514_DIG_SOURCE_CTRL,
RT5514_AD1_DMIC_INPUT_SEL_SFT, rt5514_dmic_src);
static const struct snd_kcontrol_new rt5514_sto2_dmic_mux =
SOC_DAPM_ENUM("Stereo2 DMIC Source", rt5514_stereo2_dmic_enum);
/**
* rt5514_calc_dmic_clk - Calculate the frequency divider parameter of dmic.
*
* @rate: base clock rate.
*
* Choose divider parameter that gives the highest possible DMIC frequency in
* 1MHz - 3MHz range.
*/
static int rt5514_calc_dmic_clk(struct snd_soc_codec *codec, int rate)
{
int div[] = {2, 3, 4, 8, 12, 16, 24, 32};
int i;
if (rate < 1000000 * div[0]) {
pr_warn("Base clock rate %d is too low\n", rate);
return -EINVAL;
}
for (i = 0; i < ARRAY_SIZE(div); i++) {
/* find divider that gives DMIC frequency below 3.072MHz */
if (3072000 * div[i] >= rate)
return i;
}
dev_warn(codec->dev, "Base clock rate %d is too high\n", rate);
return -EINVAL;
}
static int rt5514_set_dmic_clk(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
struct rt5514_priv *rt5514 = snd_soc_codec_get_drvdata(codec);
int idx;
idx = rt5514_calc_dmic_clk(codec, rt5514->sysclk);
if (idx < 0)
dev_err(codec->dev, "Failed to set DMIC clock\n");
else
regmap_update_bits(rt5514->regmap, RT5514_CLK_CTRL1,
RT5514_CLK_DMIC_OUT_SEL_MASK,
idx << RT5514_CLK_DMIC_OUT_SEL_SFT);
return idx;
}
static int rt5514_is_sys_clk_from_pll(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink)
{
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
struct rt5514_priv *rt5514 = snd_soc_codec_get_drvdata(codec);
if (rt5514->sysclk_src == RT5514_SCLK_S_PLL1)
return 1;
else
return 0;
}
static const struct snd_soc_dapm_widget rt5514_dapm_widgets[] = {
/* Input Lines */
SND_SOC_DAPM_INPUT("DMIC1L"),
SND_SOC_DAPM_INPUT("DMIC1R"),
SND_SOC_DAPM_INPUT("DMIC2L"),
SND_SOC_DAPM_INPUT("DMIC2R"),
SND_SOC_DAPM_INPUT("AMICL"),
SND_SOC_DAPM_INPUT("AMICR"),
SND_SOC_DAPM_PGA("DMIC1", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_PGA("DMIC2", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("DMIC CLK", SND_SOC_NOPM, 0, 0,
rt5514_set_dmic_clk, SND_SOC_DAPM_PRE_PMU),
SND_SOC_DAPM_SUPPLY("ADC CLK", RT5514_CLK_CTRL1,
RT5514_CLK_AD_ANA1_EN_BIT, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("LDO18 IN", RT5514_PWR_ANA1,
RT5514_POW_LDO18_IN_BIT, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("LDO18 ADC", RT5514_PWR_ANA1,
RT5514_POW_LDO18_ADC_BIT, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("LDO21", RT5514_PWR_ANA1, RT5514_POW_LDO21_BIT, 0,
NULL, 0),
SND_SOC_DAPM_SUPPLY("BG LDO18 IN", RT5514_PWR_ANA1,
RT5514_POW_BG_LDO18_IN_BIT, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("BG LDO21", RT5514_PWR_ANA1,
RT5514_POW_BG_LDO21_BIT, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("BG MBIAS", RT5514_PWR_ANA2,
RT5514_POW_BG_MBIAS_BIT, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("MBIAS", RT5514_PWR_ANA2, RT5514_POW_MBIAS_BIT, 0,
NULL, 0),
SND_SOC_DAPM_SUPPLY("VREF2", RT5514_PWR_ANA2, RT5514_POW_VREF2_BIT, 0,
NULL, 0),
SND_SOC_DAPM_SUPPLY("VREF1", RT5514_PWR_ANA2, RT5514_POW_VREF1_BIT, 0,
NULL, 0),
SND_SOC_DAPM_SUPPLY("ADC Power", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("LDO16L", RT5514_PWR_ANA2, RT5514_POWL_LDO16_BIT, 0,
NULL, 0),
SND_SOC_DAPM_SUPPLY("ADC1L", RT5514_PWR_ANA2, RT5514_POW_ADC1_L_BIT, 0,
NULL, 0),
SND_SOC_DAPM_SUPPLY("BSTL2", RT5514_PWR_ANA2, RT5514_POW2_BSTL_BIT, 0,
NULL, 0),
SND_SOC_DAPM_SUPPLY("BSTL", RT5514_PWR_ANA2, RT5514_POW_BSTL_BIT, 0,
NULL, 0),
SND_SOC_DAPM_SUPPLY("ADCFEDL", RT5514_PWR_ANA2, RT5514_POW_ADCFEDL_BIT,
0, NULL, 0),
SND_SOC_DAPM_SUPPLY("ADCL Power", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("LDO16R", RT5514_PWR_ANA2, RT5514_POWR_LDO16_BIT, 0,
NULL, 0),
SND_SOC_DAPM_SUPPLY("ADC1R", RT5514_PWR_ANA2, RT5514_POW_ADC1_R_BIT, 0,
NULL, 0),
SND_SOC_DAPM_SUPPLY("BSTR2", RT5514_PWR_ANA2, RT5514_POW2_BSTR_BIT, 0,
NULL, 0),
SND_SOC_DAPM_SUPPLY("BSTR", RT5514_PWR_ANA2, RT5514_POW_BSTR_BIT, 0,
NULL, 0),
SND_SOC_DAPM_SUPPLY("ADCFEDR", RT5514_PWR_ANA2, RT5514_POW_ADCFEDR_BIT,
0, NULL, 0),
SND_SOC_DAPM_SUPPLY("ADCR Power", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("PLL1 LDO ENABLE", RT5514_ANA_CTRL_PLL1_2,
RT5514_EN_LDO_PLL1_BIT, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("PLL1 LDO", RT5514_PWR_ANA2,
RT5514_POW_PLL1_LDO_BIT, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("PLL1", RT5514_PWR_ANA2, RT5514_POW_PLL1_BIT, 0,
NULL, 0),
/* ADC Mux */
SND_SOC_DAPM_MUX("Stereo1 DMIC Mux", SND_SOC_NOPM, 0, 0,
&rt5514_sto1_dmic_mux),
SND_SOC_DAPM_MUX("Stereo2 DMIC Mux", SND_SOC_NOPM, 0, 0,
&rt5514_sto2_dmic_mux),
/* ADC Mixer */
SND_SOC_DAPM_SUPPLY("adc stereo1 filter", RT5514_CLK_CTRL1,
RT5514_CLK_AD0_EN_BIT, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("adc stereo2 filter", RT5514_CLK_CTRL1,
RT5514_CLK_AD1_EN_BIT, 0, NULL, 0),
SND_SOC_DAPM_MIXER("Sto1 ADC MIXL", SND_SOC_NOPM, 0, 0,
rt5514_sto1_adc_l_mix, ARRAY_SIZE(rt5514_sto1_adc_l_mix)),
SND_SOC_DAPM_MIXER("Sto1 ADC MIXR", SND_SOC_NOPM, 0, 0,
rt5514_sto1_adc_r_mix, ARRAY_SIZE(rt5514_sto1_adc_r_mix)),
SND_SOC_DAPM_MIXER("Sto2 ADC MIXL", SND_SOC_NOPM, 0, 0,
rt5514_sto2_adc_l_mix, ARRAY_SIZE(rt5514_sto2_adc_l_mix)),
SND_SOC_DAPM_MIXER("Sto2 ADC MIXR", SND_SOC_NOPM, 0, 0,
rt5514_sto2_adc_r_mix, ARRAY_SIZE(rt5514_sto2_adc_r_mix)),
SND_SOC_DAPM_ADC("Stereo1 ADC MIXL", NULL, RT5514_DOWNFILTER0_CTRL1,
RT5514_AD_AD_MUTE_BIT, 1),
SND_SOC_DAPM_ADC("Stereo1 ADC MIXR", NULL, RT5514_DOWNFILTER0_CTRL2,
RT5514_AD_AD_MUTE_BIT, 1),
SND_SOC_DAPM_ADC("Stereo2 ADC MIXL", NULL, RT5514_DOWNFILTER1_CTRL1,
RT5514_AD_AD_MUTE_BIT, 1),
SND_SOC_DAPM_ADC("Stereo2 ADC MIXR", NULL, RT5514_DOWNFILTER1_CTRL2,
RT5514_AD_AD_MUTE_BIT, 1),
/* ADC PGA */
SND_SOC_DAPM_PGA("Stereo1 ADC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_PGA("Stereo2 ADC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
/* Audio Interface */
SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0),
};
static const struct snd_soc_dapm_route rt5514_dapm_routes[] = {
{ "DMIC1", NULL, "DMIC1L" },
{ "DMIC1", NULL, "DMIC1R" },
{ "DMIC2", NULL, "DMIC2L" },
{ "DMIC2", NULL, "DMIC2R" },
{ "DMIC1L", NULL, "DMIC CLK" },
{ "DMIC1R", NULL, "DMIC CLK" },
{ "DMIC2L", NULL, "DMIC CLK" },
{ "DMIC2R", NULL, "DMIC CLK" },
{ "Stereo1 DMIC Mux", "DMIC1", "DMIC1" },
{ "Stereo1 DMIC Mux", "DMIC2", "DMIC2" },
{ "Sto1 ADC MIXL", "DMIC Switch", "Stereo1 DMIC Mux" },
{ "Sto1 ADC MIXL", "ADC Switch", "AMICL" },
{ "Sto1 ADC MIXR", "DMIC Switch", "Stereo1 DMIC Mux" },
{ "Sto1 ADC MIXR", "ADC Switch", "AMICR" },
{ "ADC Power", NULL, "LDO18 IN" },
{ "ADC Power", NULL, "LDO18 ADC" },
{ "ADC Power", NULL, "LDO21" },
{ "ADC Power", NULL, "BG LDO18 IN" },
{ "ADC Power", NULL, "BG LDO21" },
{ "ADC Power", NULL, "BG MBIAS" },
{ "ADC Power", NULL, "MBIAS" },
{ "ADC Power", NULL, "VREF2" },
{ "ADC Power", NULL, "VREF1" },
{ "ADCL Power", NULL, "LDO16L" },
{ "ADCL Power", NULL, "ADC1L" },
{ "ADCL Power", NULL, "BSTL2" },
{ "ADCL Power", NULL, "BSTL" },
{ "ADCL Power", NULL, "ADCFEDL" },
{ "ADCR Power", NULL, "LDO16R" },
{ "ADCR Power", NULL, "ADC1R" },
{ "ADCR Power", NULL, "BSTR2" },
{ "ADCR Power", NULL, "BSTR" },
{ "ADCR Power", NULL, "ADCFEDR" },
{ "AMICL", NULL, "ADC CLK" },
{ "AMICL", NULL, "ADC Power" },
{ "AMICL", NULL, "ADCL Power" },
{ "AMICR", NULL, "ADC CLK" },
{ "AMICR", NULL, "ADC Power" },
{ "AMICR", NULL, "ADCR Power" },
{ "PLL1 LDO", NULL, "PLL1 LDO ENABLE" },
{ "PLL1", NULL, "PLL1 LDO" },
{ "Stereo1 ADC MIXL", NULL, "Sto1 ADC MIXL" },
{ "Stereo1 ADC MIXR", NULL, "Sto1 ADC MIXR" },
{ "Stereo1 ADC MIX", NULL, "Stereo1 ADC MIXL" },
{ "Stereo1 ADC MIX", NULL, "Stereo1 ADC MIXR" },
{ "Stereo1 ADC MIX", NULL, "adc stereo1 filter" },
{ "adc stereo1 filter", NULL, "PLL1", rt5514_is_sys_clk_from_pll },
{ "Stereo2 DMIC Mux", "DMIC1", "DMIC1" },
{ "Stereo2 DMIC Mux", "DMIC2", "DMIC2" },
{ "Sto2 ADC MIXL", "DMIC Switch", "Stereo2 DMIC Mux" },
{ "Sto2 ADC MIXL", "ADC Switch", "AMICL" },
{ "Sto2 ADC MIXR", "DMIC Switch", "Stereo2 DMIC Mux" },
{ "Sto2 ADC MIXR", "ADC Switch", "AMICR" },
{ "Stereo2 ADC MIXL", NULL, "Sto2 ADC MIXL" },
{ "Stereo2 ADC MIXR", NULL, "Sto2 ADC MIXR" },
{ "Stereo2 ADC MIX", NULL, "Stereo2 ADC MIXL" },
{ "Stereo2 ADC MIX", NULL, "Stereo2 ADC MIXR" },
{ "Stereo2 ADC MIX", NULL, "adc stereo2 filter" },
{ "adc stereo2 filter", NULL, "PLL1", rt5514_is_sys_clk_from_pll },
{ "AIF1TX", NULL, "Stereo1 ADC MIX"},
{ "AIF1TX", NULL, "Stereo2 ADC MIX"},
};
static int rt5514_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 rt5514_priv *rt5514 = snd_soc_codec_get_drvdata(codec);
int pre_div, bclk_ms, frame_size;
unsigned int val_len = 0;
rt5514->lrck = params_rate(params);
pre_div = rl6231_get_clk_info(rt5514->sysclk, rt5514->lrck);
if (pre_div < 0) {
dev_err(codec->dev, "Unsupported clock setting\n");
return -EINVAL;
}
frame_size = snd_soc_params_to_frame_size(params);
if (frame_size < 0) {
dev_err(codec->dev, "Unsupported frame size: %d\n", frame_size);
return -EINVAL;
}
bclk_ms = frame_size > 32;
rt5514->bclk = rt5514->lrck * (32 << bclk_ms);
dev_dbg(dai->dev, "bclk is %dHz and lrck is %dHz\n",
rt5514->bclk, rt5514->lrck);
dev_dbg(dai->dev, "bclk_ms is %d and pre_div is %d for iis %d\n",
bclk_ms, pre_div, dai->id);
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE:
break;
case SNDRV_PCM_FORMAT_S20_3LE:
val_len = RT5514_I2S_DL_20;
break;
case SNDRV_PCM_FORMAT_S24_LE:
val_len = RT5514_I2S_DL_24;
break;
case SNDRV_PCM_FORMAT_S8:
val_len = RT5514_I2S_DL_8;
break;
default:
return -EINVAL;
}
regmap_update_bits(rt5514->regmap, RT5514_I2S_CTRL1, RT5514_I2S_DL_MASK,
val_len);
regmap_update_bits(rt5514->regmap, RT5514_CLK_CTRL2,
RT5514_CLK_SYS_DIV_OUT_MASK | RT5514_SEL_ADC_OSR_MASK,
pre_div << RT5514_CLK_SYS_DIV_OUT_SFT |
pre_div << RT5514_SEL_ADC_OSR_SFT);
return 0;
}
static int rt5514_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
struct snd_soc_codec *codec = dai->codec;
struct rt5514_priv *rt5514 = snd_soc_codec_get_drvdata(codec);
unsigned int reg_val = 0;
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_NB_NF:
break;
case SND_SOC_DAIFMT_NB_IF:
reg_val |= RT5514_I2S_LR_INV;
break;
case SND_SOC_DAIFMT_IB_NF:
reg_val |= RT5514_I2S_BP_INV;
break;
case SND_SOC_DAIFMT_IB_IF:
reg_val |= RT5514_I2S_BP_INV | RT5514_I2S_LR_INV;
break;
default:
return -EINVAL;
}
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
break;
case SND_SOC_DAIFMT_LEFT_J:
reg_val |= RT5514_I2S_DF_LEFT;
break;
case SND_SOC_DAIFMT_DSP_A:
reg_val |= RT5514_I2S_DF_PCM_A;
break;
case SND_SOC_DAIFMT_DSP_B:
reg_val |= RT5514_I2S_DF_PCM_B;
break;
default:
return -EINVAL;
}
regmap_update_bits(rt5514->regmap, RT5514_I2S_CTRL1,
RT5514_I2S_DF_MASK | RT5514_I2S_BP_MASK | RT5514_I2S_LR_MASK,
reg_val);
return 0;
}
static int rt5514_set_dai_sysclk(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir)
{
struct snd_soc_codec *codec = dai->codec;
struct rt5514_priv *rt5514 = snd_soc_codec_get_drvdata(codec);
unsigned int reg_val = 0;
if (freq == rt5514->sysclk && clk_id == rt5514->sysclk_src)
return 0;
switch (clk_id) {
case RT5514_SCLK_S_MCLK:
reg_val |= RT5514_CLK_SYS_PRE_SEL_MCLK;
break;
case RT5514_SCLK_S_PLL1:
reg_val |= RT5514_CLK_SYS_PRE_SEL_PLL;
break;
default:
dev_err(codec->dev, "Invalid clock id (%d)\n", clk_id);
return -EINVAL;
}
regmap_update_bits(rt5514->regmap, RT5514_CLK_CTRL2,
RT5514_CLK_SYS_PRE_SEL_MASK, reg_val);
rt5514->sysclk = freq;
rt5514->sysclk_src = clk_id;
dev_dbg(dai->dev, "Sysclk is %dHz and clock id is %d\n", freq, clk_id);
return 0;
}
static int rt5514_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
unsigned int freq_in, unsigned int freq_out)
{
struct snd_soc_codec *codec = dai->codec;
struct rt5514_priv *rt5514 = snd_soc_codec_get_drvdata(codec);
struct rl6231_pll_code pll_code;
int ret;
if (!freq_in || !freq_out) {
dev_dbg(codec->dev, "PLL disabled\n");
rt5514->pll_in = 0;
rt5514->pll_out = 0;
regmap_update_bits(rt5514->regmap, RT5514_CLK_CTRL2,
RT5514_CLK_SYS_PRE_SEL_MASK,
RT5514_CLK_SYS_PRE_SEL_MCLK);
return 0;
}
if (source == rt5514->pll_src && freq_in == rt5514->pll_in &&
freq_out == rt5514->pll_out)
return 0;
switch (source) {
case RT5514_PLL1_S_MCLK:
regmap_update_bits(rt5514->regmap, RT5514_PLL_SOURCE_CTRL,
RT5514_PLL_1_SEL_MASK, RT5514_PLL_1_SEL_MCLK);
break;
case RT5514_PLL1_S_BCLK:
regmap_update_bits(rt5514->regmap, RT5514_PLL_SOURCE_CTRL,
RT5514_PLL_1_SEL_MASK, RT5514_PLL_1_SEL_SCLK);
break;
default:
dev_err(codec->dev, "Unknown PLL source %d\n", source);
return -EINVAL;
}
ret = rl6231_pll_calc(freq_in, freq_out, &pll_code);
if (ret < 0) {
dev_err(codec->dev, "Unsupport input clock %d\n", freq_in);
return ret;
}
dev_dbg(codec->dev, "bypass=%d m=%d n=%d k=%d\n",
pll_code.m_bp, (pll_code.m_bp ? 0 : pll_code.m_code),
pll_code.n_code, pll_code.k_code);
regmap_write(rt5514->regmap, RT5514_ANA_CTRL_PLL1_1,
pll_code.k_code << RT5514_PLL_K_SFT |
pll_code.n_code << RT5514_PLL_N_SFT |
(pll_code.m_bp ? 0 : pll_code.m_code) << RT5514_PLL_M_SFT);
regmap_update_bits(rt5514->regmap, RT5514_ANA_CTRL_PLL1_2,
RT5514_PLL_M_BP, pll_code.m_bp << RT5514_PLL_M_BP_SFT);
rt5514->pll_in = freq_in;
rt5514->pll_out = freq_out;
rt5514->pll_src = source;
return 0;
}
static int rt5514_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
unsigned int rx_mask, int slots, int slot_width)
{
struct snd_soc_codec *codec = dai->codec;
struct rt5514_priv *rt5514 = snd_soc_codec_get_drvdata(codec);
unsigned int val = 0;
if (rx_mask || tx_mask)
val |= RT5514_TDM_MODE;
if (slots == 4)
val |= RT5514_TDMSLOT_SEL_RX_4CH | RT5514_TDMSLOT_SEL_TX_4CH;
switch (slot_width) {
case 20:
val |= RT5514_CH_LEN_RX_20 | RT5514_CH_LEN_TX_20;
break;
case 24:
val |= RT5514_CH_LEN_RX_24 | RT5514_CH_LEN_TX_24;
break;
case 32:
val |= RT5514_CH_LEN_RX_32 | RT5514_CH_LEN_TX_32;
break;
case 16:
default:
break;
}
regmap_update_bits(rt5514->regmap, RT5514_I2S_CTRL1, RT5514_TDM_MODE |
RT5514_TDMSLOT_SEL_RX_MASK | RT5514_TDMSLOT_SEL_TX_MASK |
RT5514_CH_LEN_RX_MASK | RT5514_CH_LEN_TX_MASK, val);
return 0;
}
static int rt5514_probe(struct snd_soc_codec *codec)
{
struct rt5514_priv *rt5514 = snd_soc_codec_get_drvdata(codec);
rt5514->codec = codec;
return 0;
}
static int rt5514_i2c_read(void *context, unsigned int reg, unsigned int *val)
{
struct i2c_client *client = context;
struct rt5514_priv *rt5514 = i2c_get_clientdata(client);
regmap_read(rt5514->i2c_regmap, reg | RT5514_DSP_MAPPING, val);
return 0;
}
static int rt5514_i2c_write(void *context, unsigned int reg, unsigned int val)
{
struct i2c_client *client = context;
struct rt5514_priv *rt5514 = i2c_get_clientdata(client);
regmap_write(rt5514->i2c_regmap, reg | RT5514_DSP_MAPPING, val);
return 0;
}
#define RT5514_STEREO_RATES SNDRV_PCM_RATE_8000_192000
#define RT5514_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)
struct snd_soc_dai_ops rt5514_aif_dai_ops = {
.hw_params = rt5514_hw_params,
.set_fmt = rt5514_set_dai_fmt,
.set_sysclk = rt5514_set_dai_sysclk,
.set_pll = rt5514_set_dai_pll,
.set_tdm_slot = rt5514_set_tdm_slot,
};
struct snd_soc_dai_driver rt5514_dai[] = {
{
.name = "rt5514-aif1",
.id = 0,
.capture = {
.stream_name = "AIF1 Capture",
.channels_min = 1,
.channels_max = 4,
.rates = RT5514_STEREO_RATES,
.formats = RT5514_FORMATS,
},
.ops = &rt5514_aif_dai_ops,
}
};
static struct snd_soc_codec_driver soc_codec_dev_rt5514 = {
.probe = rt5514_probe,
.idle_bias_off = true,
.controls = rt5514_snd_controls,
.num_controls = ARRAY_SIZE(rt5514_snd_controls),
.dapm_widgets = rt5514_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(rt5514_dapm_widgets),
.dapm_routes = rt5514_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(rt5514_dapm_routes),
};
static const struct regmap_config rt5514_i2c_regmap = {
.name = "i2c",
.reg_bits = 32,
.val_bits = 32,
.max_register = RT5514_DSP_MAPPING | RT5514_VENDOR_ID2,
.readable_reg = rt5514_i2c_readable_register,
.cache_type = REGCACHE_NONE,
};
static const struct regmap_config rt5514_regmap = {
.reg_bits = 16,
.val_bits = 32,
.max_register = RT5514_VENDOR_ID2,
.volatile_reg = rt5514_volatile_register,
.readable_reg = rt5514_readable_register,
.reg_read = rt5514_i2c_read,
.reg_write = rt5514_i2c_write,
.cache_type = REGCACHE_RBTREE,
.reg_defaults = rt5514_reg,
.num_reg_defaults = ARRAY_SIZE(rt5514_reg),
.use_single_rw = true,
};
static const struct i2c_device_id rt5514_i2c_id[] = {
{ "rt5514", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, rt5514_i2c_id);
#if defined(CONFIG_OF)
static const struct of_device_id rt5514_of_match[] = {
{ .compatible = "realtek,rt5514", },
{},
};
MODULE_DEVICE_TABLE(of, rt5514_of_match);
#endif
static int rt5514_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct rt5514_priv *rt5514;
int ret;
unsigned int val;
rt5514 = devm_kzalloc(&i2c->dev, sizeof(struct rt5514_priv),
GFP_KERNEL);
if (rt5514 == NULL)
return -ENOMEM;
i2c_set_clientdata(i2c, rt5514);
rt5514->i2c_regmap = devm_regmap_init_i2c(i2c, &rt5514_i2c_regmap);
if (IS_ERR(rt5514->i2c_regmap)) {
ret = PTR_ERR(rt5514->i2c_regmap);
dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
ret);
return ret;
}
rt5514->regmap = devm_regmap_init(&i2c->dev, NULL, i2c, &rt5514_regmap);
if (IS_ERR(rt5514->regmap)) {
ret = PTR_ERR(rt5514->regmap);
dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
ret);
return ret;
}
regmap_read(rt5514->regmap, RT5514_VENDOR_ID2, &val);
if (val != RT5514_DEVICE_ID) {
dev_err(&i2c->dev,
"Device with ID register %x is not rt5514\n", val);
return -ENODEV;
}
ret = regmap_register_patch(rt5514->i2c_regmap, rt5514_i2c_patch,
ARRAY_SIZE(rt5514_i2c_patch));
if (ret != 0)
dev_warn(&i2c->dev, "Failed to apply i2c_regmap patch: %d\n",
ret);
ret = regmap_register_patch(rt5514->regmap, rt5514_patch,
ARRAY_SIZE(rt5514_patch));
if (ret != 0)
dev_warn(&i2c->dev, "Failed to apply regmap patch: %d\n", ret);
return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5514,
rt5514_dai, ARRAY_SIZE(rt5514_dai));
}
static int rt5514_i2c_remove(struct i2c_client *i2c)
{
snd_soc_unregister_codec(&i2c->dev);
return 0;
}
struct i2c_driver rt5514_i2c_driver = {
.driver = {
.name = "rt5514",
.of_match_table = of_match_ptr(rt5514_of_match),
},
.probe = rt5514_i2c_probe,
.remove = rt5514_i2c_remove,
.id_table = rt5514_i2c_id,
};
module_i2c_driver(rt5514_i2c_driver);
MODULE_DESCRIPTION("ASoC RT5514 driver");
MODULE_AUTHOR("Oder Chiou <oder_chiou@realtek.com>");
MODULE_LICENSE("GPL v2");

252
sound/soc/codecs/rt5514.h Normal file
View File

@ -0,0 +1,252 @@
/*
* rt5514.h -- RT5514 ALSA SoC audio driver
*
* Copyright 2015 Realtek Microelectronics
* Author: Oder Chiou <oder_chiou@realtek.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef __RT5514_H__
#define __RT5514_H__
#define RT5514_DEVICE_ID 0x10ec5514
#define RT5514_RESET 0x2000
#define RT5514_PWR_ANA1 0x2004
#define RT5514_PWR_ANA2 0x2008
#define RT5514_I2S_CTRL1 0x2010
#define RT5514_I2S_CTRL2 0x2014
#define RT5514_VAD_CTRL6 0x2030
#define RT5514_EXT_VAD_CTRL 0x206c
#define RT5514_DIG_IO_CTRL 0x2070
#define RT5514_PAD_CTRL1 0x2080
#define RT5514_DMIC_DATA_CTRL 0x20a0
#define RT5514_DIG_SOURCE_CTRL 0x20a4
#define RT5514_SRC_CTRL 0x20ac
#define RT5514_DOWNFILTER2_CTRL1 0x20d0
#define RT5514_PLL_SOURCE_CTRL 0x2100
#define RT5514_CLK_CTRL1 0x2104
#define RT5514_CLK_CTRL2 0x2108
#define RT5514_PLL3_CALIB_CTRL1 0x2110
#define RT5514_PLL3_CALIB_CTRL5 0x2124
#define RT5514_DELAY_BUF_CTRL1 0x2140
#define RT5514_DELAY_BUF_CTRL3 0x2148
#define RT5514_DOWNFILTER0_CTRL1 0x2190
#define RT5514_DOWNFILTER0_CTRL2 0x2194
#define RT5514_DOWNFILTER0_CTRL3 0x2198
#define RT5514_DOWNFILTER1_CTRL1 0x21a0
#define RT5514_DOWNFILTER1_CTRL2 0x21a4
#define RT5514_DOWNFILTER1_CTRL3 0x21a8
#define RT5514_ANA_CTRL_LDO10 0x2200
#define RT5514_ANA_CTRL_LDO18_16 0x2204
#define RT5514_ANA_CTRL_ADC12 0x2210
#define RT5514_ANA_CTRL_ADC21 0x2214
#define RT5514_ANA_CTRL_ADC22 0x2218
#define RT5514_ANA_CTRL_ADC23 0x221c
#define RT5514_ANA_CTRL_MICBST 0x2220
#define RT5514_ANA_CTRL_ADCFED 0x2224
#define RT5514_ANA_CTRL_INBUF 0x2228
#define RT5514_ANA_CTRL_VREF 0x222c
#define RT5514_ANA_CTRL_PLL3 0x2240
#define RT5514_ANA_CTRL_PLL1_1 0x2260
#define RT5514_ANA_CTRL_PLL1_2 0x2264
#define RT5514_DMIC_LP_CTRL 0x2e00
#define RT5514_MISC_CTRL_DSP 0x2e04
#define RT5514_DSP_CTRL1 0x2f00
#define RT5514_DSP_CTRL3 0x2f08
#define RT5514_DSP_CTRL4 0x2f10
#define RT5514_VENDOR_ID1 0x2ff0
#define RT5514_VENDOR_ID2 0x2ff4
#define RT5514_DSP_MAPPING 0x18000000
/* RT5514_PWR_ANA1 (0x2004) */
#define RT5514_POW_LDO18_IN (0x1 << 5)
#define RT5514_POW_LDO18_IN_BIT 5
#define RT5514_POW_LDO18_ADC (0x1 << 4)
#define RT5514_POW_LDO18_ADC_BIT 4
#define RT5514_POW_LDO21 (0x1 << 3)
#define RT5514_POW_LDO21_BIT 3
#define RT5514_POW_BG_LDO18_IN (0x1 << 2)
#define RT5514_POW_BG_LDO18_IN_BIT 2
#define RT5514_POW_BG_LDO21 (0x1 << 1)
#define RT5514_POW_BG_LDO21_BIT 1
/* RT5514_PWR_ANA2 (0x2008) */
#define RT5514_POW_PLL1 (0x1 << 18)
#define RT5514_POW_PLL1_BIT 18
#define RT5514_POW_PLL1_LDO (0x1 << 16)
#define RT5514_POW_PLL1_LDO_BIT 16
#define RT5514_POW_BG_MBIAS (0x1 << 15)
#define RT5514_POW_BG_MBIAS_BIT 15
#define RT5514_POW_MBIAS (0x1 << 14)
#define RT5514_POW_MBIAS_BIT 14
#define RT5514_POW_VREF2 (0x1 << 13)
#define RT5514_POW_VREF2_BIT 13
#define RT5514_POW_VREF1 (0x1 << 12)
#define RT5514_POW_VREF1_BIT 12
#define RT5514_POWR_LDO16 (0x1 << 11)
#define RT5514_POWR_LDO16_BIT 11
#define RT5514_POWL_LDO16 (0x1 << 10)
#define RT5514_POWL_LDO16_BIT 10
#define RT5514_POW_ADC2 (0x1 << 9)
#define RT5514_POW_ADC2_BIT 9
#define RT5514_POW_INPUT_BUF (0x1 << 8)
#define RT5514_POW_INPUT_BUF_BIT 8
#define RT5514_POW_ADC1_R (0x1 << 7)
#define RT5514_POW_ADC1_R_BIT 7
#define RT5514_POW_ADC1_L (0x1 << 6)
#define RT5514_POW_ADC1_L_BIT 6
#define RT5514_POW2_BSTR (0x1 << 5)
#define RT5514_POW2_BSTR_BIT 5
#define RT5514_POW2_BSTL (0x1 << 4)
#define RT5514_POW2_BSTL_BIT 4
#define RT5514_POW_BSTR (0x1 << 3)
#define RT5514_POW_BSTR_BIT 3
#define RT5514_POW_BSTL (0x1 << 2)
#define RT5514_POW_BSTL_BIT 2
#define RT5514_POW_ADCFEDR (0x1 << 1)
#define RT5514_POW_ADCFEDR_BIT 1
#define RT5514_POW_ADCFEDL (0x1 << 0)
#define RT5514_POW_ADCFEDL_BIT 0
/* RT5514_I2S_CTRL1 (0x2010) */
#define RT5514_TDM_MODE (0x1 << 28)
#define RT5514_TDM_MODE_SFT 28
#define RT5514_I2S_LR_MASK (0x1 << 26)
#define RT5514_I2S_LR_SFT 26
#define RT5514_I2S_LR_NOR (0x0 << 26)
#define RT5514_I2S_LR_INV (0x1 << 26)
#define RT5514_I2S_BP_MASK (0x1 << 25)
#define RT5514_I2S_BP_SFT 25
#define RT5514_I2S_BP_NOR (0x0 << 25)
#define RT5514_I2S_BP_INV (0x1 << 25)
#define RT5514_I2S_DF_MASK (0x7 << 16)
#define RT5514_I2S_DF_SFT 16
#define RT5514_I2S_DF_I2S (0x0 << 16)
#define RT5514_I2S_DF_LEFT (0x1 << 16)
#define RT5514_I2S_DF_PCM_A (0x2 << 16)
#define RT5514_I2S_DF_PCM_B (0x3 << 16)
#define RT5514_TDMSLOT_SEL_RX_MASK (0x3 << 10)
#define RT5514_TDMSLOT_SEL_RX_SFT 10
#define RT5514_TDMSLOT_SEL_RX_4CH (0x1 << 10)
#define RT5514_CH_LEN_RX_MASK (0x3 << 8)
#define RT5514_CH_LEN_RX_SFT 8
#define RT5514_CH_LEN_RX_16 (0x0 << 8)
#define RT5514_CH_LEN_RX_20 (0x1 << 8)
#define RT5514_CH_LEN_RX_24 (0x2 << 8)
#define RT5514_CH_LEN_RX_32 (0x3 << 8)
#define RT5514_TDMSLOT_SEL_TX_MASK (0x3 << 6)
#define RT5514_TDMSLOT_SEL_TX_SFT 6
#define RT5514_TDMSLOT_SEL_TX_4CH (0x1 << 6)
#define RT5514_CH_LEN_TX_MASK (0x3 << 4)
#define RT5514_CH_LEN_TX_SFT 4
#define RT5514_CH_LEN_TX_16 (0x0 << 4)
#define RT5514_CH_LEN_TX_20 (0x1 << 4)
#define RT5514_CH_LEN_TX_24 (0x2 << 4)
#define RT5514_CH_LEN_TX_32 (0x3 << 4)
#define RT5514_I2S_DL_MASK (0x3 << 0)
#define RT5514_I2S_DL_SFT 0
#define RT5514_I2S_DL_16 (0x0 << 0)
#define RT5514_I2S_DL_20 (0x1 << 0)
#define RT5514_I2S_DL_24 (0x2 << 0)
#define RT5514_I2S_DL_8 (0x3 << 0)
/* RT5514_DIG_SOURCE_CTRL (0x20a4) */
#define RT5514_AD1_DMIC_INPUT_SEL (0x1 << 1)
#define RT5514_AD1_DMIC_INPUT_SEL_SFT 1
#define RT5514_AD0_DMIC_INPUT_SEL (0x1 << 0)
#define RT5514_AD0_DMIC_INPUT_SEL_SFT 0
/* RT5514_PLL_SOURCE_CTRL (0x2100) */
#define RT5514_PLL_1_SEL_MASK (0x7 << 12)
#define RT5514_PLL_1_SEL_SFT 12
#define RT5514_PLL_1_SEL_SCLK (0x3 << 12)
#define RT5514_PLL_1_SEL_MCLK (0x4 << 12)
/* RT5514_CLK_CTRL1 (0x2104) */
#define RT5514_CLK_AD_ANA1_EN (0x1 << 31)
#define RT5514_CLK_AD_ANA1_EN_BIT 31
#define RT5514_CLK_AD1_EN (0x1 << 24)
#define RT5514_CLK_AD1_EN_BIT 24
#define RT5514_CLK_AD0_EN (0x1 << 23)
#define RT5514_CLK_AD0_EN_BIT 23
#define RT5514_CLK_DMIC_OUT_SEL_MASK (0x7 << 8)
#define RT5514_CLK_DMIC_OUT_SEL_SFT 8
/* RT5514_CLK_CTRL2 (0x2108) */
#define RT5514_CLK_SYS_DIV_OUT_MASK (0x7 << 8)
#define RT5514_CLK_SYS_DIV_OUT_SFT 8
#define RT5514_SEL_ADC_OSR_MASK (0x7 << 4)
#define RT5514_SEL_ADC_OSR_SFT 4
#define RT5514_CLK_SYS_PRE_SEL_MASK (0x3 << 0)
#define RT5514_CLK_SYS_PRE_SEL_SFT 0
#define RT5514_CLK_SYS_PRE_SEL_MCLK (0x2 << 0)
#define RT5514_CLK_SYS_PRE_SEL_PLL (0x3 << 0)
/* RT5514_DOWNFILTER_CTRL (0x2190 0x2194 0x21a0 0x21a4) */
#define RT5514_AD_DMIC_MIX (0x1 << 11)
#define RT5514_AD_DMIC_MIX_BIT 11
#define RT5514_AD_AD_MIX (0x1 << 10)
#define RT5514_AD_AD_MIX_BIT 10
#define RT5514_AD_AD_MUTE (0x1 << 7)
#define RT5514_AD_AD_MUTE_BIT 7
#define RT5514_AD_GAIN_MASK (0x7f << 0)
#define RT5514_AD_GAIN_SFT 0
/* RT5514_ANA_CTRL_MICBST (0x2220) */
#define RT5514_SEL_BSTL_MASK (0xf << 4)
#define RT5514_SEL_BSTL_SFT 4
#define RT5514_SEL_BSTR_MASK (0xf << 0)
#define RT5514_SEL_BSTR_SFT 0
/* RT5514_ANA_CTRL_PLL1_1 (0x2260) */
#define RT5514_PLL_K_MAX 0x1f
#define RT5514_PLL_K_MASK (RT5514_PLL_K_MAX << 16)
#define RT5514_PLL_K_SFT 16
#define RT5514_PLL_N_MAX 0x1ff
#define RT5514_PLL_N_MASK (RT5514_PLL_N_MAX << 7)
#define RT5514_PLL_N_SFT 4
#define RT5514_PLL_M_MAX 0xf
#define RT5514_PLL_M_MASK (RT5514_PLL_M_MAX << 0)
#define RT5514_PLL_M_SFT 0
/* RT5514_ANA_CTRL_PLL1_2 (0x2264) */
#define RT5514_PLL_M_BP (0x1 << 2)
#define RT5514_PLL_M_BP_SFT 2
#define RT5514_PLL_K_BP (0x1 << 1)
#define RT5514_PLL_K_BP_SFT 1
#define RT5514_EN_LDO_PLL1 (0x1 << 0)
#define RT5514_EN_LDO_PLL1_BIT 0
#define RT5514_PLL_INP_MAX 40000000
#define RT5514_PLL_INP_MIN 256000
/* System Clock Source */
enum {
RT5514_SCLK_S_MCLK,
RT5514_SCLK_S_PLL1,
};
/* PLL1 Source */
enum {
RT5514_PLL1_S_MCLK,
RT5514_PLL1_S_BCLK,
};
struct rt5514_priv {
struct snd_soc_codec *codec;
struct regmap *i2c_regmap, *regmap;
int sysclk;
int sysclk_src;
int lrck;
int bclk;
int pll_src;
int pll_in;
int pll_out;
};
#endif /* __RT5514_H__ */

View File

@ -12,6 +12,7 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/i2c.h>
@ -53,6 +54,7 @@ static const struct reg_sequence init_list[] = {
{RT5616_PR_BASE + 0x21, 0x4040},
{RT5616_PR_BASE + 0x23, 0x0004},
};
#define RT5616_INIT_REG_LEN ARRAY_SIZE(init_list)
static const struct reg_default rt5616_reg[] = {
@ -143,6 +145,7 @@ struct rt5616_priv {
struct snd_soc_codec *codec;
struct delayed_work patch_work;
struct regmap *regmap;
struct clk *mclk;
int sysclk;
int sysclk_src;
@ -162,9 +165,8 @@ static bool rt5616_volatile_register(struct device *dev, unsigned int reg)
for (i = 0; i < ARRAY_SIZE(rt5616_ranges); i++) {
if (reg >= rt5616_ranges[i].range_min &&
reg <= rt5616_ranges[i].range_max) {
reg <= rt5616_ranges[i].range_max)
return true;
}
}
switch (reg) {
@ -190,9 +192,8 @@ static bool rt5616_readable_register(struct device *dev, unsigned int reg)
for (i = 0; i < ARRAY_SIZE(rt5616_ranges); i++) {
if (reg >= rt5616_ranges[i].range_min &&
reg <= rt5616_ranges[i].range_max) {
reg <= rt5616_ranges[i].range_max)
return true;
}
}
switch (reg) {
@ -307,45 +308,47 @@ static unsigned int bst_tlv[] = {
static const struct snd_kcontrol_new rt5616_snd_controls[] = {
/* Headphone Output Volume */
SOC_DOUBLE("HP Playback Switch", RT5616_HP_VOL,
RT5616_L_MUTE_SFT, RT5616_R_MUTE_SFT, 1, 1),
RT5616_L_MUTE_SFT, RT5616_R_MUTE_SFT, 1, 1),
SOC_DOUBLE("HPVOL Playback Switch", RT5616_HP_VOL,
RT5616_VOL_L_SFT, RT5616_VOL_R_SFT, 1, 1),
SOC_DOUBLE_TLV("HP Playback Volume", RT5616_HP_VOL,
RT5616_L_VOL_SFT, RT5616_R_VOL_SFT, 39, 1, out_vol_tlv),
RT5616_L_VOL_SFT, RT5616_R_VOL_SFT, 39, 1, out_vol_tlv),
/* OUTPUT Control */
SOC_DOUBLE("OUT Playback Switch", RT5616_LOUT_CTRL1,
RT5616_L_MUTE_SFT, RT5616_R_MUTE_SFT, 1, 1),
RT5616_L_MUTE_SFT, RT5616_R_MUTE_SFT, 1, 1),
SOC_DOUBLE("OUT Channel Switch", RT5616_LOUT_CTRL1,
RT5616_VOL_L_SFT, RT5616_VOL_R_SFT, 1, 1),
RT5616_VOL_L_SFT, RT5616_VOL_R_SFT, 1, 1),
SOC_DOUBLE_TLV("OUT Playback Volume", RT5616_LOUT_CTRL1,
RT5616_L_VOL_SFT, RT5616_R_VOL_SFT, 39, 1, out_vol_tlv),
RT5616_L_VOL_SFT, RT5616_R_VOL_SFT, 39, 1, out_vol_tlv),
/* DAC Digital Volume */
SOC_DOUBLE_TLV("DAC1 Playback Volume", RT5616_DAC1_DIG_VOL,
RT5616_L_VOL_SFT, RT5616_R_VOL_SFT,
175, 0, dac_vol_tlv),
RT5616_L_VOL_SFT, RT5616_R_VOL_SFT,
175, 0, dac_vol_tlv),
/* IN1/IN2 Control */
SOC_SINGLE_TLV("IN1 Boost Volume", RT5616_IN1_IN2,
RT5616_BST_SFT1, 8, 0, bst_tlv),
RT5616_BST_SFT1, 8, 0, bst_tlv),
SOC_SINGLE_TLV("IN2 Boost Volume", RT5616_IN1_IN2,
RT5616_BST_SFT2, 8, 0, bst_tlv),
RT5616_BST_SFT2, 8, 0, bst_tlv),
/* INL/INR Volume Control */
SOC_DOUBLE_TLV("IN Capture Volume", RT5616_INL1_INR1_VOL,
RT5616_INL_VOL_SFT, RT5616_INR_VOL_SFT,
31, 1, in_vol_tlv),
RT5616_INL_VOL_SFT, RT5616_INR_VOL_SFT,
31, 1, in_vol_tlv),
/* ADC Digital Volume Control */
SOC_DOUBLE("ADC Capture Switch", RT5616_ADC_DIG_VOL,
RT5616_L_MUTE_SFT, RT5616_R_MUTE_SFT, 1, 1),
RT5616_L_MUTE_SFT, RT5616_R_MUTE_SFT, 1, 1),
SOC_DOUBLE_TLV("ADC Capture Volume", RT5616_ADC_DIG_VOL,
RT5616_L_VOL_SFT, RT5616_R_VOL_SFT,
127, 0, adc_vol_tlv),
RT5616_L_VOL_SFT, RT5616_R_VOL_SFT,
127, 0, adc_vol_tlv),
/* ADC Boost Volume Control */
SOC_DOUBLE_TLV("ADC Boost Volume", RT5616_ADC_BST_VOL,
RT5616_ADC_L_BST_SFT, RT5616_ADC_R_BST_SFT,
3, 0, adc_bst_tlv),
RT5616_ADC_L_BST_SFT, RT5616_ADC_R_BST_SFT,
3, 0, adc_bst_tlv),
};
static int is_sys_clk_from_pll(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink)
struct snd_soc_dapm_widget *sink)
{
unsigned int val;
@ -462,20 +465,20 @@ static const struct snd_kcontrol_new rt5616_lout_mix[] = {
};
static int rt5616_adc_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
snd_soc_update_bits(codec, RT5616_ADC_DIG_VOL,
RT5616_L_MUTE | RT5616_R_MUTE, 0);
RT5616_L_MUTE | RT5616_R_MUTE, 0);
break;
case SND_SOC_DAPM_POST_PMD:
snd_soc_update_bits(codec, RT5616_ADC_DIG_VOL,
RT5616_L_MUTE | RT5616_R_MUTE,
RT5616_L_MUTE | RT5616_R_MUTE);
RT5616_L_MUTE | RT5616_R_MUTE,
RT5616_L_MUTE | RT5616_R_MUTE);
break;
default:
@ -486,7 +489,7 @@ static int rt5616_adc_event(struct snd_soc_dapm_widget *w,
}
static int rt5616_charge_pump_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
@ -494,54 +497,55 @@ static int rt5616_charge_pump_event(struct snd_soc_dapm_widget *w,
case SND_SOC_DAPM_POST_PMU:
/* depop parameters */
snd_soc_update_bits(codec, RT5616_DEPOP_M2,
RT5616_DEPOP_MASK, RT5616_DEPOP_MAN);
RT5616_DEPOP_MASK, RT5616_DEPOP_MAN);
snd_soc_update_bits(codec, RT5616_DEPOP_M1,
RT5616_HP_CP_MASK | RT5616_HP_SG_MASK |
RT5616_HP_CB_MASK, RT5616_HP_CP_PU |
RT5616_HP_SG_DIS | RT5616_HP_CB_PU);
RT5616_HP_CP_MASK | RT5616_HP_SG_MASK |
RT5616_HP_CB_MASK, RT5616_HP_CP_PU |
RT5616_HP_SG_DIS | RT5616_HP_CB_PU);
snd_soc_write(codec, RT5616_PR_BASE +
RT5616_HP_DCC_INT1, 0x9f00);
RT5616_HP_DCC_INT1, 0x9f00);
/* headphone amp power on */
snd_soc_update_bits(codec, RT5616_PWR_ANLG1,
RT5616_PWR_FV1 | RT5616_PWR_FV2, 0);
RT5616_PWR_FV1 | RT5616_PWR_FV2, 0);
snd_soc_update_bits(codec, RT5616_PWR_VOL,
RT5616_PWR_HV_L | RT5616_PWR_HV_R,
RT5616_PWR_HV_L | RT5616_PWR_HV_R);
RT5616_PWR_HV_L | RT5616_PWR_HV_R,
RT5616_PWR_HV_L | RT5616_PWR_HV_R);
snd_soc_update_bits(codec, RT5616_PWR_ANLG1,
RT5616_PWR_HP_L | RT5616_PWR_HP_R |
RT5616_PWR_HA, RT5616_PWR_HP_L |
RT5616_PWR_HP_R | RT5616_PWR_HA);
RT5616_PWR_HP_L | RT5616_PWR_HP_R |
RT5616_PWR_HA, RT5616_PWR_HP_L |
RT5616_PWR_HP_R | RT5616_PWR_HA);
msleep(50);
snd_soc_update_bits(codec, RT5616_PWR_ANLG1,
RT5616_PWR_FV1 | RT5616_PWR_FV2,
RT5616_PWR_FV1 | RT5616_PWR_FV2);
RT5616_PWR_FV1 | RT5616_PWR_FV2,
RT5616_PWR_FV1 | RT5616_PWR_FV2);
snd_soc_update_bits(codec, RT5616_CHARGE_PUMP,
RT5616_PM_HP_MASK, RT5616_PM_HP_HV);
RT5616_PM_HP_MASK, RT5616_PM_HP_HV);
snd_soc_update_bits(codec, RT5616_PR_BASE +
RT5616_CHOP_DAC_ADC, 0x0200, 0x0200);
RT5616_CHOP_DAC_ADC, 0x0200, 0x0200);
snd_soc_update_bits(codec, RT5616_DEPOP_M1,
RT5616_HP_CO_MASK | RT5616_HP_SG_MASK,
RT5616_HP_CO_EN | RT5616_HP_SG_EN);
RT5616_HP_CO_MASK | RT5616_HP_SG_MASK,
RT5616_HP_CO_EN | RT5616_HP_SG_EN);
break;
case SND_SOC_DAPM_PRE_PMD:
snd_soc_update_bits(codec, RT5616_PR_BASE +
RT5616_CHOP_DAC_ADC, 0x0200, 0x0);
RT5616_CHOP_DAC_ADC, 0x0200, 0x0);
snd_soc_update_bits(codec, RT5616_DEPOP_M1,
RT5616_HP_SG_MASK | RT5616_HP_L_SMT_MASK |
RT5616_HP_R_SMT_MASK, RT5616_HP_SG_DIS |
RT5616_HP_L_SMT_DIS | RT5616_HP_R_SMT_DIS);
RT5616_HP_SG_MASK | RT5616_HP_L_SMT_MASK |
RT5616_HP_R_SMT_MASK, RT5616_HP_SG_DIS |
RT5616_HP_L_SMT_DIS | RT5616_HP_R_SMT_DIS);
/* headphone amp power down */
snd_soc_update_bits(codec, RT5616_DEPOP_M1,
RT5616_SMT_TRIG_MASK | RT5616_HP_CD_PD_MASK |
RT5616_HP_CO_MASK | RT5616_HP_CP_MASK |
RT5616_HP_SG_MASK | RT5616_HP_CB_MASK,
RT5616_SMT_TRIG_DIS | RT5616_HP_CD_PD_EN |
RT5616_HP_CO_DIS | RT5616_HP_CP_PD |
RT5616_HP_SG_EN | RT5616_HP_CB_PD);
RT5616_SMT_TRIG_MASK |
RT5616_HP_CD_PD_MASK | RT5616_HP_CO_MASK |
RT5616_HP_CP_MASK | RT5616_HP_SG_MASK |
RT5616_HP_CB_MASK,
RT5616_SMT_TRIG_DIS | RT5616_HP_CD_PD_EN |
RT5616_HP_CO_DIS | RT5616_HP_CP_PD |
RT5616_HP_SG_EN | RT5616_HP_CB_PD);
snd_soc_update_bits(codec, RT5616_PWR_ANLG1,
RT5616_PWR_HP_L | RT5616_PWR_HP_R |
RT5616_PWR_HA, 0);
RT5616_PWR_HP_L | RT5616_PWR_HP_R |
RT5616_PWR_HA, 0);
break;
default:
return 0;
@ -551,7 +555,7 @@ static int rt5616_charge_pump_event(struct snd_soc_dapm_widget *w,
}
static int rt5616_hp_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
@ -559,57 +563,57 @@ static int rt5616_hp_event(struct snd_soc_dapm_widget *w,
case SND_SOC_DAPM_POST_PMU:
/* headphone unmute sequence */
snd_soc_update_bits(codec, RT5616_DEPOP_M3,
RT5616_CP_FQ1_MASK | RT5616_CP_FQ2_MASK |
RT5616_CP_FQ3_MASK,
(RT5616_CP_FQ_192_KHZ << RT5616_CP_FQ1_SFT) |
(RT5616_CP_FQ_12_KHZ << RT5616_CP_FQ2_SFT) |
(RT5616_CP_FQ_192_KHZ << RT5616_CP_FQ3_SFT));
RT5616_CP_FQ1_MASK | RT5616_CP_FQ2_MASK |
RT5616_CP_FQ3_MASK,
RT5616_CP_FQ_192_KHZ << RT5616_CP_FQ1_SFT |
RT5616_CP_FQ_12_KHZ << RT5616_CP_FQ2_SFT |
RT5616_CP_FQ_192_KHZ << RT5616_CP_FQ3_SFT);
snd_soc_write(codec, RT5616_PR_BASE +
RT5616_MAMP_INT_REG2, 0xfc00);
RT5616_MAMP_INT_REG2, 0xfc00);
snd_soc_update_bits(codec, RT5616_DEPOP_M1,
RT5616_SMT_TRIG_MASK, RT5616_SMT_TRIG_EN);
RT5616_SMT_TRIG_MASK, RT5616_SMT_TRIG_EN);
snd_soc_update_bits(codec, RT5616_DEPOP_M1,
RT5616_RSTN_MASK, RT5616_RSTN_EN);
RT5616_RSTN_MASK, RT5616_RSTN_EN);
snd_soc_update_bits(codec, RT5616_DEPOP_M1,
RT5616_RSTN_MASK | RT5616_HP_L_SMT_MASK |
RT5616_HP_R_SMT_MASK, RT5616_RSTN_DIS |
RT5616_HP_L_SMT_EN | RT5616_HP_R_SMT_EN);
RT5616_RSTN_MASK | RT5616_HP_L_SMT_MASK |
RT5616_HP_R_SMT_MASK, RT5616_RSTN_DIS |
RT5616_HP_L_SMT_EN | RT5616_HP_R_SMT_EN);
snd_soc_update_bits(codec, RT5616_HP_VOL,
RT5616_L_MUTE | RT5616_R_MUTE, 0);
RT5616_L_MUTE | RT5616_R_MUTE, 0);
msleep(100);
snd_soc_update_bits(codec, RT5616_DEPOP_M1,
RT5616_HP_SG_MASK | RT5616_HP_L_SMT_MASK |
RT5616_HP_R_SMT_MASK, RT5616_HP_SG_DIS |
RT5616_HP_L_SMT_DIS | RT5616_HP_R_SMT_DIS);
RT5616_HP_SG_MASK | RT5616_HP_L_SMT_MASK |
RT5616_HP_R_SMT_MASK, RT5616_HP_SG_DIS |
RT5616_HP_L_SMT_DIS | RT5616_HP_R_SMT_DIS);
msleep(20);
snd_soc_update_bits(codec, RT5616_HP_CALIB_AMP_DET,
RT5616_HPD_PS_MASK, RT5616_HPD_PS_EN);
RT5616_HPD_PS_MASK, RT5616_HPD_PS_EN);
break;
case SND_SOC_DAPM_PRE_PMD:
/* headphone mute sequence */
snd_soc_update_bits(codec, RT5616_DEPOP_M3,
RT5616_CP_FQ1_MASK | RT5616_CP_FQ2_MASK |
RT5616_CP_FQ3_MASK,
(RT5616_CP_FQ_96_KHZ << RT5616_CP_FQ1_SFT) |
(RT5616_CP_FQ_12_KHZ << RT5616_CP_FQ2_SFT) |
(RT5616_CP_FQ_96_KHZ << RT5616_CP_FQ3_SFT));
RT5616_CP_FQ1_MASK | RT5616_CP_FQ2_MASK |
RT5616_CP_FQ3_MASK,
RT5616_CP_FQ_96_KHZ << RT5616_CP_FQ1_SFT |
RT5616_CP_FQ_12_KHZ << RT5616_CP_FQ2_SFT |
RT5616_CP_FQ_96_KHZ << RT5616_CP_FQ3_SFT);
snd_soc_write(codec, RT5616_PR_BASE +
RT5616_MAMP_INT_REG2, 0xfc00);
RT5616_MAMP_INT_REG2, 0xfc00);
snd_soc_update_bits(codec, RT5616_DEPOP_M1,
RT5616_HP_SG_MASK, RT5616_HP_SG_EN);
RT5616_HP_SG_MASK, RT5616_HP_SG_EN);
snd_soc_update_bits(codec, RT5616_DEPOP_M1,
RT5616_RSTP_MASK, RT5616_RSTP_EN);
RT5616_RSTP_MASK, RT5616_RSTP_EN);
snd_soc_update_bits(codec, RT5616_DEPOP_M1,
RT5616_RSTP_MASK | RT5616_HP_L_SMT_MASK |
RT5616_HP_R_SMT_MASK, RT5616_RSTP_DIS |
RT5616_HP_L_SMT_EN | RT5616_HP_R_SMT_EN);
RT5616_RSTP_MASK | RT5616_HP_L_SMT_MASK |
RT5616_HP_R_SMT_MASK, RT5616_RSTP_DIS |
RT5616_HP_L_SMT_EN | RT5616_HP_R_SMT_EN);
snd_soc_update_bits(codec, RT5616_HP_CALIB_AMP_DET,
RT5616_HPD_PS_MASK, RT5616_HPD_PS_DIS);
RT5616_HPD_PS_MASK, RT5616_HPD_PS_DIS);
msleep(90);
snd_soc_update_bits(codec, RT5616_HP_VOL,
RT5616_L_MUTE | RT5616_R_MUTE,
RT5616_L_MUTE | RT5616_R_MUTE);
RT5616_L_MUTE | RT5616_R_MUTE,
RT5616_L_MUTE | RT5616_R_MUTE);
msleep(30);
break;
@ -621,24 +625,24 @@ static int rt5616_hp_event(struct snd_soc_dapm_widget *w,
}
static int rt5616_lout_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
snd_soc_update_bits(codec, RT5616_PWR_ANLG1,
RT5616_PWR_LM, RT5616_PWR_LM);
RT5616_PWR_LM, RT5616_PWR_LM);
snd_soc_update_bits(codec, RT5616_LOUT_CTRL1,
RT5616_L_MUTE | RT5616_R_MUTE, 0);
RT5616_L_MUTE | RT5616_R_MUTE, 0);
break;
case SND_SOC_DAPM_PRE_PMD:
snd_soc_update_bits(codec, RT5616_LOUT_CTRL1,
RT5616_L_MUTE | RT5616_R_MUTE,
RT5616_L_MUTE | RT5616_R_MUTE);
RT5616_L_MUTE | RT5616_R_MUTE,
RT5616_L_MUTE | RT5616_R_MUTE);
snd_soc_update_bits(codec, RT5616_PWR_ANLG1,
RT5616_PWR_LM, 0);
RT5616_PWR_LM, 0);
break;
default:
@ -649,19 +653,19 @@ static int rt5616_lout_event(struct snd_soc_dapm_widget *w,
}
static int rt5616_bst1_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
snd_soc_update_bits(codec, RT5616_PWR_ANLG2,
RT5616_PWR_BST1_OP2, RT5616_PWR_BST1_OP2);
RT5616_PWR_BST1_OP2, RT5616_PWR_BST1_OP2);
break;
case SND_SOC_DAPM_PRE_PMD:
snd_soc_update_bits(codec, RT5616_PWR_ANLG2,
RT5616_PWR_BST1_OP2, 0);
RT5616_PWR_BST1_OP2, 0);
break;
default:
@ -672,19 +676,19 @@ static int rt5616_bst1_event(struct snd_soc_dapm_widget *w,
}
static int rt5616_bst2_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
snd_soc_update_bits(codec, RT5616_PWR_ANLG2,
RT5616_PWR_BST2_OP2, RT5616_PWR_BST2_OP2);
RT5616_PWR_BST2_OP2, RT5616_PWR_BST2_OP2);
break;
case SND_SOC_DAPM_PRE_PMD:
snd_soc_update_bits(codec, RT5616_PWR_ANLG2,
RT5616_PWR_BST2_OP2, 0);
RT5616_PWR_BST2_OP2, 0);
break;
default:
@ -696,13 +700,13 @@ static int rt5616_bst2_event(struct snd_soc_dapm_widget *w,
static const struct snd_soc_dapm_widget rt5616_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY("PLL1", RT5616_PWR_ANLG2,
RT5616_PWR_PLL_BIT, 0, NULL, 0),
RT5616_PWR_PLL_BIT, 0, NULL, 0),
/* Input Side */
/* micbias */
SND_SOC_DAPM_SUPPLY("LDO", RT5616_PWR_ANLG1,
RT5616_PWR_LDO_BIT, 0, NULL, 0),
RT5616_PWR_LDO_BIT, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("micbias1", RT5616_PWR_ANLG2,
RT5616_PWR_MB1_BIT, 0, NULL, 0),
RT5616_PWR_MB1_BIT, 0, NULL, 0),
/* Input Lines */
SND_SOC_DAPM_INPUT("MIC1"),
@ -714,45 +718,47 @@ static const struct snd_soc_dapm_widget rt5616_dapm_widgets[] = {
/* Boost */
SND_SOC_DAPM_PGA_E("BST1", RT5616_PWR_ANLG2,
RT5616_PWR_BST1_BIT, 0, NULL, 0, rt5616_bst1_event,
SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
RT5616_PWR_BST1_BIT, 0, NULL, 0, rt5616_bst1_event,
SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_PGA_E("BST2", RT5616_PWR_ANLG2,
RT5616_PWR_BST2_BIT, 0, NULL, 0, rt5616_bst2_event,
SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
RT5616_PWR_BST2_BIT, 0, NULL, 0, rt5616_bst2_event,
SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
/* Input Volume */
SND_SOC_DAPM_PGA("INL1 VOL", RT5616_PWR_VOL,
RT5616_PWR_IN1_L_BIT, 0, NULL, 0),
RT5616_PWR_IN1_L_BIT, 0, NULL, 0),
SND_SOC_DAPM_PGA("INR1 VOL", RT5616_PWR_VOL,
RT5616_PWR_IN1_R_BIT, 0, NULL, 0),
RT5616_PWR_IN1_R_BIT, 0, NULL, 0),
SND_SOC_DAPM_PGA("INL2 VOL", RT5616_PWR_VOL,
RT5616_PWR_IN2_L_BIT, 0, NULL, 0),
RT5616_PWR_IN2_L_BIT, 0, NULL, 0),
SND_SOC_DAPM_PGA("INR2 VOL", RT5616_PWR_VOL,
RT5616_PWR_IN2_R_BIT, 0, NULL, 0),
RT5616_PWR_IN2_R_BIT, 0, NULL, 0),
/* REC Mixer */
SND_SOC_DAPM_MIXER("RECMIXL", RT5616_PWR_MIXER, RT5616_PWR_RM_L_BIT, 0,
rt5616_rec_l_mix, ARRAY_SIZE(rt5616_rec_l_mix)),
rt5616_rec_l_mix, ARRAY_SIZE(rt5616_rec_l_mix)),
SND_SOC_DAPM_MIXER("RECMIXR", RT5616_PWR_MIXER, RT5616_PWR_RM_R_BIT, 0,
rt5616_rec_r_mix, ARRAY_SIZE(rt5616_rec_r_mix)),
rt5616_rec_r_mix, ARRAY_SIZE(rt5616_rec_r_mix)),
/* ADCs */
SND_SOC_DAPM_ADC_E("ADC L", NULL, RT5616_PWR_DIG1,
RT5616_PWR_ADC_L_BIT, 0, rt5616_adc_event,
SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU),
RT5616_PWR_ADC_L_BIT, 0, rt5616_adc_event,
SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_ADC_E("ADC R", NULL, RT5616_PWR_DIG1,
RT5616_PWR_ADC_R_BIT, 0, rt5616_adc_event,
SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU),
RT5616_PWR_ADC_R_BIT, 0, rt5616_adc_event,
SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU),
/* ADC Mixer */
SND_SOC_DAPM_SUPPLY("stereo1 filter", RT5616_PWR_DIG2,
RT5616_PWR_ADC_STO1_F_BIT, 0, NULL, 0),
RT5616_PWR_ADC_STO1_F_BIT, 0, NULL, 0),
SND_SOC_DAPM_MIXER("Stereo1 ADC MIXL", SND_SOC_NOPM, 0, 0,
rt5616_sto1_adc_l_mix, ARRAY_SIZE(rt5616_sto1_adc_l_mix)),
rt5616_sto1_adc_l_mix,
ARRAY_SIZE(rt5616_sto1_adc_l_mix)),
SND_SOC_DAPM_MIXER("Stereo1 ADC MIXR", SND_SOC_NOPM, 0, 0,
rt5616_sto1_adc_r_mix, ARRAY_SIZE(rt5616_sto1_adc_r_mix)),
rt5616_sto1_adc_r_mix,
ARRAY_SIZE(rt5616_sto1_adc_r_mix)),
/* Digital Interface */
SND_SOC_DAPM_SUPPLY("I2S1", RT5616_PWR_DIG1,
RT5616_PWR_I2S1_BIT, 0, NULL, 0),
RT5616_PWR_I2S1_BIT, 0, NULL, 0),
SND_SOC_DAPM_PGA("IF1 DAC", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_PGA("IF1 DAC1 L", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_PGA("IF1 DAC1 R", SND_SOC_NOPM, 0, 0, NULL, 0),
@ -770,68 +776,70 @@ static const struct snd_soc_dapm_widget rt5616_dapm_widgets[] = {
/* Output Side */
/* DAC mixer before sound effect */
SND_SOC_DAPM_MIXER("DAC MIXL", SND_SOC_NOPM, 0, 0,
rt5616_dac_l_mix, ARRAY_SIZE(rt5616_dac_l_mix)),
rt5616_dac_l_mix, ARRAY_SIZE(rt5616_dac_l_mix)),
SND_SOC_DAPM_MIXER("DAC MIXR", SND_SOC_NOPM, 0, 0,
rt5616_dac_r_mix, ARRAY_SIZE(rt5616_dac_r_mix)),
rt5616_dac_r_mix, ARRAY_SIZE(rt5616_dac_r_mix)),
SND_SOC_DAPM_SUPPLY("Stero1 DAC Power", RT5616_PWR_DIG2,
RT5616_PWR_DAC_STO1_F_BIT, 0, NULL, 0),
RT5616_PWR_DAC_STO1_F_BIT, 0, NULL, 0),
/* DAC Mixer */
SND_SOC_DAPM_MIXER("Stereo DAC MIXL", SND_SOC_NOPM, 0, 0,
rt5616_sto_dac_l_mix, ARRAY_SIZE(rt5616_sto_dac_l_mix)),
rt5616_sto_dac_l_mix,
ARRAY_SIZE(rt5616_sto_dac_l_mix)),
SND_SOC_DAPM_MIXER("Stereo DAC MIXR", SND_SOC_NOPM, 0, 0,
rt5616_sto_dac_r_mix, ARRAY_SIZE(rt5616_sto_dac_r_mix)),
rt5616_sto_dac_r_mix,
ARRAY_SIZE(rt5616_sto_dac_r_mix)),
/* DACs */
SND_SOC_DAPM_DAC("DAC L1", NULL, RT5616_PWR_DIG1,
RT5616_PWR_DAC_L1_BIT, 0),
RT5616_PWR_DAC_L1_BIT, 0),
SND_SOC_DAPM_DAC("DAC R1", NULL, RT5616_PWR_DIG1,
RT5616_PWR_DAC_R1_BIT, 0),
RT5616_PWR_DAC_R1_BIT, 0),
/* OUT Mixer */
SND_SOC_DAPM_MIXER("OUT MIXL", RT5616_PWR_MIXER, RT5616_PWR_OM_L_BIT,
0, rt5616_out_l_mix, ARRAY_SIZE(rt5616_out_l_mix)),
0, rt5616_out_l_mix, ARRAY_SIZE(rt5616_out_l_mix)),
SND_SOC_DAPM_MIXER("OUT MIXR", RT5616_PWR_MIXER, RT5616_PWR_OM_R_BIT,
0, rt5616_out_r_mix, ARRAY_SIZE(rt5616_out_r_mix)),
0, rt5616_out_r_mix, ARRAY_SIZE(rt5616_out_r_mix)),
/* Output Volume */
SND_SOC_DAPM_PGA("OUTVOL L", RT5616_PWR_VOL,
RT5616_PWR_OV_L_BIT, 0, NULL, 0),
RT5616_PWR_OV_L_BIT, 0, NULL, 0),
SND_SOC_DAPM_PGA("OUTVOL R", RT5616_PWR_VOL,
RT5616_PWR_OV_R_BIT, 0, NULL, 0),
RT5616_PWR_OV_R_BIT, 0, NULL, 0),
SND_SOC_DAPM_PGA("HPOVOL L", RT5616_PWR_VOL,
RT5616_PWR_HV_L_BIT, 0, NULL, 0),
RT5616_PWR_HV_L_BIT, 0, NULL, 0),
SND_SOC_DAPM_PGA("HPOVOL R", RT5616_PWR_VOL,
RT5616_PWR_HV_R_BIT, 0, NULL, 0),
RT5616_PWR_HV_R_BIT, 0, NULL, 0),
SND_SOC_DAPM_PGA("DAC 1", SND_SOC_NOPM,
0, 0, NULL, 0),
0, 0, NULL, 0),
SND_SOC_DAPM_PGA("DAC 2", SND_SOC_NOPM,
0, 0, NULL, 0),
0, 0, NULL, 0),
SND_SOC_DAPM_PGA("HPOVOL", SND_SOC_NOPM,
0, 0, NULL, 0),
0, 0, NULL, 0),
SND_SOC_DAPM_PGA("INL1", RT5616_PWR_VOL,
RT5616_PWR_IN1_L_BIT, 0, NULL, 0),
RT5616_PWR_IN1_L_BIT, 0, NULL, 0),
SND_SOC_DAPM_PGA("INR1", RT5616_PWR_VOL,
RT5616_PWR_IN1_R_BIT, 0, NULL, 0),
RT5616_PWR_IN1_R_BIT, 0, NULL, 0),
SND_SOC_DAPM_PGA("INL2", RT5616_PWR_VOL,
RT5616_PWR_IN2_L_BIT, 0, NULL, 0),
RT5616_PWR_IN2_L_BIT, 0, NULL, 0),
SND_SOC_DAPM_PGA("INR2", RT5616_PWR_VOL,
RT5616_PWR_IN2_R_BIT, 0, NULL, 0),
RT5616_PWR_IN2_R_BIT, 0, NULL, 0),
/* HPO/LOUT/Mono Mixer */
SND_SOC_DAPM_MIXER("HPO MIX", SND_SOC_NOPM, 0, 0,
rt5616_hpo_mix, ARRAY_SIZE(rt5616_hpo_mix)),
rt5616_hpo_mix, ARRAY_SIZE(rt5616_hpo_mix)),
SND_SOC_DAPM_MIXER("LOUT MIX", SND_SOC_NOPM, 0, 0,
rt5616_lout_mix, ARRAY_SIZE(rt5616_lout_mix)),
rt5616_lout_mix, ARRAY_SIZE(rt5616_lout_mix)),
SND_SOC_DAPM_PGA_S("HP amp", 1, SND_SOC_NOPM, 0, 0,
rt5616_hp_event, SND_SOC_DAPM_PRE_PMD |
SND_SOC_DAPM_POST_PMU),
rt5616_hp_event, SND_SOC_DAPM_PRE_PMD |
SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_PGA_S("LOUT amp", 1, SND_SOC_NOPM, 0, 0,
rt5616_lout_event, SND_SOC_DAPM_PRE_PMD |
SND_SOC_DAPM_POST_PMU),
rt5616_lout_event, SND_SOC_DAPM_PRE_PMD |
SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_SUPPLY_S("Charge Pump", 1, SND_SOC_NOPM, 0, 0,
rt5616_charge_pump_event, SND_SOC_DAPM_POST_PMU |
SND_SOC_DAPM_PRE_PMD),
rt5616_charge_pump_event, SND_SOC_DAPM_POST_PMU |
SND_SOC_DAPM_PRE_PMD),
/* Output Lines */
SND_SOC_DAPM_OUTPUT("HPOL"),
@ -950,7 +958,8 @@ static const struct snd_soc_dapm_route rt5616_dapm_routes[] = {
};
static int rt5616_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_codec *codec = rtd->codec;
@ -977,7 +986,7 @@ static int rt5616_hw_params(struct snd_pcm_substream *substream,
dev_dbg(dai->dev, "bclk is %dHz and lrck is %dHz\n",
rt5616->bclk[dai->id], rt5616->lrck[dai->id]);
dev_dbg(dai->dev, "bclk_ms is %d and pre_div is %d for iis %d\n",
bclk_ms, pre_div, dai->id);
bclk_ms, pre_div, dai->id);
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE:
@ -998,10 +1007,9 @@ static int rt5616_hw_params(struct snd_pcm_substream *substream,
mask_clk = RT5616_I2S_PD1_MASK;
val_clk = pre_div << RT5616_I2S_PD1_SFT;
snd_soc_update_bits(codec, RT5616_I2S1_SDP,
RT5616_I2S_DL_MASK, val_len);
RT5616_I2S_DL_MASK, val_len);
snd_soc_update_bits(codec, RT5616_ADDA_CLK1, mask_clk, val_clk);
return 0;
}
@ -1050,15 +1058,14 @@ static int rt5616_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
}
snd_soc_update_bits(codec, RT5616_I2S1_SDP,
RT5616_I2S_MS_MASK | RT5616_I2S_BP_MASK |
RT5616_I2S_DF_MASK, reg_val);
RT5616_I2S_MS_MASK | RT5616_I2S_BP_MASK |
RT5616_I2S_DF_MASK, reg_val);
return 0;
}
static int rt5616_set_dai_sysclk(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir)
int clk_id, unsigned int freq, int dir)
{
struct snd_soc_codec *codec = dai->codec;
struct rt5616_priv *rt5616 = snd_soc_codec_get_drvdata(codec);
@ -1078,8 +1085,9 @@ static int rt5616_set_dai_sysclk(struct snd_soc_dai *dai,
dev_err(codec->dev, "Invalid clock id (%d)\n", clk_id);
return -EINVAL;
}
snd_soc_update_bits(codec, RT5616_GLB_CLK,
RT5616_SCLK_SRC_MASK, reg_val);
RT5616_SCLK_SRC_MASK, reg_val);
rt5616->sysclk = freq;
rt5616->sysclk_src = clk_id;
@ -1089,7 +1097,7 @@ static int rt5616_set_dai_sysclk(struct snd_soc_dai *dai,
}
static int rt5616_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
unsigned int freq_in, unsigned int freq_out)
unsigned int freq_in, unsigned int freq_out)
{
struct snd_soc_codec *codec = dai->codec;
struct rt5616_priv *rt5616 = snd_soc_codec_get_drvdata(codec);
@ -1106,19 +1114,22 @@ static int rt5616_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
rt5616->pll_in = 0;
rt5616->pll_out = 0;
snd_soc_update_bits(codec, RT5616_GLB_CLK,
RT5616_SCLK_SRC_MASK, RT5616_SCLK_SRC_MCLK);
RT5616_SCLK_SRC_MASK,
RT5616_SCLK_SRC_MCLK);
return 0;
}
switch (source) {
case RT5616_PLL1_S_MCLK:
snd_soc_update_bits(codec, RT5616_GLB_CLK,
RT5616_PLL1_SRC_MASK, RT5616_PLL1_SRC_MCLK);
RT5616_PLL1_SRC_MASK,
RT5616_PLL1_SRC_MCLK);
break;
case RT5616_PLL1_S_BCLK1:
case RT5616_PLL1_S_BCLK2:
snd_soc_update_bits(codec, RT5616_GLB_CLK,
RT5616_PLL1_SRC_MASK, RT5616_PLL1_SRC_BCLK1);
RT5616_PLL1_SRC_MASK,
RT5616_PLL1_SRC_BCLK1);
break;
default:
dev_err(codec->dev, "Unknown PLL source %d\n", source);
@ -1136,10 +1147,11 @@ static int rt5616_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
pll_code.n_code, pll_code.k_code);
snd_soc_write(codec, RT5616_PLL_CTRL1,
pll_code.n_code << RT5616_PLL_N_SFT | pll_code.k_code);
pll_code.n_code << RT5616_PLL_N_SFT | pll_code.k_code);
snd_soc_write(codec, RT5616_PLL_CTRL2,
(pll_code.m_bp ? 0 : pll_code.m_code) << RT5616_PLL_M_SFT |
pll_code.m_bp << RT5616_PLL_M_BP_SFT);
(pll_code.m_bp ? 0 : pll_code.m_code) <<
RT5616_PLL_M_SFT |
pll_code.m_bp << RT5616_PLL_M_BP_SFT);
rt5616->pll_in = freq_in;
rt5616->pll_out = freq_out;
@ -1149,22 +1161,50 @@ static int rt5616_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
}
static int rt5616_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
enum snd_soc_bias_level level)
{
struct rt5616_priv *rt5616 = snd_soc_codec_get_drvdata(codec);
int ret;
switch (level) {
case SND_SOC_BIAS_ON:
break;
case SND_SOC_BIAS_PREPARE:
/*
* SND_SOC_BIAS_PREPARE is called while preparing for a
* transition to ON or away from ON. If current bias_level
* is SND_SOC_BIAS_ON, then it is preparing for a transition
* away from ON. Disable the clock in that case, otherwise
* enable it.
*/
if (IS_ERR(rt5616->mclk))
break;
if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_ON) {
clk_disable_unprepare(rt5616->mclk);
} else {
ret = clk_prepare_enable(rt5616->mclk);
if (ret)
return ret;
}
break;
case SND_SOC_BIAS_STANDBY:
if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
snd_soc_update_bits(codec, RT5616_PWR_ANLG1,
RT5616_PWR_VREF1 | RT5616_PWR_MB |
RT5616_PWR_BG | RT5616_PWR_VREF2,
RT5616_PWR_VREF1 | RT5616_PWR_MB |
RT5616_PWR_BG | RT5616_PWR_VREF2);
RT5616_PWR_VREF1 | RT5616_PWR_MB |
RT5616_PWR_BG | RT5616_PWR_VREF2,
RT5616_PWR_VREF1 | RT5616_PWR_MB |
RT5616_PWR_BG | RT5616_PWR_VREF2);
mdelay(10);
snd_soc_update_bits(codec, RT5616_PWR_ANLG1,
RT5616_PWR_FV1 | RT5616_PWR_FV2,
RT5616_PWR_FV1 | RT5616_PWR_FV2);
RT5616_PWR_FV1 | RT5616_PWR_FV2,
RT5616_PWR_FV1 | RT5616_PWR_FV2);
snd_soc_update_bits(codec, RT5616_D_MISC,
RT5616_D_GATE_EN, RT5616_D_GATE_EN);
RT5616_D_GATE_EN,
RT5616_D_GATE_EN);
}
break;
@ -1189,6 +1229,11 @@ static int rt5616_probe(struct snd_soc_codec *codec)
{
struct rt5616_priv *rt5616 = snd_soc_codec_get_drvdata(codec);
/* Check if MCLK provided */
rt5616->mclk = devm_clk_get(codec->dev, "mclk");
if (PTR_ERR(rt5616->mclk) == -EPROBE_DEFER)
return -EPROBE_DEFER;
rt5616->codec = codec;
return 0;
@ -1218,11 +1263,10 @@ static int rt5616_resume(struct snd_soc_codec *codec)
#define rt5616_resume NULL
#endif
#define RT5616_STEREO_RATES SNDRV_PCM_RATE_8000_96000
#define RT5616_STEREO_RATES SNDRV_PCM_RATE_8000_192000
#define RT5616_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)
struct snd_soc_dai_ops rt5616_aif_dai_ops = {
.hw_params = rt5616_hw_params,
.set_fmt = rt5616_set_dai_fmt,
@ -1296,15 +1340,15 @@ MODULE_DEVICE_TABLE(of, rt5616_of_match);
#endif
static int rt5616_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
const struct i2c_device_id *id)
{
struct rt5616_priv *rt5616;
unsigned int val;
int ret;
rt5616 = devm_kzalloc(&i2c->dev, sizeof(struct rt5616_priv),
GFP_KERNEL);
if (rt5616 == NULL)
GFP_KERNEL);
if (!rt5616)
return -ENOMEM;
i2c_set_clientdata(i2c, rt5616);
@ -1326,14 +1370,14 @@ static int rt5616_i2c_probe(struct i2c_client *i2c,
}
regmap_write(rt5616->regmap, RT5616_RESET, 0);
regmap_update_bits(rt5616->regmap, RT5616_PWR_ANLG1,
RT5616_PWR_VREF1 | RT5616_PWR_MB |
RT5616_PWR_BG | RT5616_PWR_VREF2,
RT5616_PWR_VREF1 | RT5616_PWR_MB |
RT5616_PWR_BG | RT5616_PWR_VREF2);
RT5616_PWR_VREF1 | RT5616_PWR_MB |
RT5616_PWR_BG | RT5616_PWR_VREF2,
RT5616_PWR_VREF1 | RT5616_PWR_MB |
RT5616_PWR_BG | RT5616_PWR_VREF2);
mdelay(10);
regmap_update_bits(rt5616->regmap, RT5616_PWR_ANLG1,
RT5616_PWR_FV1 | RT5616_PWR_FV2,
RT5616_PWR_FV1 | RT5616_PWR_FV2);
RT5616_PWR_FV1 | RT5616_PWR_FV2,
RT5616_PWR_FV1 | RT5616_PWR_FV2);
ret = regmap_register_patch(rt5616->regmap, init_list,
ARRAY_SIZE(init_list));
@ -1341,11 +1385,10 @@ static int rt5616_i2c_probe(struct i2c_client *i2c,
dev_warn(&i2c->dev, "Failed to apply regmap patch: %d\n", ret);
regmap_update_bits(rt5616->regmap, RT5616_PWR_ANLG1,
RT5616_PWR_LDO_DVO_MASK, RT5616_PWR_LDO_DVO_1_2V);
RT5616_PWR_LDO_DVO_MASK, RT5616_PWR_LDO_DVO_1_2V);
return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5616,
rt5616_dai, ARRAY_SIZE(rt5616_dai));
rt5616_dai, ARRAY_SIZE(rt5616_dai));
}
static int rt5616_i2c_remove(struct i2c_client *i2c)
@ -1361,7 +1404,6 @@ static void rt5616_i2c_shutdown(struct i2c_client *client)
regmap_write(rt5616->regmap, RT5616_HP_VOL, 0xc8c8);
regmap_write(rt5616->regmap, RT5616_LOUT_CTRL1, 0xc8c8);
}
static struct i2c_driver rt5616_i2c_driver = {

View File

@ -440,11 +440,21 @@ static bool rockchip_i2s_precious_reg(struct device *dev, unsigned int reg)
}
}
static const struct reg_default rockchip_i2s_reg_defaults[] = {
{0x00, 0x0000000f},
{0x04, 0x0000000f},
{0x08, 0x00071f1f},
{0x10, 0x001f0000},
{0x14, 0x01f00000},
};
static const struct regmap_config rockchip_i2s_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
.max_register = I2S_RXDR,
.reg_defaults = rockchip_i2s_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(rockchip_i2s_reg_defaults),
.writeable_reg = rockchip_i2s_wr_reg,
.readable_reg = rockchip_i2s_rd_reg,
.volatile_reg = rockchip_i2s_volatile_reg,
@ -575,6 +585,9 @@ static int rockchip_i2s_remove(struct platform_device *pdev)
static const struct of_device_id rockchip_i2s_match[] = {
{ .compatible = "rockchip,rk3066-i2s", },
{ .compatible = "rockchip,rk3188-i2s", },
{ .compatible = "rockchip,rk3288-i2s", },
{ .compatible = "rockchip,rk3399-i2s", },
{},
};

View File

@ -28,6 +28,7 @@ enum rk_spdif_type {
RK_SPDIF_RK3066,
RK_SPDIF_RK3188,
RK_SPDIF_RK3288,
RK_SPDIF_RK3366,
};
#define RK3288_GRF_SOC_CON2 0x24c
@ -45,16 +46,22 @@ struct rk_spdif_dev {
static const struct of_device_id rk_spdif_match[] = {
{ .compatible = "rockchip,rk3066-spdif",
.data = (void *) RK_SPDIF_RK3066 },
.data = (void *)RK_SPDIF_RK3066 },
{ .compatible = "rockchip,rk3188-spdif",
.data = (void *) RK_SPDIF_RK3188 },
.data = (void *)RK_SPDIF_RK3188 },
{ .compatible = "rockchip,rk3288-spdif",
.data = (void *) RK_SPDIF_RK3288 },
.data = (void *)RK_SPDIF_RK3288 },
{ .compatible = "rockchip,rk3366-spdif",
.data = (void *)RK_SPDIF_RK3366 },
{ .compatible = "rockchip,rk3368-spdif",
.data = (void *)RK_SPDIF_RK3366 },
{ .compatible = "rockchip,rk3399-spdif",
.data = (void *)RK_SPDIF_RK3366 },
{},
};
MODULE_DEVICE_TABLE(of, rk_spdif_match);
static int rk_spdif_runtime_suspend(struct device *dev)
static int __maybe_unused rk_spdif_runtime_suspend(struct device *dev)
{
struct rk_spdif_dev *spdif = dev_get_drvdata(dev);
@ -64,7 +71,7 @@ static int rk_spdif_runtime_suspend(struct device *dev)
return 0;
}
static int rk_spdif_runtime_resume(struct device *dev)
static int __maybe_unused rk_spdif_runtime_resume(struct device *dev)
{
struct rk_spdif_dev *spdif = dev_get_drvdata(dev);
int ret;

View File

@ -90,6 +90,108 @@ static u32 rsnd_adg_ssi_ws_timing_gen2(struct rsnd_dai_stream *io)
return (0x6 + ws) << 8;
}
static void __rsnd_adg_get_timesel_ratio(struct rsnd_priv *priv,
struct rsnd_dai_stream *io,
unsigned int target_rate,
unsigned int *target_val,
unsigned int *target_en)
{
struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
struct device *dev = rsnd_priv_to_dev(priv);
int idx, sel, div, step;
unsigned int val, en;
unsigned int min, diff;
unsigned int sel_rate[] = {
clk_get_rate(adg->clk[CLKA]), /* 0000: CLKA */
clk_get_rate(adg->clk[CLKB]), /* 0001: CLKB */
clk_get_rate(adg->clk[CLKC]), /* 0010: CLKC */
adg->rbga_rate_for_441khz, /* 0011: RBGA */
adg->rbgb_rate_for_48khz, /* 0100: RBGB */
};
min = ~0;
val = 0;
en = 0;
for (sel = 0; sel < ARRAY_SIZE(sel_rate); sel++) {
idx = 0;
step = 2;
if (!sel_rate[sel])
continue;
for (div = 2; div <= 98304; div += step) {
diff = abs(target_rate - sel_rate[sel] / div);
if (min > diff) {
val = (sel << 8) | idx;
min = diff;
en = 1 << (sel + 1); /* fixme */
}
/*
* step of 0_0000 / 0_0001 / 0_1101
* are out of order
*/
if ((idx > 2) && (idx % 2))
step *= 2;
if (idx == 0x1c) {
div += step;
step *= 2;
}
idx++;
}
}
if (min == ~0) {
dev_err(dev, "no Input clock\n");
return;
}
*target_val = val;
if (target_en)
*target_en = en;
}
static void rsnd_adg_get_timesel_ratio(struct rsnd_priv *priv,
struct rsnd_dai_stream *io,
unsigned int in_rate,
unsigned int out_rate,
u32 *in, u32 *out, u32 *en)
{
struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
unsigned int target_rate;
u32 *target_val;
u32 _in;
u32 _out;
u32 _en;
/* default = SSI WS */
_in =
_out = rsnd_adg_ssi_ws_timing_gen2(io);
target_rate = 0;
target_val = NULL;
_en = 0;
if (runtime->rate != in_rate) {
target_rate = out_rate;
target_val = &_out;
} else if (runtime->rate != out_rate) {
target_rate = in_rate;
target_val = &_in;
}
if (target_rate)
__rsnd_adg_get_timesel_ratio(priv, io,
target_rate,
target_val, &_en);
if (in)
*in = _in;
if (out)
*out = _out;
if (en)
*en = _en;
}
int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_mod *cmd_mod,
struct rsnd_dai_stream *io)
{
@ -100,7 +202,10 @@ int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_mod *cmd_mod,
int shift = (id % 2) ? 16 : 0;
u32 mask, val;
val = rsnd_adg_ssi_ws_timing_gen2(io);
rsnd_adg_get_timesel_ratio(priv, io,
rsnd_src_get_in_rate(priv, io),
rsnd_src_get_out_rate(priv, io),
NULL, &val, NULL);
val = val << shift;
mask = 0xffff << shift;
@ -110,25 +215,24 @@ int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_mod *cmd_mod,
return 0;
}
static int rsnd_adg_set_src_timsel_gen2(struct rsnd_mod *src_mod,
struct rsnd_dai_stream *io,
u32 timsel)
int rsnd_adg_set_src_timesel_gen2(struct rsnd_mod *src_mod,
struct rsnd_dai_stream *io,
unsigned int in_rate,
unsigned int out_rate)
{
struct rsnd_priv *priv = rsnd_mod_to_priv(src_mod);
struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
struct rsnd_mod *adg_mod = rsnd_mod_get(adg);
int is_play = rsnd_io_is_play(io);
u32 in, out;
u32 mask, en;
int id = rsnd_mod_id(src_mod);
int shift = (id % 2) ? 16 : 0;
u32 mask, ws;
u32 in, out;
rsnd_mod_confirm_src(src_mod);
ws = rsnd_adg_ssi_ws_timing_gen2(io);
in = (is_play) ? timsel : ws;
out = (is_play) ? ws : timsel;
rsnd_adg_get_timesel_ratio(priv, io,
in_rate, out_rate,
&in, &out, &en);
in = in << shift;
out = out << shift;
@ -157,91 +261,12 @@ static int rsnd_adg_set_src_timsel_gen2(struct rsnd_mod *src_mod,
break;
}
return 0;
}
int rsnd_adg_set_convert_clk_gen2(struct rsnd_mod *src_mod,
struct rsnd_dai_stream *io,
unsigned int src_rate,
unsigned int dst_rate)
{
struct rsnd_priv *priv = rsnd_mod_to_priv(src_mod);
struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
struct rsnd_mod *adg_mod = rsnd_mod_get(adg);
struct device *dev = rsnd_priv_to_dev(priv);
int idx, sel, div, step, ret;
u32 val, en;
unsigned int min, diff;
unsigned int sel_rate [] = {
clk_get_rate(adg->clk[CLKA]), /* 0000: CLKA */
clk_get_rate(adg->clk[CLKB]), /* 0001: CLKB */
clk_get_rate(adg->clk[CLKC]), /* 0010: CLKC */
adg->rbga_rate_for_441khz, /* 0011: RBGA */
adg->rbgb_rate_for_48khz, /* 0100: RBGB */
};
rsnd_mod_confirm_src(src_mod);
min = ~0;
val = 0;
en = 0;
for (sel = 0; sel < ARRAY_SIZE(sel_rate); sel++) {
idx = 0;
step = 2;
if (!sel_rate[sel])
continue;
for (div = 2; div <= 98304; div += step) {
diff = abs(src_rate - sel_rate[sel] / div);
if (min > diff) {
val = (sel << 8) | idx;
min = diff;
en = 1 << (sel + 1); /* fixme */
}
/*
* step of 0_0000 / 0_0001 / 0_1101
* are out of order
*/
if ((idx > 2) && (idx % 2))
step *= 2;
if (idx == 0x1c) {
div += step;
step *= 2;
}
idx++;
}
}
if (min == ~0) {
dev_err(dev, "no Input clock\n");
return -EIO;
}
ret = rsnd_adg_set_src_timsel_gen2(src_mod, io, val);
if (ret < 0) {
dev_err(dev, "timsel error\n");
return ret;
}
rsnd_mod_bset(adg_mod, DIV_EN, en, en);
dev_dbg(dev, "convert rate %d <-> %d\n", src_rate, dst_rate);
if (en)
rsnd_mod_bset(adg_mod, DIV_EN, en, en);
return 0;
}
int rsnd_adg_set_convert_timing_gen2(struct rsnd_mod *src_mod,
struct rsnd_dai_stream *io)
{
u32 val = rsnd_adg_ssi_ws_timing_gen2(io);
rsnd_mod_confirm_src(src_mod);
return rsnd_adg_set_src_timsel_gen2(src_mod, io, val);
}
static void rsnd_adg_set_ssi_clk(struct rsnd_mod *ssi_mod, u32 val)
{
struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod);
@ -518,13 +543,8 @@ int rsnd_adg_probe(struct rsnd_priv *priv)
return -ENOMEM;
}
/*
* ADG is special module.
* Use ADG mod without rsnd_mod_init() to make debug easy
* for rsnd_write/rsnd_read
*/
adg->mod.ops = &adg_ops;
adg->mod.priv = priv;
rsnd_mod_init(priv, &adg->mod, &adg_ops,
NULL, NULL, 0, 0);
rsnd_adg_get_clkin(priv, adg);
rsnd_adg_get_clkout(priv, adg);

View File

@ -29,7 +29,6 @@ static int rsnd_cmd_init(struct rsnd_mod *mod,
{
struct rsnd_mod *dvc = rsnd_io_to_mod_dvc(io);
struct rsnd_mod *mix = rsnd_io_to_mod_mix(io);
struct rsnd_mod *src = rsnd_io_to_mod_src(io);
struct device *dev = rsnd_priv_to_dev(priv);
u32 data;
@ -38,6 +37,8 @@ static int rsnd_cmd_init(struct rsnd_mod *mod,
if (mix) {
struct rsnd_dai *rdai;
struct rsnd_mod *src;
struct rsnd_dai_stream *tio;
int i;
u32 path[] = {
[0] = 0,
@ -55,16 +56,20 @@ static int rsnd_cmd_init(struct rsnd_mod *mod,
*/
data = 0;
for_each_rsnd_dai(rdai, priv, i) {
io = &rdai->playback;
if (mix == rsnd_io_to_mod_mix(io))
tio = &rdai->playback;
src = rsnd_io_to_mod_src(tio);
if (mix == rsnd_io_to_mod_mix(tio))
data |= path[rsnd_mod_id(src)];
io = &rdai->capture;
if (mix == rsnd_io_to_mod_mix(io))
tio = &rdai->capture;
src = rsnd_io_to_mod_src(tio);
if (mix == rsnd_io_to_mod_mix(tio))
data |= path[rsnd_mod_id(src)];
}
} else {
struct rsnd_mod *src = rsnd_io_to_mod_src(io);
u32 path[] = {
[0] = 0x30000,
[1] = 0x30001,
@ -152,7 +157,8 @@ int rsnd_cmd_probe(struct rsnd_priv *priv)
for_each_rsnd_cmd(cmd, priv, i) {
ret = rsnd_mod_init(priv, rsnd_mod_get(cmd),
&rsnd_cmd_ops, NULL, RSND_MOD_CMD, i);
&rsnd_cmd_ops, NULL,
rsnd_mod_get_status, RSND_MOD_CMD, i);
if (ret)
return ret;
}

View File

@ -138,12 +138,22 @@ struct dma_chan *rsnd_mod_dma_req(struct rsnd_dai_stream *io,
return mod->ops->dma_req(io, mod);
}
u32 *rsnd_mod_get_status(struct rsnd_dai_stream *io,
struct rsnd_mod *mod,
enum rsnd_mod_type type)
{
return &mod->status;
}
int rsnd_mod_init(struct rsnd_priv *priv,
struct rsnd_mod *mod,
struct rsnd_mod_ops *ops,
struct clk *clk,
enum rsnd_mod_type type,
int id)
struct rsnd_mod_ops *ops,
struct clk *clk,
u32* (*get_status)(struct rsnd_dai_stream *io,
struct rsnd_mod *mod,
enum rsnd_mod_type type),
enum rsnd_mod_type type,
int id)
{
int ret = clk_prepare(clk);
@ -155,6 +165,7 @@ int rsnd_mod_init(struct rsnd_priv *priv,
mod->type = type;
mod->clk = clk;
mod->priv = priv;
mod->get_status = get_status;
return ret;
}
@ -163,6 +174,7 @@ void rsnd_mod_quit(struct rsnd_mod *mod)
{
if (mod->clk)
clk_unprepare(mod->clk);
mod->clk = NULL;
}
void rsnd_mod_interrupt(struct rsnd_mod *mod,
@ -212,13 +224,36 @@ int rsnd_get_slot_num(struct rsnd_dai_stream *io)
return rdai->slots_num;
}
int rsnd_get_slot_width(struct rsnd_dai_stream *io)
int rsnd_runtime_channel_original(struct rsnd_dai_stream *io)
{
struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
int chan = runtime->channels;
/* Multi channel Mode */
if (rsnd_ssi_multi_slaves(io))
return runtime->channels;
}
int rsnd_runtime_channel_after_ctu(struct rsnd_dai_stream *io)
{
int chan = rsnd_runtime_channel_original(io);
struct rsnd_mod *ctu_mod = rsnd_io_to_mod_ctu(io);
if (ctu_mod) {
u32 converted_chan = rsnd_ctu_converted_channel(ctu_mod);
if (converted_chan)
return converted_chan;
}
return chan;
}
int rsnd_runtime_channel_for_ssi(struct rsnd_dai_stream *io)
{
int chan = rsnd_io_is_play(io) ?
rsnd_runtime_channel_after_ctu(io) :
rsnd_runtime_channel_original(io);
/* Use Multi SSI */
if (rsnd_runtime_is_ssi_multi(io))
chan /= rsnd_get_slot_num(io);
/* TDM Extend Mode needs 8ch */
@ -228,6 +263,21 @@ int rsnd_get_slot_width(struct rsnd_dai_stream *io)
return chan;
}
int rsnd_runtime_is_ssi_multi(struct rsnd_dai_stream *io)
{
int slots = rsnd_get_slot_num(io);
int chan = rsnd_io_is_play(io) ?
rsnd_runtime_channel_after_ctu(io) :
rsnd_runtime_channel_original(io);
return (chan >= 6) && (slots > 1);
}
int rsnd_runtime_is_ssi_tdm(struct rsnd_dai_stream *io)
{
return rsnd_runtime_channel_for_ssi(io) >= 6;
}
/*
* ADINR function
*/
@ -249,29 +299,6 @@ u32 rsnd_get_adinr_bit(struct rsnd_mod *mod, struct rsnd_dai_stream *io)
return 0;
}
u32 rsnd_get_adinr_chan(struct rsnd_mod *mod, struct rsnd_dai_stream *io)
{
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
struct device *dev = rsnd_priv_to_dev(priv);
u32 chan = runtime->channels;
switch (chan) {
case 1:
case 2:
case 4:
case 6:
case 8:
break;
default:
dev_warn(dev, "not supported channel\n");
chan = 0;
break;
}
return chan;
}
/*
* DALIGN function
*/
@ -324,31 +351,73 @@ u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io)
struct rsnd_priv *priv = rsnd_mod_to_priv(mod); \
struct rsnd_mod *mod = (io)->mod[idx]; \
struct device *dev = rsnd_priv_to_dev(priv); \
u32 *status = (io)->mod_status + idx; \
u32 *status = mod->get_status(io, mod, idx); \
u32 mask = 0xF << __rsnd_mod_shift_##func; \
u8 val = (*status >> __rsnd_mod_shift_##func) & 0xF; \
u8 add = ((val + __rsnd_mod_add_##func) & 0xF); \
int ret = 0; \
int call = (val == __rsnd_mod_call_##func) && (mod)->ops->func; \
*status = (*status & ~mask) + \
(add << __rsnd_mod_shift_##func); \
if (add == 0xF) \
call = 0; \
else \
*status = (*status & ~mask) + \
(add << __rsnd_mod_shift_##func); \
dev_dbg(dev, "%s[%d]\t0x%08x %s\n", \
rsnd_mod_name(mod), rsnd_mod_id(mod), \
*status, call ? #func : ""); \
if (call) \
ret = (mod)->ops->func(mod, io, param); \
if (ret) \
dev_dbg(dev, "%s[%d] : rsnd_mod_call error %d\n", \
rsnd_mod_name(mod), rsnd_mod_id(mod), ret); \
ret; \
})
static enum rsnd_mod_type rsnd_mod_sequence[][RSND_MOD_MAX] = {
{
/* CAPTURE */
RSND_MOD_AUDMAPP,
RSND_MOD_AUDMA,
RSND_MOD_DVC,
RSND_MOD_MIX,
RSND_MOD_CTU,
RSND_MOD_CMD,
RSND_MOD_SRC,
RSND_MOD_SSIU,
RSND_MOD_SSIM3,
RSND_MOD_SSIM2,
RSND_MOD_SSIM1,
RSND_MOD_SSIP,
RSND_MOD_SSI,
}, {
/* PLAYBACK */
RSND_MOD_AUDMAPP,
RSND_MOD_AUDMA,
RSND_MOD_SSIM3,
RSND_MOD_SSIM2,
RSND_MOD_SSIM1,
RSND_MOD_SSIP,
RSND_MOD_SSI,
RSND_MOD_SSIU,
RSND_MOD_DVC,
RSND_MOD_MIX,
RSND_MOD_CTU,
RSND_MOD_CMD,
RSND_MOD_SRC,
},
};
#define rsnd_dai_call(fn, io, param...) \
({ \
struct rsnd_mod *mod; \
int type, is_play = rsnd_io_is_play(io); \
int ret = 0, i; \
for (i = 0; i < RSND_MOD_MAX; i++) { \
mod = (io)->mod[i]; \
type = rsnd_mod_sequence[is_play][i]; \
mod = (io)->mod[type]; \
if (!mod) \
continue; \
ret |= rsnd_mod_call(i, io, fn, param); \
ret |= rsnd_mod_call(type, io, fn, param); \
} \
ret; \
})
@ -363,6 +432,9 @@ int rsnd_dai_connect(struct rsnd_mod *mod,
if (!mod)
return -EIO;
if (io->mod[type] == mod)
return 0;
if (io->mod[type])
return -EINVAL;
@ -511,9 +583,16 @@ static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd,
ret = rsnd_dai_call(start, io, priv);
if (ret < 0)
goto dai_trigger_end;
ret = rsnd_dai_call(irq, io, priv, 1);
if (ret < 0)
goto dai_trigger_end;
break;
case SNDRV_PCM_TRIGGER_STOP:
ret = rsnd_dai_call(stop, io, priv);
ret = rsnd_dai_call(irq, io, priv, 0);
ret |= rsnd_dai_call(stop, io, priv);
ret |= rsnd_dai_call(quit, io, priv);
@ -863,7 +942,7 @@ static int rsnd_kctrl_put(struct snd_kcontrol *kctrl,
}
}
if (change)
if (change && cfg->update)
cfg->update(cfg->io, mod);
return change;
@ -923,7 +1002,7 @@ int rsnd_kctrl_new_m(struct rsnd_mod *mod,
int ch_size,
u32 max)
{
if (ch_size > RSND_DVC_CHANNELS)
if (ch_size > RSND_MAX_CHANNELS)
return -EINVAL;
_cfg->cfg.max = max;
@ -1055,7 +1134,6 @@ static int rsnd_probe(struct platform_device *pdev)
struct rsnd_priv *priv;
struct device *dev = &pdev->dev;
struct rsnd_dai *rdai;
const struct of_device_id *of_id = of_match_device(rsnd_of_match, dev);
int (*probe_func[])(struct rsnd_priv *priv) = {
rsnd_gen_probe,
rsnd_dma_probe,
@ -1081,7 +1159,7 @@ static int rsnd_probe(struct platform_device *pdev)
}
priv->pdev = pdev;
priv->flags = (unsigned long)of_id->data;
priv->flags = (unsigned long)of_device_get_match_data(dev);
spin_lock_init(&priv->lock);
/*

View File

@ -12,8 +12,75 @@
#define CTU_NAME_SIZE 16
#define CTU_NAME "ctu"
/*
* User needs to setup CTU by amixer, and its settings are
* based on below registers
*
* CTUn_CPMDR : amixser set "CTU Pass"
* CTUn_SV0xR : amixser set "CTU SV0"
* CTUn_SV1xR : amixser set "CTU SV1"
* CTUn_SV2xR : amixser set "CTU SV2"
* CTUn_SV3xR : amixser set "CTU SV3"
*
* [CTU Pass]
* 0000: default
* 0001: Connect input data of channel 0
* 0010: Connect input data of channel 1
* 0011: Connect input data of channel 2
* 0100: Connect input data of channel 3
* 0101: Connect input data of channel 4
* 0110: Connect input data of channel 5
* 0111: Connect input data of channel 6
* 1000: Connect input data of channel 7
* 1001: Connect calculated data by scale values of matrix row 0
* 1010: Connect calculated data by scale values of matrix row 1
* 1011: Connect calculated data by scale values of matrix row 2
* 1100: Connect calculated data by scale values of matrix row 3
*
* [CTU SVx]
* [Output0] = [SV00, SV01, SV02, SV03, SV04, SV05, SV06, SV07]
* [Output1] = [SV10, SV11, SV12, SV13, SV14, SV15, SV16, SV17]
* [Output2] = [SV20, SV21, SV22, SV23, SV24, SV25, SV26, SV27]
* [Output3] = [SV30, SV31, SV32, SV33, SV34, SV35, SV36, SV37]
* [Output4] = [ 0, 0, 0, 0, 0, 0, 0, 0 ]
* [Output5] = [ 0, 0, 0, 0, 0, 0, 0, 0 ]
* [Output6] = [ 0, 0, 0, 0, 0, 0, 0, 0 ]
* [Output7] = [ 0, 0, 0, 0, 0, 0, 0, 0 ]
*
* [SVxx]
* Plus Minus
* value time dB value time dB
* -----------------------------------------------------------------------
* H'7F_FFFF 2 6 H'80_0000 2 6
* ...
* H'40_0000 1 0 H'C0_0000 1 0
* ...
* H'00_0001 2.38 x 10^-7 -132
* H'00_0000 0 Mute H'FF_FFFF 2.38 x 10^-7 -132
*
*
* Ex) Input ch -> Output ch
* 1ch -> 0ch
* 0ch -> 1ch
*
* amixer set "CTU Reset" on
* amixer set "CTU Pass" 9,10
* amixer set "CTU SV0" 0,4194304
* amixer set "CTU SV1" 4194304,0
* or
* amixer set "CTU Reset" on
* amixer set "CTU Pass" 2,1
*/
struct rsnd_ctu {
struct rsnd_mod mod;
struct rsnd_kctrl_cfg_m pass;
struct rsnd_kctrl_cfg_m sv0;
struct rsnd_kctrl_cfg_m sv1;
struct rsnd_kctrl_cfg_m sv2;
struct rsnd_kctrl_cfg_m sv3;
struct rsnd_kctrl_cfg_s reset;
int channels;
};
#define rsnd_ctu_nr(priv) ((priv)->ctu_nr)
@ -23,12 +90,28 @@ struct rsnd_ctu {
((pos) = (struct rsnd_ctu *)(priv)->ctu + i); \
i++)
#define rsnd_mod_to_ctu(_mod) \
container_of((_mod), struct rsnd_ctu, mod)
#define rsnd_ctu_get(priv, id) ((struct rsnd_ctu *)(priv->ctu) + id)
#define rsnd_ctu_initialize_lock(mod) __rsnd_ctu_initialize_lock(mod, 1)
#define rsnd_ctu_initialize_unlock(mod) __rsnd_ctu_initialize_lock(mod, 0)
static void __rsnd_ctu_initialize_lock(struct rsnd_mod *mod, u32 enable)
static void rsnd_ctu_activation(struct rsnd_mod *mod)
{
rsnd_mod_write(mod, CTU_CTUIR, enable);
rsnd_mod_write(mod, CTU_SWRSR, 0);
rsnd_mod_write(mod, CTU_SWRSR, 1);
}
static void rsnd_ctu_halt(struct rsnd_mod *mod)
{
rsnd_mod_write(mod, CTU_CTUIR, 1);
rsnd_mod_write(mod, CTU_SWRSR, 0);
}
int rsnd_ctu_converted_channel(struct rsnd_mod *mod)
{
struct rsnd_ctu *ctu = rsnd_mod_to_ctu(mod);
return ctu->channels;
}
static int rsnd_ctu_probe_(struct rsnd_mod *mod,
@ -38,17 +121,103 @@ static int rsnd_ctu_probe_(struct rsnd_mod *mod,
return rsnd_cmd_attach(io, rsnd_mod_id(mod) / 4);
}
static void rsnd_ctu_value_init(struct rsnd_dai_stream *io,
struct rsnd_mod *mod)
{
struct rsnd_ctu *ctu = rsnd_mod_to_ctu(mod);
u32 cpmdr = 0;
u32 scmdr = 0;
int i;
for (i = 0; i < RSND_MAX_CHANNELS; i++) {
u32 val = ctu->pass.val[i];
cpmdr |= val << (28 - (i * 4));
if ((val > 0x8) && (scmdr < (val - 0x8)))
scmdr = val - 0x8;
}
rsnd_mod_write(mod, CTU_CTUIR, 1);
rsnd_mod_write(mod, CTU_ADINR, rsnd_runtime_channel_original(io));
rsnd_mod_write(mod, CTU_CPMDR, cpmdr);
rsnd_mod_write(mod, CTU_SCMDR, scmdr);
if (scmdr > 0) {
rsnd_mod_write(mod, CTU_SV00R, ctu->sv0.val[0]);
rsnd_mod_write(mod, CTU_SV01R, ctu->sv0.val[1]);
rsnd_mod_write(mod, CTU_SV02R, ctu->sv0.val[2]);
rsnd_mod_write(mod, CTU_SV03R, ctu->sv0.val[3]);
rsnd_mod_write(mod, CTU_SV04R, ctu->sv0.val[4]);
rsnd_mod_write(mod, CTU_SV05R, ctu->sv0.val[5]);
rsnd_mod_write(mod, CTU_SV06R, ctu->sv0.val[6]);
rsnd_mod_write(mod, CTU_SV07R, ctu->sv0.val[7]);
}
if (scmdr > 1) {
rsnd_mod_write(mod, CTU_SV10R, ctu->sv1.val[0]);
rsnd_mod_write(mod, CTU_SV11R, ctu->sv1.val[1]);
rsnd_mod_write(mod, CTU_SV12R, ctu->sv1.val[2]);
rsnd_mod_write(mod, CTU_SV13R, ctu->sv1.val[3]);
rsnd_mod_write(mod, CTU_SV14R, ctu->sv1.val[4]);
rsnd_mod_write(mod, CTU_SV15R, ctu->sv1.val[5]);
rsnd_mod_write(mod, CTU_SV16R, ctu->sv1.val[6]);
rsnd_mod_write(mod, CTU_SV17R, ctu->sv1.val[7]);
}
if (scmdr > 2) {
rsnd_mod_write(mod, CTU_SV20R, ctu->sv2.val[0]);
rsnd_mod_write(mod, CTU_SV21R, ctu->sv2.val[1]);
rsnd_mod_write(mod, CTU_SV22R, ctu->sv2.val[2]);
rsnd_mod_write(mod, CTU_SV23R, ctu->sv2.val[3]);
rsnd_mod_write(mod, CTU_SV24R, ctu->sv2.val[4]);
rsnd_mod_write(mod, CTU_SV25R, ctu->sv2.val[5]);
rsnd_mod_write(mod, CTU_SV26R, ctu->sv2.val[6]);
rsnd_mod_write(mod, CTU_SV27R, ctu->sv2.val[7]);
}
if (scmdr > 3) {
rsnd_mod_write(mod, CTU_SV30R, ctu->sv3.val[0]);
rsnd_mod_write(mod, CTU_SV31R, ctu->sv3.val[1]);
rsnd_mod_write(mod, CTU_SV32R, ctu->sv3.val[2]);
rsnd_mod_write(mod, CTU_SV33R, ctu->sv3.val[3]);
rsnd_mod_write(mod, CTU_SV34R, ctu->sv3.val[4]);
rsnd_mod_write(mod, CTU_SV35R, ctu->sv3.val[5]);
rsnd_mod_write(mod, CTU_SV36R, ctu->sv3.val[6]);
rsnd_mod_write(mod, CTU_SV37R, ctu->sv3.val[7]);
}
rsnd_mod_write(mod, CTU_CTUIR, 0);
}
static void rsnd_ctu_value_reset(struct rsnd_dai_stream *io,
struct rsnd_mod *mod)
{
struct rsnd_ctu *ctu = rsnd_mod_to_ctu(mod);
int i;
if (!ctu->reset.val)
return;
for (i = 0; i < RSND_MAX_CHANNELS; i++) {
ctu->pass.val[i] = 0;
ctu->sv0.val[i] = 0;
ctu->sv1.val[i] = 0;
ctu->sv2.val[i] = 0;
ctu->sv3.val[i] = 0;
}
ctu->reset.val = 0;
}
static int rsnd_ctu_init(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
rsnd_mod_power_on(mod);
rsnd_ctu_initialize_lock(mod);
rsnd_ctu_activation(mod);
rsnd_mod_write(mod, CTU_ADINR, rsnd_get_adinr_chan(mod, io));
rsnd_ctu_initialize_unlock(mod);
rsnd_ctu_value_init(io, mod);
return 0;
}
@ -57,16 +226,110 @@ static int rsnd_ctu_quit(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
rsnd_ctu_halt(mod);
rsnd_mod_power_off(mod);
return 0;
}
static int rsnd_ctu_hw_params(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *fe_params)
{
struct rsnd_ctu *ctu = rsnd_mod_to_ctu(mod);
struct snd_soc_pcm_runtime *fe = substream->private_data;
/*
* CTU assumes that it is used under DPCM if user want to use
* channel transfer. Then, CTU should be FE.
* And then, this function will be called *after* BE settings.
* this means, each BE already has fixuped hw_params.
* see
* dpcm_fe_dai_hw_params()
* dpcm_be_dai_hw_params()
*/
ctu->channels = 0;
if (fe->dai_link->dynamic) {
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
struct device *dev = rsnd_priv_to_dev(priv);
struct snd_soc_dpcm *dpcm;
struct snd_pcm_hw_params *be_params;
int stream = substream->stream;
list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
be_params = &dpcm->hw_params;
if (params_channels(fe_params) != params_channels(be_params))
ctu->channels = params_channels(be_params);
}
dev_dbg(dev, "CTU convert channels %d\n", ctu->channels);
}
return 0;
}
static int rsnd_ctu_pcm_new(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct snd_soc_pcm_runtime *rtd)
{
struct rsnd_ctu *ctu = rsnd_mod_to_ctu(mod);
int ret;
/* CTU Pass */
ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU Pass",
NULL,
&ctu->pass, RSND_MAX_CHANNELS,
0xC);
/* ROW0 */
ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU SV0",
NULL,
&ctu->sv0, RSND_MAX_CHANNELS,
0x00FFFFFF);
if (ret < 0)
return ret;
/* ROW1 */
ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU SV1",
NULL,
&ctu->sv1, RSND_MAX_CHANNELS,
0x00FFFFFF);
if (ret < 0)
return ret;
/* ROW2 */
ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU SV2",
NULL,
&ctu->sv2, RSND_MAX_CHANNELS,
0x00FFFFFF);
if (ret < 0)
return ret;
/* ROW3 */
ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU SV3",
NULL,
&ctu->sv3, RSND_MAX_CHANNELS,
0x00FFFFFF);
if (ret < 0)
return ret;
/* Reset */
ret = rsnd_kctrl_new_s(mod, io, rtd, "CTU Reset",
rsnd_ctu_value_reset,
&ctu->reset, 1);
return ret;
}
static struct rsnd_mod_ops rsnd_ctu_ops = {
.name = CTU_NAME,
.probe = rsnd_ctu_probe_,
.init = rsnd_ctu_init,
.quit = rsnd_ctu_quit,
.hw_params = rsnd_ctu_hw_params,
.pcm_new = rsnd_ctu_pcm_new,
};
struct rsnd_mod *rsnd_ctu_mod_get(struct rsnd_priv *priv, int id)
@ -129,7 +392,7 @@ int rsnd_ctu_probe(struct rsnd_priv *priv)
}
ret = rsnd_mod_init(priv, rsnd_mod_get(ctu), &rsnd_ctu_ops,
clk, RSND_MOD_CTU, i);
clk, rsnd_mod_get_status, RSND_MOD_CTU, i);
if (ret)
goto rsnd_ctu_probe_done;

View File

@ -622,15 +622,13 @@ static void rsnd_dma_of_path(struct rsnd_mod *this,
}
}
struct rsnd_mod *rsnd_dma_attach(struct rsnd_dai_stream *io,
struct rsnd_mod *mod, int id)
int rsnd_dma_attach(struct rsnd_dai_stream *io, struct rsnd_mod *mod,
struct rsnd_mod **dma_mod, int id)
{
struct rsnd_mod *dma_mod;
struct rsnd_mod *mod_from = NULL;
struct rsnd_mod *mod_to = NULL;
struct rsnd_priv *priv = rsnd_io_to_priv(io);
struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv);
struct rsnd_dma *dma;
struct device *dev = rsnd_priv_to_dev(priv);
struct rsnd_mod_ops *ops;
enum rsnd_mod_type type;
@ -646,17 +644,10 @@ struct rsnd_mod *rsnd_dma_attach(struct rsnd_dai_stream *io,
* rsnd_rdai_continuance_probe()
*/
if (!dmac)
return ERR_PTR(-EAGAIN);
dma = devm_kzalloc(dev, sizeof(*dma), GFP_KERNEL);
if (!dma)
return ERR_PTR(-ENOMEM);
return -EAGAIN;
rsnd_dma_of_path(mod, io, is_play, &mod_from, &mod_to);
dma->src_addr = rsnd_dma_addr(io, mod_from, is_play, 1);
dma->dst_addr = rsnd_dma_addr(io, mod_to, is_play, 0);
/* for Gen2 */
if (mod_from && mod_to) {
ops = &rsnd_dmapp_ops;
@ -678,27 +669,38 @@ struct rsnd_mod *rsnd_dma_attach(struct rsnd_dai_stream *io,
type = RSND_MOD_AUDMA;
}
dma_mod = rsnd_mod_get(dma);
if (!(*dma_mod)) {
struct rsnd_dma *dma;
ret = rsnd_mod_init(priv, dma_mod,
ops, NULL, type, dma_id);
dma = devm_kzalloc(dev, sizeof(*dma), GFP_KERNEL);
if (!dma)
return -ENOMEM;
*dma_mod = rsnd_mod_get(dma);
dma->src_addr = rsnd_dma_addr(io, mod_from, is_play, 1);
dma->dst_addr = rsnd_dma_addr(io, mod_to, is_play, 0);
ret = rsnd_mod_init(priv, *dma_mod, ops, NULL,
rsnd_mod_get_status, type, dma_id);
if (ret < 0)
return ret;
dev_dbg(dev, "%s[%d] %s[%d] -> %s[%d]\n",
rsnd_mod_name(*dma_mod), rsnd_mod_id(*dma_mod),
rsnd_mod_name(mod_from), rsnd_mod_id(mod_from),
rsnd_mod_name(mod_to), rsnd_mod_id(mod_to));
ret = attach(io, dma, id, mod_from, mod_to);
if (ret < 0)
return ret;
}
ret = rsnd_dai_connect(*dma_mod, io, type);
if (ret < 0)
return ERR_PTR(ret);
return ret;
dev_dbg(dev, "%s[%d] %s[%d] -> %s[%d]\n",
rsnd_mod_name(dma_mod), rsnd_mod_id(dma_mod),
rsnd_mod_name(mod_from), rsnd_mod_id(mod_from),
rsnd_mod_name(mod_to), rsnd_mod_id(mod_to));
ret = attach(io, dma, id, mod_from, mod_to);
if (ret < 0)
return ERR_PTR(ret);
ret = rsnd_dai_connect(dma_mod, io, type);
if (ret < 0)
return ERR_PTR(ret);
return rsnd_mod_get(dma);
return 0;
}
int rsnd_dma_probe(struct rsnd_priv *priv)

View File

@ -8,6 +8,29 @@
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
/*
* Playback Volume
* amixer set "DVC Out" 100%
*
* Capture Volume
* amixer set "DVC In" 100%
*
* Playback Mute
* amixer set "DVC Out Mute" on
*
* Capture Mute
* amixer set "DVC In Mute" on
*
* Volume Ramp
* amixer set "DVC Out Ramp Up Rate" "0.125 dB/64 steps"
* amixer set "DVC Out Ramp Down Rate" "0.125 dB/512 steps"
* amixer set "DVC Out Ramp" on
* aplay xxx.wav &
* amixer set "DVC Out" 80% // Volume Down
* amixer set "DVC Out" 100% // Volume Up
*/
#include "rsnd.h"
#define RSND_DVC_NAME_SIZE 16
@ -83,15 +106,15 @@ static void rsnd_dvc_volume_parameter(struct rsnd_dai_stream *io,
struct rsnd_mod *mod)
{
struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod);
u32 val[RSND_DVC_CHANNELS];
u32 val[RSND_MAX_CHANNELS];
int i;
/* Enable Ramp */
if (dvc->ren.val)
for (i = 0; i < RSND_DVC_CHANNELS; i++)
for (i = 0; i < RSND_MAX_CHANNELS; i++)
val[i] = dvc->volume.cfg.max;
else
for (i = 0; i < RSND_DVC_CHANNELS; i++)
for (i = 0; i < RSND_MAX_CHANNELS; i++)
val[i] = dvc->volume.val[i];
/* Enable Digital Volume */
@ -116,7 +139,7 @@ static void rsnd_dvc_volume_init(struct rsnd_dai_stream *io,
u32 vrdbr = 0;
adinr = rsnd_get_adinr_bit(mod, io) |
rsnd_get_adinr_chan(mod, io);
rsnd_runtime_channel_after_ctu(io);
/* Enable Digital Volume, Zero Cross Mute Mode */
dvucr |= 0x101;
@ -373,7 +396,7 @@ int rsnd_dvc_probe(struct rsnd_priv *priv)
}
ret = rsnd_mod_init(priv, rsnd_mod_get(dvc), &rsnd_dvc_ops,
clk, RSND_MOD_DVC, i);
clk, rsnd_mod_get_status, RSND_MOD_DVC, i);
if (ret)
goto rsnd_dvc_probe_done;

View File

@ -101,23 +101,6 @@ void rsnd_write(struct rsnd_priv *priv,
struct device *dev = rsnd_priv_to_dev(priv);
struct rsnd_gen *gen = rsnd_priv_to_gen(priv);
if (!rsnd_is_accessible_reg(priv, gen, reg))
return;
regmap_fields_write(gen->regs[reg], rsnd_mod_id(mod), data);
dev_dbg(dev, "w %s[%d] - %-18s (%4d) : %08x\n",
rsnd_mod_name(mod), rsnd_mod_id(mod),
rsnd_reg_name(gen, reg), reg, data);
}
void rsnd_force_write(struct rsnd_priv *priv,
struct rsnd_mod *mod,
enum rsnd_reg reg, u32 data)
{
struct device *dev = rsnd_priv_to_dev(priv);
struct rsnd_gen *gen = rsnd_priv_to_gen(priv);
if (!rsnd_is_accessible_reg(priv, gen, reg))
return;
@ -137,8 +120,8 @@ void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod,
if (!rsnd_is_accessible_reg(priv, gen, reg))
return;
regmap_fields_update_bits(gen->regs[reg], rsnd_mod_id(mod),
mask, data);
regmap_fields_force_update_bits(gen->regs[reg],
rsnd_mod_id(mod), mask, data);
dev_dbg(dev, "b %s[%d] - %-18s (%4d) : %08x/%08x\n",
rsnd_mod_name(mod), rsnd_mod_id(mod),
@ -260,8 +243,43 @@ static int rsnd_gen2_probe(struct rsnd_priv *priv)
RSND_GEN_M_REG(SRC_SRCCR, 0x224, 0x40),
RSND_GEN_M_REG(SRC_BSDSR, 0x22c, 0x40),
RSND_GEN_M_REG(SRC_BSISR, 0x238, 0x40),
RSND_GEN_M_REG(CTU_SWRSR, 0x500, 0x100),
RSND_GEN_M_REG(CTU_CTUIR, 0x504, 0x100),
RSND_GEN_M_REG(CTU_ADINR, 0x508, 0x100),
RSND_GEN_M_REG(CTU_CPMDR, 0x510, 0x100),
RSND_GEN_M_REG(CTU_SCMDR, 0x514, 0x100),
RSND_GEN_M_REG(CTU_SV00R, 0x518, 0x100),
RSND_GEN_M_REG(CTU_SV01R, 0x51c, 0x100),
RSND_GEN_M_REG(CTU_SV02R, 0x520, 0x100),
RSND_GEN_M_REG(CTU_SV03R, 0x524, 0x100),
RSND_GEN_M_REG(CTU_SV04R, 0x528, 0x100),
RSND_GEN_M_REG(CTU_SV05R, 0x52c, 0x100),
RSND_GEN_M_REG(CTU_SV06R, 0x530, 0x100),
RSND_GEN_M_REG(CTU_SV07R, 0x534, 0x100),
RSND_GEN_M_REG(CTU_SV10R, 0x538, 0x100),
RSND_GEN_M_REG(CTU_SV11R, 0x53c, 0x100),
RSND_GEN_M_REG(CTU_SV12R, 0x540, 0x100),
RSND_GEN_M_REG(CTU_SV13R, 0x544, 0x100),
RSND_GEN_M_REG(CTU_SV14R, 0x548, 0x100),
RSND_GEN_M_REG(CTU_SV15R, 0x54c, 0x100),
RSND_GEN_M_REG(CTU_SV16R, 0x550, 0x100),
RSND_GEN_M_REG(CTU_SV17R, 0x554, 0x100),
RSND_GEN_M_REG(CTU_SV20R, 0x558, 0x100),
RSND_GEN_M_REG(CTU_SV21R, 0x55c, 0x100),
RSND_GEN_M_REG(CTU_SV22R, 0x560, 0x100),
RSND_GEN_M_REG(CTU_SV23R, 0x564, 0x100),
RSND_GEN_M_REG(CTU_SV24R, 0x568, 0x100),
RSND_GEN_M_REG(CTU_SV25R, 0x56c, 0x100),
RSND_GEN_M_REG(CTU_SV26R, 0x570, 0x100),
RSND_GEN_M_REG(CTU_SV27R, 0x574, 0x100),
RSND_GEN_M_REG(CTU_SV30R, 0x578, 0x100),
RSND_GEN_M_REG(CTU_SV31R, 0x57c, 0x100),
RSND_GEN_M_REG(CTU_SV32R, 0x580, 0x100),
RSND_GEN_M_REG(CTU_SV33R, 0x584, 0x100),
RSND_GEN_M_REG(CTU_SV34R, 0x588, 0x100),
RSND_GEN_M_REG(CTU_SV35R, 0x58c, 0x100),
RSND_GEN_M_REG(CTU_SV36R, 0x590, 0x100),
RSND_GEN_M_REG(CTU_SV37R, 0x594, 0x100),
RSND_GEN_M_REG(MIX_SWRSR, 0xd00, 0x40),
RSND_GEN_M_REG(MIX_MIXIR, 0xd04, 0x40),
RSND_GEN_M_REG(MIX_ADINR, 0xd08, 0x40),

View File

@ -51,7 +51,7 @@ static void rsnd_mix_volume_init(struct rsnd_dai_stream *io,
rsnd_mod_write(mod, MIX_MIXIR, 1);
/* General Information */
rsnd_mod_write(mod, MIX_ADINR, rsnd_get_adinr_chan(mod, io));
rsnd_mod_write(mod, MIX_ADINR, rsnd_runtime_channel_after_ctu(io));
/* volume step */
rsnd_mod_write(mod, MIX_MIXMR, 0);
@ -172,7 +172,7 @@ int rsnd_mix_probe(struct rsnd_priv *priv)
}
ret = rsnd_mod_init(priv, rsnd_mod_get(mix), &rsnd_mix_ops,
clk, RSND_MOD_MIX, i);
clk, rsnd_mod_get_status, RSND_MOD_MIX, i);
if (ret)
goto rsnd_mix_probe_done;

View File

@ -86,8 +86,43 @@ enum rsnd_reg {
RSND_REG_CMD_BUSIF_DALIGN, /* Gen2 only */
RSND_REG_CMD_ROUTE_SLCT,
RSND_REG_CMDOUT_TIMSEL, /* Gen2 only */
RSND_REG_CTU_SWRSR,
RSND_REG_CTU_CTUIR,
RSND_REG_CTU_ADINR,
RSND_REG_CTU_CPMDR,
RSND_REG_CTU_SCMDR,
RSND_REG_CTU_SV00R,
RSND_REG_CTU_SV01R,
RSND_REG_CTU_SV02R,
RSND_REG_CTU_SV03R,
RSND_REG_CTU_SV04R,
RSND_REG_CTU_SV05R,
RSND_REG_CTU_SV06R,
RSND_REG_CTU_SV07R,
RSND_REG_CTU_SV10R,
RSND_REG_CTU_SV11R,
RSND_REG_CTU_SV12R,
RSND_REG_CTU_SV13R,
RSND_REG_CTU_SV14R,
RSND_REG_CTU_SV15R,
RSND_REG_CTU_SV16R,
RSND_REG_CTU_SV17R,
RSND_REG_CTU_SV20R,
RSND_REG_CTU_SV21R,
RSND_REG_CTU_SV22R,
RSND_REG_CTU_SV23R,
RSND_REG_CTU_SV24R,
RSND_REG_CTU_SV25R,
RSND_REG_CTU_SV26R,
RSND_REG_CTU_SV27R,
RSND_REG_CTU_SV30R,
RSND_REG_CTU_SV31R,
RSND_REG_CTU_SV32R,
RSND_REG_CTU_SV33R,
RSND_REG_CTU_SV34R,
RSND_REG_CTU_SV35R,
RSND_REG_CTU_SV36R,
RSND_REG_CTU_SV37R,
RSND_REG_MIX_SWRSR,
RSND_REG_MIX_MIXIR,
RSND_REG_MIX_ADINR,
@ -147,8 +182,6 @@ struct rsnd_dai_stream;
rsnd_read(rsnd_mod_to_priv(m), m, RSND_REG_##r)
#define rsnd_mod_write(m, r, d) \
rsnd_write(rsnd_mod_to_priv(m), m, RSND_REG_##r, d)
#define rsnd_mod_force_write(m, r, d) \
rsnd_force_write(rsnd_mod_to_priv(m), m, RSND_REG_##r, d)
#define rsnd_mod_bset(m, r, s, d) \
rsnd_bset(rsnd_mod_to_priv(m), m, RSND_REG_##r, s, d)
@ -160,14 +193,13 @@ void rsnd_force_write(struct rsnd_priv *priv, struct rsnd_mod *mod,
void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod, enum rsnd_reg reg,
u32 mask, u32 data);
u32 rsnd_get_adinr_bit(struct rsnd_mod *mod, struct rsnd_dai_stream *io);
u32 rsnd_get_adinr_chan(struct rsnd_mod *mod, struct rsnd_dai_stream *io);
u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io);
/*
* R-Car DMA
*/
struct rsnd_mod *rsnd_dma_attach(struct rsnd_dai_stream *io,
struct rsnd_mod *mod, int id);
int rsnd_dma_attach(struct rsnd_dai_stream *io,
struct rsnd_mod *mod, struct rsnd_mod **dma_mod, int id);
int rsnd_dma_probe(struct rsnd_priv *priv);
struct dma_chan *rsnd_dma_request_channel(struct device_node *of_node,
struct rsnd_mod *mod, char *name);
@ -214,6 +246,9 @@ struct rsnd_mod_ops {
int (*stop)(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv);
int (*irq)(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv, int enable);
int (*pcm_new)(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct snd_soc_pcm_runtime *rtd);
@ -233,47 +268,54 @@ struct rsnd_mod {
struct rsnd_mod_ops *ops;
struct rsnd_priv *priv;
struct clk *clk;
u32 *(*get_status)(struct rsnd_dai_stream *io,
struct rsnd_mod *mod,
enum rsnd_mod_type type);
u32 status;
};
/*
* status
*
* 0xH0000CBA
* 0xH0000CB0
*
* A 0: probe 1: remove
* B 0: init 1: quit
* C 0: start 1: stop
*
* H is always called (see __rsnd_mod_call)
* H 0: probe 1: remove
* H 0: pcm_new
* H 0: fallback
* H 0: hw_params
*/
#define __rsnd_mod_shift_probe 0
#define __rsnd_mod_shift_remove 0
#define __rsnd_mod_shift_init 4
#define __rsnd_mod_shift_quit 4
#define __rsnd_mod_shift_start 8
#define __rsnd_mod_shift_stop 8
#define __rsnd_mod_shift_probe 28 /* always called */
#define __rsnd_mod_shift_remove 28 /* always called */
#define __rsnd_mod_shift_irq 28 /* always called */
#define __rsnd_mod_shift_pcm_new 28 /* always called */
#define __rsnd_mod_shift_fallback 28 /* always called */
#define __rsnd_mod_shift_hw_params 28 /* always called */
#define __rsnd_mod_add_probe 1
#define __rsnd_mod_add_remove -1
#define __rsnd_mod_add_probe 0
#define __rsnd_mod_add_remove 0
#define __rsnd_mod_add_init 1
#define __rsnd_mod_add_quit -1
#define __rsnd_mod_add_start 1
#define __rsnd_mod_add_stop -1
#define __rsnd_mod_add_irq 0
#define __rsnd_mod_add_pcm_new 0
#define __rsnd_mod_add_fallback 0
#define __rsnd_mod_add_hw_params 0
#define __rsnd_mod_call_probe 0
#define __rsnd_mod_call_remove 1
#define __rsnd_mod_call_remove 0
#define __rsnd_mod_call_init 0
#define __rsnd_mod_call_quit 1
#define __rsnd_mod_call_start 0
#define __rsnd_mod_call_stop 1
#define __rsnd_mod_call_irq 0
#define __rsnd_mod_call_pcm_new 0
#define __rsnd_mod_call_fallback 0
#define __rsnd_mod_call_hw_params 0
@ -286,10 +328,13 @@ struct rsnd_mod {
int rsnd_mod_init(struct rsnd_priv *priv,
struct rsnd_mod *mod,
struct rsnd_mod_ops *ops,
struct clk *clk,
enum rsnd_mod_type type,
int id);
struct rsnd_mod_ops *ops,
struct clk *clk,
u32* (*get_status)(struct rsnd_dai_stream *io,
struct rsnd_mod *mod,
enum rsnd_mod_type type),
enum rsnd_mod_type type,
int id);
void rsnd_mod_quit(struct rsnd_mod *mod);
char *rsnd_mod_name(struct rsnd_mod *mod);
struct dma_chan *rsnd_mod_dma_req(struct rsnd_dai_stream *io,
@ -297,6 +342,10 @@ struct dma_chan *rsnd_mod_dma_req(struct rsnd_dai_stream *io,
void rsnd_mod_interrupt(struct rsnd_mod *mod,
void (*callback)(struct rsnd_mod *mod,
struct rsnd_dai_stream *io));
u32 *rsnd_mod_get_status(struct rsnd_dai_stream *io,
struct rsnd_mod *mod,
enum rsnd_mod_type type);
void rsnd_parse_connect_common(struct rsnd_dai *rdai,
struct rsnd_mod* (*mod_get)(struct rsnd_priv *priv, int id),
struct device_node *node,
@ -306,9 +355,14 @@ void rsnd_parse_connect_common(struct rsnd_dai *rdai,
void rsnd_set_slot(struct rsnd_dai *rdai,
int slots, int slots_total);
int rsnd_get_slot(struct rsnd_dai_stream *io);
int rsnd_get_slot_width(struct rsnd_dai_stream *io);
int rsnd_get_slot_num(struct rsnd_dai_stream *io);
int rsnd_runtime_channel_original(struct rsnd_dai_stream *io);
int rsnd_runtime_channel_after_ctu(struct rsnd_dai_stream *io);
int rsnd_runtime_channel_for_ssi(struct rsnd_dai_stream *io);
int rsnd_runtime_is_ssi_multi(struct rsnd_dai_stream *io);
int rsnd_runtime_is_ssi_tdm(struct rsnd_dai_stream *io);
/*
* R-Car sound DAI
*/
@ -319,7 +373,7 @@ struct rsnd_dai_stream {
struct rsnd_mod *mod[RSND_MOD_MAX];
struct rsnd_dai_path_info *info; /* rcar_snd.h */
struct rsnd_dai *rdai;
u32 mod_status[RSND_MOD_MAX];
u32 parent_ssi_status;
int byte_pos;
int period_pos;
int byte_per_period;
@ -392,12 +446,10 @@ int rsnd_adg_ssi_clk_stop(struct rsnd_mod *mod);
int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *mod, unsigned int rate);
int rsnd_adg_probe(struct rsnd_priv *priv);
void rsnd_adg_remove(struct rsnd_priv *priv);
int rsnd_adg_set_convert_clk_gen2(struct rsnd_mod *mod,
int rsnd_adg_set_src_timesel_gen2(struct rsnd_mod *src_mod,
struct rsnd_dai_stream *io,
unsigned int src_rate,
unsigned int dst_rate);
int rsnd_adg_set_convert_timing_gen2(struct rsnd_mod *mod,
struct rsnd_dai_stream *io);
unsigned int in_rate,
unsigned int out_rate);
int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_mod *mod,
struct rsnd_dai_stream *io);
@ -498,10 +550,10 @@ struct rsnd_kctrl_cfg {
struct snd_kcontrol *kctrl;
};
#define RSND_DVC_CHANNELS 8
#define RSND_MAX_CHANNELS 8
struct rsnd_kctrl_cfg_m {
struct rsnd_kctrl_cfg cfg;
u32 val[RSND_DVC_CHANNELS];
u32 val[RSND_MAX_CHANNELS];
};
struct rsnd_kctrl_cfg_s {
@ -547,7 +599,7 @@ void rsnd_ssi_remove(struct rsnd_priv *priv);
struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id);
int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod);
int rsnd_ssi_use_busif(struct rsnd_dai_stream *io);
u32 rsnd_ssi_multi_slaves(struct rsnd_dai_stream *io);
u32 rsnd_ssi_multi_slaves_runtime(struct rsnd_dai_stream *io);
#define rsnd_ssi_is_pin_sharing(io) \
__rsnd_ssi_is_pin_sharing(rsnd_io_to_mod_ssi(io))
@ -573,9 +625,13 @@ void rsnd_ssiu_remove(struct rsnd_priv *priv);
int rsnd_src_probe(struct rsnd_priv *priv);
void rsnd_src_remove(struct rsnd_priv *priv);
struct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id);
unsigned int rsnd_src_get_ssi_rate(struct rsnd_priv *priv,
struct rsnd_dai_stream *io,
struct snd_pcm_runtime *runtime);
#define rsnd_src_get_in_rate(priv, io) rsnd_src_get_rate(priv, io, 1)
#define rsnd_src_get_out_rate(priv, io) rsnd_src_get_rate(priv, io, 0)
unsigned int rsnd_src_get_rate(struct rsnd_priv *priv,
struct rsnd_dai_stream *io,
int is_in);
#define rsnd_src_of_node(priv) \
of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,src")
#define rsnd_parse_connect_src(rdai, playback, capture) \
@ -588,6 +644,7 @@ unsigned int rsnd_src_get_ssi_rate(struct rsnd_priv *priv,
*/
int rsnd_ctu_probe(struct rsnd_priv *priv);
void rsnd_ctu_remove(struct rsnd_priv *priv);
int rsnd_ctu_converted_channel(struct rsnd_mod *mod);
struct rsnd_mod *rsnd_ctu_mod_get(struct rsnd_priv *priv, int id);
#define rsnd_ctu_of_node(priv) \
of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,ctu")

View File

@ -66,12 +66,12 @@ struct rsrc_card_priv {
struct snd_soc_dai_link *dai_link;
int dai_num;
u32 convert_rate;
u32 convert_channels;
};
#define rsrc_priv_to_dev(priv) ((priv)->snd_card.dev)
#define rsrc_priv_to_link(priv, i) ((priv)->snd_card.dai_link + (i))
#define rsrc_priv_to_props(priv, i) ((priv)->dai_props + (i))
#define rsrc_dev_to_of_data(dev) (of_match_device(rsrc_card_of_match, (dev))->data)
static int rsrc_card_startup(struct snd_pcm_substream *substream)
{
@ -145,11 +145,16 @@ static int rsrc_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
struct rsrc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card);
struct snd_interval *rate = hw_param_interval(params,
SNDRV_PCM_HW_PARAM_RATE);
struct snd_interval *channels = hw_param_interval(params,
SNDRV_PCM_HW_PARAM_CHANNELS);
if (!priv->convert_rate)
return 0;
if (priv->convert_rate)
rate->min =
rate->max = priv->convert_rate;
rate->min = rate->max = priv->convert_rate;
if (priv->convert_channels)
channels->min =
channels->max = priv->convert_channels;
return 0;
}
@ -246,7 +251,7 @@ static int rsrc_card_parse_links(struct device_node *np,
struct device *dev = rsrc_priv_to_dev(priv);
const struct rsrc_card_of_data *of_data;
of_data = rsrc_dev_to_of_data(dev);
of_data = of_device_get_match_data(dev);
/* FE is dummy */
dai_link->cpu_of_node = NULL;
@ -396,7 +401,7 @@ static int rsrc_card_parse_of(struct device_node *node,
struct rsrc_card_priv *priv,
struct device *dev)
{
const struct rsrc_card_of_data *of_data = rsrc_dev_to_of_data(dev);
const struct rsrc_card_of_data *of_data = of_device_get_match_data(dev);
struct rsrc_card_dai *props;
struct snd_soc_dai_link *links;
int ret;
@ -437,9 +442,13 @@ static int rsrc_card_parse_of(struct device_node *node,
/* sampling rate convert */
of_property_read_u32(node, "convert-rate", &priv->convert_rate);
dev_dbg(dev, "New rsrc-audio-card: %s (%d)\n",
priv->snd_card.name ? priv->snd_card.name : "",
priv->convert_rate);
/* channels transfer */
of_property_read_u32(node, "convert-channels", &priv->convert_channels);
dev_dbg(dev, "New rsrc-audio-card: %s\n",
priv->snd_card.name ? priv->snd_card.name : "");
dev_dbg(dev, "SRC : convert_rate %d\n", priv->convert_rate);
dev_dbg(dev, "CTU : convert_channels %d\n", priv->convert_channels);
ret = rsrc_card_dai_link_of(node, priv);
if (ret < 0)

View File

@ -25,7 +25,6 @@ struct rsnd_src {
struct rsnd_kctrl_cfg_s sen; /* sync convert enable */
struct rsnd_kctrl_cfg_s sync; /* sync convert */
u32 convert_rate; /* sampling rate convert */
int err;
int irq;
};
@ -34,7 +33,7 @@ struct rsnd_src {
#define rsnd_src_get(priv, id) ((struct rsnd_src *)(priv->src) + id)
#define rsnd_src_to_dma(src) ((src)->dma)
#define rsnd_src_nr(priv) ((priv)->src_nr)
#define rsnd_enable_sync_convert(src) ((src)->sen.val)
#define rsnd_src_sync_is_enabled(mod) (rsnd_mod_to_src(mod)->sen.val)
#define rsnd_mod_to_src(_mod) \
container_of((_mod), struct rsnd_src, mod)
@ -94,15 +93,16 @@ static struct dma_chan *rsnd_src_dma_req(struct rsnd_dai_stream *io,
}
static u32 rsnd_src_convert_rate(struct rsnd_dai_stream *io,
struct rsnd_src *src)
struct rsnd_mod *mod)
{
struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
struct rsnd_src *src = rsnd_mod_to_src(mod);
u32 convert_rate;
if (!runtime)
return 0;
if (!rsnd_enable_sync_convert(src))
if (!rsnd_src_sync_is_enabled(mod))
return src->convert_rate;
convert_rate = src->sync.val;
@ -116,23 +116,33 @@ static u32 rsnd_src_convert_rate(struct rsnd_dai_stream *io,
return convert_rate;
}
unsigned int rsnd_src_get_ssi_rate(struct rsnd_priv *priv,
struct rsnd_dai_stream *io,
struct snd_pcm_runtime *runtime)
unsigned int rsnd_src_get_rate(struct rsnd_priv *priv,
struct rsnd_dai_stream *io,
int is_in)
{
struct rsnd_mod *src_mod = rsnd_io_to_mod_src(io);
struct rsnd_src *src;
struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
unsigned int rate = 0;
int is_play = rsnd_io_is_play(io);
if (src_mod) {
src = rsnd_mod_to_src(src_mod);
/*
*
* Playback
* runtime_rate -> [SRC] -> convert_rate
*
* Capture
* convert_rate -> [SRC] -> runtime_rate
*/
/*
* return convert rate if SRC is used,
* otherwise, return runtime->rate as usual
*/
rate = rsnd_src_convert_rate(io, src);
}
if (is_play == is_in)
return runtime->rate;
/*
* return convert rate if SRC is used,
* otherwise, return runtime->rate as usual
*/
if (src_mod)
rate = rsnd_src_convert_rate(io, src_mod);
if (!rate)
rate = runtime->rate;
@ -179,8 +189,7 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io,
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
struct device *dev = rsnd_priv_to_dev(priv);
struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
struct rsnd_src *src = rsnd_mod_to_src(mod);
u32 convert_rate = rsnd_src_convert_rate(io, src);
u32 fin, fout;
u32 ifscr, fsrate, adinr;
u32 cr, route;
u32 bsdsr, bsisr;
@ -189,13 +198,16 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io,
if (!runtime)
return;
fin = rsnd_src_get_in_rate(priv, io);
fout = rsnd_src_get_out_rate(priv, io);
/* 6 - 1/6 are very enough ratio for SRC_BSDSR */
if (!convert_rate)
if (fin == fout)
ratio = 0;
else if (convert_rate > runtime->rate)
ratio = 100 * convert_rate / runtime->rate;
else if (fin > fout)
ratio = 100 * fin / fout;
else
ratio = 100 * runtime->rate / convert_rate;
ratio = 100 * fout / fin;
if (ratio > 600) {
dev_err(dev, "FSO/FSI ratio error\n");
@ -206,16 +218,16 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io,
* SRC_ADINR
*/
adinr = rsnd_get_adinr_bit(mod, io) |
rsnd_get_adinr_chan(mod, io);
rsnd_runtime_channel_original(io);
/*
* SRC_IFSCR / SRC_IFSVR
*/
ifscr = 0;
fsrate = 0;
if (convert_rate) {
if (fin != fout) {
ifscr = 1;
fsrate = 0x0400000 / convert_rate * runtime->rate;
fsrate = 0x0400000 / fout * fin;
}
/*
@ -223,10 +235,10 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io,
*/
cr = 0x00011110;
route = 0x0;
if (convert_rate) {
if (fin != fout) {
route = 0x1;
if (rsnd_enable_sync_convert(src)) {
if (rsnd_src_sync_is_enabled(mod)) {
cr |= 0x1;
route |= rsnd_io_is_play(io) ?
(0x1 << 24) : (0x1 << 25);
@ -250,6 +262,8 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io,
break;
}
rsnd_mod_write(mod, SRC_ROUTE_MODE0, route);
rsnd_mod_write(mod, SRC_SRCIR, 1); /* initialize */
rsnd_mod_write(mod, SRC_ADINR, adinr);
rsnd_mod_write(mod, SRC_IFSCR, ifscr);
@ -259,22 +273,17 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io,
rsnd_mod_write(mod, SRC_BSISR, bsisr);
rsnd_mod_write(mod, SRC_SRCIR, 0); /* cancel initialize */
rsnd_mod_write(mod, SRC_ROUTE_MODE0, route);
rsnd_mod_write(mod, SRC_I_BUSIF_MODE, 1);
rsnd_mod_write(mod, SRC_O_BUSIF_MODE, 1);
rsnd_mod_write(mod, SRC_BUSIF_DALIGN, rsnd_get_dalign(mod, io));
if (convert_rate)
rsnd_adg_set_convert_clk_gen2(mod, io,
runtime->rate,
convert_rate);
else
rsnd_adg_set_convert_timing_gen2(mod, io);
rsnd_adg_set_src_timesel_gen2(mod, io, fin, fout);
}
#define rsnd_src_irq_enable(mod) rsnd_src_irq_ctrol(mod, 1)
#define rsnd_src_irq_disable(mod) rsnd_src_irq_ctrol(mod, 0)
static void rsnd_src_irq_ctrol(struct rsnd_mod *mod, int enable)
static int rsnd_src_irq(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv,
int enable)
{
struct rsnd_src *src = rsnd_mod_to_src(mod);
u32 sys_int_val, int_val, sys_int_mask;
@ -298,14 +307,16 @@ static void rsnd_src_irq_ctrol(struct rsnd_mod *mod, int enable)
/*
* WORKAROUND
*
* ignore over flow error when rsnd_enable_sync_convert()
* ignore over flow error when rsnd_src_sync_is_enabled()
*/
if (rsnd_enable_sync_convert(src))
if (rsnd_src_sync_is_enabled(mod))
sys_int_val = sys_int_val & 0xffff;
rsnd_mod_write(mod, SRC_INT_ENABLE0, int_val);
rsnd_mod_bset(mod, SCU_SYS_INT_EN0, sys_int_mask, sys_int_val);
rsnd_mod_bset(mod, SCU_SYS_INT_EN1, sys_int_mask, sys_int_val);
return 0;
}
static void rsnd_src_status_clear(struct rsnd_mod *mod)
@ -316,9 +327,8 @@ static void rsnd_src_status_clear(struct rsnd_mod *mod)
rsnd_mod_bset(mod, SCU_SYS_STATUS1, val, val);
}
static bool rsnd_src_record_error(struct rsnd_mod *mod)
static bool rsnd_src_error_occurred(struct rsnd_mod *mod)
{
struct rsnd_src *src = rsnd_mod_to_src(mod);
u32 val0, val1;
bool ret = false;
@ -327,18 +337,14 @@ static bool rsnd_src_record_error(struct rsnd_mod *mod)
/*
* WORKAROUND
*
* ignore over flow error when rsnd_enable_sync_convert()
* ignore over flow error when rsnd_src_sync_is_enabled()
*/
if (rsnd_enable_sync_convert(src))
if (rsnd_src_sync_is_enabled(mod))
val0 = val0 & 0xffff;
if ((rsnd_mod_read(mod, SCU_SYS_STATUS0) & val0) ||
(rsnd_mod_read(mod, SCU_SYS_STATUS1) & val1)) {
struct rsnd_src *src = rsnd_mod_to_src(mod);
src->err++;
(rsnd_mod_read(mod, SCU_SYS_STATUS1) & val1))
ret = true;
}
return ret;
}
@ -347,7 +353,6 @@ static int rsnd_src_start(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
struct rsnd_src *src = rsnd_mod_to_src(mod);
u32 val;
/*
@ -355,7 +360,7 @@ static int rsnd_src_start(struct rsnd_mod *mod,
*
* Enable SRC output if you want to use sync convert together with DVC
*/
val = (rsnd_io_to_mod_dvc(io) && !rsnd_enable_sync_convert(src)) ?
val = (rsnd_io_to_mod_dvc(io) && !rsnd_src_sync_is_enabled(mod)) ?
0x01 : 0x11;
rsnd_mod_write(mod, SRC_CTRL, val);
@ -367,11 +372,7 @@ static int rsnd_src_stop(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
/*
* stop SRC output only
* see rsnd_src_quit
*/
rsnd_mod_write(mod, SRC_CTRL, 0x01);
rsnd_mod_write(mod, SRC_CTRL, 0);
return 0;
}
@ -390,10 +391,6 @@ static int rsnd_src_init(struct rsnd_mod *mod,
rsnd_src_status_clear(mod);
rsnd_src_irq_enable(mod);
src->err = 0;
/* reset sync convert_rate */
src->sync.val = 0;
@ -405,21 +402,11 @@ static int rsnd_src_quit(struct rsnd_mod *mod,
struct rsnd_priv *priv)
{
struct rsnd_src *src = rsnd_mod_to_src(mod);
struct device *dev = rsnd_priv_to_dev(priv);
rsnd_src_irq_disable(mod);
/* stop both out/in */
rsnd_mod_write(mod, SRC_CTRL, 0);
rsnd_src_halt(mod);
rsnd_mod_power_off(mod);
if (src->err)
dev_warn(dev, "%s[%d] under/over flow err = %d\n",
rsnd_mod_name(mod), rsnd_mod_id(mod), src->err);
src->convert_rate = 0;
/* reset sync convert_rate */
@ -432,8 +419,7 @@ static void __rsnd_src_interrupt(struct rsnd_mod *mod,
struct rsnd_dai_stream *io)
{
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
struct rsnd_src *src = rsnd_mod_to_src(mod);
struct device *dev = rsnd_priv_to_dev(priv);
bool stop = false;
spin_lock(&priv->lock);
@ -441,26 +427,16 @@ static void __rsnd_src_interrupt(struct rsnd_mod *mod,
if (!rsnd_io_is_working(io))
goto rsnd_src_interrupt_out;
if (rsnd_src_record_error(mod)) {
dev_dbg(dev, "%s[%d] restart\n",
rsnd_mod_name(mod), rsnd_mod_id(mod));
rsnd_src_stop(mod, io, priv);
rsnd_src_start(mod, io, priv);
}
if (src->err > 1024) {
rsnd_src_irq_disable(mod);
dev_warn(dev, "no more %s[%d] restart\n",
rsnd_mod_name(mod), rsnd_mod_id(mod));
}
if (rsnd_src_error_occurred(mod))
stop = true;
rsnd_src_status_clear(mod);
rsnd_src_interrupt_out:
spin_unlock(&priv->lock);
if (stop)
snd_pcm_stop_xrun(io->substream);
}
static irqreturn_t rsnd_src_interrupt(int irq, void *data)
@ -485,7 +461,7 @@ static int rsnd_src_probe_(struct rsnd_mod *mod,
/*
* IRQ is not supported on non-DT
* see
* rsnd_src_irq_enable()
* rsnd_src_irq()
*/
ret = devm_request_irq(dev, irq,
rsnd_src_interrupt,
@ -495,9 +471,7 @@ static int rsnd_src_probe_(struct rsnd_mod *mod,
return ret;
}
src->dma = rsnd_dma_attach(io, mod, 0);
if (IS_ERR(src->dma))
return PTR_ERR(src->dma);
ret = rsnd_dma_attach(io, mod, &src->dma, 0);
return ret;
}
@ -506,8 +480,6 @@ static int rsnd_src_pcm_new(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct snd_soc_pcm_runtime *rtd)
{
struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
struct rsnd_mod *dvc = rsnd_io_to_mod_dvc(io);
struct rsnd_src *src = rsnd_mod_to_src(mod);
int ret;
@ -516,15 +488,10 @@ static int rsnd_src_pcm_new(struct rsnd_mod *mod,
*/
/*
* SRC sync convert needs clock master
* It can't use SRC Synchronous convert
* when Capture if it uses CMD
*/
if (!rsnd_rdai_is_clk_master(rdai))
return 0;
/*
* SRC In doesn't work if DVC was enabled
*/
if (dvc && !rsnd_io_is_play(io))
if (rsnd_io_to_mod_cmd(io) && !rsnd_io_is_play(io))
return 0;
/*
@ -557,6 +524,7 @@ static struct rsnd_mod_ops rsnd_src_ops = {
.quit = rsnd_src_quit,
.start = rsnd_src_start,
.stop = rsnd_src_stop,
.irq = rsnd_src_irq,
.hw_params = rsnd_src_hw_params,
.pcm_new = rsnd_src_pcm_new,
};
@ -622,7 +590,8 @@ int rsnd_src_probe(struct rsnd_priv *priv)
}
ret = rsnd_mod_init(priv, rsnd_mod_get(src),
&rsnd_src_ops, clk, RSND_MOD_SRC, i);
&rsnd_src_ops, clk, rsnd_mod_get_status,
RSND_MOD_SRC, i);
if (ret)
goto rsnd_src_probe_done;

View File

@ -64,7 +64,6 @@
#define SSI_NAME "ssi"
struct rsnd_ssi {
struct rsnd_ssi *parent;
struct rsnd_mod mod;
struct rsnd_mod *dma;
@ -75,7 +74,6 @@ struct rsnd_ssi {
u32 wsr;
int chan;
int rate;
int err;
int irq;
unsigned int usrcnt;
};
@ -96,7 +94,10 @@ struct rsnd_ssi {
#define rsnd_mod_to_ssi(_mod) container_of((_mod), struct rsnd_ssi, mod)
#define rsnd_ssi_mode_flags(p) ((p)->flags)
#define rsnd_ssi_is_parent(ssi, io) ((ssi) == rsnd_io_to_mod_ssip(io))
#define rsnd_ssi_is_multi_slave(ssi, io) ((mod) != rsnd_io_to_mod_ssi(io))
#define rsnd_ssi_is_multi_slave(mod, io) \
(rsnd_ssi_multi_slaves(io) & (1 << rsnd_mod_id(mod)))
#define rsnd_ssi_is_run_mods(mod, io) \
(rsnd_ssi_run_mods(io) & (1 << rsnd_mod_id(mod)))
int rsnd_ssi_use_busif(struct rsnd_dai_stream *io)
{
@ -141,43 +142,13 @@ static void rsnd_ssi_status_check(struct rsnd_mod *mod,
udelay(50);
}
dev_warn(dev, "status check failed\n");
dev_warn(dev, "%s[%d] status check failed\n",
rsnd_mod_name(mod), rsnd_mod_id(mod));
}
static int rsnd_ssi_irq_enable(struct rsnd_mod *ssi_mod)
{
struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod);
if (rsnd_is_gen1(priv))
return 0;
/* enable SSI interrupt if Gen2 */
rsnd_mod_write(ssi_mod, SSI_INT_ENABLE,
rsnd_ssi_is_dma_mode(ssi_mod) ?
0x0e000000 : 0x0f000000);
return 0;
}
static int rsnd_ssi_irq_disable(struct rsnd_mod *ssi_mod)
{
struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod);
if (rsnd_is_gen1(priv))
return 0;
/* disable SSI interrupt if Gen2 */
rsnd_mod_write(ssi_mod, SSI_INT_ENABLE, 0x00000000);
return 0;
}
u32 rsnd_ssi_multi_slaves(struct rsnd_dai_stream *io)
static u32 rsnd_ssi_multi_slaves(struct rsnd_dai_stream *io)
{
struct rsnd_mod *mod;
struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
struct rsnd_priv *priv = rsnd_io_to_priv(io);
struct device *dev = rsnd_priv_to_dev(priv);
enum rsnd_mod_type types[] = {
RSND_MOD_SSIM1,
RSND_MOD_SSIM2,
@ -185,16 +156,6 @@ u32 rsnd_ssi_multi_slaves(struct rsnd_dai_stream *io)
};
int i, mask;
switch (runtime->channels) {
case 2: /* Multi channel is not needed for Stereo */
return 0;
case 6:
break;
default:
dev_err(dev, "unsupported channel\n");
return 0;
}
mask = 0;
for (i = 0; i < ARRAY_SIZE(types); i++) {
mod = rsnd_io_to_mod(io, types[i]);
@ -207,22 +168,41 @@ u32 rsnd_ssi_multi_slaves(struct rsnd_dai_stream *io)
return mask;
}
static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi,
static u32 rsnd_ssi_run_mods(struct rsnd_dai_stream *io)
{
struct rsnd_mod *ssi_mod = rsnd_io_to_mod_ssi(io);
struct rsnd_mod *ssi_parent_mod = rsnd_io_to_mod_ssip(io);
return rsnd_ssi_multi_slaves_runtime(io) |
1 << rsnd_mod_id(ssi_mod) |
1 << rsnd_mod_id(ssi_parent_mod);
}
u32 rsnd_ssi_multi_slaves_runtime(struct rsnd_dai_stream *io)
{
if (rsnd_runtime_is_ssi_multi(io))
return rsnd_ssi_multi_slaves(io);
return 0;
}
static int rsnd_ssi_master_clk_start(struct rsnd_mod *mod,
struct rsnd_dai_stream *io)
{
struct rsnd_priv *priv = rsnd_io_to_priv(io);
struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
struct device *dev = rsnd_priv_to_dev(priv);
struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
struct rsnd_mod *mod = rsnd_mod_get(ssi);
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
struct rsnd_mod *ssi_parent_mod = rsnd_io_to_mod_ssip(io);
int slots = rsnd_get_slot_width(io);
int chan = rsnd_runtime_channel_for_ssi(io);
int j, ret;
int ssi_clk_mul_table[] = {
1, 2, 4, 8, 16, 6, 12,
};
unsigned int main_rate;
unsigned int rate = rsnd_src_get_ssi_rate(priv, io, runtime);
unsigned int rate = rsnd_io_is_play(io) ?
rsnd_src_get_out_rate(priv, io) :
rsnd_src_get_in_rate(priv, io);
if (!rsnd_rdai_is_clk_master(rdai))
return 0;
@ -249,10 +229,10 @@ static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi,
/*
* this driver is assuming that
* system word is 32bit x slots
* system word is 32bit x chan
* see rsnd_ssi_init()
*/
main_rate = rate * 32 * slots * ssi_clk_mul_table[j];
main_rate = rate * 32 * chan * ssi_clk_mul_table[j];
ret = rsnd_adg_ssi_clk_try_start(mod, main_rate);
if (0 == ret) {
@ -274,11 +254,11 @@ static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi,
return -EIO;
}
static void rsnd_ssi_master_clk_stop(struct rsnd_ssi *ssi,
static void rsnd_ssi_master_clk_stop(struct rsnd_mod *mod,
struct rsnd_dai_stream *io)
{
struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
struct rsnd_mod *mod = rsnd_mod_get(ssi);
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
struct rsnd_mod *ssi_parent_mod = rsnd_io_to_mod_ssip(io);
if (!rsnd_rdai_is_clk_master(rdai))
@ -296,17 +276,18 @@ static void rsnd_ssi_master_clk_stop(struct rsnd_ssi *ssi,
rsnd_adg_ssi_clk_stop(mod);
}
static int rsnd_ssi_config_init(struct rsnd_ssi *ssi,
static void rsnd_ssi_config_init(struct rsnd_mod *mod,
struct rsnd_dai_stream *io)
{
struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
u32 cr_own;
u32 cr_mode;
u32 wsr;
int is_tdm;
is_tdm = (rsnd_get_slot_width(io) >= 6) ? 1 : 0;
is_tdm = rsnd_runtime_is_ssi_tdm(io);
/*
* always use 32bit system word.
@ -332,11 +313,9 @@ static int rsnd_ssi_config_init(struct rsnd_ssi *ssi,
case 32:
cr_own |= DWL_24;
break;
default:
return -EINVAL;
}
if (rsnd_ssi_is_dma_mode(rsnd_mod_get(ssi))) {
if (rsnd_ssi_is_dma_mode(mod)) {
cr_mode = UIEN | OIEN | /* over/under run */
DMEN; /* DMA : enable DMA */
} else {
@ -357,8 +336,16 @@ static int rsnd_ssi_config_init(struct rsnd_ssi *ssi,
ssi->cr_own = cr_own;
ssi->cr_mode = cr_mode;
ssi->wsr = wsr;
}
return 0;
static void rsnd_ssi_register_setup(struct rsnd_mod *mod)
{
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
rsnd_mod_write(mod, SSIWSR, ssi->wsr);
rsnd_mod_write(mod, SSICR, ssi->cr_own |
ssi->cr_clk |
ssi->cr_mode); /* without EN */
}
/*
@ -371,28 +358,25 @@ static int rsnd_ssi_init(struct rsnd_mod *mod,
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
int ret;
if (!rsnd_ssi_is_run_mods(mod, io))
return 0;
ssi->usrcnt++;
rsnd_mod_power_on(mod);
ret = rsnd_ssi_master_clk_start(ssi, io);
ret = rsnd_ssi_master_clk_start(mod, io);
if (ret < 0)
return ret;
if (rsnd_ssi_is_parent(mod, io))
return 0;
if (!rsnd_ssi_is_parent(mod, io))
rsnd_ssi_config_init(mod, io);
ret = rsnd_ssi_config_init(ssi, io);
if (ret < 0)
return ret;
ssi->err = -1; /* ignore 1st error */
rsnd_ssi_register_setup(mod);
/* clear error status */
rsnd_ssi_status_clear(mod);
rsnd_ssi_irq_enable(mod);
return 0;
}
@ -403,25 +387,19 @@ static int rsnd_ssi_quit(struct rsnd_mod *mod,
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
struct device *dev = rsnd_priv_to_dev(priv);
if (!rsnd_ssi_is_run_mods(mod, io))
return 0;
if (!ssi->usrcnt) {
dev_err(dev, "%s[%d] usrcnt error\n",
rsnd_mod_name(mod), rsnd_mod_id(mod));
return -EIO;
}
if (!rsnd_ssi_is_parent(mod, io)) {
if (ssi->err > 0)
dev_warn(dev, "%s[%d] under/over flow err = %d\n",
rsnd_mod_name(mod), rsnd_mod_id(mod),
ssi->err);
if (!rsnd_ssi_is_parent(mod, io))
ssi->cr_own = 0;
ssi->err = 0;
rsnd_ssi_irq_disable(mod);
}
rsnd_ssi_master_clk_stop(ssi, io);
rsnd_ssi_master_clk_stop(mod, io);
rsnd_mod_power_off(mod);
@ -456,62 +434,44 @@ static int rsnd_ssi_hw_params(struct rsnd_mod *mod,
return 0;
}
static u32 rsnd_ssi_record_error(struct rsnd_ssi *ssi)
static int rsnd_ssi_start(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
struct rsnd_mod *mod = rsnd_mod_get(ssi);
u32 status = rsnd_ssi_status_get(mod);
/* under/over flow error */
if (status & (UIRQ | OIRQ))
ssi->err++;
return status;
}
static int __rsnd_ssi_start(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
u32 cr;
cr = ssi->cr_own |
ssi->cr_clk |
ssi->cr_mode;
if (!rsnd_ssi_is_run_mods(mod, io))
return 0;
/*
* EN will be set via SSIU :: SSI_CONTROL
* if Multi channel mode
*/
if (!rsnd_ssi_multi_slaves(io))
cr |= EN;
if (rsnd_ssi_multi_slaves_runtime(io))
return 0;
rsnd_mod_write(mod, SSICR, cr);
rsnd_mod_write(mod, SSIWSR, ssi->wsr);
rsnd_mod_bset(mod, SSICR, EN, EN);
return 0;
}
static int rsnd_ssi_start(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
/*
* no limit to start
* see also
* rsnd_ssi_stop
* rsnd_ssi_interrupt
*/
return __rsnd_ssi_start(mod, io, priv);
}
static int __rsnd_ssi_stop(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
static int rsnd_ssi_stop(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
u32 cr;
if (!rsnd_ssi_is_run_mods(mod, io))
return 0;
/*
* don't stop if not last user
* see also
* rsnd_ssi_start
* rsnd_ssi_interrupt
*/
if (ssi->usrcnt > 1)
return 0;
/*
* disable all IRQ,
* and, wait all data was sent
@ -532,33 +492,38 @@ static int __rsnd_ssi_stop(struct rsnd_mod *mod,
return 0;
}
static int rsnd_ssi_stop(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
static int rsnd_ssi_irq(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv,
int enable)
{
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
u32 val = 0;
/*
* don't stop if not last user
* see also
* rsnd_ssi_start
* rsnd_ssi_interrupt
*/
if (ssi->usrcnt > 1)
if (rsnd_is_gen1(priv))
return 0;
return __rsnd_ssi_stop(mod, io, priv);
if (rsnd_ssi_is_parent(mod, io))
return 0;
if (!rsnd_ssi_is_run_mods(mod, io))
return 0;
if (enable)
val = rsnd_ssi_is_dma_mode(mod) ? 0x0e000000 : 0x0f000000;
rsnd_mod_write(mod, SSI_INT_ENABLE, val);
return 0;
}
static void __rsnd_ssi_interrupt(struct rsnd_mod *mod,
struct rsnd_dai_stream *io)
{
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
struct device *dev = rsnd_priv_to_dev(priv);
int is_dma = rsnd_ssi_is_dma_mode(mod);
u32 status;
bool elapsed = false;
bool stop = false;
spin_lock(&priv->lock);
@ -566,7 +531,7 @@ static void __rsnd_ssi_interrupt(struct rsnd_mod *mod,
if (!rsnd_io_is_working(io))
goto rsnd_ssi_interrupt_out;
status = rsnd_ssi_record_error(ssi);
status = rsnd_ssi_status_get(mod);
/* PIO only */
if (!is_dma && (status & DIRQ)) {
@ -588,23 +553,8 @@ static void __rsnd_ssi_interrupt(struct rsnd_mod *mod,
}
/* DMA only */
if (is_dma && (status & (UIRQ | OIRQ))) {
/*
* restart SSI
*/
dev_dbg(dev, "%s[%d] restart\n",
rsnd_mod_name(mod), rsnd_mod_id(mod));
__rsnd_ssi_stop(mod, io, priv);
__rsnd_ssi_start(mod, io, priv);
}
if (ssi->err > 1024) {
rsnd_ssi_irq_disable(mod);
dev_warn(dev, "no more %s[%d] restart\n",
rsnd_mod_name(mod), rsnd_mod_id(mod));
}
if (is_dma && (status & (UIRQ | OIRQ)))
stop = true;
rsnd_ssi_status_clear(mod);
rsnd_ssi_interrupt_out:
@ -612,6 +562,10 @@ rsnd_ssi_interrupt_out:
if (elapsed)
rsnd_dai_period_elapsed(io);
if (stop)
snd_pcm_stop_xrun(io->substream);
}
static irqreturn_t rsnd_ssi_interrupt(int irq, void *data)
@ -627,12 +581,17 @@ static irqreturn_t rsnd_ssi_interrupt(int irq, void *data)
* SSI PIO
*/
static void rsnd_ssi_parent_attach(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
struct rsnd_dai_stream *io)
{
struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
if (!__rsnd_ssi_is_pin_sharing(mod))
return;
if (!rsnd_rdai_is_clk_master(rdai))
return;
switch (rsnd_mod_id(mod)) {
case 1:
case 2:
@ -647,6 +606,20 @@ static void rsnd_ssi_parent_attach(struct rsnd_mod *mod,
}
}
static int rsnd_ssi_pcm_new(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct snd_soc_pcm_runtime *rtd)
{
/*
* rsnd_rdai_is_clk_master() will be enabled after set_fmt,
* and, pcm_new will be called after it.
* This function reuse pcm_new at this point.
*/
rsnd_ssi_parent_attach(mod, io);
return 0;
}
static int rsnd_ssi_common_probe(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
@ -662,7 +635,10 @@ static int rsnd_ssi_common_probe(struct rsnd_mod *mod,
if (rsnd_ssi_is_multi_slave(mod, io))
return 0;
rsnd_ssi_parent_attach(mod, io, priv);
/*
* It can't judge ssi parent at this point
* see rsnd_ssi_pcm_new()
*/
ret = rsnd_ssiu_attach(io, mod);
if (ret < 0)
@ -683,6 +659,8 @@ static struct rsnd_mod_ops rsnd_ssi_pio_ops = {
.quit = rsnd_ssi_quit,
.start = rsnd_ssi_start,
.stop = rsnd_ssi_stop,
.irq = rsnd_ssi_irq,
.pcm_new = rsnd_ssi_pcm_new,
.hw_params = rsnd_ssi_hw_params,
};
@ -705,9 +683,8 @@ static int rsnd_ssi_dma_probe(struct rsnd_mod *mod,
if (ret)
return ret;
ssi->dma = rsnd_dma_attach(io, mod, dma_id);
if (IS_ERR(ssi->dma))
return PTR_ERR(ssi->dma);
/* SSI probe might be called many times in MUX multi path */
ret = rsnd_dma_attach(io, mod, &ssi->dma, dma_id);
return ret;
}
@ -772,6 +749,8 @@ static struct rsnd_mod_ops rsnd_ssi_dma_ops = {
.quit = rsnd_ssi_quit,
.start = rsnd_ssi_start,
.stop = rsnd_ssi_stop,
.irq = rsnd_ssi_irq,
.pcm_new = rsnd_ssi_pcm_new,
.fallback = rsnd_ssi_fallback,
.hw_params = rsnd_ssi_hw_params,
};
@ -858,6 +837,41 @@ int __rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod)
return !!(rsnd_ssi_mode_flags(ssi) & RSND_SSI_CLK_PIN_SHARE);
}
static u32 *rsnd_ssi_get_status(struct rsnd_dai_stream *io,
struct rsnd_mod *mod,
enum rsnd_mod_type type)
{
/*
* SSIP (= SSI parent) needs to be special, otherwise,
* 2nd SSI might doesn't start. see also rsnd_mod_call()
*
* We can't include parent SSI status on SSI, because we don't know
* how many SSI requests parent SSI. Thus, it is localed on "io" now.
* ex) trouble case
* Playback: SSI0
* Capture : SSI1 (needs SSI0)
*
* 1) start Capture -> SSI0/SSI1 are started.
* 2) start Playback -> SSI0 doesn't work, because it is already
* marked as "started" on 1)
*
* OTOH, using each mod's status is good for MUX case.
* It doesn't need to start in 2nd start
* ex)
* IO-0: SRC0 -> CTU1 -+-> MUX -> DVC -> SSIU -> SSI0
* |
* IO-1: SRC1 -> CTU2 -+
*
* 1) start IO-0 -> start SSI0
* 2) start IO-1 -> SSI0 doesn't need to start, because it is
* already started on 1)
*/
if (type == RSND_MOD_SSIP)
return &io->parent_ssi_status;
return rsnd_mod_get_status(io, mod, type);
}
int rsnd_ssi_probe(struct rsnd_priv *priv)
{
struct device_node *node;
@ -920,7 +934,7 @@ int rsnd_ssi_probe(struct rsnd_priv *priv)
ops = &rsnd_ssi_dma_ops;
ret = rsnd_mod_init(priv, rsnd_mod_get(ssi), ops, clk,
RSND_MOD_SSI, i);
rsnd_ssi_get_status, RSND_MOD_SSI, i);
if (ret)
goto rsnd_ssi_probe_done;

View File

@ -27,7 +27,7 @@ static int rsnd_ssiu_init(struct rsnd_mod *mod,
struct rsnd_priv *priv)
{
struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
u32 multi_ssi_slaves = rsnd_ssi_multi_slaves(io);
u32 multi_ssi_slaves = rsnd_ssi_multi_slaves_runtime(io);
int use_busif = rsnd_ssi_use_busif(io);
int id = rsnd_mod_id(mod);
u32 mask1, val1;
@ -105,7 +105,7 @@ static int rsnd_ssiu_init_gen2(struct rsnd_mod *mod,
if (ret < 0)
return ret;
if (rsnd_get_slot_width(io) >= 6) {
if (rsnd_runtime_is_ssi_tdm(io)) {
/*
* TDM Extend Mode
* see
@ -115,13 +115,14 @@ static int rsnd_ssiu_init_gen2(struct rsnd_mod *mod,
}
if (rsnd_ssi_use_busif(io)) {
u32 val = rsnd_get_dalign(mod, io);
rsnd_mod_write(mod, SSI_BUSIF_ADINR,
rsnd_get_adinr_bit(mod, io) |
rsnd_get_adinr_chan(mod, io));
(rsnd_io_is_play(io) ?
rsnd_runtime_channel_after_ctu(io) :
rsnd_runtime_channel_original(io)));
rsnd_mod_write(mod, SSI_BUSIF_MODE, 1);
rsnd_mod_write(mod, SSI_BUSIF_DALIGN, val);
rsnd_mod_write(mod, SSI_BUSIF_DALIGN,
rsnd_get_dalign(mod, io));
}
return 0;
@ -136,7 +137,7 @@ static int rsnd_ssiu_start_gen2(struct rsnd_mod *mod,
rsnd_mod_write(mod, SSI_CTRL, 0x1);
if (rsnd_ssi_multi_slaves(io))
if (rsnd_ssi_multi_slaves_runtime(io))
rsnd_mod_write(mod, SSI_CONTROL, 0x1);
return 0;
@ -151,7 +152,7 @@ static int rsnd_ssiu_stop_gen2(struct rsnd_mod *mod,
rsnd_mod_write(mod, SSI_CTRL, 0);
if (rsnd_ssi_multi_slaves(io))
if (rsnd_ssi_multi_slaves_runtime(io))
rsnd_mod_write(mod, SSI_CONTROL, 0);
return 0;
@ -206,7 +207,8 @@ int rsnd_ssiu_probe(struct rsnd_priv *priv)
for_each_rsnd_ssiu(ssiu, priv, i) {
ret = rsnd_mod_init(priv, rsnd_mod_get(ssiu),
ops, NULL, RSND_MOD_SSIU, i);
ops, NULL, rsnd_mod_get_status,
RSND_MOD_SSIU, i);
if (ret)
return ret;
}