mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-23 01:10:04 +00:00
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:
commit
4bfd5ba31f
30 changed files with 2967 additions and 920 deletions
|
@ -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>;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
|
25
Documentation/devicetree/bindings/sound/rt5514.txt
Normal file
25
Documentation/devicetree/bindings/sound/rt5514.txt
Normal 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>;
|
||||
};
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
return regmap_update_bits_base(field->regmap,
|
||||
field->reg + (field->id_offset * id),
|
||||
mask, val << field->shift);
|
||||
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;
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
WARN_ONCE(1, "regmap API is disabled");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static inline int regmap_update_bits_check(struct regmap *map,
|
||||
unsigned int reg,
|
||||
static inline int regmap_field_update_bits_base(struct regmap_field *field,
|
||||
unsigned int mask, unsigned int val,
|
||||
bool *change)
|
||||
bool *change, bool async, bool force)
|
||||
{
|
||||
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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
982
sound/soc/codecs/rt5514.c
Normal 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
252
sound/soc/codecs/rt5514.h
Normal 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__ */
|
|
@ -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,10 +165,9 @@ 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) {
|
||||
case RT5616_RESET:
|
||||
|
@ -190,10 +192,9 @@ 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) {
|
||||
case RT5616_RESET:
|
||||
|
@ -308,6 +309,8 @@ 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),
|
||||
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),
|
||||
/* OUTPUT Control */
|
||||
|
@ -533,9 +536,10 @@ static int rt5616_charge_pump_event(struct snd_soc_dapm_widget *w,
|
|||
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_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);
|
||||
|
@ -561,9 +565,9 @@ static int rt5616_hp_event(struct snd_soc_dapm_widget *w,
|
|||
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_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);
|
||||
snd_soc_update_bits(codec, RT5616_DEPOP_M1,
|
||||
|
@ -591,9 +595,9 @@ static int rt5616_hp_event(struct snd_soc_dapm_widget *w,
|
|||
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_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);
|
||||
snd_soc_update_bits(codec, RT5616_DEPOP_M1,
|
||||
|
@ -746,9 +750,11 @@ static const struct snd_soc_dapm_widget rt5616_dapm_widgets[] = {
|
|||
SND_SOC_DAPM_SUPPLY("stereo1 filter", RT5616_PWR_DIG2,
|
||||
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,
|
||||
|
@ -779,9 +785,11 @@ static const struct snd_soc_dapm_widget rt5616_dapm_widgets[] = {
|
|||
|
||||
/* 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,
|
||||
|
@ -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;
|
||||
|
@ -1001,7 +1010,6 @@ static int rt5616_hw_params(struct snd_pcm_substream *substream,
|
|||
RT5616_I2S_DL_MASK, val_len);
|
||||
snd_soc_update_bits(codec, RT5616_ADDA_CLK1, mask_clk, val_clk);
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1053,7 +1061,6 @@ static int rt5616_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
|
|||
RT5616_I2S_MS_MASK | RT5616_I2S_BP_MASK |
|
||||
RT5616_I2S_DF_MASK, reg_val);
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1078,6 +1085,7 @@ 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->sysclk = freq;
|
||||
|
@ -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);
|
||||
|
@ -1138,7 +1149,8 @@ static int rt5616_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
|
|||
snd_soc_write(codec, RT5616_PLL_CTRL1,
|
||||
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 ? 0 : pll_code.m_code) <<
|
||||
RT5616_PLL_M_SFT |
|
||||
pll_code.m_bp << RT5616_PLL_M_BP_SFT);
|
||||
|
||||
rt5616->pll_in = freq_in;
|
||||
|
@ -1151,7 +1163,34 @@ 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)
|
||||
{
|
||||
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,
|
||||
|
@ -1164,7 +1203,8 @@ static int rt5616_set_bias_level(struct snd_soc_codec *codec,
|
|||
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,
|
||||
|
@ -1304,7 +1348,7 @@ static int rt5616_i2c_probe(struct i2c_client *i2c,
|
|||
|
||||
rt5616 = devm_kzalloc(&i2c->dev, sizeof(struct rt5616_priv),
|
||||
GFP_KERNEL);
|
||||
if (rt5616 == NULL)
|
||||
if (!rt5616)
|
||||
return -ENOMEM;
|
||||
|
||||
i2c_set_clientdata(i2c, rt5616);
|
||||
|
@ -1345,7 +1389,6 @@ static int rt5616_i2c_probe(struct i2c_client *i2c,
|
|||
|
||||
return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5616,
|
||||
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 = {
|
||||
|
|
|
@ -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", },
|
||||
{},
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
int rsnd_adg_set_src_timesel_gen2(struct rsnd_mod *src_mod,
|
||||
struct rsnd_dai_stream *io,
|
||||
u32 timsel)
|
||||
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;
|
||||
}
|
||||
|
||||
if (en)
|
||||
rsnd_mod_bset(adg_mod, DIV_EN, en, en);
|
||||
|
||||
dev_dbg(dev, "convert rate %d <-> %d\n", src_rate, dst_rate);
|
||||
|
||||
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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -138,10 +138,20 @@ 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,
|
||||
u32* (*get_status)(struct rsnd_dai_stream *io,
|
||||
struct rsnd_mod *mod,
|
||||
enum rsnd_mod_type type),
|
||||
enum rsnd_mod_type type,
|
||||
int id)
|
||||
{
|
||||
|
@ -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,12 +351,15 @@ 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; \
|
||||
if (add == 0xF) \
|
||||
call = 0; \
|
||||
else \
|
||||
*status = (*status & ~mask) + \
|
||||
(add << __rsnd_mod_shift_##func); \
|
||||
dev_dbg(dev, "%s[%d]\t0x%08x %s\n", \
|
||||
|
@ -337,18 +367,57 @@ u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io)
|
|||
*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);
|
||||
|
||||
/*
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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 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(*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);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = rsnd_dai_connect(dma_mod, io, type);
|
||||
ret = rsnd_dai_connect(*dma_mod, io, type);
|
||||
if (ret < 0)
|
||||
return ERR_PTR(ret);
|
||||
return ret;
|
||||
|
||||
return rsnd_mod_get(dma);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rsnd_dma_probe(struct rsnd_priv *priv)
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
@ -288,6 +330,9 @@ int rsnd_mod_init(struct rsnd_priv *priv,
|
|||
struct rsnd_mod *mod,
|
||||
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);
|
||||
|
@ -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,
|
||||
|
||||
#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,
|
||||
struct snd_pcm_runtime *runtime);
|
||||
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")
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
unsigned int rsnd_src_get_rate(struct rsnd_priv *priv,
|
||||
struct rsnd_dai_stream *io,
|
||||
struct snd_pcm_runtime *runtime)
|
||||
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
|
||||
*/
|
||||
|
||||
if (is_play == is_in)
|
||||
return runtime->rate;
|
||||
|
||||
/*
|
||||
* return convert rate if SRC is used,
|
||||
* otherwise, return runtime->rate as usual
|
||||
*/
|
||||
rate = rsnd_src_convert_rate(io, src);
|
||||
}
|
||||
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;
|
||||
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
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,
|
||||
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,
|
||||
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,
|
||||
static int rsnd_ssi_irq(struct rsnd_mod *mod,
|
||||
struct rsnd_dai_stream *io,
|
||||
struct rsnd_priv *priv)
|
||||
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 @@ static void __rsnd_ssi_interrupt(struct rsnd_mod *mod,
|
|||
|
||||
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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue